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