libzypp 17.37.2
MediaCurl.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12
13#include <iostream>
14#include <chrono>
15#include <list>
16
17#include <zypp/base/Logger.h>
19#include <zypp/base/String.h>
20#include <zypp/base/Gettext.h>
21#include <utility>
22#include <zypp-core/parser/Sysconfig>
23#include <zypp/base/Gettext.h>
24
28#include <zypp-curl/ProxyInfo>
29#include <zypp-curl/auth/CurlAuthData>
30#include <zypp-media/auth/CredentialManager>
31#include <zypp-curl/CurlConfig>
32#include <zypp/Target.h>
33#include <zypp/ZYppFactory.h>
34#include <zypp/ZConfig.h>
35#include <zypp/zypp_detail/ZYppImpl.h> // for zypp_poll
36
37#include <cstdlib>
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <sys/mount.h>
41#include <dirent.h>
42#include <unistd.h>
43#include <glib.h>
44
46
47using std::endl;
48
49namespace internal {
50 using namespace zypp;
52 {
53 ProgressData( CURL *curl, time_t timeout = 0, zypp::Url url = zypp::Url(),
54 zypp::ByteCount expectedFileSize_r = 0,
56
57 void updateStats( curl_off_t dltotal = 0.0, curl_off_t dlnow = 0.0 );
58
59 int reportProgress() const;
60
61 CURL * curl()
62 { return _curl; }
63
64 bool timeoutReached() const
65 { return _timeoutReached; }
66
67 bool fileSizeExceeded() const
68 { return _fileSizeExceeded; }
69
72
73 void expectedFileSize( ByteCount newval_r )
74 { _expectedFileSize = newval_r; }
75
76 private:
77 CURL * _curl;
79 time_t _timeout;
84
85 time_t _timeStart = 0;
86 time_t _timeLast = 0;
87 time_t _timeRcv = 0;
88 time_t _timeNow = 0;
89
90 curl_off_t _dnlTotal = 0.0;
91 curl_off_t _dnlLast = 0.0;
92 curl_off_t _dnlNow = 0.0;
93
94 int _dnlPercent= 0;
95
96 double _drateTotal= 0.0;
97 double _drateLast = 0.0;
98 };
99
100
101
103 : _curl( curl )
104 , _url(std::move( url ))
105 , _timeout( timeout )
106 , _timeoutReached( false )
107 , _fileSizeExceeded ( false )
108 , _expectedFileSize( expectedFileSize_r )
109 , report( _report )
110 {}
111
112 void ProgressData::updateStats( curl_off_t dltotal, curl_off_t dlnow )
113 {
114 time_t now = _timeNow = time(0);
115
116 // If called without args (0.0), recompute based on the last values seen
117 if ( dltotal && dltotal != _dnlTotal )
118 _dnlTotal = dltotal;
119
120 if ( dlnow && dlnow != _dnlNow )
121 {
122 _timeRcv = now;
123 _dnlNow = dlnow;
124 }
125
126 // init or reset if time jumps back
127 if ( !_timeStart || _timeStart > now )
128 _timeStart = _timeLast = _timeRcv = now;
129
130 // timeout condition
131 if ( _timeout )
132 _timeoutReached = ( (now - _timeRcv) > _timeout );
133
134 // check if the downloaded data is already bigger than what we expected
136
137 // percentage:
138 if ( _dnlTotal )
139 _dnlPercent = int( _dnlNow * 100 / _dnlTotal );
140
141 // download rates:
142 _drateTotal = double(_dnlNow) / std::max( int(now - _timeStart), 1 );
143
144 if ( _timeLast < now )
145 {
146 _drateLast = double(_dnlNow - _dnlLast) / int(now - _timeLast);
147 // start new period
148 _timeLast = now;
150 }
151 else if ( _timeStart == _timeLast )
153 }
154
156 {
157 if ( _fileSizeExceeded )
158 return 1;
159 if ( _timeoutReached )
160 return 1; // no-data timeout
161 if ( report && !(*report)->progress( _dnlPercent, _url, _drateTotal, _drateLast ) )
162 return 1; // user requested abort
163 return 0;
164 }
165
170 {
171 public:
173 const std::string & err_r,
174 const std::string & msg_r )
175 : media::MediaCurlException( url_r, err_r, msg_r )
176 {}
177 //~MediaCurlExceptionMayRetryInternaly() noexcept {}
178 };
179
180}
181
182
183using namespace internal;
184using namespace zypp::base;
185
186namespace zypp {
187
188 namespace media {
189
190Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
191
192// we use this define to unbloat code as this C setting option
193// and catching exception is done frequently.
195#define SET_OPTION(opt,val) do { \
196 ret = curl_easy_setopt ( curl, opt, val ); \
197 if ( ret != 0) { \
198 ZYPP_THROW(MediaCurlSetOptException(_urls.at(rData.mirror).url(), _curlError)); \
199 } \
200 } while ( false )
201
202#define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val)
203#define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val)
204#define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val)
205
206MediaCurl::MediaCurl(const MediaUrl &url_r, const std::vector<MediaUrl> &mirrors_r,
207 const Pathname & attach_point_hint_r )
208 : MediaNetworkCommonHandler( url_r, mirrors_r, attach_point_hint_r,
209 "/", // urlpath at attachpoint
210 true ), // does_download
212{
213 _multi = curl_multi_init();
214
215 _curlError[0] = '\0';
216
217 MIL << "MediaCurl::MediaCurl(" << url_r .url() << ", " << attach_point_hint_r << ")" << endl;
218
220
221 if( !attachPoint().empty())
222 {
223 PathInfo ainfo(attachPoint());
224 Pathname apath(attachPoint() + "XXXXXX");
225 char *atemp = ::strdup( apath.asString().c_str());
226 char *atest = NULL;
227 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
228 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
229 {
230 WAR << "attach point " << ainfo.path()
231 << " is not useable for " << url_r.url().getScheme() << endl;
232 setAttachPoint("", true);
233 }
234 else if( atest != NULL)
235 ::rmdir(atest);
236
237 if( atemp != NULL)
238 ::free(atemp);
239 }
240}
241
243{
244 try { release(); } catch(...) {}
245 if (_multi)
246 curl_multi_cleanup(_multi);
247}
248
249void MediaCurl::setCookieFile( const Pathname &fileName )
250{
251 _cookieFile = fileName;
252}
253
254void MediaCurl::setCurlError(const char* error)
255{
256 // FIXME(dmllr): Use strlcpy if available for better performance
257 strncpy(_curlError, error, sizeof(_curlError)-1);
258 _curlError[sizeof(_curlError)-1] = '\0';
259}
260
262
264{
265 curl_version_info_data *curl_info = NULL;
266 curl_info = curl_version_info(CURLVERSION_NOW);
267 // curl_info does not need any free (is static)
268 if (curl_info->protocols)
269 {
270 const char * const *proto = nullptr;
271 std::string scheme( url.getScheme());
272 bool found = false;
273 for(proto=curl_info->protocols; !found && *proto; ++proto)
274 {
275 if( scheme == std::string((const char *)*proto))
276 found = true;
277 }
278 if( !found)
279 {
280 std::string msg("Unsupported protocol '");
281 msg += scheme;
282 msg += "'";
284 }
285 }
286}
287
289{
290 CURL *curl = rData.curl;
291
292 // kill old settings
293 curl_easy_reset ( curl );
294
296 curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, log_redirects_curl);
297 curl_easy_setopt(curl, CURLOPT_HEADERDATA, &_lastRedirect);
298 CURLcode ret = curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, _curlError );
299 if ( ret != 0 ) {
300 ZYPP_THROW(MediaCurlSetOptException( _urls.at(rData.mirror).url(), "Error setting error buffer"));
301 }
302
303 SET_OPTION(CURLOPT_FAILONERROR, 1L);
304 SET_OPTION(CURLOPT_NOSIGNAL, 1L);
305
308 {
309 case 4: SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); break;
310 case 6: SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); break;
311 }
312
316 SET_OPTION(CURLOPT_CONNECTTIMEOUT, settings.connectTimeout());
317 // If a transfer timeout is set, also set CURLOPT_TIMEOUT to an upper limit
318 // just in case curl does not trigger its progress callback frequently
319 // enough.
320 if ( settings.timeout() )
321 {
322 SET_OPTION(CURLOPT_TIMEOUT, 3600L);
323 }
324
325 // follow any Location: header that the server sends as part of
326 // an HTTP header (#113275)
327 SET_OPTION(CURLOPT_FOLLOWLOCATION, 1L);
328 // 3 redirects seem to be too few in some cases (bnc #465532)
329 SET_OPTION(CURLOPT_MAXREDIRS, 6L);
330
331 if ( _urls.at(rData.mirror).url().getScheme() == "https" )
332 {
333 if ( :: internal::setCurlRedirProtocols ( curl ) != CURLE_OK ) {
335 }
336
337 if( settings.verifyPeerEnabled() ||
338 settings.verifyHostEnabled() )
339 {
340 SET_OPTION(CURLOPT_CAPATH, settings.certificateAuthoritiesPath().c_str());
341 }
342
343 if( ! settings.clientCertificatePath().empty() )
344 {
345 SET_OPTION(CURLOPT_SSLCERT, settings.clientCertificatePath().c_str());
346 }
347 if( ! settings.clientKeyPath().empty() )
348 {
349 SET_OPTION(CURLOPT_SSLKEY, settings.clientKeyPath().c_str());
350 }
351
352#ifdef CURLSSLOPT_ALLOW_BEAST
353 // see bnc#779177
354 ret = curl_easy_setopt( curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST );
355 if ( ret != 0 ) {
358 }
359#endif
360 SET_OPTION(CURLOPT_SSL_VERIFYPEER, settings.verifyPeerEnabled() ? 1L : 0L);
361 SET_OPTION(CURLOPT_SSL_VERIFYHOST, settings.verifyHostEnabled() ? 2L : 0L);
362 // bnc#903405 - POODLE: libzypp should only talk TLS
363 SET_OPTION(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
364 }
365
366 SET_OPTION(CURLOPT_USERAGENT, settings.userAgentString().c_str() );
367
368 /* Fixes bsc#1174011 "auth=basic ignored in some cases"
369 * We should proactively add the password to the request if basic auth is configured
370 * and a password is available in the credentials but not in the URL.
371 *
372 * We will be a bit paranoid here and require that the URL has a user embedded, otherwise we go the default route
373 * and ask the server first about the auth method
374 */
375 if ( settings.authType() == "basic"
376 && settings.username().size()
377 && !settings.password().size() ) {
378
380 const auto cred = cm.getCred( _urls.at(rData.mirror).url() );
381 if ( cred && cred->valid() ) {
382 if ( !settings.username().size() )
383 settings.setUsername(cred->username());
384 settings.setPassword(cred->password());
385 }
386 }
387
388 /*---------------------------------------------------------------*
389 CURLOPT_USERPWD: [user name]:[password]
390
391 Url::username/password -> CURLOPT_USERPWD
392 If not provided, anonymous FTP identification
393 *---------------------------------------------------------------*/
394
395 if ( settings.userPassword().size() )
396 {
397 SET_OPTION(CURLOPT_USERPWD, settings.userPassword().c_str());
398 std::string use_auth = settings.authType();
399 if (use_auth.empty())
400 use_auth = "digest,basic"; // our default
401 long auth = CurlAuthData::auth_type_str2long(use_auth);
402 if( auth != CURLAUTH_NONE)
403 {
404 DBG << "Enabling HTTP authentication methods: " << use_auth
405 << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
406 SET_OPTION(CURLOPT_HTTPAUTH, auth);
407 }
408 }
409
410 if ( settings.proxyEnabled() && ! settings.proxy().empty() )
411 {
412 DBG << "Proxy: '" << settings.proxy() << "'" << endl;
413 SET_OPTION(CURLOPT_PROXY, settings.proxy().c_str());
414 SET_OPTION(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
415 /*---------------------------------------------------------------*
416 * CURLOPT_PROXYUSERPWD: [user name]:[password]
417 *
418 * Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
419 * If not provided, $HOME/.curlrc is evaluated
420 *---------------------------------------------------------------*/
421
422 std::string proxyuserpwd = settings.proxyUserPassword();
423
424 if ( proxyuserpwd.empty() )
425 {
426 CurlConfig curlconf;
427 CurlConfig::parseConfig(curlconf); // parse ~/.curlrc
428 if ( curlconf.proxyuserpwd.empty() )
429 DBG << "Proxy: ~/.curlrc does not contain the proxy-user option" << endl;
430 else
431 {
432 proxyuserpwd = curlconf.proxyuserpwd;
433 DBG << "Proxy: using proxy-user from ~/.curlrc" << endl;
434 }
435 }
436 else
437 {
438 DBG << "Proxy: using provided proxy-user '" << settings.proxyUsername() << "'" << endl;
439 }
440
441 if ( ! proxyuserpwd.empty() )
442 {
443 SET_OPTION(CURLOPT_PROXYUSERPWD, curlUnEscape( proxyuserpwd ).c_str());
444 }
445 }
446#if CURLVERSION_AT_LEAST(7,19,4)
447 else if ( settings.proxy() == EXPLICITLY_NO_PROXY )
448 {
449 // Explicitly disabled in URL (see fillSettingsFromUrl()).
450 // This should also prevent libcurl from looking into the environment.
451 DBG << "Proxy: explicitly NOPROXY" << endl;
452 SET_OPTION(CURLOPT_NOPROXY, "*");
453 }
454#endif
455 else
456 {
457 DBG << "Proxy: not explicitly set" << endl;
458 DBG << "Proxy: libcurl may look into the environment" << endl;
459 }
460
462 if ( settings.minDownloadSpeed() != 0 )
463 {
464 SET_OPTION(CURLOPT_LOW_SPEED_LIMIT, settings.minDownloadSpeed());
465 // default to 10 seconds at low speed
466 SET_OPTION(CURLOPT_LOW_SPEED_TIME, 60L);
467 }
468
469#if CURLVERSION_AT_LEAST(7,15,5)
470 if ( settings.maxDownloadSpeed() != 0 )
471 SET_OPTION_OFFT(CURLOPT_MAX_RECV_SPEED_LARGE, settings.maxDownloadSpeed());
472#endif
473
474 /*---------------------------------------------------------------*
475 *---------------------------------------------------------------*/
476
477 _currentCookieFile = _cookieFile.asString();
478 if ( ::geteuid() == 0 || PathInfo(_currentCookieFile).owner() == ::geteuid() )
480
481 const auto &cookieFileParam = _urls.at(rData.mirror).url().getQueryParam( "cookies" );
482 if ( !cookieFileParam.empty() && str::strToBool( cookieFileParam, true ) )
483 SET_OPTION(CURLOPT_COOKIEFILE, _currentCookieFile.c_str() );
484 else
485 MIL << "No cookies requested" << endl;
486 SET_OPTION(CURLOPT_COOKIEJAR, _currentCookieFile.c_str() );
487 SET_OPTION(CURLOPT_XFERINFOFUNCTION, &progressCallback );
488 SET_OPTION(CURLOPT_NOPROGRESS, 0L);
489
490#if CURLVERSION_AT_LEAST(7,18,0)
491 // bnc #306272
492 SET_OPTION(CURLOPT_PROXY_TRANSFER_MODE, 1L );
493#endif
494 // Append settings custom headers to curl.
495 // TransferSettings assert strings are trimmed (HTTP/2 RFC 9113)
496 if ( _customHeaders ) {
497 curl_slist_free_all(_customHeaders);
498 _customHeaders = 0L;
499 }
500 for ( const auto &header : settings.headers() ) {
501 _customHeaders = curl_slist_append(_customHeaders, header.c_str());
502 if ( !_customHeaders )
504 }
505 SET_OPTION(CURLOPT_HTTPHEADER, _customHeaders);
506}
507
509
511{
512 if ( _customHeaders ) {
513 curl_slist_free_all(_customHeaders);
514 _customHeaders = 0L;
515 }
516
517 // clear effective settings
518 _mirrorSettings.clear();
519}
520
522
523void MediaCurl::releaseFrom( const std::string & ejectDev )
524{
525 disconnect();
526}
527
529
530void MediaCurl::getFileCopy( const OnMediaLocation & srcFile , const Pathname & target ) const
531{
532 // we need a non const pointer to work around the current API
533 auto that = const_cast<MediaCurl *>(this);
534 std::exception_ptr lastErr;
535 for ( int i = 0; i < (int)_urls.size (); i++ ) {
536 try {
537 return that->getFileCopyFromMirror ( i, srcFile, target );
538
539 } catch (MediaException & excpt_r) {
540 if ( !canTryNextMirror ( excpt_r ) )
541 ZYPP_RETHROW(excpt_r);
542 lastErr = ZYPP_FWD_CURRENT_EXCPT();
543 }
544 }
545 if ( lastErr ) {
546 ZYPP_RETHROW( lastErr );
547 }
548
549 // should not happen
550 ZYPP_THROW( MediaException("No usable mirror available.") );
551
552}
553
554void MediaCurl::getFileCopyFromMirror(const int mirror, const OnMediaLocation &srcFile, const Pathname &target)
555{
556 const auto &filename = srcFile.filename();
557
558 // Optional files will send no report until data are actually received (we know it exists).
559 OptionalDownloadProgressReport reportfilter( srcFile.optional() );
561
562 auto &settings = _mirrorSettings.at(mirror);
563 AutoDispose<CURL*> curl( curl_easy_init(), []( CURL *hdl ) { if ( hdl ) { curl_easy_cleanup(hdl); } } );
564
565 RequestData rData;
566 rData.mirror = mirror;
567 rData.curl = curl.value ();
568
569 const auto &myUrl = _urls[mirror];
570
571 if( !myUrl.url().isValid() )
572 ZYPP_THROW(MediaBadUrlException(myUrl.url()));
573
574 if( myUrl.url().getHost().empty() )
576
577 Url fileurl( getFileUrl(mirror, filename) );
578
579 bool firstAuth = true; // bsc#1210870: authenticate must not return stored credentials more than once.
580 unsigned internalTry = 0;
581 static constexpr unsigned maxInternalTry = 3;
582
583 do
584 {
585 try
586 {
587 Pathname dest = target.absolutename();
588 if( assert_dir( dest.dirname() ) )
589 {
590 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
591 ZYPP_THROW( MediaSystemException(fileurl, "System error on " + dest.dirname().asString()) );
592 }
593
594 ManagedFile destNew { target.extend( ".new.zypp.XXXXXX" ) };
595 AutoFILE file;
596 {
597 AutoFREE<char> buf { ::strdup( (*destNew).c_str() ) };
598 if( ! buf )
599 {
600 ERR << "out of memory for temp file name" << endl;
601 ZYPP_THROW(MediaSystemException(fileurl, "out of memory for temp file name"));
602 }
603
604 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
605 if( tmp_fd == -1 )
606 {
607 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
609 }
610 destNew = ManagedFile( (*buf), filesystem::unlink );
611
612 file = ::fdopen( tmp_fd, "we" );
613 if ( ! file )
614 {
615 ERR << "fopen failed for file '" << destNew << "'" << endl;
617 }
618 tmp_fd.resetDispose(); // don't close it here! ::fdopen moved ownership to file
619 }
620
621 DBG << "dest: " << dest << endl;
622 DBG << "temp: " << destNew << endl;
623
624 setupEasy( rData, settings );
625
626 // set IFMODSINCE time condition (no download if not modified)
627 if( PathInfo(target).isExist() )
628 {
629 curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
630 curl_easy_setopt(curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
631 }
632 else
633 {
634 curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
635 curl_easy_setopt(curl, CURLOPT_TIMEVALUE, 0L);
636 }
637
639 curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
640 curl_easy_setopt(curl, CURLOPT_TIMEVALUE, 0L);
641 };
642
643 DBG << srcFile.filename().asString() << endl;
644
645 DBG << "URL: " << fileurl.asString() << endl;
646 // Use URL without options and without username and passwd
647 // (some proxies dislike them in the URL).
648 // Curl seems to need the just scheme, hostname and a path;
649 // the rest was already passed as curl options (in attachTo).
650 Url curlUrl( clearQueryString(fileurl) );
651
652 //
653 // See also Bug #154197 and ftp url definition in RFC 1738:
654 // The url "ftp://user@host/foo/bar/file" contains a path,
655 // that is relative to the user's home.
656 // The url "ftp://user@host//foo/bar/file" (or also with
657 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
658 // contains an absolute path.
659 //
660 _lastRedirect.clear();
661 std::string urlBuffer( curlUrl.asString());
662 CURLcode ret = curl_easy_setopt( curl, CURLOPT_URL,
663 urlBuffer.c_str() );
664 if ( ret != 0 ) {
666 }
667
668 ret = curl_easy_setopt( curl, CURLOPT_WRITEDATA, file.value() );
669 if ( ret != 0 ) {
671 }
672
673 // Set callback and perform.
674 internal::ProgressData progressData( curl, settings.timeout(), fileurl, srcFile.downloadSize(), &report );
675 report->start(fileurl, dest);
676
677 if ( curl_easy_setopt( curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
678 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
679 }
680
681 ret = executeCurl( rData );
682 #if CURLVERSION_AT_LEAST(7,19,4)
683 // bnc#692260: If the client sends a request with an If-Modified-Since header
684 // with a future date for the server, the server may respond 200 sending a
685 // zero size file.
686 // curl-7.19.4 introduces CURLINFO_CONDITION_UNMET to check this condition.
687 if ( ftell(file) == 0 && ret == 0 )
688 {
689 long httpReturnCode = 33;
690 if ( curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
691 {
692 long conditionUnmet = 33;
693 if ( curl_easy_getinfo( curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
694 {
695 WAR << "TIMECONDITION unmet - retry without." << endl;
696 curl_easy_setopt( curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
697 curl_easy_setopt( curl, CURLOPT_TIMEVALUE, 0L);
698 ret = executeCurl( rData );
699 }
700 }
701 }
702 #endif
703
704 if ( curl_easy_setopt( curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
705 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
706 }
707
708 if ( ret != 0 ) {
709 ERR << "curl error: " << ret << ": " << _curlError
710 << ", temp file size " << ftell(file)
711 << " bytes." << endl;
712
713 // the timeout is determined by the progress data object
714 // which holds whether the timeout was reached or not,
715 // otherwise it would be a user cancel
716
717 if ( progressData.fileSizeExceeded() )
719
720 evaluateCurlCode( rData, srcFile.filename(), ret, progressData.timeoutReached() );
721 }
722
723 long httpReturnCode = 0;
724 CURLcode infoRet = curl_easy_getinfo(curl,
725 CURLINFO_RESPONSE_CODE,
726 &httpReturnCode);
727 bool modified = true;
728 if (infoRet == CURLE_OK)
729 {
730 DBG << "HTTP response: " + str::numstring(httpReturnCode);
731 if ( httpReturnCode == 304
732 || ( httpReturnCode == 213 && (myUrl.url().getScheme() == "ftp" || myUrl.url().getScheme() == "tftp") ) ) // not modified
733 {
734 DBG << " Not modified.";
735 modified = false;
736 }
737 DBG << endl;
738 }
739 else
740 {
741 WAR << "Could not get the response code." << endl;
742 }
743
744 if (modified || infoRet != CURLE_OK)
745 {
746 // apply umask
747 if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
748 {
749 ERR << "Failed to chmod file " << destNew << endl;
750 }
751
752 file.resetDispose(); // we're going to close it manually here
753 if ( ::fclose( file ) )
754 {
755 ERR << "Fclose failed for file '" << destNew << "'" << endl;
757 }
758
759 // move the temp file into dest
760 if ( rename( destNew, dest ) != 0 ) {
761 ERR << "Rename failed" << endl;
763 }
764 destNew.resetDispose(); // no more need to unlink it
765 }
766
767 DBG << "done: " << PathInfo(dest) << endl;
768 break; // success!
769 }
770 // retry with proper authentication data
771 catch (MediaUnauthorizedException & ex_r)
772 {
773 if ( authenticate( myUrl.url(), settings, ex_r.hint(), firstAuth) ) {
774 firstAuth = false; // must not return stored credentials again
775 continue; // retry
776 }
777
779 ZYPP_RETHROW(ex_r);
780 }
781 // unexpected exception
782 catch (MediaException & excpt_r)
783 {
784 if ( typeid(excpt_r) == typeid( MediaCurlExceptionMayRetryInternaly ) ) {
785 ++internalTry;
786 if ( internalTry < maxInternalTry ) {
787 // just report (NO_ERROR); no interactive request to the user
788 report->problem(fileurl, media::DownloadProgressReport::NO_ERROR, excpt_r.asUserHistory()+_("Will try again..."));
789 continue; // retry
790 }
791 excpt_r.addHistory( str::Format(_("Giving up after %1% attempts.")) % maxInternalTry );
792 }
793
795 if( typeid(excpt_r) == typeid( media::MediaFileNotFoundException ) ||
796 typeid(excpt_r) == typeid( media::MediaNotAFileException ) )
797 {
799 }
800 report->finish(fileurl, reason, excpt_r.asUserHistory());
801 ZYPP_RETHROW(excpt_r);
802 }
803 } while ( true );
804
805 report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
806}
807
809
810bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
811{
812 // we need a non const pointer to work around the current API
813 auto that = const_cast<MediaCurl *>(this);
814
815 std::exception_ptr lastErr;
816 for ( int i = 0; i < (int)_urls.size (); i++ ) {
817 try {
818 return that->doGetDoesFileExist( i, filename );
819
820 } catch (MediaException & excpt_r) {
821 if ( !canTryNextMirror ( excpt_r ) )
822 ZYPP_RETHROW(excpt_r);
823 lastErr = ZYPP_FWD_CURRENT_EXCPT();
824 }
825 }
826 if ( lastErr ) {
827 try {
828 ZYPP_RETHROW( lastErr );
829 } catch ( const MediaFileNotFoundException &e ) {
830 // on file not found we return false
831 ZYPP_CAUGHT(e);
832 return false;
833 }
834 }
835 return false;
836}
837
839
841 const Pathname &filename,
842 CURLcode code,
843 bool timeout_reached) const
844{
845 if ( code != 0 )
846 {
847 const auto &baseMirr = _urls[rData.mirror];
848 Url url;
849 if (filename.empty())
850 url = baseMirr.url();
851 else
852 url = getFileUrl(rData.mirror, filename);
853
854 std::string err;
855 {
856 switch ( code )
857 {
858 case CURLE_UNSUPPORTED_PROTOCOL:
859 err = " Unsupported protocol";
860 if ( !_lastRedirect.empty() )
861 {
862 err += " or redirect (";
863 err += _lastRedirect;
864 err += ")";
865 }
866 break;
867 case CURLE_URL_MALFORMAT:
868 case CURLE_URL_MALFORMAT_USER:
869 err = " Bad URL";
870 break;
871 case CURLE_LOGIN_DENIED:
873 MediaUnauthorizedException(url, "Login failed.", _curlError, ""));
874 break;
875 case CURLE_HTTP_RETURNED_ERROR:
876 {
877 long httpReturnCode = 0;
878 CURLcode infoRet = curl_easy_getinfo( rData.curl,
879 CURLINFO_RESPONSE_CODE,
880 &httpReturnCode );
881 if ( infoRet == CURLE_OK )
882 {
883 std::string msg = "HTTP response: " + str::numstring( httpReturnCode );
884 switch ( httpReturnCode )
885 {
886 case 401:
887 {
888 std::string auth_hint = getAuthHint( rData.curl );
889
890 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
891 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
892
894 url, "Login failed.", _curlError, auth_hint
895 ));
896 }
897
898 case 502: // bad gateway (bnc #1070851)
899 case 503: // service temporarily unavailable (bnc #462545)
901 case 504: // gateway timeout
903 case 403:
904 {
905 std::string msg403;
906 if ( url.getHost().find(".suse.com") != std::string::npos )
907 msg403 = _("Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
908 else if (url.asString().find("novell.com") != std::string::npos)
909 msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
911 }
912 case 404:
913 case 410:
914 ZYPP_THROW(MediaFileNotFoundException(baseMirr.url(), filename));
915 }
916
917 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
919 }
920 else
921 {
922 std::string msg = "Unable to retrieve HTTP response:";
923 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
925 }
926 }
927 break;
928 case CURLE_FTP_COULDNT_RETR_FILE:
929#if CURLVERSION_AT_LEAST(7,16,0)
930 case CURLE_REMOTE_FILE_NOT_FOUND:
931#endif
932 case CURLE_FTP_ACCESS_DENIED:
933 case CURLE_TFTP_NOTFOUND:
934 err = "File not found";
935 ZYPP_THROW(MediaFileNotFoundException(baseMirr.url(), filename));
936 break;
937 case CURLE_BAD_PASSWORD_ENTERED:
938 case CURLE_FTP_USER_PASSWORD_INCORRECT:
939 err = "Login failed";
940 break;
941 case CURLE_COULDNT_RESOLVE_PROXY:
942 case CURLE_COULDNT_RESOLVE_HOST:
943 case CURLE_COULDNT_CONNECT:
944 case CURLE_FTP_CANT_GET_HOST:
945 err = "Connection failed";
946 break;
947 case CURLE_WRITE_ERROR:
948 err = "Write error";
949 break;
950 case CURLE_PARTIAL_FILE:
951 case CURLE_OPERATION_TIMEDOUT:
952 timeout_reached = true; // fall though to TimeoutException
953 // fall though...
954 case CURLE_ABORTED_BY_CALLBACK:
955 if( timeout_reached )
956 {
957 err = "Timeout reached";
959 }
960 else
961 {
962 err = "User abort";
963 }
964 break;
965
966 default:
967 err = "Curl error " + str::numstring( code );
968 break;
969 }
970
971 // uhm, no 0 code but unknown curl exception
973 }
974 }
975 else
976 {
977 // actually the code is 0, nothing happened
978 }
979}
980
982
983bool MediaCurl::doGetDoesFileExist( const int mirror, const Pathname & filename )
984{
985 DBG << filename.asString() << endl;
986
987 AutoDispose<CURL*> curl( curl_easy_init(), []( CURL *hdl ) { if ( hdl ) { curl_easy_cleanup(hdl); } } );
988 RequestData rData;
989 rData.mirror = mirror;
990 rData.curl = curl.value ();
991
992 const auto &myUrl = _urls[mirror];
993
994 if( !myUrl.url().isValid() )
995 ZYPP_THROW(MediaBadUrlException(myUrl.url()));
996
997 if( myUrl.url().getHost().empty() )
999
1000 Url url(getFileUrl(mirror, filename));
1001
1002 DBG << "URL: " << url.asString() << endl;
1003 // Use URL without options and without username and passwd
1004 // (some proxies dislike them in the URL).
1005 // Curl seems to need the just scheme, hostname and a path;
1006 // the rest was already passed as curl options (in attachTo).
1007 Url curlUrl( clearQueryString(url) );
1008
1009 // See also Bug #154197 and ftp url definition in RFC 1738:
1010 // The url "ftp://user@host/foo/bar/file" contains a path,
1011 // that is relative to the user's home.
1012 // The url "ftp://user@host//foo/bar/file" (or also with
1013 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1014 // contains an absolute path.
1015 //
1016 _lastRedirect.clear();
1017 std::string urlBuffer( curlUrl.asString());
1018
1019 CURLcode ok;
1020 bool canRetry = true;
1021 bool firstAuth = false;
1022 auto &settings = _mirrorSettings[mirror];
1023
1024 while ( canRetry ) {
1025 canRetry = false;
1026 setupEasy( rData, settings );
1027
1028 CURLcode ret = curl_easy_setopt( curl, CURLOPT_URL,
1029 urlBuffer.c_str() );
1030 if ( ret != 0 ) {
1032 }
1033
1034 AutoFILE file { ::fopen( "/dev/null", "w" ) };
1035 if ( !file ) {
1036 ERR << "fopen failed for /dev/null" << endl;
1037 ZYPP_THROW(MediaWriteException("/dev/null"));
1038 }
1039
1040 ret = curl_easy_setopt( curl, CURLOPT_WRITEDATA, (*file) );
1041 if ( ret != 0 ) {
1043 }
1044
1045 // If no head requests allowed (?head_requests=no):
1046 // Instead of returning no data with NOBODY, we return
1047 // little data, that works with broken servers, and
1048 // works for ftp as well, because retrieving only headers
1049 // ftp will return always OK code ?
1050 // See http://curl.haxx.se/docs/knownbugs.html #58
1051 const bool doHeadRequest = (myUrl.url().getScheme() == "http" || myUrl.url().getScheme() == "https") && settings.headRequestsAllowed();
1052 if ( doHeadRequest ) {
1053 curl_easy_setopt( curl, CURLOPT_NOBODY, 1L );
1054 } else {
1055 curl_easy_setopt( curl, CURLOPT_RANGE, "0-1" );
1056 }
1057
1058 try {
1059 ok = const_cast<MediaCurl *>(this)->executeCurl( rData );
1060 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
1061
1062 // as we are not having user interaction, the user can't cancel
1063 // the file existence checking, a callback or timeout return code
1064 // will be always a timeout.
1065 evaluateCurlCode( rData, filename, ok, true /* timeout */);
1066 }
1067 catch ( const MediaFileNotFoundException &e ) {
1068 // if the file did not exist then we can return false
1069 return false;
1070 }
1071 catch ( const MediaUnauthorizedException &e ) {
1072 if ( authenticate( myUrl.url(), settings, getAuthHint ( curl ), firstAuth ) ) {
1073 firstAuth = false;
1074 canRetry = true;
1075 continue;
1076 }
1077 }
1078
1079 // exists
1080 return ( ok == CURLE_OK );
1081 }
1082
1083 return false;
1084}
1085
1087//
1088int MediaCurl::aliveCallback( void *clientp, curl_off_t /*dltotal*/, curl_off_t dlnow, curl_off_t /*ultotal*/, curl_off_t /*ulnow*/ )
1089{
1090 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1091 if( pdata )
1092 {
1093 // Do not propagate dltotal in alive callbacks. MultiCurl uses this to
1094 // prevent a percentage raise while downloading a metalink file. Download
1095 // activity however is indicated by propagating the download rate (via dlnow).
1096 pdata->updateStats( 0.0, dlnow );
1097 return pdata->reportProgress();
1098 }
1099 return 0;
1100}
1101
1102int MediaCurl::progressCallback( void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow )
1103{
1104 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1105 if( pdata )
1106 {
1107 // work around curl bug that gives us old data
1108 long httpReturnCode = 0;
1109 if ( curl_easy_getinfo( pdata->curl(), CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
1110 return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
1111
1112 pdata->updateStats( dltotal, dlnow );
1113 return pdata->reportProgress();
1114 }
1115 return 0;
1116}
1117
1119{
1120 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>(clientp);
1121 return pdata ? pdata->curl() : 0;
1122}
1123
1125
1126std::string MediaCurl::getAuthHint( CURL *curl ) const
1127{
1128 long auth_info = CURLAUTH_NONE;
1129
1130 CURLcode infoRet =
1131 curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1132
1133 if(infoRet == CURLE_OK)
1134 {
1135 return CurlAuthData::auth_type_long2str(auth_info);
1136 }
1137
1138 return "";
1139}
1140
1145void MediaCurl::resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
1146{
1147 internal::ProgressData *data = reinterpret_cast<internal::ProgressData *>(clientp);
1148 if ( data ) {
1149 data->expectedFileSize( expectedFileSize );
1150 }
1151}
1152
1159{
1160 CURL *curl = rData.curl;
1161 const auto &baseUrl = _urls.at(rData.mirror);
1162
1163 if (!_multi)
1164 ZYPP_THROW(MediaCurlInitException(baseUrl.url()));
1165
1166 internal::CurlPollHelper _curlHelper(*this);
1167
1168 // add the easy handle to the multi instance
1169 if ( curl_multi_add_handle( _multi, curl ) != CURLM_OK )
1170 ZYPP_THROW(MediaCurlException( baseUrl.url(), "curl_multi_add_handle", "unknown error"));
1171
1172 // make sure the handle is cleanly removed from the multi handle
1173 OnScopeExit autoRemove([&](){ curl_multi_remove_handle( _multi, curl ); });
1174
1175 // kickstart curl, this will cause libcurl to go over the added handles and register sockets and timeouts
1176 CURLMcode mcode = _curlHelper.handleTimout();
1177 if (mcode != CURLM_OK)
1178 ZYPP_THROW(MediaCurlException( baseUrl.url(), "curl_multi_socket_action", "unknown error"));
1179
1180 bool canContinue = true;
1181 while ( canContinue ) {
1182
1183 CURLMsg *msg = nullptr;
1184 int nqueue = 0;
1185 while ((msg = curl_multi_info_read( _multi, &nqueue)) != 0) {
1186 if ( msg->msg != CURLMSG_DONE ) continue;
1187 if ( msg->easy_handle != curl ) continue;
1188
1189 return msg->data.result;
1190 }
1191
1192 // copy watched sockets in case curl changes the vector as we go over the events later
1193 std::vector<GPollFD> requestedFds = _curlHelper.socks;
1194
1195 int r = zypp_detail::zypp_poll( requestedFds, _curlHelper.timeout_ms.value_or( -1 ) );
1196 if ( r == -1 )
1197 ZYPP_THROW( MediaCurlException(baseUrl.url(), "zypp_poll() failed", "unknown error") );
1198
1199 // run curl
1200 if ( r == 0 ) {
1201 CURLMcode mcode = _curlHelper.handleTimout();
1202 if (mcode != CURLM_OK)
1203 ZYPP_THROW(MediaCurlException(baseUrl.url(), "curl_multi_socket_action", "unknown error"));
1204 } else {
1205 CURLMcode mcode = _curlHelper.handleSocketActions( requestedFds );
1206 if (mcode != CURLM_OK)
1207 ZYPP_THROW(MediaCurlException(baseUrl.url(), "curl_multi_socket_action", "unknown error"));
1208 }
1209 }
1210 return CURLE_OK;
1211}
1212
1213
1214 } // namespace media
1215} // namespace zypp
1216//
#define SET_OPTION_OFFT(opt, val)
Definition MediaCurl.cc:202
#define SET_OPTION(opt, val)
Definition MediaCurl.cc:195
Attempt to work around certain issues by autoretry in MediaCurl::getFileCopy E.g.
Definition MediaCurl.cc:170
MediaCurlExceptionMayRetryInternaly(const Url &url_r, const std::string &err_r, const std::string &msg_r)
Definition MediaCurl.cc:172
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:95
reference value() const
Reference to the Tp object.
void resetDispose()
Set no dispose function.
Store and operate with byte count.
Definition ByteCount.h:32
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition Exception.cc:140
void addHistory(const std::string &msg_r)
Add some message text to the history.
Definition Exception.cc:189
Describes a resource file located on a medium.
bool optional() const
Whether this is an optional resource.
const ByteCount & downloadSize() const
The size of the resource on the server.
const Pathname & filename() const
The path to the resource on the medium.
ProgressData()
Ctor no range [0,0](0).
Url manipulation class.
Definition Url.h:93
std::string asString() const
Returns a default string representation of the Url object.
Definition Url.cc:515
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:940
Wrapper class for stat/lstat.
Definition PathInfo.h:226
const Pathname & path() const
Return current Pathname.
Definition PathInfo.h:251
Pathname dirname() const
Return all but the last component od this path.
Definition Pathname.h:126
const char * c_str() const
String representation.
Definition Pathname.h:112
const std::string & asString() const
String representation.
Definition Pathname.h:93
bool empty() const
Test for an empty path.
Definition Pathname.h:116
AuthData_Ptr getCred(const Url &url)
Get credentials for the specified url.
static std::string auth_type_long2str(long auth_type)
Converts a long of ORed CURLAUTH_* identifiers into a string of comma separated list of authenticatio...
static long auth_type_str2long(std::string &auth_type_str)
Converts a string of comma separated list of authetication type names into a long of ORed CURLAUTH_* ...
MediaCurlException(const Url &url_r, std::string err_r, std::string msg_r)
bool doGetDoesFileExist(const int mirror, const Pathname &filename)
Definition MediaCurl.cc:983
void checkProtocol(const Url &url) const override
check the url is supported by the curl library
Definition MediaCurl.cc:263
static void setCookieFile(const Pathname &)
Definition MediaCurl.cc:249
bool getDoesFileExist(const Pathname &filename) const override
Repeatedly calls doGetDoesFileExist() until it successfully returns, fails unexpectedly,...
Definition MediaCurl.cc:810
static void resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
MediaMultiCurl needs to reset the expected filesize in case a metalink file is downloaded otherwise t...
std::string _currentCookieFile
Definition MediaCurl.h:126
static Pathname _cookieFile
Definition MediaCurl.h:127
std::string getAuthHint(CURL *curl) const
Return a comma separated list of available authentication methods supported by server.
void getFileCopyFromMirror(const int mirror, const OnMediaLocation &srcFile, const Pathname &target)
Definition MediaCurl.cc:554
static int progressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
Callback reporting download progress.
MediaCurl(const MediaUrl &url_r, const std::vector< MediaUrl > &mirrors_r, const Pathname &attach_point_hint_r)
Definition MediaCurl.cc:206
std::string _lastRedirect
to log/report redirections
Definition MediaCurl.h:130
char _curlError[CURL_ERROR_SIZE]
Definition MediaCurl.h:128
void evaluateCurlCode(RequestData &rData, const zypp::Pathname &fileName, CURLcode code, bool timeout) const
Evaluates a curl return code and throws the right MediaException filename Filename being downloaded c...
Definition MediaCurl.cc:840
CURLcode executeCurl(RequestData &rData)
void setCurlError(const char *error)
Definition MediaCurl.cc:254
static CURL * progressCallback_getcurl(void *clientp)
void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
Definition MediaCurl.cc:523
static int aliveCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
Callback sending just an alive trigger to the UI, without stats (e.g.
void disconnectFrom() override
Definition MediaCurl.cc:510
void setupEasy(RequestData &rData, TransferSettings &settings)
initializes the curl easy handle with the data from the url
Definition MediaCurl.cc:288
curl_slist * _customHeaders
Definition MediaCurl.h:131
void getFileCopy(const OnMediaLocation &srcFile, const Pathname &target) const override
Definition MediaCurl.cc:530
Just inherits Exception to separate media exceptions.
Url url() const
Primary Url used.
void disconnect()
Use concrete handler to isconnect media.
void release(const std::string &ejectDev="")
Use concrete handler to release the media.
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)
static bool canTryNextMirror(const Excpt &excpt_r)
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
std::vector< TransferSettings > _mirrorSettings
const std::string & hint() const
comma separated list of available authentication types
Holds transfer setting.
const std::string & password() const
auth password
long maxDownloadSpeed() const
Maximum download speed (bytes per second)
long connectTimeout() const
connection timeout
const std::string & authType() const
get the allowed authentication types
long timeout() const
transfer timeout
void setUsername(const std::string &val_r)
sets the auth username
const Pathname & clientCertificatePath() const
SSL client certificate file.
std::string userPassword() const
returns the user and password as a user:pass string
long minDownloadSpeed() const
Minimum download speed (bytes per second) until the connection is dropped.
const Headers & headers() const
returns a list of all added headers (trimmed)
const std::string & proxy() const
proxy host
const Pathname & clientKeyPath() const
SSL client key file.
std::string proxyUserPassword() const
returns the proxy user and password as a user:pass string
bool verifyHostEnabled() const
Whether to verify host for ssl.
const std::string & userAgentString() const
user agent string (trimmed)
void setPassword(const std::string &val_r)
sets the auth password
bool proxyEnabled() const
proxy is enabled
const std::string & username() const
auth username
const std::string & proxyUsername() const
proxy auth username
const Pathname & certificateAuthoritiesPath() const
SSL certificate authorities path ( default: /etc/ssl/certs )
bool verifyPeerEnabled() const
Whether to verify peer for ssl.
#define EXPLICITLY_NO_PROXY
size_t log_redirects_curl(char *ptr, size_t size, size_t nmemb, void *userdata)
void globalInitCurlOnce()
Definition curlhelper.cc:64
std::string curlUnEscape(const std::string &text_r)
void setupZYPP_MEDIA_CURL_DEBUG(CURL *curl)
Setup CURLOPT_VERBOSE and CURLOPT_DEBUGFUNCTION according to env::ZYPP_MEDIA_CURL_DEBUG.
CURLcode setCurlRedirProtocols(CURL *curl)
Definition Arch.h:364
int ZYPP_MEDIA_CURL_IPRESOLVE()
4/6 to force IPv4/v6
Definition curlhelper.cc:45
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
Definition PathInfo.h:805
int assert_file_mode(const Pathname &path, unsigned mode)
Like assert_file but enforce mode even if the file already exists.
Definition PathInfo.cc:1210
int unlink(const Pathname &path)
Like 'unlink'.
Definition PathInfo.cc:705
std::string numstring(char n, int w=0)
Definition String.h:290
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition String.h:500
Url details namespace.
Definition UrlBase.cc:58
int zypp_poll(std::vector< GPollFD > &fds, int timeout)
Small wrapper around g_poll that additionally listens to the shutdown FD returned by ZYpp::shutdownSi...
Definition ZYppImpl.cc:313
Easy-to use interface to the ZYPP dependency resolver.
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition ManagedFile.h:27
AutoDispose< void > OnScopeExit
CURLMcode handleSocketActions(const std::vector< GPollFD > &actionsFds, int first=0)
std::vector< GPollFD > socks
std::optional< long > timeout_ms
Bottleneck filtering all DownloadProgressReport issued from Media[Muli]Curl.
ByteCount _expectedFileSize
Definition MediaCurl.cc:82
curl_off_t _dnlNow
Bytes downloaded now.
Definition MediaCurl.cc:92
int _dnlPercent
Percent completed or 0 if _dnlTotal is unknown.
Definition MediaCurl.cc:94
time_t _timeRcv
Start of no-data timeout.
Definition MediaCurl.cc:87
ByteCount expectedFileSize() const
Definition MediaCurl.cc:70
time_t _timeLast
Start last period(~1sec)
Definition MediaCurl.cc:86
int reportProgress() const
Definition MediaCurl.cc:155
double _drateLast
Download rate in last period.
Definition MediaCurl.cc:97
bool timeoutReached() const
Definition MediaCurl.cc:64
void expectedFileSize(ByteCount newval_r)
Definition MediaCurl.cc:73
curl_off_t _dnlLast
Bytes downloaded at period start.
Definition MediaCurl.cc:91
bool fileSizeExceeded() const
Definition MediaCurl.cc:67
void updateStats(curl_off_t dltotal=0.0, curl_off_t dlnow=0.0)
Definition MediaCurl.cc:112
double _drateTotal
Download rate so far.
Definition MediaCurl.cc:96
zypp::callback::SendReport< zypp::media::DownloadProgressReport > * report
Definition MediaCurl.cc:83
curl_off_t _dnlTotal
Bytes to download or 0 if unknown.
Definition MediaCurl.cc:90
time_t _timeStart
Start total stats.
Definition MediaCurl.cc:85
ProgressData(CURL *curl, time_t timeout=0, zypp::Url url=zypp::Url(), zypp::ByteCount expectedFileSize_r=0, zypp::callback::SendReport< zypp::media::DownloadProgressReport > *_report=nullptr)
Definition MediaCurl.cc:102
AutoDispose<int> calling ::close
AutoDispose<FILE*> calling ::fclose
Structure holding values of curlrc options.
Definition curlconfig.h:27
std::string proxyuserpwd
Definition curlconfig.h:49
static int parseConfig(CurlConfig &config, const std::string &filename="")
Parse a curlrc file and store the result in the config structure.
Definition curlconfig.cc:24
Convenient building of std::string with boost::format.
Definition String.h:254
#define zypp_defer
#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_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition Exception.h:471
#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 ERR
Definition Logger.h:102
#define WAR
Definition Logger.h:101
Interface to gettext.