libzypp 17.37.1
MediaNetworkCommonHandler.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12
15
16#include <zypp/ZConfig.h>
19#include <zypp/base/Logger.h>
20#include <zypp/base/Gettext.h>
21#include <zypp/Target.h>
23#include <zypp-media/auth/CredentialManager>
24#include <zypp-curl/auth/CurlAuthData>
26
27#include <zypp/ZYppCallbacks.h>
28
29#include <fstream>
30#include <curl/curl.h>
31
32using std::endl;
33
34namespace zypp::media
35{
36 MediaNetworkCommonHandler::MediaNetworkCommonHandler(const MediaUrl &url_r, const std::vector<MediaUrl> &mirrors_r, const Pathname &attach_point_r, const Pathname &urlpath_below_attachpoint_r, const bool does_download_r)
37 : MediaHandler( url_r, mirrors_r, attach_point_r, urlpath_below_attachpoint_r, does_download_r )
38 , _redirTargets( zyppng::transform ( _urls, []( const MediaUrl &url_r ) { return findGeoIPRedirect(url_r.url()); } ) )
39 {
40 }
41
43 {
44 if ( next )
46
49 }
50
51 disconnectFrom(); // clean state if needed
52
53 // here : setup TransferSettings
55
56 // FIXME: need a derived class to propelly compare url's
57 MediaSourceRef media( new MediaSource(_url.url().getScheme(), _url.url().asString()));
59 }
60
62 {
63 return MediaHandler::checkAttachPoint( apoint, true, true);
64 }
65
67 {
68 return ::internal::clearQueryString(url);
69 }
70
72 {
73 try {
74 const auto &conf = ZConfig::instance();
75 if ( !conf.geoipEnabled() ) {
76 MIL << "GeoIp rewrites disabled via ZConfig." << std::endl;
77 return Url();
78 }
79
80 if ( !( url.getQueryParam("COUNTRY").empty() && url.getQueryParam("AVOID_COUNTRY").empty() )) {
81 MIL << "GeoIp rewrites disabled since the baseurl " << url << " uses an explicit country setting." << std::endl;
82 return Url();
83 }
84
85 const auto &hostname = url.getHost();
86 auto geoipFile = conf.geoipCachePath() / hostname ;
87 if ( PathInfo( geoipFile ).isFile() ) {
88
89 MIL << "Found GeoIP file for host: " << hostname << std::endl;
90
91 std::ifstream in( geoipFile.asString() );
92 if (!in.is_open()) {
93 MIL << "Failed to open GeoIP for host: " << hostname << std::endl;
94 return Url();
95 }
96
97 try {
98 std::string newHost;
99 in >> newHost;
100
101 Url newUrl = url;
102 newUrl.setHost( newHost );
103
104 MIL << "Found GeoIP rewrite: " << hostname << " -> " << newHost << std::endl;
105
106 return newUrl;
107
108 } catch ( const zypp::Exception &e ) {
109 ZYPP_CAUGHT(e);
110 MIL << "No valid GeoIP rewrite target found for " << url << std::endl;
111 }
112 }
113 } catch ( const zypp::Exception &e ) {
114 ZYPP_CAUGHT(e);
115 MIL << "Failed to query GeoIP data, url rewriting disabled." << std::endl;
116 }
117
118 // no rewrite
119 return Url();
120 }
121
123
125 {
126 // Use absolute file name to prevent access of files outside of the
127 // hierarchy below the attach point.
128 getFileCopy( file, localPath(file.filename()).absolutename() );
129 }
130
131 void MediaNetworkCommonHandler::getDir( const Pathname & dirname, bool recurse_r ) const
132 {
134 getDirInfo( content, dirname, /*dots*/false );
135
136 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
137 Pathname filename = dirname + it->name;
138 int res = 0;
139
140 switch ( it->type ) {
141 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
143 getFile( OnMediaLocation( filename ) );
144 break;
145 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
146 if ( recurse_r ) {
147 getDir( filename, recurse_r );
148 } else {
149 res = assert_dir( localPath( filename ) );
150 if ( res ) {
151 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
152 }
153 }
154 break;
155 default:
156 // don't provide devices, sockets, etc.
157 break;
158 }
159 }
160 }
161
162 void MediaNetworkCommonHandler::getDirInfo( std::list<std::string> & retlist,
163 const Pathname & dirname, bool dots ) const
164 {
165 getDirectoryYast( retlist, dirname, dots );
166 }
167
169 const Pathname & dirname, bool dots ) const
170 {
171 getDirectoryYast( retlist, dirname, dots );
172 }
173
175 {
176 // we need to add the release and identifier to the
177 // agent string.
178 // The target could be not initialized, and then this information
179 // is guessed.
180 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
181 static const std::string _value( str::trim( str::form(
182 "X-ZYpp-AnonymousId: %s",
183 Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str()
184 )));
185 return _value.c_str();
186 }
187
189 {
190 // we need to add the release and identifier to the
191 // agent string.
192 // The target could be not initialized, and then this information
193 // is guessed.
194 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
195 static const std::string _value( str::trim( str::form(
196 "X-ZYpp-DistributionFlavor: %s",
197 Target::distributionFlavor( Pathname()/*guess root*/ ).c_str()
198 )));
199 return _value.c_str();
200 }
201
202 Url MediaNetworkCommonHandler::getFileUrl( int mirrorIdx, const Pathname & filename_r ) const
203 {
204 static const zypp::str::regex invalidRewrites("^.*\\/repomd.xml(.asc|.key)?$|^\\/geoip$");
205
206 if ( mirrorIdx < 0 || mirrorIdx > _redirTargets.size() )
207 return {};
208
209 const bool canRedir = _redirTargets[mirrorIdx].isValid() && !invalidRewrites.matches(filename_r.asString());
210 const auto &baseUrl = ( canRedir ) ? _redirTargets[mirrorIdx] : _urls[mirrorIdx].url();
211
212 if ( canRedir )
213 MIL << "Redirecting " << filename_r << " request to geoip location." << std::endl;
214
215 // Simply extend the URLs pathname:
216 Url newurl { baseUrl };
217 newurl.appendPathName( filename_r );
218 return newurl;
219 }
220
222 // we need to add the release and identifier to the
223 // agent string.
224 // The target could be not initialized, and then this information
225 // is guessed.
226 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
227 static const std::string _value(str::trim(str::form(
228 "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s",
229 curl_version_info(CURLVERSION_NOW)->version,
230 Target::targetDistribution(Pathname() /*guess root*/).c_str())));
231 return _value.c_str();
232 }
233
235 {
236 // fill some settings from url query parameters
237 try
238 {
240 _mirrorSettings = zyppng::transform( _urls, [&]( const MediaUrl &u ) {
242
243 if ( !u.url().isValid() )
245
246 checkProtocol(u.url());
247
249
250 // apply MediaUrl settings
251 if ( u.hasConfig ("http-headers") ) {
252 // Set up the handler
253 for ( const auto & el : u.getConfig<UrlResolverPlugin::HeaderList>("http-headers") ) {
254 std::string header { el.first };
255 header += ": ";
256 header += el.second;
257 MIL << "Added custom header -> " << header << std::endl;
258 set.addHeader( std::move(header) );
259 }
260 }
261
262 // add custom headers for download.opensuse.org (bsc#955801)
263 if ( u.url().getHost() == "download.opensuse.org" )
264 {
267 }
268 set.addHeader("Pragma:");
269
270 // apply legacy Url encoded settings
271 ::internal::fillSettingsFromUrl( u.url(), set);
272
273 // if the proxy was not set (or explicitly unset) by url, then look...
274 if ( set.proxy().empty() )
275 {
276 // ...at the system proxy settings
278 }
279
280 /* Fixes bsc#1174011 "auth=basic ignored in some cases"
281 * We should proactively add the password to the request if basic auth is configured
282 * and a password is available in the credentials but not in the URL.
283 *
284 * We will be a bit paranoid here and require that the URL has a user embedded, otherwise we go the default route
285 * and ask the server first about the auth method
286 */
287 if ( set.authType() == "basic"
288 && set.username().size()
289 && !set.password().size() ) {
290 const auto cred = cm.getCred( u.url() );
291 if ( cred && cred->valid() ) {
292 if ( !set.username().size() )
293 set.setUsername(cred->username());
294 set.setPassword(cred->password());
295 }
296 }
297
298 return set;
299 });
300 }
301 catch ( const MediaException &e )
302 {
304 ZYPP_RETHROW(e);
305 }
306 }
307
308
309 bool MediaNetworkCommonHandler::authenticate(const Url &url, TransferSettings &settings, const std::string & availAuthTypes, bool firstTry)
310 {
313 return authenticate( url, cm, settings, availAuthTypes, firstTry );
314 }
315
316 bool MediaNetworkCommonHandler::authenticate( const Url &url, CredentialManager &cm, TransferSettings &settings, const std::string & availAuthTypes, bool firstTry )
317 {
318 CurlAuthData_Ptr credentials;
319
320 // get stored credentials
321 AuthData_Ptr cmcred = cm.getCred(url);
322
323 if (cmcred && firstTry)
324 {
325 credentials.reset(new CurlAuthData(*cmcred));
326 DBG << "got stored credentials:" << endl << *credentials << endl;
327 }
328 // if not found, ask user
329 else
330 {
331
332 CurlAuthData_Ptr curlcred;
333 curlcred.reset(new CurlAuthData());
335
336 // preset the username if present in current url
337 if (!url.getUsername().empty() && firstTry)
338 curlcred->setUsername(url.getUsername());
339 // if CM has found some credentials, preset the username from there
340 else if (cmcred)
341 curlcred->setUsername(cmcred->username());
342
343 // indicate we have no good credentials from CM
344 cmcred.reset();
345
346 std::string prompt_msg = str::Format(_("Authentication required for '%s'")) % url.asString();
347
348 // set available authentication types from the exception
349 // might be needed in prompt
350 curlcred->setAuthType(availAuthTypes);
351
352 // ask user
353 if (auth_report->prompt(url, prompt_msg, *curlcred))
354 {
355 DBG << "callback answer: retry" << endl
356 << "CurlAuthData: " << *curlcred << endl;
357
358 if (curlcred->valid())
359 {
360 credentials = curlcred;
361 // if (credentials->username() != _url.url().getUsername())
362 // _url.url().setUsername(credentials->username());
370 }
371 }
372 else
373 {
374 DBG << "callback answer: cancel" << endl;
375 }
376 }
377
378 // set username and password
379 if (credentials)
380 {
381 settings.setUsername(credentials->username());
382 settings.setPassword(credentials->password());
383
384 // set available authentication types from the exception
385 if (credentials->authType() == CURLAUTH_NONE)
386 credentials->setAuthType(availAuthTypes);
387
388 // set auth type (seems this must be set _after_ setting the userpwd)
389 if (credentials->authType() != CURLAUTH_NONE) {
390 settings.setAuthType(credentials->authTypeAsString());
391 }
392
393 if (!cmcred)
394 {
395 credentials->setUrl(url);
396 cm.addCred(*credentials);
397 cm.save();
398 }
399
400 return true;
401 }
402
403 return false;
404 }
405
406
407} // namespace zypp::media
Base class for Exception.
Definition Exception.h:153
Describes a resource file located on a medium.
const Pathname & filename() const
The path to the resource on the medium.
std::string distributionFlavor() const
This is flavor attribute of the installed base product but does not require the target to be loaded a...
Definition Target.cc:127
std::string anonymousUniqueId() const
anonymous unique id
Definition Target.cc:132
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition Target.cc:102
Url manipulation class.
Definition Url.h:93
void setHost(const std::string &host)
Set the hostname or IP in the URL authority.
Definition Url.cc:766
void appendPathName(const Pathname &path_r, EEncoding eflag_r=zypp::url::E_DECODED)
Extend the path name.
Definition Url.cc:804
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:940
Wrapper class for stat/lstat.
Definition PathInfo.h:226
const std::string & asString() const
String representation.
Definition Pathname.h:93
Pathname absolutename() const
Return this path, adding a leading '/' if relative.
Definition Pathname.h:141
void save()
Saves any unsaved credentials added via addUserCred() or addGlobalCred() methods.
AuthData_Ptr getCred(const Url &url)
Get credentials for the specified url.
void addCred(const AuthData &cred)
Add new credentials with user callbacks.
Curl HTTP authentication data.
Just inherits Exception to separate media exceptions.
bool isUseableAttachPoint(const Pathname &path, bool mtab=true) const
Ask media manager, if the specified path is already used as attach point or if there are another atta...
Url url() const
Primary Url used.
const MediaUrl _url
Primary Url.
virtual bool checkAttachPoint(const Pathname &apoint) const
Verify if the specified directory as attach point (root) as requires by the particular media handler ...
virtual void disconnectFrom()
Call concrete handler to disconnect media.
Pathname createAttachPoint() const
Try to create a default / temporary attach point.
MediaHandler(MediaUrl primaryUrl_r, std::vector< MediaUrl > urls_r, const Pathname &attach_point_r, Pathname urlpath_below_attachpoint_r, const bool does_download_r)
If the concrete media handler provides a nonempty attach_point, it must be an existing directory.
virtual void getFileCopy(const OnMediaLocation &file, const Pathname &targetFilename) const
Call concrete handler to provide a file under a different place in the file system (usually not under...
void setMediaSource(const MediaSourceRef &ref)
Set new media source reference.
Pathname localPath(const Pathname &pathname) const
Files provided will be available at 'localPath(filename)'.
void getDirectoryYast(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const
Retrieve and if available scan dirname/directory.yast.
void setAttachPoint(const Pathname &path, bool temp)
Set a new attach point.
Pathname attachPoint() const
Return the currently used attach point.
const std::vector< MediaUrl > _urls
All mirrors including the primary.
bool authenticate(const Url &url, TransferSettings &settings, const std::string &availAuthTypes, bool firstTry)
void getDir(const Pathname &dirname, bool recurse_r) const override
Call concrete handler to provide directory content (not recursive!) below attach point.
bool checkAttachPoint(const Pathname &apoint) const override
Verify if the specified directory as attach point (root) as requires by the particular media handler ...
MediaNetworkCommonHandler(const MediaUrl &url_r, const std::vector< MediaUrl > &mirrors_r, const Pathname &attach_point_r, const Pathname &urlpath_below_attachpoint_r, const bool does_download_r)
Url getFileUrl(int mirrorIdx, const Pathname &filename) const
concatenate the attach url and the filename to a complete download url
void setupTransferSettings()
initializes the curl easy handle with the data from the url
virtual void checkProtocol(const Url &url) const =0
check the url is supported by the curl library
void attachTo(bool next) override
Call concrete handler to attach the media.
void getFile(const OnMediaLocation &file) const override
Call concrete handler to provide file below attach point.
void getDirInfo(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const override
Call concrete handler to provide a content list of directory on media via retlist.
std::vector< TransferSettings > _mirrorSettings
static zypp::Url findGeoIPRedirect(const zypp::Url &url)
Rewrites the baseURL to the geoIP target if one is found in the metadata cache, otherwise simply retu...
Media source internally used by MediaManager and MediaHandler.
Definition MediaSource.h:38
Holds transfer setting.
const std::string & password() const
auth password
const std::string & authType() const
get the allowed authentication types
void setUsername(const std::string &val_r)
sets the auth username
const std::string & proxy() const
proxy host
void setUserAgentString(std::string &&val_r)
sets the user agent ie: "Mozilla v3" (trims)
void addHeader(std::string &&val_r)
add a header, on the form "Foo: Bar" (trims)
void setPassword(const std::string &val_r)
sets the auth password
const std::string & username() const
auth username
void setAuthType(const std::string &val_r)
set the allowed authentication types
std::multimap< std::string, std::string > HeaderList
Regular expression.
Definition Regex.h:95
bool matches(const char *s, str::smatch &matches, int flags=none) const
Definition Regex.cc:57
void fillSettingsFromUrl(const Url &url, media::TransferSettings &s)
Fills the settings structure using options passed on the url for example ?timeout=x&proxy=foo.
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
std::list< DirEntry > DirContent
Returned by readdir.
Definition PathInfo.h:526
shared_ptr< AuthData > AuthData_Ptr
Definition authdata.h:81
zypp::RW_pointer< MediaSource > MediaSourceRef
shared_ptr< CurlAuthData > CurlAuthData_Ptr
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition String.cc:39
std::string trim(const std::string &s, const Trim trim_r)
Definition String.cc:226
Container< Ret > transform(Container< Msg, CArgs... > &&val, Transformation &&transformation)
Definition transform.h:31
Convenient building of std::string with boost::format.
Definition String.h:254
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition Exception.h:479
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:475
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:459
#define _(MSG)
Definition Gettext.h:39
#define DBG
Definition Logger.h:99
#define MIL
Definition Logger.h:100
#define WAR
Definition Logger.h:101
Interface to gettext.