libzypp 17.31.31
MediaCurl.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
13#include <iostream>
14#include <chrono>
15#include <list>
16
17#include <zypp/base/Logger.h>
18#include <zypp/ExternalProgram.h>
19#include <zypp/base/String.h>
20#include <zypp/base/Gettext.h>
21#include <zypp-core/parser/Sysconfig>
22#include <zypp/base/Gettext.h>
23
25#include <zypp-core/zyppng/base/private/linuxhelpers_p.h>
27#include <zypp-curl/ProxyInfo>
28#include <zypp-curl/auth/CurlAuthData>
29#include <zypp-media/auth/CredentialManager>
30#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
45using std::endl;
46
47namespace internal {
48 using namespace zypp;
52 struct OptionalDownloadProgressReport : public callback::ReceiveReport<media::DownloadProgressReport>
53 {
54 using TimePoint = std::chrono::steady_clock::time_point;
55
56 OptionalDownloadProgressReport( bool isOptional=false )
57 : _oldRec { Distributor::instance().getReceiver() }
58 , _isOptional { isOptional }
59 { connect(); }
60
63
64 void reportbegin() override
65 { if ( _oldRec ) _oldRec->reportbegin(); }
66
67 void reportend() override
68 { if ( _oldRec ) _oldRec->reportend(); }
69
70 void report( const UserData & userData_r = UserData() ) override
71 { if ( _oldRec ) _oldRec->report( userData_r ); }
72
73
74 void start( const Url & file_r, Pathname localfile_r ) override
75 {
76 if ( not _oldRec ) return;
77 if ( _isOptional ) {
78 // delay start until first data are received.
79 _startFile = file_r;
80 _startLocalfile = std::move(localfile_r);
81 return;
82 }
83 _oldRec->start( file_r, localfile_r );
84 }
85
86 bool progress( int value_r, const Url & file_r, double dbps_avg_r = -1, double dbps_current_r = -1 ) override
87 {
88 if ( not _oldRec ) return true;
89 if ( notStarted() ) {
90 if ( not ( value_r || dbps_avg_r || dbps_current_r ) )
91 return true;
92 sendStart();
93 }
94
95 //static constexpr std::chrono::milliseconds minfequency { 1000 }; only needed if we'd avoid sending reports without change
96 static constexpr std::chrono::milliseconds maxfequency { 100 };
97 TimePoint now { TimePoint::clock::now() };
98 TimePoint::duration elapsed { now - _lastProgressSent };
99 if ( elapsed < maxfequency )
100 return true; // continue
101 _lastProgressSent = now;
102 return _oldRec->progress( value_r, file_r, dbps_avg_r, dbps_current_r );
103 }
104
105 Action problem( const Url & file_r, Error error_r, const std::string & description_r ) override
106 {
107 if ( not _oldRec || notStarted() ) return ABORT;
108 return _oldRec->problem( file_r, error_r, description_r );
109 }
110
111 void finish( const Url & file_r, Error error_r, const std::string & reason_r ) override
112 {
113 if ( not _oldRec || notStarted() ) return;
114 _oldRec->finish( file_r, error_r, reason_r );
115 }
116
117 private:
118 // _isOptional also indicates the delayed start
119 bool notStarted() const
120 { return _isOptional; }
121
123 {
124 if ( _isOptional ) {
125 // we know _oldRec is valid...
126 _oldRec->start( std::move(_startFile), std::move(_startLocalfile) );
127 _isOptional = false;
128 }
129 }
130
131 private:
137 };
138
140 {
141 ProgressData( CURL *curl, time_t timeout = 0, const zypp::Url & url = zypp::Url(),
142 zypp::ByteCount expectedFileSize_r = 0,
144
145 void updateStats( curl_off_t dltotal = 0.0, curl_off_t dlnow = 0.0 );
146
147 int reportProgress() const;
148
149 CURL * curl()
150 { return _curl; }
151
152 bool timeoutReached() const
153 { return _timeoutReached; }
154
155 bool fileSizeExceeded() const
156 { return _fileSizeExceeded; }
157
160
162 { _expectedFileSize = newval_r; }
163
164 private:
165 CURL * _curl;
167 time_t _timeout;
172
173 time_t _timeStart = 0;
174 time_t _timeLast = 0;
175 time_t _timeRcv = 0;
176 time_t _timeNow = 0;
177
178 curl_off_t _dnlTotal = 0.0;
179 curl_off_t _dnlLast = 0.0;
180 curl_off_t _dnlNow = 0.0;
181
182 int _dnlPercent= 0;
183
184 double _drateTotal= 0.0;
185 double _drateLast = 0.0;
186 };
187
188
189
190 ProgressData::ProgressData(CURL *curl, time_t timeout, const Url &url, ByteCount expectedFileSize_r, zypp::callback::SendReport< zypp::media::DownloadProgressReport> *_report)
191 : _curl( curl )
192 , _url( url )
193 , _timeout( timeout )
194 , _timeoutReached( false )
195 , _fileSizeExceeded ( false )
196 , _expectedFileSize( expectedFileSize_r )
197 , report( _report )
198 {}
199
200 void ProgressData::updateStats( curl_off_t dltotal, curl_off_t dlnow )
201 {
202 time_t now = _timeNow = time(0);
203
204 // If called without args (0.0), recompute based on the last values seen
205 if ( dltotal && dltotal != _dnlTotal )
206 _dnlTotal = dltotal;
207
208 if ( dlnow && dlnow != _dnlNow )
209 {
210 _timeRcv = now;
211 _dnlNow = dlnow;
212 }
213
214 // init or reset if time jumps back
215 if ( !_timeStart || _timeStart > now )
216 _timeStart = _timeLast = _timeRcv = now;
217
218 // timeout condition
219 if ( _timeout )
220 _timeoutReached = ( (now - _timeRcv) > _timeout );
221
222 // check if the downloaded data is already bigger than what we expected
223 _fileSizeExceeded = _expectedFileSize > 0 && _expectedFileSize < static_cast<ByteCount::SizeType>(_dnlNow);
224
225 // percentage:
226 if ( _dnlTotal )
227 _dnlPercent = int( _dnlNow * 100 / _dnlTotal );
228
229 // download rates:
230 _drateTotal = double(_dnlNow) / std::max( int(now - _timeStart), 1 );
231
232 if ( _timeLast < now )
233 {
234 _drateLast = double(_dnlNow - _dnlLast) / int(now - _timeLast);
235 // start new period
236 _timeLast = now;
238 }
239 else if ( _timeStart == _timeLast )
241 }
242
244 {
245 if ( _fileSizeExceeded )
246 return 1;
247 if ( _timeoutReached )
248 return 1; // no-data timeout
249 if ( report && !(*report)->progress( _dnlPercent, _url, _drateTotal, _drateLast ) )
250 return 1; // user requested abort
251 return 0;
252 }
253
254 const char * anonymousIdHeader()
255 {
256 // we need to add the release and identifier to the
257 // agent string.
258 // The target could be not initialized, and then this information
259 // is guessed.
260 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
261 static const std::string _value( str::trim( str::form(
262 "X-ZYpp-AnonymousId: %s",
263 Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str()
264 )));
265 return _value.c_str();
266 }
267
269 {
270 // we need to add the release and identifier to the
271 // agent string.
272 // The target could be not initialized, and then this information
273 // is guessed.
274 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
275 static const std::string _value( str::trim( str::form(
276 "X-ZYpp-DistributionFlavor: %s",
277 Target::distributionFlavor( Pathname()/*guess root*/ ).c_str()
278 )));
279 return _value.c_str();
280 }
281
282 const char * agentString()
283 {
284 // we need to add the release and identifier to the
285 // agent string.
286 // The target could be not initialized, and then this information
287 // is guessed.
288 // bsc#1212187: HTTP/2 RFC 9113 forbids fields ending with a space
289 static const std::string _value( str::trim( str::form(
290 "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s"
291 , curl_version_info(CURLVERSION_NOW)->version
292 , Target::targetDistribution( Pathname()/*guess root*/ ).c_str()
293 )));
294 return _value.c_str();
295 }
296
301 {
302 public:
304 const std::string & err_r,
305 const std::string & msg_r )
306 : media::MediaCurlException( url_r, err_r, msg_r )
307 {}
308 //~MediaCurlExceptionMayRetryInternaly() noexcept {}
309 };
310
311}
312
313
314using namespace internal;
315using namespace zypp::base;
316
317namespace zypp {
318
319 namespace media {
320
321Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
322
323// we use this define to unbloat code as this C setting option
324// and catching exception is done frequently.
326#define SET_OPTION(opt,val) do { \
327 ret = curl_easy_setopt ( _curl, opt, val ); \
328 if ( ret != 0) { \
329 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError)); \
330 } \
331 } while ( false )
332
333#define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val)
334#define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val)
335#define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val)
336
338 const Pathname & attach_point_hint_r )
339 : MediaNetworkCommonHandler( url_r, attach_point_hint_r,
340 "/", // urlpath at attachpoint
341 true ), // does_download
342 _curl( NULL ),
343 _customHeaders(0L)
344{
345 _curlError[0] = '\0';
346
347 MIL << "MediaCurl::MediaCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
348
350
351 if( !attachPoint().empty())
352 {
353 PathInfo ainfo(attachPoint());
354 Pathname apath(attachPoint() + "XXXXXX");
355 char *atemp = ::strdup( apath.asString().c_str());
356 char *atest = NULL;
357 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
358 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
359 {
360 WAR << "attach point " << ainfo.path()
361 << " is not useable for " << url_r.getScheme() << endl;
362 setAttachPoint("", true);
363 }
364 else if( atest != NULL)
365 ::rmdir(atest);
366
367 if( atemp != NULL)
368 ::free(atemp);
369 }
370}
371
373{
375}
376
377void MediaCurl::setCookieFile( const Pathname &fileName )
378{
379 _cookieFile = fileName;
380}
381
383
384void MediaCurl::checkProtocol(const Url &url) const
385{
386 curl_version_info_data *curl_info = NULL;
387 curl_info = curl_version_info(CURLVERSION_NOW);
388 // curl_info does not need any free (is static)
389 if (curl_info->protocols)
390 {
391 const char * const *proto;
392 std::string scheme( url.getScheme());
393 bool found = false;
394 for(proto=curl_info->protocols; !found && *proto; ++proto)
395 {
396 if( scheme == std::string((const char *)*proto))
397 found = true;
398 }
399 if( !found)
400 {
401 std::string msg("Unsupported protocol '");
402 msg += scheme;
403 msg += "'";
405 }
406 }
407}
408
410{
412
413 curl_easy_setopt(_curl, CURLOPT_HEADERFUNCTION, log_redirects_curl);
414 curl_easy_setopt(_curl, CURLOPT_HEADERDATA, &_lastRedirect);
415 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
416 if ( ret != 0 ) {
417 ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
418 }
419
420 SET_OPTION(CURLOPT_FAILONERROR, 1L);
421 SET_OPTION(CURLOPT_NOSIGNAL, 1L);
422
423 // create non persistant settings
424 // so that we don't add headers twice
425 TransferSettings vol_settings(_settings);
426
427 // add custom headers for download.opensuse.org (bsc#955801)
428 if ( _url.getHost() == "download.opensuse.org" )
429 {
430 vol_settings.addHeader(anonymousIdHeader());
431 vol_settings.addHeader(distributionFlavorHeader());
432 }
433 vol_settings.addHeader("Pragma:");
434
436
437 // fill some settings from url query parameters
438 try
439 {
441 }
442 catch ( const MediaException &e )
443 {
445 ZYPP_RETHROW(e);
446 }
447 // if the proxy was not set (or explicitly unset) by url, then look...
448 if ( _settings.proxy().empty() )
449 {
450 // ...at the system proxy settings
452 }
453
456 {
457 case 4: SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); break;
458 case 6: SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); break;
459 }
460
464 SET_OPTION(CURLOPT_CONNECTTIMEOUT, _settings.connectTimeout());
465 // If a transfer timeout is set, also set CURLOPT_TIMEOUT to an upper limit
466 // just in case curl does not trigger its progress callback frequently
467 // enough.
468 if ( _settings.timeout() )
469 {
470 SET_OPTION(CURLOPT_TIMEOUT, 3600L);
471 }
472
473 // follow any Location: header that the server sends as part of
474 // an HTTP header (#113275)
475 SET_OPTION(CURLOPT_FOLLOWLOCATION, 1L);
476 // 3 redirects seem to be too few in some cases (bnc #465532)
477 SET_OPTION(CURLOPT_MAXREDIRS, 6L);
478
479 if ( _url.getScheme() == "https" )
480 {
481 // restrict following of redirections from https to https only
482 // but be less restrictive for d.o.o
483 bool allowHttp = ( _url.getHost() == "download.opensuse.org" );
484 if ( :: internal::setCurlRedirProtocols ( _curl, allowHttp ) != CURLE_OK ) {
486 }
487
490 {
492 }
493
495 {
496 SET_OPTION(CURLOPT_SSLCERT, _settings.clientCertificatePath().c_str());
497 }
498 if( ! _settings.clientKeyPath().empty() )
499 {
500 SET_OPTION(CURLOPT_SSLKEY, _settings.clientKeyPath().c_str());
501 }
502
503#ifdef CURLSSLOPT_ALLOW_BEAST
504 // see bnc#779177
505 ret = curl_easy_setopt( _curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST );
506 if ( ret != 0 ) {
509 }
510#endif
511 SET_OPTION(CURLOPT_SSL_VERIFYPEER, _settings.verifyPeerEnabled() ? 1L : 0L);
512 SET_OPTION(CURLOPT_SSL_VERIFYHOST, _settings.verifyHostEnabled() ? 2L : 0L);
513 // bnc#903405 - POODLE: libzypp should only talk TLS
514 SET_OPTION(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
515 }
516
517 SET_OPTION(CURLOPT_USERAGENT, _settings.userAgentString().c_str() );
518
519 /* Fixes bsc#1174011 "auth=basic ignored in some cases"
520 * We should proactively add the password to the request if basic auth is configured
521 * and a password is available in the credentials but not in the URL.
522 *
523 * We will be a bit paranoid here and require that the URL has a user embedded, otherwise we go the default route
524 * and ask the server first about the auth method
525 */
526 if ( _settings.authType() == "basic"
527 && _settings.username().size()
528 && !_settings.password().size() ) {
529
531 const auto cred = cm.getCred( _url );
532 if ( cred && cred->valid() ) {
533 if ( !_settings.username().size() )
534 _settings.setUsername(cred->username());
535 _settings.setPassword(cred->password());
536 }
537 }
538
539 /*---------------------------------------------------------------*
540 CURLOPT_USERPWD: [user name]:[password]
541
542 Url::username/password -> CURLOPT_USERPWD
543 If not provided, anonymous FTP identification
544 *---------------------------------------------------------------*/
545
546 if ( _settings.userPassword().size() )
547 {
548 SET_OPTION(CURLOPT_USERPWD, _settings.userPassword().c_str());
549 std::string use_auth = _settings.authType();
550 if (use_auth.empty())
551 use_auth = "digest,basic"; // our default
552 long auth = CurlAuthData::auth_type_str2long(use_auth);
553 if( auth != CURLAUTH_NONE)
554 {
555 DBG << "Enabling HTTP authentication methods: " << use_auth
556 << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
557 SET_OPTION(CURLOPT_HTTPAUTH, auth);
558 }
559 }
560
561 if ( _settings.proxyEnabled() && ! _settings.proxy().empty() )
562 {
563 DBG << "Proxy: '" << _settings.proxy() << "'" << endl;
564 SET_OPTION(CURLOPT_PROXY, _settings.proxy().c_str());
565 SET_OPTION(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
566 /*---------------------------------------------------------------*
567 * CURLOPT_PROXYUSERPWD: [user name]:[password]
568 *
569 * Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
570 * If not provided, $HOME/.curlrc is evaluated
571 *---------------------------------------------------------------*/
572
573 std::string proxyuserpwd = _settings.proxyUserPassword();
574
575 if ( proxyuserpwd.empty() )
576 {
577 CurlConfig curlconf;
578 CurlConfig::parseConfig(curlconf); // parse ~/.curlrc
579 if ( curlconf.proxyuserpwd.empty() )
580 DBG << "Proxy: ~/.curlrc does not contain the proxy-user option" << endl;
581 else
582 {
583 proxyuserpwd = curlconf.proxyuserpwd;
584 DBG << "Proxy: using proxy-user from ~/.curlrc" << endl;
585 }
586 }
587 else
588 {
589 DBG << "Proxy: using provided proxy-user '" << _settings.proxyUsername() << "'" << endl;
590 }
591
592 if ( ! proxyuserpwd.empty() )
593 {
594 SET_OPTION(CURLOPT_PROXYUSERPWD, curlUnEscape( proxyuserpwd ).c_str());
595 }
596 }
597#if CURLVERSION_AT_LEAST(7,19,4)
598 else if ( _settings.proxy() == EXPLICITLY_NO_PROXY )
599 {
600 // Explicitly disabled in URL (see fillSettingsFromUrl()).
601 // This should also prevent libcurl from looking into the environment.
602 DBG << "Proxy: explicitly NOPROXY" << endl;
603 SET_OPTION(CURLOPT_NOPROXY, "*");
604 }
605#endif
606 else
607 {
608 DBG << "Proxy: not explicitly set" << endl;
609 DBG << "Proxy: libcurl may look into the environment" << endl;
610 }
611
613 if ( _settings.minDownloadSpeed() != 0 )
614 {
615 SET_OPTION(CURLOPT_LOW_SPEED_LIMIT, _settings.minDownloadSpeed());
616 // default to 10 seconds at low speed
617 SET_OPTION(CURLOPT_LOW_SPEED_TIME, 60L);
618 }
619
620#if CURLVERSION_AT_LEAST(7,15,5)
621 if ( _settings.maxDownloadSpeed() != 0 )
622 SET_OPTION_OFFT(CURLOPT_MAX_RECV_SPEED_LARGE, _settings.maxDownloadSpeed());
623#endif
624
625 /*---------------------------------------------------------------*
626 *---------------------------------------------------------------*/
627
629 if ( ::geteuid() == 0 || PathInfo(_currentCookieFile).owner() == ::geteuid() )
631 if ( str::strToBool( _url.getQueryParam( "cookies" ), true ) )
632 SET_OPTION(CURLOPT_COOKIEFILE, _currentCookieFile.c_str() );
633 else
634 MIL << "No cookies requested" << endl;
635 SET_OPTION(CURLOPT_COOKIEJAR, _currentCookieFile.c_str() );
636 SET_OPTION(CURLOPT_XFERINFOFUNCTION, &progressCallback );
637 SET_OPTION(CURLOPT_NOPROGRESS, 0L);
638
639#if CURLVERSION_AT_LEAST(7,18,0)
640 // bnc #306272
641 SET_OPTION(CURLOPT_PROXY_TRANSFER_MODE, 1L );
642#endif
643 // Append settings custom headers to curl.
644 // TransferSettings assert strings are trimmed (HTTP/2 RFC 9113)
645 for ( const auto &header : vol_settings.headers() ) {
646 _customHeaders = curl_slist_append(_customHeaders, header.c_str());
647 if ( !_customHeaders )
649 }
650 SET_OPTION(CURLOPT_HTTPHEADER, _customHeaders);
651}
652
654
655
656void MediaCurl::attachTo (bool next)
657{
658 if ( next )
660
661 if ( !_url.isValid() )
663
666 {
668 }
669
670 disconnectFrom(); // clean _curl if needed
671 _curl = curl_easy_init();
672 if ( !_curl ) {
674 }
675 try
676 {
677 setupEasy();
678 }
679 catch (Exception & ex)
680 {
682 ZYPP_RETHROW(ex);
683 }
684
685 // FIXME: need a derived class to propelly compare url's
687 setMediaSource(media);
688}
689
690bool
692{
693 return MediaHandler::checkAttachPoint( apoint, true, true);
694}
695
697
699{
700 if ( _customHeaders )
701 {
702 curl_slist_free_all(_customHeaders);
703 _customHeaders = 0L;
704 }
705
706 if ( _curl )
707 {
708 // bsc#1201092: Strange but within global_dtors we may exceptions here.
709 try { curl_easy_cleanup( _curl ); }
710 catch (...) { ; }
711 _curl = NULL;
712 }
713}
714
716
717void MediaCurl::releaseFrom( const std::string & ejectDev )
718{
719 disconnect();
720}
721
723
724void MediaCurl::getFile( const OnMediaLocation &file ) const
725{
726 // Use absolute file name to prevent access of files outside of the
727 // hierarchy below the attach point.
728 getFileCopy( file, localPath(file.filename()).absolutename() );
729}
730
732
733void MediaCurl::getFileCopy( const OnMediaLocation & srcFile , const Pathname & target ) const
734{
735
736 const auto &filename = srcFile.filename();
737
738 // Optional files will send no report until data are actually received (we know it exists).
739 OptionalDownloadProgressReport reportfilter( srcFile.optional() );
741
742 Url fileurl(getFileUrl(filename));
743
744 bool firstAuth = true; // bsc#1210870: authenticate must not return stored credentials more than once.
745 unsigned internalTry = 0;
746 static constexpr unsigned maxInternalTry = 3;
747
748 do
749 {
750 try
751 {
752 doGetFileCopy( srcFile, target, report );
753 break; // success!
754 }
755 // retry with proper authentication data
756 catch (MediaUnauthorizedException & ex_r)
757 {
758 if ( authenticate(ex_r.hint(), firstAuth) ) {
759 firstAuth = false; // must not return stored credentials again
760 continue; // retry
761 }
762
764 ZYPP_RETHROW(ex_r);
765 }
766 // unexpected exception
767 catch (MediaException & excpt_r)
768 {
769 if ( typeid(excpt_r) == typeid( MediaCurlExceptionMayRetryInternaly ) ) {
770 ++internalTry;
771 if ( internalTry < maxInternalTry ) {
772 // just report (NO_ERROR); no interactive request to the user
773 report->problem(fileurl, media::DownloadProgressReport::NO_ERROR, excpt_r.asUserHistory()+_("Will try again..."));
774 continue; // retry
775 }
776 excpt_r.addHistory( str::Format(_("Giving up after %1% attempts.")) % maxInternalTry );
777 }
778
780 if( typeid(excpt_r) == typeid( media::MediaFileNotFoundException ) ||
781 typeid(excpt_r) == typeid( media::MediaNotAFileException ) )
782 {
784 }
785 report->finish(fileurl, reason, excpt_r.asUserHistory());
786 ZYPP_RETHROW(excpt_r);
787 }
788 }
789 while ( true );
790 report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
791}
792
794
795bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
796{
797 bool retry = false;
798
799 do
800 {
801 try
802 {
803 return doGetDoesFileExist( filename );
804 }
805 // authentication problem, retry with proper authentication data
806 catch (MediaUnauthorizedException & ex_r)
807 {
808 if(authenticate(ex_r.hint(), !retry))
809 retry = true;
810 else
811 ZYPP_RETHROW(ex_r);
812 }
813 // unexpected exception
814 catch (MediaException & excpt_r)
815 {
816 ZYPP_RETHROW(excpt_r);
817 }
818 }
819 while (retry);
820
821 return false;
822}
823
825
827 CURLcode code,
828 bool timeout_reached) const
829{
830 if ( code != 0 )
831 {
832 Url url;
833 if (filename.empty())
834 url = _url;
835 else
836 url = getFileUrl(filename);
837
838 std::string err;
839 {
840 switch ( code )
841 {
842 case CURLE_UNSUPPORTED_PROTOCOL:
843 err = " Unsupported protocol";
844 if ( !_lastRedirect.empty() )
845 {
846 err += " or redirect (";
847 err += _lastRedirect;
848 err += ")";
849 }
850 break;
851 case CURLE_URL_MALFORMAT:
852 case CURLE_URL_MALFORMAT_USER:
853 err = " Bad URL";
854 break;
855 case CURLE_LOGIN_DENIED:
857 MediaUnauthorizedException(url, "Login failed.", _curlError, ""));
858 break;
859 case CURLE_HTTP_RETURNED_ERROR:
860 {
861 long httpReturnCode = 0;
862 CURLcode infoRet = curl_easy_getinfo( _curl,
863 CURLINFO_RESPONSE_CODE,
864 &httpReturnCode );
865 if ( infoRet == CURLE_OK )
866 {
867 std::string msg = "HTTP response: " + str::numstring( httpReturnCode );
868 switch ( httpReturnCode )
869 {
870 case 401:
871 {
872 std::string auth_hint = getAuthHint();
873
874 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
875 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
876
878 url, "Login failed.", _curlError, auth_hint
879 ));
880 }
881
882 case 502: // bad gateway (bnc #1070851)
883 case 503: // service temporarily unavailable (bnc #462545)
885 case 504: // gateway timeout
887 case 403:
888 {
889 std::string msg403;
890 if ( url.getHost().find(".suse.com") != std::string::npos )
891 msg403 = _("Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
892 else if (url.asString().find("novell.com") != std::string::npos)
893 msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
895 }
896 case 404:
897 case 410:
899 }
900
901 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
903 }
904 else
905 {
906 std::string msg = "Unable to retrieve HTTP response:";
907 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
909 }
910 }
911 break;
912 case CURLE_FTP_COULDNT_RETR_FILE:
913#if CURLVERSION_AT_LEAST(7,16,0)
914 case CURLE_REMOTE_FILE_NOT_FOUND:
915#endif
916 case CURLE_FTP_ACCESS_DENIED:
917 case CURLE_TFTP_NOTFOUND:
918 err = "File not found";
920 break;
921 case CURLE_BAD_PASSWORD_ENTERED:
922 case CURLE_FTP_USER_PASSWORD_INCORRECT:
923 err = "Login failed";
924 break;
925 case CURLE_COULDNT_RESOLVE_PROXY:
926 case CURLE_COULDNT_RESOLVE_HOST:
927 case CURLE_COULDNT_CONNECT:
928 case CURLE_FTP_CANT_GET_HOST:
929 err = "Connection failed";
930 break;
931 case CURLE_WRITE_ERROR:
932 err = "Write error";
933 break;
934 case CURLE_PARTIAL_FILE:
935 case CURLE_OPERATION_TIMEDOUT:
936 timeout_reached = true; // fall though to TimeoutException
937 // fall though...
938 case CURLE_ABORTED_BY_CALLBACK:
939 if( timeout_reached )
940 {
941 err = "Timeout reached";
943 }
944 else
945 {
946 err = "User abort";
947 }
948 break;
949
950 // Attempt to work around certain issues by autoretry in MediaCurl::getFileCopy
951 case CURLE_HTTP2:
952 case CURLE_HTTP2_STREAM:
953 err = "Curl error " + str::numstring( code );
955 break;
956
957 default:
958 err = "Curl error " + str::numstring( code );
959 break;
960 }
961
962 // uhm, no 0 code but unknown curl exception
964 }
965 }
966 else
967 {
968 // actually the code is 0, nothing happened
969 }
970}
971
973
974bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
975{
976 DBG << filename.asString() << endl;
977
978 if(!_url.isValid())
980
981 if(_url.getHost().empty())
983
984 Url url(getFileUrl(filename));
985
986 DBG << "URL: " << url.asString() << endl;
987 // Use URL without options and without username and passwd
988 // (some proxies dislike them in the URL).
989 // Curl seems to need the just scheme, hostname and a path;
990 // the rest was already passed as curl options (in attachTo).
991 Url curlUrl( clearQueryString(url) );
992
993 //
994 // See also Bug #154197 and ftp url definition in RFC 1738:
995 // The url "ftp://user@host/foo/bar/file" contains a path,
996 // that is relative to the user's home.
997 // The url "ftp://user@host//foo/bar/file" (or also with
998 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
999 // contains an absolute path.
1000 //
1001 _lastRedirect.clear();
1002 std::string urlBuffer( curlUrl.asString());
1003 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1004 urlBuffer.c_str() );
1005 if ( ret != 0 ) {
1007 }
1008
1009 // If no head requests allowed (?head_requests=no):
1010 // Instead of returning no data with NOBODY, we return
1011 // little data, that works with broken servers, and
1012 // works for ftp as well, because retrieving only headers
1013 // ftp will return always OK code ?
1014 // See http://curl.haxx.se/docs/knownbugs.html #58
1016 struct TempSetHeadRequest
1017 {
1018 TempSetHeadRequest( CURL * curl_r, bool doHttpHeadRequest_r )
1019 : _curl { curl_r }
1020 , _doHttpHeadRequest { doHttpHeadRequest_r }
1021 {
1022 if ( _doHttpHeadRequest ) {
1023 curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
1024 } else {
1025 curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
1026 }
1027 }
1028 ~TempSetHeadRequest() {
1029 if ( _doHttpHeadRequest ) {
1030 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
1031 /* yes, this is why we never got to get NOBODY working before,
1032 because setting it changes this option too, and we also*
1033 need to reset it
1034 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1035 */
1036 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
1037 } else {
1038 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1039 }
1040 }
1041 private:
1042 CURL * _curl;
1043 bool _doHttpHeadRequest;
1044 } _guard( _curl, (_url.getScheme() == "http" || _url.getScheme() == "https") && _settings.headRequestsAllowed() );
1045
1046
1047 AutoFILE file { ::fopen( "/dev/null", "w" ) };
1048 if ( !file ) {
1049 ERR << "fopen failed for /dev/null" << endl;
1050 ZYPP_THROW(MediaWriteException("/dev/null"));
1051 }
1052
1053 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, (*file) );
1054 if ( ret != 0 ) {
1056 }
1057
1058 CURLcode ok = executeCurl();
1059 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
1060
1061 // as we are not having user interaction, the user can't cancel
1062 // the file existence checking, a callback or timeout return code
1063 // will be always a timeout.
1064 try {
1065 evaluateCurlCode( 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 MediaException &e ) {
1072 // some error, we are not sure about file existence, rethrw
1073 ZYPP_RETHROW(e);
1074 }
1075 // exists
1076 return ( ok == CURLE_OK );
1077}
1078
1080
1081void MediaCurl::doGetFileCopy( const OnMediaLocation &srcFile , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1082{
1083 Pathname dest = target.absolutename();
1084 if( assert_dir( dest.dirname() ) )
1085 {
1086 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
1087 ZYPP_THROW( MediaSystemException(getFileUrl(srcFile.filename()), "System error on " + dest.dirname().asString()) );
1088 }
1089
1090 ManagedFile destNew { target.extend( ".new.zypp.XXXXXX" ) };
1091 AutoFILE file;
1092 {
1093 AutoFREE<char> buf { ::strdup( (*destNew).c_str() ) };
1094 if( ! buf )
1095 {
1096 ERR << "out of memory for temp file name" << endl;
1097 ZYPP_THROW(MediaSystemException(getFileUrl(srcFile.filename()), "out of memory for temp file name"));
1098 }
1099
1100 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
1101 if( tmp_fd == -1 )
1102 {
1103 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
1105 }
1106 destNew = ManagedFile( (*buf), filesystem::unlink );
1107
1108 file = ::fdopen( tmp_fd, "we" );
1109 if ( ! file )
1110 {
1111 ERR << "fopen failed for file '" << destNew << "'" << endl;
1113 }
1114 tmp_fd.resetDispose(); // don't close it here! ::fdopen moved ownership to file
1115 }
1116
1117 DBG << "dest: " << dest << endl;
1118 DBG << "temp: " << destNew << endl;
1119
1120 // set IFMODSINCE time condition (no download if not modified)
1121 if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
1122 {
1123 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1124 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
1125 }
1126 else
1127 {
1128 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1129 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1130 }
1131 try
1132 {
1133 doGetFileCopyFile( srcFile, dest, file, report, options);
1134 }
1135 catch (Exception &e)
1136 {
1137 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1138 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1139 ZYPP_RETHROW(e);
1140 }
1141
1142 long httpReturnCode = 0;
1143 CURLcode infoRet = curl_easy_getinfo(_curl,
1144 CURLINFO_RESPONSE_CODE,
1145 &httpReturnCode);
1146 bool modified = true;
1147 if (infoRet == CURLE_OK)
1148 {
1149 DBG << "HTTP response: " + str::numstring(httpReturnCode);
1150 if ( httpReturnCode == 304
1151 || ( httpReturnCode == 213 && (_url.getScheme() == "ftp" || _url.getScheme() == "tftp") ) ) // not modified
1152 {
1153 DBG << " Not modified.";
1154 modified = false;
1155 }
1156 DBG << endl;
1157 }
1158 else
1159 {
1160 WAR << "Could not get the response code." << endl;
1161 }
1162
1163 if (modified || infoRet != CURLE_OK)
1164 {
1165 // apply umask
1166 if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
1167 {
1168 ERR << "Failed to chmod file " << destNew << endl;
1169 }
1170
1171 file.resetDispose(); // we're going to close it manually here
1172 if ( ::fclose( file ) )
1173 {
1174 ERR << "Fclose failed for file '" << destNew << "'" << endl;
1176 }
1177
1178 // move the temp file into dest
1179 if ( rename( destNew, dest ) != 0 ) {
1180 ERR << "Rename failed" << endl;
1182 }
1183 destNew.resetDispose(); // no more need to unlink it
1184 }
1185
1186 DBG << "done: " << PathInfo(dest) << endl;
1187}
1188
1190
1191void MediaCurl::doGetFileCopyFile( const OnMediaLocation & srcFile, const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1192{
1193 DBG << srcFile.filename().asString() << endl;
1194
1195 if(!_url.isValid())
1197
1198 if(_url.getHost().empty())
1200
1201 Url url(getFileUrl(srcFile.filename()));
1202
1203 DBG << "URL: " << url.asString() << endl;
1204 // Use URL without options and without username and passwd
1205 // (some proxies dislike them in the URL).
1206 // Curl seems to need the just scheme, hostname and a path;
1207 // the rest was already passed as curl options (in attachTo).
1208 Url curlUrl( clearQueryString(url) );
1209
1210 //
1211 // See also Bug #154197 and ftp url definition in RFC 1738:
1212 // The url "ftp://user@host/foo/bar/file" contains a path,
1213 // that is relative to the user's home.
1214 // The url "ftp://user@host//foo/bar/file" (or also with
1215 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1216 // contains an absolute path.
1217 //
1218 _lastRedirect.clear();
1219 std::string urlBuffer( curlUrl.asString());
1220 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1221 urlBuffer.c_str() );
1222 if ( ret != 0 ) {
1224 }
1225
1226 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
1227 if ( ret != 0 ) {
1229 }
1230
1231 // Set callback and perform.
1232 internal::ProgressData progressData(_curl, _settings.timeout(), url, srcFile.downloadSize(), &report);
1233 if (!(options & OPTION_NO_REPORT_START))
1234 report->start(url, dest);
1235 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1236 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1237 }
1238
1239 ret = executeCurl();
1240#if CURLVERSION_AT_LEAST(7,19,4)
1241 // bnc#692260: If the client sends a request with an If-Modified-Since header
1242 // with a future date for the server, the server may respond 200 sending a
1243 // zero size file.
1244 // curl-7.19.4 introduces CURLINFO_CONDITION_UNMET to check this condition.
1245 if ( ftell(file) == 0 && ret == 0 )
1246 {
1247 long httpReturnCode = 33;
1248 if ( curl_easy_getinfo( _curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
1249 {
1250 long conditionUnmet = 33;
1251 if ( curl_easy_getinfo( _curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
1252 {
1253 WAR << "TIMECONDITION unmet - retry without." << endl;
1254 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1255 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1256 ret = executeCurl();
1257 }
1258 }
1259 }
1260#endif
1261
1262 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1263 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1264 }
1265
1266 if ( ret != 0 )
1267 {
1268 ERR << "curl error: " << ret << ": " << _curlError
1269 << ", temp file size " << ftell(file)
1270 << " bytes." << endl;
1271
1272 // the timeout is determined by the progress data object
1273 // which holds whether the timeout was reached or not,
1274 // otherwise it would be a user cancel
1275 try {
1276
1277 if ( progressData.fileSizeExceeded() )
1279
1280 evaluateCurlCode( srcFile.filename(), ret, progressData.timeoutReached() );
1281 }
1282 catch ( const MediaException &e ) {
1283 // some error, we are not sure about file existence, rethrw
1284 ZYPP_RETHROW(e);
1285 }
1286 }
1287}
1288
1290
1291void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
1292{
1293 filesystem::DirContent content;
1294 getDirInfo( content, dirname, /*dots*/false );
1295
1296 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
1297 Pathname filename = dirname + it->name;
1298 int res = 0;
1299
1300 switch ( it->type ) {
1301 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
1303 getFile( OnMediaLocation( filename ) );
1304 break;
1305 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
1306 if ( recurse_r ) {
1307 getDir( filename, recurse_r );
1308 } else {
1309 res = assert_dir( localPath( filename ) );
1310 if ( res ) {
1311 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
1312 }
1313 }
1314 break;
1315 default:
1316 // don't provide devices, sockets, etc.
1317 break;
1318 }
1319 }
1320}
1321
1323
1324void MediaCurl::getDirInfo( std::list<std::string> & retlist,
1325 const Pathname & dirname, bool dots ) const
1326{
1327 getDirectoryYast( retlist, dirname, dots );
1328}
1329
1331
1333 const Pathname & dirname, bool dots ) const
1334{
1335 getDirectoryYast( retlist, dirname, dots );
1336}
1337
1339//
1340int MediaCurl::aliveCallback( void *clientp, curl_off_t /*dltotal*/, curl_off_t dlnow, curl_off_t /*ultotal*/, curl_off_t /*ulnow*/ )
1341{
1342 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1343 if( pdata )
1344 {
1345 // Do not propagate dltotal in alive callbacks. MultiCurl uses this to
1346 // prevent a percentage raise while downloading a metalink file. Download
1347 // activity however is indicated by propagating the download rate (via dlnow).
1348 pdata->updateStats( 0.0, dlnow );
1349 return pdata->reportProgress();
1350 }
1351 return 0;
1352}
1353
1354int MediaCurl::progressCallback( void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow )
1355{
1356 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1357 if( pdata )
1358 {
1359 // work around curl bug that gives us old data
1360 long httpReturnCode = 0;
1361 if ( curl_easy_getinfo( pdata->curl(), CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
1362 return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
1363
1364 pdata->updateStats( dltotal, dlnow );
1365 return pdata->reportProgress();
1366 }
1367 return 0;
1368}
1369
1371{
1372 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>(clientp);
1373 return pdata ? pdata->curl() : 0;
1374}
1375
1377
1378std::string MediaCurl::getAuthHint() const
1379{
1380 long auth_info = CURLAUTH_NONE;
1381
1382 CURLcode infoRet =
1383 curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1384
1385 if(infoRet == CURLE_OK)
1386 {
1387 return CurlAuthData::auth_type_long2str(auth_info);
1388 }
1389
1390 return "";
1391}
1392
1397void MediaCurl::resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
1398{
1399 internal::ProgressData *data = reinterpret_cast<internal::ProgressData *>(clientp);
1400 if ( data ) {
1401 data->expectedFileSize( expectedFileSize );
1402 }
1403}
1404
1411{
1412 // initialize our helpers
1414 internal::CurlPollHelper::CurlPoll{ curl_multi_init() }
1415 ,[](auto &releaseMe ){ if (releaseMe._multi) curl_multi_cleanup(releaseMe._multi); }
1416 );
1417
1418 if (!cMulti->_multi)
1420
1421 // we could derive from that, but currently that does not make a lot of sense
1422 internal::CurlPollHelper _curlHelper(cMulti.value());
1423
1424 // add the easy handle to the multi instance
1425 if ( curl_multi_add_handle( cMulti->_multi, _curl ) != CURLM_OK )
1426 ZYPP_THROW(MediaCurlException( _url, "curl_multi_add_handle", "unknown error"));
1427
1428 // make sure the handle is cleanly removed from the multi handle
1429 OnScopeExit autoRemove([&](){ curl_multi_remove_handle( cMulti->_multi, _curl ); });
1430
1431 // kickstart curl, this will cause libcurl to go over the added handles and register sockets and timeouts
1432 CURLMcode mcode = _curlHelper.handleTimout();
1433 if (mcode != CURLM_OK)
1434 ZYPP_THROW(MediaCurlException( _url, "curl_multi_socket_action", "unknown error"));
1435
1436 bool canContinue = true;
1437 while ( canContinue ) {
1438
1439 // copy watched sockets in case curl changes the vector as we go over the events later
1440 std::vector<GPollFD> requestedFds = _curlHelper.socks;
1441
1442 int r = zypp_detail::zypp_poll( requestedFds, _curlHelper.timeout_ms.value_or( -1 ) );
1443 if ( r == -1 )
1444 ZYPP_THROW( MediaCurlException(_url, "zypp_poll() failed", "unknown error") );
1445
1446 // run curl
1447 if ( r == 0 ) {
1448 CURLMcode mcode = _curlHelper.handleTimout();
1449 if (mcode != CURLM_OK)
1450 ZYPP_THROW(MediaCurlException(_url, "curl_multi_socket_action", "unknown error"));
1451 } else {
1452 CURLMcode mcode = _curlHelper.handleSocketActions( requestedFds );
1453 if (mcode != CURLM_OK)
1454 ZYPP_THROW(MediaCurlException(_url, "curl_multi_socket_action", "unknown error"));
1455 }
1456
1457 CURLMsg *msg;
1458 int nqueue;
1459 while ((msg = curl_multi_info_read( cMulti->_multi, &nqueue)) != 0) {
1460 if ( msg->msg != CURLMSG_DONE ) continue;
1461 if ( msg->easy_handle != _curl ) continue;
1462
1463 return msg->data.result;
1464 }
1465 }
1466 return CURLE_OK;
1467}
1468
1470
1471bool MediaCurl::authenticate(const std::string & availAuthTypes, bool firstTry) const
1472{
1475 CurlAuthData_Ptr credentials;
1476
1477 // get stored credentials
1478 AuthData_Ptr cmcred = cm.getCred(_url);
1479
1480 if (cmcred && firstTry)
1481 {
1482 credentials.reset(new CurlAuthData(*cmcred));
1483 DBG << "got stored credentials:" << endl << *credentials << endl;
1484 }
1485 // if not found, ask user
1486 else
1487 {
1488
1489 CurlAuthData_Ptr curlcred;
1490 curlcred.reset(new CurlAuthData());
1492
1493 // preset the username if present in current url
1494 if (!_url.getUsername().empty() && firstTry)
1495 curlcred->setUsername(_url.getUsername());
1496 // if CM has found some credentials, preset the username from there
1497 else if (cmcred)
1498 curlcred->setUsername(cmcred->username());
1499
1500 // indicate we have no good credentials from CM
1501 cmcred.reset();
1502
1503 std::string prompt_msg = str::Format(_("Authentication required for '%s'")) % _url.asString();
1504
1505 // set available authentication types from the exception
1506 // might be needed in prompt
1507 curlcred->setAuthType(availAuthTypes);
1508
1509 // ask user
1510 if (auth_report->prompt(_url, prompt_msg, *curlcred))
1511 {
1512 DBG << "callback answer: retry" << endl
1513 << "CurlAuthData: " << *curlcred << endl;
1514
1515 if (curlcred->valid())
1516 {
1517 credentials = curlcred;
1518 // if (credentials->username() != _url.getUsername())
1519 // _url.setUsername(credentials->username());
1527 }
1528 }
1529 else
1530 {
1531 DBG << "callback answer: cancel" << endl;
1532 }
1533 }
1534
1535 // set username and password
1536 if (credentials)
1537 {
1538 // HACK, why is this const?
1539 const_cast<MediaCurl*>(this)->_settings.setUsername(credentials->username());
1540 const_cast<MediaCurl*>(this)->_settings.setPassword(credentials->password());
1541
1542 // set username and password
1543 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _settings.userPassword().c_str());
1545
1546 // set available authentication types from the exception
1547 if (credentials->authType() == CURLAUTH_NONE)
1548 credentials->setAuthType(availAuthTypes);
1549
1550 // set auth type (seems this must be set _after_ setting the userpwd)
1551 if (credentials->authType() != CURLAUTH_NONE)
1552 {
1553 // FIXME: only overwrite if not empty?
1554 const_cast<MediaCurl*>(this)->_settings.setAuthType(credentials->authTypeAsString());
1555 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, credentials->authType());
1557 }
1558
1559 if (!cmcred)
1560 {
1561 credentials->setUrl(_url);
1562 cm.addCred(*credentials);
1563 cm.save();
1564 }
1565
1566 return true;
1567 }
1568
1569 return false;
1570}
1571
1572//need a out of line definiton, otherwise vtable is emitted for every translation unit
1574
1575 } // namespace media
1576} // namespace zypp
1577//
#define SET_OPTION_OFFT(opt, val)
Definition MediaCurl.cc:333
#define SET_OPTION(opt, val)
Definition MediaCurl.cc:326
Edition * _value
TrueBool _guard
Attempt to work around certain issues by autoretry in MediaCurl::getFileCopy E.g.
Definition MediaCurl.cc:301
MediaCurlExceptionMayRetryInternaly(const Url &url_r, const std::string &err_r, const std::string &msg_r)
Definition MediaCurl.cc:303
void resetDispose()
Set no dispose function.
Store and operate with byte count.
Definition ByteCount.h:31
Base class for Exception.
Definition Exception.h:146
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition Exception.cc:91
void addHistory(const std::string &msg_r)
Add some message text to the history.
Definition Exception.cc:140
const char * c_str() const
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).
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: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
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
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
Definition Url.cc:588
bool isValid() const
Verifies the Url.
Definition Url.cc:489
static ZConfig & instance()
Singleton ctor.
Definition ZConfig.cc:922
Typesafe passing of user data via callbacks.
Definition UserData.h:39
Wrapper class for stat/lstat.
Definition PathInfo.h:221
const Pathname & path() const
Return current Pathname.
Definition PathInfo.h:246
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition Pathname.h:173
Pathname dirname() const
Return all but the last component od this path.
Definition Pathname.h:124
const char * c_str() const
String representation.
Definition Pathname.h:110
const std::string & asString() const
String representation.
Definition Pathname.h:91
bool empty() const
Test for an empty path.
Definition Pathname.h:114
Pathname absolutename() const
Return this path, adding a leading '/' if relative.
Definition Pathname.h:139
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.
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, const std::string &err_r, const std::string &msg_r)
Implementation class for FTP, HTTP and HTTPS MediaHandler.
Definition MediaCurl.h:32
virtual void setupEasy()
initializes the curl easy handle with the data from the url
Definition MediaCurl.cc:409
static void setCookieFile(const Pathname &)
Definition MediaCurl.cc:377
virtual bool getDoesFileExist(const Pathname &filename) const override
Repeatedly calls doGetDoesFileExist() until it successfully returns, fails unexpectedly,...
Definition MediaCurl.cc:795
@ OPTION_NO_IFMODSINCE
to not add a IFMODSINCE header if target exists
Definition MediaCurl.h:43
@ OPTION_NO_REPORT_START
do not send a start ProgressReport
Definition MediaCurl.h:45
virtual void getFile(const OnMediaLocation &file) const override
Call concrete handler to provide file below attach point.
Definition MediaCurl.cc:724
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:163
static Pathname _cookieFile
Definition MediaCurl.h:164
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.
virtual void getFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename) const override
Definition MediaCurl.cc:733
virtual void doGetFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename, callback::SendReport< DownloadProgressReport > &_report, RequestOptions options=OPTION_NONE) const
std::string _lastRedirect
to log/report redirections
Definition MediaCurl.h:166
Url clearQueryString(const Url &url) const
Definition MediaCurl.cc:372
virtual void attachTo(bool next=false) override
Call concrete handler to attach the media.
Definition MediaCurl.cc:656
virtual 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.
char _curlError[CURL_ERROR_SIZE]
Definition MediaCurl.h:170
void doGetFileCopyFile(const OnMediaLocation &srcFile, const Pathname &dest, FILE *file, callback::SendReport< DownloadProgressReport > &report, RequestOptions options=OPTION_NONE) const
void checkProtocol(const Url &url) const
check the url is supported by the curl library
Definition MediaCurl.cc:384
MediaCurl(const Url &url_r, const Pathname &attach_point_hint_r)
Definition MediaCurl.cc:337
CURLcode executeCurl() const
void evaluateCurlCode(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:826
virtual bool checkAttachPoint(const Pathname &apoint) const override
Verify if the specified directory as attach point (root) as requires by the particular media handler ...
Definition MediaCurl.cc:691
virtual void getDir(const Pathname &dirname, bool recurse_r) const override
Call concrete handler to provide directory content (not recursive!) below attach point.
bool authenticate(const std::string &availAuthTypes, bool firstTry) const
static CURL * progressCallback_getcurl(void *clientp)
virtual void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
Definition MediaCurl.cc:717
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.
virtual void disconnectFrom() override
Definition MediaCurl.cc:698
virtual bool doGetDoesFileExist(const Pathname &filename) const
Definition MediaCurl.cc:974
curl_slist * _customHeaders
Definition MediaCurl.h:171
std::string getAuthHint() const
Return a comma separated list of available authentication methods supported by server.
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...
Url url() const
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 ...
void disconnect()
Use concrete handler to isconnect media.
Pathname createAttachPoint() const
Try to create a default / temporary attach point.
void setMediaSource(const MediaSourceRef &ref)
Set new media source reference.
Pathname localPath(const Pathname &pathname) const
Files provided will be available at 'localPath(filename)'.
const Url _url
Url to handle.
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.
Common baseclass for MediaCurl and MediaNetwork.
Url getFileUrl(const Pathname &filename) const
concatenate the attach url and the filename to a complete download url
Media source internally used by MediaManager and MediaHandler.
Definition MediaSource.h:37
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.
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)
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 headRequestsAllowed() const
whether HEAD requests are allowed
bool proxyEnabled() const
proxy is enabled
const std::string & username() const
auth username
const std::string & proxyUsername() const
proxy auth username
void setAuthType(const std::string &val_r)
set the allowed authentication types
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
void fillSettingsFromUrl(const Url &url, media::TransferSettings &s)
Fills the settings structure using options passed on the url for example ?timeout=x&proxy=foo.
size_t log_redirects_curl(char *ptr, size_t size, size_t nmemb, void *userdata)
const char * anonymousIdHeader()
Definition MediaCurl.cc:254
void globalInitCurlOnce()
Definition curlhelper.cc:64
const char * distributionFlavorHeader()
Definition MediaCurl.cc:268
std::string curlUnEscape(std::string text_r)
void setupZYPP_MEDIA_CURL_DEBUG(CURL *curl)
Setup CURLOPT_VERBOSE and CURLOPT_DEBUGFUNCTION according to env::ZYPP_MEDIA_CURL_DEBUG.
const char * agentString()
Definition MediaCurl.cc:282
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
Url clearQueryString(const Url &url)
CURLcode setCurlRedirProtocols(CURL *curl, bool enableHttp)
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:789
int unlink(const Pathname &path)
Like 'unlink'.
Definition PathInfo.cc:700
int assert_file_mode(const Pathname &path, unsigned mode)
Like assert_file but enforce mode even if the file already exists.
Definition PathInfo.cc:1202
std::list< DirEntry > DirContent
Returned by readdir.
Definition PathInfo.h:518
shared_ptr< CurlAuthData > CurlAuthData_Ptr
shared_ptr< AuthData > AuthData_Ptr
Definition authdata.h:79
std::string numstring(char n, int w=0)
Definition String.h:289
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition String.cc:36
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition String.h:429
std::string trim(const std::string &s, const Trim trim_r)
Definition String.cc:223
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
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.
Definition MediaCurl.cc:53
void report(const UserData &userData_r=UserData()) override
The most generic way of sending/receiving data.
Definition MediaCurl.cc:70
Action problem(const Url &file_r, Error error_r, const std::string &description_r) override
Definition MediaCurl.cc:105
OptionalDownloadProgressReport(bool isOptional=false)
Definition MediaCurl.cc:56
std::chrono::steady_clock::time_point TimePoint
Definition MediaCurl.cc:54
void finish(const Url &file_r, Error error_r, const std::string &reason_r) override
Definition MediaCurl.cc:111
bool progress(int value_r, const Url &file_r, double dbps_avg_r=-1, double dbps_current_r=-1) override
Download progress.
Definition MediaCurl.cc:86
void start(const Url &file_r, Pathname localfile_r) override
Definition MediaCurl.cc:74
ByteCount _expectedFileSize
Definition MediaCurl.cc:170
curl_off_t _dnlNow
Bytes downloaded now.
Definition MediaCurl.cc:180
int _dnlPercent
Percent completed or 0 if _dnlTotal is unknown.
Definition MediaCurl.cc:182
time_t _timeRcv
Start of no-data timeout.
Definition MediaCurl.cc:175
ByteCount expectedFileSize() const
Definition MediaCurl.cc:158
time_t _timeLast
Start last period(~1sec)
Definition MediaCurl.cc:174
int reportProgress() const
Definition MediaCurl.cc:243
double _drateLast
Download rate in last period.
Definition MediaCurl.cc:185
bool timeoutReached() const
Definition MediaCurl.cc:152
void expectedFileSize(ByteCount newval_r)
Definition MediaCurl.cc:161
curl_off_t _dnlLast
Bytes downloaded at period start.
Definition MediaCurl.cc:179
bool fileSizeExceeded() const
Definition MediaCurl.cc:155
void updateStats(curl_off_t dltotal=0.0, curl_off_t dlnow=0.0)
Definition MediaCurl.cc:200
double _drateTotal
Download rate so far.
Definition MediaCurl.cc:184
zypp::callback::SendReport< zypp::media::DownloadProgressReport > * report
Definition MediaCurl.cc:171
curl_off_t _dnlTotal
Bytes to download or 0 if unknown.
Definition MediaCurl.cc:178
time_t _timeStart
Start total stats.
Definition MediaCurl.cc:173
AutoDispose<int> calling ::close
AutoDispose<FILE*> calling ::fclose
static DistributeReport & instance()
Definition Callback.h:204
void setReceiver(Receiver &rec_r)
Definition Callback.h:213
virtual void report(const UserData &userData_r=UserData())
The most generic way of sending/receiving data.
Definition Callback.h:155
callback::UserData UserData
Definition Callback.h:151
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
virtual void finish(const Url &, Error, const std::string &)
virtual bool progress(int, const Url &, double dbps_avg=-1, double dbps_current=-1)
Download progress.
virtual void start(const Url &, Pathname)
virtual Action problem(const Url &, Error, const std::string &)
Convenient building of std::string with boost::format.
Definition String.h:253
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition Exception.h:440
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:428
#define _(MSG)
Definition Gettext.h:37
#define DBG
Definition Logger.h:95
#define MIL
Definition Logger.h:96
#define ERR
Definition Logger.h:98
#define WAR
Definition Logger.h:97