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