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