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