libzypp 17.28.8
CurlHelper.cc
Go to the documentation of this file.
1#include "CurlHelper.h"
2
3#include <zypp/PathInfo.h>
4#include <zypp/Pathname.h>
5#include <zypp/Target.h>
6#include <zypp/base/Logger.h>
7#include <zypp/base/String.h>
11#include <list>
12
13using std::endl;
14using namespace zypp;
15
16namespace zypp
17{
18 namespace env
19 {
20 namespace
21 {
22 inline int getZYPP_MEDIA_CURL_IPRESOLVE()
23 {
24 int ret = 0;
25 if ( const char * envp = getenv( "ZYPP_MEDIA_CURL_IPRESOLVE" ) )
26 {
27 WAR << "env set: $ZYPP_MEDIA_CURL_IPRESOLVE='" << envp << "'" << std::endl;
28 if ( strcmp( envp, "4" ) == 0 ) ret = 4;
29 else if ( strcmp( envp, "6" ) == 0 ) ret = 6;
30 }
31 return ret;
32 }
33 } //namespace
34
36 {
37 static int _v = getZYPP_MEDIA_CURL_IPRESOLVE();
38 return _v;
39 }
40 } // namespace env
41} // namespace zypp
42
43namespace internal
44{
45
47{
48 // function-level static <=> std::call_once
49 static bool once __attribute__ ((__unused__)) = ( [] {
50 if ( curl_global_init( CURL_GLOBAL_ALL ) != 0 )
51 WAR << "curl global init failed" << std::endl;
52 } (), true );
53}
54
55int log_curl(CURL *curl, curl_infotype info,
56 char *ptr, size_t len, void *max_lvl)
57{
58 if ( max_lvl == nullptr )
59 return 0;
60
61 long maxlvl = *((long *)max_lvl);
62
63 char pfx = ' ';
64 switch( info )
65 {
66 case CURLINFO_TEXT: if ( maxlvl < 1 ) return 0; pfx = '*'; break;
67 case CURLINFO_HEADER_IN: if ( maxlvl < 2 ) return 0; pfx = '<'; break;
68 case CURLINFO_HEADER_OUT: if ( maxlvl < 2 ) return 0; pfx = '>'; break;
69 default:
70 return 0;
71 }
72
73 std::vector<std::string> lines;
74 str::split( std::string(ptr,len), std::back_inserter(lines), "\r\n" );
75 for( const auto & line : lines )
76 {
77 if ( str::startsWith( line, "Authorization:" ) ) {
78 std::string::size_type pos { line.find( " ", 15 ) }; // Authorization: <type> <credentials>
79 if ( pos == std::string::npos )
80 pos = 15;
81 DBG << pfx << " " << line.substr( 0, pos ) << " <credentials removed>" << std::endl;
82 }
83 else
84 DBG << pfx << " " << line << std::endl;
85 }
86 return 0;
87}
88
89size_t log_redirects_curl( char *ptr, size_t size, size_t nmemb, void *userdata)
90{
91 //INT << "got header: " << std::string(ptr, ptr + size*nmemb) << endl;
92
93 char * lstart = ptr, * lend = ptr;
94 size_t pos = 0;
95 size_t max = size * nmemb;
96 while (pos + 1 < max)
97 {
98 // get line
99 for (lstart = lend; *lend != '\n' && pos < max; ++lend, ++pos);
100
101 // look for "Location"
102 if ( strncasecmp( lstart, "Location:", 9 ) == 0 )
103 {
104 std::string line { lstart, *(lend-1)=='\r' ? lend-1 : lend };
105 DBG << "redirecting to " << line << std::endl;
106 if ( userdata ) {
107 *reinterpret_cast<std::string *>( userdata ) = line;
108 }
109 return max;
110 }
111
112 // continue with the next line
113 if (pos + 1 < max)
114 {
115 ++lend;
116 ++pos;
117 }
118 else
119 break;
120 }
121
122 return max;
123}
124
130{
131 {
132 const std::string & param { url.getQueryParam("timeout") };
133 if( ! param.empty() )
134 {
135 long num = str::strtonum<long>(param);
136 if( num >= 0 && num <= TRANSFER_TIMEOUT_MAX )
137 s.setTimeout( num );
138 }
139 }
140 {
141 std::string param { url.getUsername() };
142 if ( ! param.empty() )
143 {
144 s.setUsername( std::move(param) );
145 param = url.getPassword();
146 if ( ! param.empty() )
147 s.setPassword( std::move(param) );
148 }
149 else
150 {
151 // if there is no username, set anonymous auth
152 if ( ( url.getScheme() == "ftp" || url.getScheme() == "tftp" ) && s.username().empty() )
154 }
155 }
156 if ( url.getScheme() == "https" )
157 {
158 s.setVerifyPeerEnabled( false );
159 s.setVerifyHostEnabled( false );
160
161 const std::string & verify { url.getQueryParam("ssl_verify") };
162 if( verify.empty() || verify == "yes" )
163 {
164 s.setVerifyPeerEnabled( true );
165 s.setVerifyHostEnabled( true );
166 }
167 else if ( verify == "no" )
168 {
169 s.setVerifyPeerEnabled( false );
170 s.setVerifyHostEnabled( false );
171 }
172 else
173 {
174 std::vector<std::string> flags;
175 str::split( verify, std::back_inserter(flags), "," );
176 for ( const auto & flag : flags )
177 {
178 if ( flag == "host" )
179 s.setVerifyHostEnabled( true );
180 else if ( flag == "peer" )
181 s.setVerifyPeerEnabled( true );
182 else
183 ZYPP_THROW( media::MediaBadUrlException(url, "Unknown ssl_verify flag "+flag) );
184 }
185 }
186 }
187 {
188 Pathname ca_path { url.getQueryParam("ssl_capath") };
189 if( ! ca_path.empty() )
190 {
191 if( ! PathInfo(ca_path).isDir() || ! ca_path.absolute() )
192 ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_capath path"));
193 else
194 s.setCertificateAuthoritiesPath( std::move(ca_path) );
195 }
196 }
197 {
198 Pathname client_cert { url.getQueryParam("ssl_clientcert") };
199 if( ! client_cert.empty() )
200 {
201 if( ! PathInfo(client_cert).isFile() || ! client_cert.absolute() )
202 ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_clientcert file"));
203 else
204 s.setClientCertificatePath( std::move(client_cert) );
205 }
206 }
207 {
208 Pathname client_key { url.getQueryParam("ssl_clientkey") };
209 if( ! client_key.empty() )
210 {
211 if( ! PathInfo(client_key).isFile() || ! client_key.absolute() )
212 ZYPP_THROW(media::MediaBadUrlException(url, "Invalid ssl_clientkey file"));
213 else
214 s.setClientKeyPath( std::move(client_key) );
215 }
216 }
217 {
218 std::string param { url.getQueryParam( "proxy" ) };
219 if ( ! param.empty() )
220 {
221 if ( param == EXPLICITLY_NO_PROXY ) {
222 // Workaround TransferSettings shortcoming: With an
223 // empty proxy string, code will continue to look for
224 // valid proxy settings. So set proxy to some non-empty
225 // string, to indicate it has been explicitly disabled.
227 s.setProxyEnabled(false);
228 }
229 else {
230 const std::string & proxyport { url.getQueryParam( "proxyport" ) };
231 if ( ! proxyport.empty() ) {
232 param += ":";
233 param += proxyport;
234 }
235 s.setProxy( std::move(param) );
236 s.setProxyEnabled( true );
237 }
238 }
239 }
240 {
241 std::string param { url.getQueryParam( "proxyuser" ) };
242 if ( ! param.empty() )
243 {
244 s.setProxyUsername( std::move(param) );
245 s.setProxyPassword( url.getQueryParam( "proxypass" ) );
246 }
247 }
248 {
249 // HTTP authentication type
250 std::string param { url.getQueryParam("auth") };
251 if ( ! param.empty() && (url.getScheme() == "http" || url.getScheme() == "https") )
252 {
253 try
254 {
255 media::CurlAuthData::auth_type_str2long (param ); // check if we know it
256 }
257 catch ( const media::MediaException & ex_r )
258 {
259 DBG << "Rethrowing as MediaUnauthorizedException.";
261 }
262 s.setAuthType( std::move(param) );
263 }
264 }
265 {
266 // workarounds
267 const std::string & param { url.getQueryParam("head_requests") };
268 if( ! param.empty() && param == "no" )
269 s.setHeadRequestsAllowed( false );
270 }
271}
272
278{
279 media::ProxyInfo proxy_info;
280 if ( proxy_info.useProxyFor( url ) )
281 {
282 // We must extract any 'user:pass' from the proxy url
283 // otherwise they won't make it into curl (.curlrc wins).
284 try {
285 Url u( proxy_info.proxy( url ) );
286 s.setProxy( u.asString( url::ViewOption::WITH_SCHEME + url::ViewOption::WITH_HOST + url::ViewOption::WITH_PORT ) );
287 // don't overwrite explicit auth settings
288 if ( s.proxyUsername().empty() )
289 {
292 }
293 s.setProxyEnabled( true );
294 }
295 catch (...) {} // no proxy if URL is malformed
296 }
297}
298
299
300// Disable custom headers -- Arachnos
301#ifdef ANONYMOUS_ID
302const char * anonymousIdHeader()
303{
304 // we need to add the release and identifier to the
305 // agent string.
306 // The target could be not initialized, and then this information
307 // is guessed.
308 static const std::string _value(
310 "X-ZYpp-AnonymousId: %s",
311 Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str() ) )
312 );
313 return _value.c_str();
314}
315
316const char * distributionFlavorHeader()
317{
318 // we need to add the release and identifier to the
319 // agent string.
320 // The target could be not initialized, and then this information
321 // is guessed.
322 static const std::string _value(
324 "X-ZYpp-DistributionFlavor: %s",
325 Target::distributionFlavor( Pathname()/*guess root*/ ).c_str() ) )
326 );
327 return _value.c_str();
328}
329#endif
330
331const char * agentString()
332{
333 // we need to add the release and identifier to the
334 // agent string.
335 // The target could be not initialized, and then this information
336 // is guessed.
337 static const std::string _value(
338 str::form(
339 "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s"
340 , curl_version_info(CURLVERSION_NOW)->version
341 , Target::targetDistribution( Pathname()/*guess root*/ ).c_str()
342 )
343 );
344 return _value.c_str();
345}
346
347void curlEscape( std::string & str_r,
348 const char char_r, const std::string & escaped_r ) {
349 for ( std::string::size_type pos = str_r.find( char_r );
350 pos != std::string::npos; pos = str_r.find( char_r, pos ) ) {
351 str_r.replace( pos, 1, escaped_r );
352 }
353}
354
355std::string curlEscapedPath( std::string path_r ) {
356 curlEscape( path_r, ' ', "%20" );
357 return path_r;
358}
359
360std::string curlUnEscape( std::string text_r ) {
361 char * tmp = curl_unescape( text_r.c_str(), 0 );
362 std::string ret( tmp );
363 curl_free( tmp );
364 return ret;
365}
366
368{
369 Url curlUrl (url);
370 curlUrl.setUsername( "" );
371 curlUrl.setPassword( "" );
372 curlUrl.setPathParams( "" );
373 curlUrl.setFragment( "" );
374 curlUrl.delQueryParam("cookies");
375 curlUrl.delQueryParam("proxy");
376 curlUrl.delQueryParam("proxyport");
377 curlUrl.delQueryParam("proxyuser");
378 curlUrl.delQueryParam("proxypass");
379 curlUrl.delQueryParam("ssl_capath");
380 curlUrl.delQueryParam("ssl_verify");
381 curlUrl.delQueryParam("ssl_clientcert");
382 curlUrl.delQueryParam("timeout");
383 curlUrl.delQueryParam("auth");
384 curlUrl.delQueryParam("username");
385 curlUrl.delQueryParam("password");
386 curlUrl.delQueryParam("mediahandler");
387 curlUrl.delQueryParam("credentials");
388 curlUrl.delQueryParam("head_requests");
389 return curlUrl;
390}
391
392// bsc#933839: propagate proxy settings passed in the repo URL
394{
395 for ( std::string param : { "proxy", "proxyport", "proxyuser", "proxypass"} )
396 {
397 const std::string & value( template_r.getQueryParam( param ) );
398 if ( ! value.empty() )
399 url_r.setQueryParam( param, value );
400 }
401 return url_r;
402}
403
404ProgressData::ProgressData(CURL *_curl, time_t _timeout, const Url &_url, ByteCount expectedFileSize_r, zypp::callback::SendReport< zypp::media::DownloadProgressReport> *_report)
405: curl( _curl )
406, url( _url )
407, timeout( _timeout )
408, reached( false )
409, fileSizeExceeded ( false )
410, report( _report )
411, _expectedFileSize( expectedFileSize_r )
412{}
413
414void ProgressData::updateStats(double dltotal, double dlnow)
415{
416 time_t now = _timeNow = time(0);
417
418 // If called without args (0.0), recompute based on the last values seen
419 if ( dltotal && dltotal != _dnlTotal )
420 _dnlTotal = dltotal;
421
422 if ( dlnow && dlnow != _dnlNow )
423 {
424 _timeRcv = now;
425 _dnlNow = dlnow;
426 }
427 else if ( !_dnlNow && !_dnlTotal )
428 {
429 // Start time counting as soon as first data arrives.
430 // Skip the connection / redirection time at begin.
431 return;
432 }
433
434 // init or reset if time jumps back
435 if ( !_timeStart || _timeStart > now )
436 _timeStart = _timeLast = _timeRcv = now;
437
438 // timeout condition
439 if ( timeout )
440 reached = ( (now - _timeRcv) > timeout );
441
442 // check if the downloaded data is already bigger than what we expected
443 fileSizeExceeded = _expectedFileSize > 0 && _expectedFileSize < static_cast<ByteCount::SizeType>(_dnlNow);
444
445 // percentage:
446 if ( _dnlTotal )
447 _dnlPercent = int(_dnlNow * 100 / _dnlTotal);
448
449 // download rates:
450 _drateTotal = _dnlNow / std::max( int(now - _timeStart), 1 );
451
452 if ( _timeLast < now )
453 {
454 _drateLast = (_dnlNow - _dnlLast) / int(now - _timeLast);
455 // start new period
456 _timeLast = now;
458 }
459 else if ( _timeStart == _timeLast )
461}
462
464{
465 if ( fileSizeExceeded )
466 return 1;
467 if ( reached )
468 return 1; // no-data timeout
469 if ( report && !(*report)->progress( _dnlPercent, url, _drateTotal, _drateLast ) )
470 return 1; // user requested abort
471 return 0;
472}
473
474}
#define TRANSFER_TIMEOUT_MAX
Definition: CurlHelper.h:23
#define EXPLICITLY_NO_PROXY
Definition: CurlHelper.h:26
Convenience interface for handling authentication data of media user.
Edition * _value
Definition: SysContent.cc:311
Store and operate with byte count.
Definition: ByteCount.h:31
const std::string & msg() const
Return the message string provided to the ctor.
Definition: Exception.h:195
const char * c_str() const
Definition: IdStringType.h:105
Url manipulation class.
Definition: Url.h:92
std::string getScheme() const
Returns the scheme name of the URL.
Definition: Url.cc:533
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:497
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:572
void setFragment(const std::string &fragment, EEncoding eflag=zypp::url::E_DECODED)
Set the fragment string in the URL.
Definition: Url.cc:722
std::string getQueryParam(const std::string &param, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
Definition: Url.cc:660
void delQueryParam(const std::string &param)
remove the specified query parameter.
Definition: Url.cc:845
void setPassword(const std::string &pass, EEncoding eflag=zypp::url::E_DECODED)
Set the password in the URL authority.
Definition: Url.cc:739
void setPathParams(const std::string &params)
Set the path parameters.
Definition: Url.cc:791
void setQueryParam(const std::string &param, const std::string &value)
Set or add value for the specified query parameter.
Definition: Url.cc:838
void setUsername(const std::string &user, EEncoding eflag=zypp::url::E_DECODED)
Set the username in the URL authority.
Definition: Url.cc:730
std::string getPassword(EEncoding eflag=zypp::url::E_DECODED) const
Returns the password from the URL authority.
Definition: Url.cc:580
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:221
Just inherits Exception to separate media exceptions.
bool useProxyFor(const Url &url_r) const
Return true if enabled and url_r does not match noProxy.
Definition: ProxyInfo.cc:55
std::string proxy(const Url &url) const
Definition: ProxyInfo.cc:43
Holds transfer setting.
void setProxyEnabled(bool enabled)
whether the proxy is used or not
void setProxyUsername(std::string &&val_r)
sets the proxy user
void setClientCertificatePath(Pathname &&val_r)
Sets the SSL client certificate file.
void setProxy(std::string &&val_r)
proxy to use if it is enabled
void setHeadRequestsAllowed(bool allowed)
set whether HEAD requests are allowed
void setAuthType(std::string &&val_r)
set the allowed authentication types
void setUsername(std::string &&val_r)
sets the auth username
void setVerifyHostEnabled(bool enabled)
Sets whether to verify host for ssl.
void setPassword(std::string &&val_r)
sets the auth password
void setCertificateAuthoritiesPath(Pathname &&val_r)
Sets the SSL certificate authorities path.
void setClientKeyPath(Pathname &&val_r)
Sets the SSL client key file.
void setProxyPassword(std::string &&val_r)
sets the proxy password
std::string proxyUsername() const
proxy auth username
void setVerifyPeerEnabled(bool enabled)
Sets whether to verify host for ssl.
std::string username() const
auth username
void setAnonymousAuth()
sets anonymous authentication (ie: for ftp)
void setTimeout(long t)
set the transfer timeout
void fillSettingsFromUrl(const Url &url, media::TransferSettings &s)
Fills the settings structure using options passed on the url for example ?timeout=x&proxy=foo.
Definition: CurlHelper.cc:129
size_t log_redirects_curl(char *ptr, size_t size, size_t nmemb, void *userdata)
Definition: CurlHelper.cc:89
void globalInitCurlOnce()
Definition: CurlHelper.cc:46
zypp::Url propagateQueryParams(zypp::Url url_r, const zypp::Url &template_r)
Definition: CurlHelper.cc:393
std::string curlUnEscape(std::string text_r)
Definition: CurlHelper.cc:360
std::string curlEscapedPath(std::string path_r)
Definition: CurlHelper.cc:355
const char * agentString()
initialized only once, this gets the agent string which also includes the curl version
Definition: CurlHelper.cc:331
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
Definition: CurlHelper.cc:277
Url clearQueryString(const Url &url)
Definition: CurlHelper.cc:367
void curlEscape(std::string &str_r, const char char_r, const std::string &escaped_r)
Definition: CurlHelper.cc:347
int log_curl(CURL *curl, curl_infotype info, char *ptr, size_t len, void *max_lvl)
Definition: CurlHelper.cc:55
int ZYPP_MEDIA_CURL_IPRESOLVE()
4/6 to force IPv4/v6
Definition: CurlHelper.cc:35
SolvableIdType size_type
Definition: PoolMember.h:126
bool startsWith(const C_Str &str_r, const C_Str &prefix_r)
alias for hasPrefix
Definition: String.h:1085
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", const Trim trim_r=NO_TRIM)
Split line_r into words.
Definition: String.h:531
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:223
@ E_ENCODED
Flag to request encoded string(s).
Definition: UrlUtils.h:53
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
zypp::ByteCount _expectedFileSize
Definition: CurlHelper.h:64
ProgressData(CURL *_curl, time_t _timeout=0, const zypp::Url &_url=zypp::Url(), zypp::ByteCount expectedFileSize_r=0, zypp::callback::SendReport< zypp::media::DownloadProgressReport > *_report=nullptr)
Definition: CurlHelper.cc:404
void updateStats(double dltotal=0.0, double dlnow=0.0)
Definition: CurlHelper.cc:414
int _dnlPercent
Percent completed or 0 if _dnlTotal is unknown.
Definition: CurlHelper.h:75
time_t _timeRcv
Start of no-data timeout.
Definition: CurlHelper.h:68
time_t _timeLast
Start last period(~1sec)
Definition: CurlHelper.h:67
int reportProgress() const
Definition: CurlHelper.cc:463
double _drateLast
Download rate in last period.
Definition: CurlHelper.h:78
double _dnlTotal
Bytes to download or 0 if unknown.
Definition: CurlHelper.h:71
double _dnlNow
Bytes downloaded now.
Definition: CurlHelper.h:73
double _drateTotal
Download rate so far.
Definition: CurlHelper.h:77
zypp::callback::SendReport< zypp::media::DownloadProgressReport > * report
Definition: CurlHelper.h:63
double _dnlLast
Bytes downloaded at period start.
Definition: CurlHelper.h:72
time_t _timeStart
Start total stats.
Definition: CurlHelper.h:66
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:392
#define DBG
Definition: Logger.h:95
#define WAR
Definition: Logger.h:97