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