libzypp 17.38.5
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>
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 MirroredOrigin &origin_r, const Pathname &attach_point_r, const Pathname &urlpath_below_attachpoint_r, const bool does_download_r)
37 : MediaHandler( origin_r, attach_point_r, urlpath_below_attachpoint_r, does_download_r )
38 {
39 _redirTargets.clear ();
40 std::transform ( _origin.begin(), _origin.end(), std::back_inserter(_redirTargets), []( const OriginEndpoint &url_r ) { return findGeoIPRedirect(url_r.url()); } );
41
42 // initialize default mirror order: mirrors [1..N] then authority [0]
43 _mirrOrder.reserve( _origin.endpointCount() );
44 for( unsigned i = 1; i < _origin.endpointCount () ; i++ ) { _mirrOrder.push_back(i); }
45 _mirrOrder.push_back(0);
46 }
47
49 {
50 if ( next )
52
55 }
56
57 disconnectFrom(); // clean state if needed
58
59 // here : setup TransferSettings
61
62 // FIXME: need a derived class to propelly compare url's
63 MediaSourceRef media( new MediaSource( url().getScheme(), url().asString()) );
65 }
66
68 {
69 return MediaHandler::checkAttachPoint( apoint, true, true);
70 }
71
73 {
74 return ::internal::clearQueryString(url);
75 }
76
78 {
79 try {
80 const auto &conf = ZConfig::instance();
81 if ( !conf.geoipEnabled() ) {
82 MIL << "GeoIp rewrites disabled via ZConfig." << std::endl;
83 return Url();
84 }
85
86 if ( !( url.getQueryParam("COUNTRY").empty() && url.getQueryParam("AVOID_COUNTRY").empty() )) {
87 MIL << "GeoIp rewrites disabled since the baseurl " << url << " uses an explicit country setting." << std::endl;
88 return Url();
89 }
90
91 const auto &hostname = url.getHost();
92 auto geoipFile = conf.geoipCachePath() / hostname ;
93 if ( PathInfo( geoipFile ).isFile() ) {
94
95 MIL << "Found GeoIP file for host: " << hostname << std::endl;
96
97 std::ifstream in( geoipFile.asString() );
98 if (!in.is_open()) {
99 MIL << "Failed to open GeoIP for host: " << hostname << std::endl;
100 return Url();
101 }
102
103 try {
104 std::string newHost;
105 in >> newHost;
106
107 Url newUrl = url;
108 newUrl.setHost( newHost );
109
110 MIL << "Found GeoIP rewrite: " << hostname << " -> " << newHost << std::endl;
111
112 return newUrl;
113
114 } catch ( const zypp::Exception &e ) {
115 ZYPP_CAUGHT(e);
116 MIL << "No valid GeoIP rewrite target found for " << url << std::endl;
117 }
118 }
119 } catch ( const zypp::Exception &e ) {
120 ZYPP_CAUGHT(e);
121 MIL << "Failed to query GeoIP data, url rewriting disabled." << std::endl;
122 }
123
124 // no rewrite
125 return Url();
126 }
127
129
131 {
132 // Use absolute file name to prevent access of files outside of the
133 // hierarchy below the attach point.
134 getFileCopy( file, localPath(file.filename()).absolutename() );
135 }
136
137 void MediaNetworkCommonHandler::getDir( const Pathname & dirname, bool recurse_r ) const
138 {
140 getDirInfo( content, dirname, /*dots*/false );
141
142 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
143 Pathname filename = dirname + it->name;
144 int res = 0;
145
146 switch ( it->type ) {
147 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
149 getFile( OnMediaLocation( filename ) );
150 break;
151 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
152 if ( recurse_r ) {
153 getDir( filename, recurse_r );
154 } else {
155 res = assert_dir( localPath( filename ) );
156 if ( res ) {
157 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
158 }
159 }
160 break;
161 default:
162 // don't provide devices, sockets, etc.
163 break;
164 }
165 }
166 }
167
168 void MediaNetworkCommonHandler::getDirInfo( std::list<std::string> & retlist,
169 const Pathname & dirname, bool dots ) const
170 {
171 getDirectoryYast( retlist, dirname, dots );
172 }
173
175 const Pathname & dirname, bool dots ) const
176 {
177 getDirectoryYast( retlist, dirname, dots );
178 }
179
181 {
182 // we need to add the release and identifier to the
183 // agent string.
184 // The target could be not initialized, and then this information
185 // is guessed.
186 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
187 static const std::string _value( str::trim( str::form(
188 "X-ZYpp-AnonymousId: %s",
189 Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str()
190 )));
191 return _value.c_str();
192 }
193
195 {
196 // we need to add the release and identifier to the
197 // agent string.
198 // The target could be not initialized, and then this information
199 // is guessed.
200 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
201 static const std::string _value( str::trim( str::form(
202 "X-ZYpp-DistributionFlavor: %s",
203 Target::distributionFlavor( Pathname()/*guess root*/ ).c_str()
204 )));
205 return _value.c_str();
206 }
207
208 Url MediaNetworkCommonHandler::getFileUrl( int mirrorIdx, const Pathname & filename_r ) const
209 {
210 static const zypp::str::regex invalidRewrites("^.*\\/repomd.xml(.asc|.key)?$|^\\/geoip$");
211
212 if ( mirrorIdx < 0 || mirrorIdx > _redirTargets.size() )
213 return {};
214
215 const bool canRedir = _redirTargets[mirrorIdx].isValid() && !invalidRewrites.matches(filename_r.asString());
216 const auto &baseUrl = ( canRedir ) ? _redirTargets[mirrorIdx] : _origin[mirrorIdx].url();
217
218 if ( canRedir )
219 MIL << "Redirecting " << filename_r << " request to geoip location." << std::endl;
220
221 // Simply extend the URLs pathname:
222 Url newurl { baseUrl };
223 newurl.appendPathName( filename_r );
224 return newurl;
225 }
226
228 // we need to add the release and identifier to the
229 // agent string.
230 // The target could be not initialized, and then this information
231 // is guessed.
232 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
233 static const std::string _value(str::trim(str::form(
234 "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s",
235 curl_version_info(CURLVERSION_NOW)->version,
236 Target::targetDistribution(Pathname() /*guess root*/).c_str())));
237 return _value.c_str();
238 }
239
241 {
242 // fill some settings from url query parameters
243 try
244 {
246
248 for( OriginEndpoint &u : _origin ) {
249
250 u.setConfig( MIRR_SETTINGS_KEY.data() , std::make_any<TransferSettings>()); // init or reset to default
251
252 if ( !u.isValid() )
254
255 TransferSettings &set = u.getConfig<TransferSettings>( MIRR_SETTINGS_KEY.data() );
256
257 checkProtocol(u.url());
258
260
261 // apply MediaUrl settings
262 if ( u.hasConfig ("http-headers") ) {
263 // Set up the handler
264 for ( const auto & el : u.getConfig<UrlResolverPlugin::HeaderList>("http-headers") ) {
265 std::string header { el.first };
266 header += ": ";
267 header += el.second;
268 MIL << "Added custom header -> " << header << std::endl;
269 set.addHeader( std::move(header) );
270 }
271 }
272
273 // add custom headers for download.opensuse.org (bsc#955801)
274 if ( u.url().getHost() == "download.opensuse.org" )
275 {
278 }
279 set.addHeader("Pragma:");
280
281 // apply legacy Url encoded settings
282 ::internal::fillSettingsFromUrl( u.url(), set);
283
284 // if the proxy was not set (or explicitly unset) by url, then look...
285 if ( set.proxy().empty() )
286 {
287 // ...at the system proxy settings
289 }
290
291 /* Fixes bsc#1174011 "auth=basic ignored in some cases"
292 * We should proactively add the password to the request if basic auth is configured
293 * and a password is available in the credentials but not in the URL.
294 *
295 * We will be a bit paranoid here and require that the URL has a user embedded, otherwise we go the default route
296 * and ask the server first about the auth method
297 */
298 if ( set.authType() == "basic" && not set.username().empty() && set.password().empty() ) {
299 const auto cred = cm.getCred( u.url() );
300 if ( cred && cred->valid() ) {
301 set.setPassword(cred->password());
302 }
303 }
304 }
305 }
306 catch ( const MediaException &e )
307 {
309 ZYPP_RETHROW(e);
310 }
311 }
312
314 {
315 for( OriginEndpoint &u : _origin ) {
316 u.eraseConfigValue ( MIRR_SETTINGS_KEY.data() );
317 }
318 }
319
320
321 bool MediaNetworkCommonHandler::authenticate(const Url &url, TransferSettings &settings, const std::string & availAuthTypes, bool firstTry)
322 {
325 return authenticate( url, cm, settings, availAuthTypes, firstTry );
326 }
327
328 std::vector<unsigned int> MediaNetworkCommonHandler::mirrorOrder(const OnMediaLocation &loc) const
329 {
330 if ( !loc.mirrorsAllowed () ) {
331 MIL << "Fetching file " << loc << " from authority only: " << _origin << std::endl;
332 return { 0 }; // only authority
333 }
334 return _mirrOrder;
335 }
336
338 {
339 auto it = std::find( _mirrOrder.begin(), _mirrOrder.end(), mirr );
340 if ( it != _mirrOrder.end() && _mirrOrder.size() > 1 )
341 {
342 // move found index to the end
343 std::rotate( it, it + 1, _mirrOrder.end() );
344 }
345 }
346
347 bool MediaNetworkCommonHandler::authenticate( const Url &url, CredentialManager &cm, TransferSettings &settings, const std::string & availAuthTypes, bool firstTry )
348 {
349 CurlAuthData_Ptr credentials;
350
351 // get stored credentials
352 AuthData_Ptr cmcred = cm.getCred(url);
353
354 if (cmcred && firstTry)
355 {
356 credentials.reset(new CurlAuthData(*cmcred));
357 DBG << "got stored credentials:" << endl << *credentials << endl;
358 }
359 // if not found, ask user
360 else
361 {
362
363 CurlAuthData_Ptr curlcred;
364 curlcred.reset(new CurlAuthData());
366
367 // preset the username if present in current url
368 if (!url.getUsername().empty() && firstTry)
369 curlcred->setUsername(url.getUsername());
370 // if CM has found some credentials, preset the username from there
371 else if (cmcred)
372 curlcred->setUsername(cmcred->username());
373
374 // indicate we have no good credentials from CM
375 cmcred.reset();
376
377 std::string prompt_msg = str::Format(_("Authentication required for '%s'")) % url.asString();
378
379 // set available authentication types from the exception
380 // might be needed in prompt
381 curlcred->setAuthType(availAuthTypes);
382
383 // ask user
384 if (auth_report->prompt(url, prompt_msg, *curlcred))
385 {
386 DBG << "callback answer: retry" << endl
387 << "CurlAuthData: " << *curlcred << endl;
388
389 if (curlcred->valid())
390 {
391 credentials = curlcred;
392 // if (credentials->username() != _url.url().getUsername())
393 // _url.url().setUsername(credentials->username());
401 }
402 }
403 else
404 {
405 DBG << "callback answer: cancel" << endl;
406 }
407 }
408
409 // set username and password
410 if (credentials)
411 {
412 settings.setUsername(credentials->username());
413 settings.setPassword(credentials->password());
414
415 // set available authentication types from the exception
416 if (credentials->authType() == CURLAUTH_NONE)
417 credentials->setAuthType(availAuthTypes);
418
419 // set auth type (seems this must be set _after_ setting the userpwd)
420 if (credentials->authType() != CURLAUTH_NONE) {
421 settings.setAuthType(credentials->authTypeAsString());
422 }
423
424 if (!cmcred)
425 {
426 credentials->setUrl(url);
427 cm.addCred(*credentials);
428 cm.save();
429 }
430
431 return true;
432 }
433
434 return false;
435 }
436
437
438} // namespace zypp::media
#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
Base class for Exception.
Definition Exception.h:153
Manages a data source characterized by an authoritative URL and a list of mirror URLs.
Describes a resource file located on a medium.
const Pathname & filename() const
The path to the resource on the medium.
bool mirrorsAllowed() const
The requested file is allowed to be fetched via mirrors ( defaults to true ).
Represents a single, configurable network endpoint, combining a URL with specific access settings.
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:775
void appendPathName(const Pathname &path_r, EEncoding eflag_r=zypp::url::E_DECODED)
Extend the path name.
Definition Url.cc:813
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:971
Wrapper class for stat/lstat.
Definition PathInfo.h:226
const std::string & asString() const
String representation.
Definition Pathname.h:94
Pathname absolutename() const
Return this path, adding a leading '/' if relative.
Definition Pathname.h:148
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...
MediaHandler(MirroredOrigin origin_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.
MirroredOrigin _origin
Contains the authority URL and mirrors.
Url url() const
Primary Url used.
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.
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.
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 ...
void deprioritizeMirror(unsigned mirr) const
Move mirror index mirr to the end of the attempt list.
std::vector< unsigned > mirrorOrder(const OnMediaLocation &loc) const
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
MediaNetworkCommonHandler(const MirroredOrigin &origin_r, const Pathname &attach_point_r, const Pathname &urlpath_below_attachpoint_r, const bool does_download_r)
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.
static constexpr std::string_view MIRR_SETTINGS_KEY
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.
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:534
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
std::string asString(const Patch::Category &obj)
Definition Patch.cc:122
Convenient building of std::string with boost::format.
Definition String.h:254