libzypp 17.31.2
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 // bsc#1201092: Strange but within global_dtors we may exceptions here.
616 try { curl_easy_cleanup( _curl ); }
617 catch (...) { ; }
618 _curl = NULL;
619 }
620}
621
623
624void MediaCurl::releaseFrom( const std::string & ejectDev )
625{
626 disconnect();
627}
628
629Url MediaCurl::getFileUrl( const Pathname & filename_r ) const
630{
631 // Simply extend the URLs pathname. An 'absolute' URL path
632 // is achieved by encoding the leading '/' in an URL path:
633 // URL: ftp://user@server -> ~user
634 // URL: ftp://user@server/ -> ~user
635 // URL: ftp://user@server// -> ~user
636 // URL: ftp://user@server/%2F -> /
637 // ^- this '/' is just a separator
638 Url newurl( _url );
639 newurl.setPathName( ( Pathname("./"+_url.getPathName()) / filename_r ).asString().substr(1) );
640 return newurl;
641}
642
644
645void MediaCurl::getFile( const OnMediaLocation &file ) const
646{
647 // Use absolute file name to prevent access of files outside of the
648 // hierarchy below the attach point.
649 getFileCopy( file, localPath(file.filename()).absolutename() );
650}
651
653
654void MediaCurl::getFileCopy( const OnMediaLocation & srcFile , const Pathname & target ) const
655{
656
657 const auto &filename = srcFile.filename();
658
660
661 Url fileurl(getFileUrl(filename));
662
663 bool retry = false;
664
665 do
666 {
667 try
668 {
669 doGetFileCopy( srcFile, target, report );
670 retry = false;
671 }
672 // retry with proper authentication data
673 catch (MediaUnauthorizedException & ex_r)
674 {
675 if(authenticate(ex_r.hint(), !retry))
676 retry = true;
677 else
678 {
679 report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserHistory());
680 ZYPP_RETHROW(ex_r);
681 }
682 }
683 // unexpected exception
684 catch (MediaException & excpt_r)
685 {
687 if( typeid(excpt_r) == typeid( media::MediaFileNotFoundException ) ||
688 typeid(excpt_r) == typeid( media::MediaNotAFileException ) )
689 {
691 }
692 report->finish(fileurl, reason, excpt_r.asUserHistory());
693 ZYPP_RETHROW(excpt_r);
694 }
695 }
696 while (retry);
697
698 report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
699}
700
702
703bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
704{
705 bool retry = false;
706
707 do
708 {
709 try
710 {
711 return doGetDoesFileExist( filename );
712 }
713 // authentication problem, retry with proper authentication data
714 catch (MediaUnauthorizedException & ex_r)
715 {
716 if(authenticate(ex_r.hint(), !retry))
717 retry = true;
718 else
719 ZYPP_RETHROW(ex_r);
720 }
721 // unexpected exception
722 catch (MediaException & excpt_r)
723 {
724 ZYPP_RETHROW(excpt_r);
725 }
726 }
727 while (retry);
728
729 return false;
730}
731
733
735 CURLcode code,
736 bool timeout_reached) const
737{
738 if ( code != 0 )
739 {
740 Url url;
741 if (filename.empty())
742 url = _url;
743 else
744 url = getFileUrl(filename);
745
746 std::string err;
747 {
748 switch ( code )
749 {
750 case CURLE_UNSUPPORTED_PROTOCOL:
751 err = " Unsupported protocol";
752 if ( !_lastRedirect.empty() )
753 {
754 err += " or redirect (";
755 err += _lastRedirect;
756 err += ")";
757 }
758 break;
759 case CURLE_URL_MALFORMAT:
760 case CURLE_URL_MALFORMAT_USER:
761 err = " Bad URL";
762 break;
763 case CURLE_LOGIN_DENIED:
765 MediaUnauthorizedException(url, "Login failed.", _curlError, ""));
766 break;
767 case CURLE_HTTP_RETURNED_ERROR:
768 {
769 long httpReturnCode = 0;
770 CURLcode infoRet = curl_easy_getinfo( _curl,
771 CURLINFO_RESPONSE_CODE,
772 &httpReturnCode );
773 if ( infoRet == CURLE_OK )
774 {
775 std::string msg = "HTTP response: " + str::numstring( httpReturnCode );
776 switch ( httpReturnCode )
777 {
778 case 401:
779 {
780 std::string auth_hint = getAuthHint();
781
782 DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
783 DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
784
785 ZYPP_THROW(MediaUnauthorizedException(
786 url, "Login failed.", _curlError, auth_hint
787 ));
788 }
789
790 case 502: // bad gateway (bnc #1070851)
791 case 503: // service temporarily unavailable (bnc #462545)
792 ZYPP_THROW(MediaTemporaryProblemException(url));
793 case 504: // gateway timeout
794 ZYPP_THROW(MediaTimeoutException(url));
795 case 403:
796 {
797 std::string msg403;
798 if ( url.getHost().find(".suse.com") != std::string::npos )
799 msg403 = _("Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
800 else if (url.asString().find("novell.com") != std::string::npos)
801 msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
802 ZYPP_THROW(MediaForbiddenException(url, msg403));
803 }
804 case 404:
805 case 410:
806 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
807 }
808
809 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
810 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
811 }
812 else
813 {
814 std::string msg = "Unable to retrieve HTTP response:";
815 DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
816 ZYPP_THROW(MediaCurlException(url, msg, _curlError));
817 }
818 }
819 break;
820 case CURLE_FTP_COULDNT_RETR_FILE:
821#if CURLVERSION_AT_LEAST(7,16,0)
822 case CURLE_REMOTE_FILE_NOT_FOUND:
823#endif
824 case CURLE_FTP_ACCESS_DENIED:
825 case CURLE_TFTP_NOTFOUND:
826 err = "File not found";
827 ZYPP_THROW(MediaFileNotFoundException(_url, filename));
828 break;
829 case CURLE_BAD_PASSWORD_ENTERED:
830 case CURLE_FTP_USER_PASSWORD_INCORRECT:
831 err = "Login failed";
832 break;
833 case CURLE_COULDNT_RESOLVE_PROXY:
834 case CURLE_COULDNT_RESOLVE_HOST:
835 case CURLE_COULDNT_CONNECT:
836 case CURLE_FTP_CANT_GET_HOST:
837 err = "Connection failed";
838 break;
839 case CURLE_WRITE_ERROR:
840 err = "Write error";
841 break;
842 case CURLE_PARTIAL_FILE:
843 case CURLE_OPERATION_TIMEDOUT:
844 timeout_reached = true; // fall though to TimeoutException
845 // fall though...
846 case CURLE_ABORTED_BY_CALLBACK:
847 if( timeout_reached )
848 {
849 err = "Timeout reached";
850 ZYPP_THROW(MediaTimeoutException(url));
851 }
852 else
853 {
854 err = "User abort";
855 }
856 break;
857 case CURLE_SSL_PEER_CERTIFICATE:
858 default:
859 err = "Curl error " + str::numstring( code );
860 break;
861 }
862
863 // uhm, no 0 code but unknown curl exception
864 ZYPP_THROW(MediaCurlException(url, err, _curlError));
865 }
866 }
867 else
868 {
869 // actually the code is 0, nothing happened
870 }
871}
872
874
875bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
876{
877 DBG << filename.asString() << endl;
878
879 if(!_url.isValid())
880 ZYPP_THROW(MediaBadUrlException(_url));
881
882 if(_url.getHost().empty())
883 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
884
885 Url url(getFileUrl(filename));
886
887 DBG << "URL: " << url.asString() << endl;
888 // Use URL without options and without username and passwd
889 // (some proxies dislike them in the URL).
890 // Curl seems to need the just scheme, hostname and a path;
891 // the rest was already passed as curl options (in attachTo).
892 Url curlUrl( clearQueryString(url) );
893
894 //
895 // See also Bug #154197 and ftp url definition in RFC 1738:
896 // The url "ftp://user@host/foo/bar/file" contains a path,
897 // that is relative to the user's home.
898 // The url "ftp://user@host//foo/bar/file" (or also with
899 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
900 // contains an absolute path.
901 //
902 _lastRedirect.clear();
903 std::string urlBuffer( curlUrl.asString());
904 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
905 urlBuffer.c_str() );
906 if ( ret != 0 ) {
907 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
908 }
909
910 // instead of returning no data with NOBODY, we return
911 // little data, that works with broken servers, and
912 // works for ftp as well, because retrieving only headers
913 // ftp will return always OK code ?
914 // See http://curl.haxx.se/docs/knownbugs.html #58
915 if ( (_url.getScheme() == "http" || _url.getScheme() == "https") &&
916 _settings.headRequestsAllowed() )
917 ret = curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
918 else
919 ret = curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
920
921 if ( ret != 0 ) {
922 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
923 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
924 /* yes, this is why we never got to get NOBODY working before,
925 because setting it changes this option too, and we also
926 need to reset it
927 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
928 */
929 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
930 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
931 }
932
933 AutoFILE file { ::fopen( "/dev/null", "w" ) };
934 if ( !file ) {
935 ERR << "fopen failed for /dev/null" << endl;
936 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
937 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
938 /* yes, this is why we never got to get NOBODY working before,
939 because setting it changes this option too, and we also
940 need to reset it
941 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
942 */
943 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
944 if ( ret != 0 ) {
945 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
946 }
947 ZYPP_THROW(MediaWriteException("/dev/null"));
948 }
949
950 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, (*file) );
951 if ( ret != 0 ) {
952 std::string err( _curlError);
953 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
954 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
955 /* yes, this is why we never got to get NOBODY working before,
956 because setting it changes this option too, and we also
957 need to reset it
958 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
959 */
960 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
961 if ( ret != 0 ) {
962 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
963 }
964 ZYPP_THROW(MediaCurlSetOptException(url, err));
965 }
966
967 CURLcode ok = curl_easy_perform( _curl );
968 MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
969
970 // reset curl settings
971 if ( _url.getScheme() == "http" || _url.getScheme() == "https" )
972 {
973 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
974 if ( ret != 0 ) {
975 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
976 }
977
978 /* yes, this is why we never got to get NOBODY working before,
979 because setting it changes this option too, and we also
980 need to reset it
981 See: http://curl.haxx.se/mail/archive-2005-07/0073.html
982 */
983 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L);
984 if ( ret != 0 ) {
985 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
986 }
987
988 }
989 else
990 {
991 // for FTP we set different options
992 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL);
993 if ( ret != 0 ) {
994 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
995 }
996 }
997
998 // as we are not having user interaction, the user can't cancel
999 // the file existence checking, a callback or timeout return code
1000 // will be always a timeout.
1001 try {
1002 evaluateCurlCode( filename, ok, true /* timeout */);
1003 }
1004 catch ( const MediaFileNotFoundException &e ) {
1005 // if the file did not exist then we can return false
1006 return false;
1007 }
1008 catch ( const MediaException &e ) {
1009 // some error, we are not sure about file existence, rethrw
1010 ZYPP_RETHROW(e);
1011 }
1012 // exists
1013 return ( ok == CURLE_OK );
1014}
1015
1017
1018
1019#if DETECT_DIR_INDEX
1020bool MediaCurl::detectDirIndex() const
1021{
1022 if(_url.getScheme() != "http" && _url.getScheme() != "https")
1023 return false;
1024 //
1025 // try to check the effective url and set the not_a_file flag
1026 // if the url path ends with a "/", what usually means, that
1027 // we've received a directory index (index.html content).
1028 //
1029 // Note: This may be dangerous and break file retrieving in
1030 // case of some server redirections ... ?
1031 //
1032 bool not_a_file = false;
1033 char *ptr = NULL;
1034 CURLcode ret = curl_easy_getinfo( _curl,
1035 CURLINFO_EFFECTIVE_URL,
1036 &ptr);
1037 if ( ret == CURLE_OK && ptr != NULL)
1038 {
1039 try
1040 {
1041 Url eurl( ptr);
1042 std::string path( eurl.getPathName());
1043 if( !path.empty() && path != "/" && *path.rbegin() == '/')
1044 {
1045 DBG << "Effective url ("
1046 << eurl
1047 << ") seems to provide the index of a directory"
1048 << endl;
1049 not_a_file = true;
1050 }
1051 }
1052 catch( ... )
1053 {}
1054 }
1055 return not_a_file;
1056}
1057#endif
1058
1060
1061void MediaCurl::doGetFileCopy( const OnMediaLocation &srcFile , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1062{
1063 Pathname dest = target.absolutename();
1064 if( assert_dir( dest.dirname() ) )
1065 {
1066 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
1067 ZYPP_THROW( MediaSystemException(getFileUrl(srcFile.filename()), "System error on " + dest.dirname().asString()) );
1068 }
1069
1070 ManagedFile destNew { target.extend( ".new.zypp.XXXXXX" ) };
1071 AutoFILE file;
1072 {
1073 AutoFREE<char> buf { ::strdup( (*destNew).c_str() ) };
1074 if( ! buf )
1075 {
1076 ERR << "out of memory for temp file name" << endl;
1077 ZYPP_THROW(MediaSystemException(getFileUrl(srcFile.filename()), "out of memory for temp file name"));
1078 }
1079
1080 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
1081 if( tmp_fd == -1 )
1082 {
1083 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
1084 ZYPP_THROW(MediaWriteException(destNew));
1085 }
1086 destNew = ManagedFile( (*buf), filesystem::unlink );
1087
1088 file = ::fdopen( tmp_fd, "we" );
1089 if ( ! file )
1090 {
1091 ERR << "fopen failed for file '" << destNew << "'" << endl;
1092 ZYPP_THROW(MediaWriteException(destNew));
1093 }
1094 tmp_fd.resetDispose(); // don't close it here! ::fdopen moved ownership to file
1095 }
1096
1097 DBG << "dest: " << dest << endl;
1098 DBG << "temp: " << destNew << endl;
1099
1100 // set IFMODSINCE time condition (no download if not modified)
1101 if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
1102 {
1103 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1104 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
1105 }
1106 else
1107 {
1108 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1109 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1110 }
1111 try
1112 {
1113 doGetFileCopyFile( srcFile, dest, file, report, options);
1114 }
1115 catch (Exception &e)
1116 {
1117 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1118 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1119 ZYPP_RETHROW(e);
1120 }
1121
1122 long httpReturnCode = 0;
1123 CURLcode infoRet = curl_easy_getinfo(_curl,
1124 CURLINFO_RESPONSE_CODE,
1125 &httpReturnCode);
1126 bool modified = true;
1127 if (infoRet == CURLE_OK)
1128 {
1129 DBG << "HTTP response: " + str::numstring(httpReturnCode);
1130 if ( httpReturnCode == 304
1131 || ( httpReturnCode == 213 && (_url.getScheme() == "ftp" || _url.getScheme() == "tftp") ) ) // not modified
1132 {
1133 DBG << " Not modified.";
1134 modified = false;
1135 }
1136 DBG << endl;
1137 }
1138 else
1139 {
1140 WAR << "Could not get the response code." << endl;
1141 }
1142
1143 if (modified || infoRet != CURLE_OK)
1144 {
1145 // apply umask
1146 if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
1147 {
1148 ERR << "Failed to chmod file " << destNew << endl;
1149 }
1150
1151 file.resetDispose(); // we're going to close it manually here
1152 if ( ::fclose( file ) )
1153 {
1154 ERR << "Fclose failed for file '" << destNew << "'" << endl;
1155 ZYPP_THROW(MediaWriteException(destNew));
1156 }
1157
1158 // move the temp file into dest
1159 if ( rename( destNew, dest ) != 0 ) {
1160 ERR << "Rename failed" << endl;
1161 ZYPP_THROW(MediaWriteException(dest));
1162 }
1163 destNew.resetDispose(); // no more need to unlink it
1164 }
1165
1166 DBG << "done: " << PathInfo(dest) << endl;
1167}
1168
1170
1171void MediaCurl::doGetFileCopyFile( const OnMediaLocation & srcFile, const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1172{
1173 DBG << srcFile.filename().asString() << endl;
1174
1175 if(!_url.isValid())
1176 ZYPP_THROW(MediaBadUrlException(_url));
1177
1178 if(_url.getHost().empty())
1179 ZYPP_THROW(MediaBadUrlEmptyHostException(_url));
1180
1181 Url url(getFileUrl(srcFile.filename()));
1182
1183 DBG << "URL: " << url.asString() << endl;
1184 // Use URL without options and without username and passwd
1185 // (some proxies dislike them in the URL).
1186 // Curl seems to need the just scheme, hostname and a path;
1187 // the rest was already passed as curl options (in attachTo).
1188 Url curlUrl( clearQueryString(url) );
1189
1190 //
1191 // See also Bug #154197 and ftp url definition in RFC 1738:
1192 // The url "ftp://user@host/foo/bar/file" contains a path,
1193 // that is relative to the user's home.
1194 // The url "ftp://user@host//foo/bar/file" (or also with
1195 // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1196 // contains an absolute path.
1197 //
1198 _lastRedirect.clear();
1199 std::string urlBuffer( curlUrl.asString());
1200 CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1201 urlBuffer.c_str() );
1202 if ( ret != 0 ) {
1203 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1204 }
1205
1206 ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
1207 if ( ret != 0 ) {
1208 ZYPP_THROW(MediaCurlSetOptException(url, _curlError));
1209 }
1210
1211 // Set callback and perform.
1212 internal::ProgressData progressData(_curl, _settings.timeout(), url, srcFile.downloadSize(), &report);
1213 if (!(options & OPTION_NO_REPORT_START))
1214 report->start(url, dest);
1215 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1216 WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1217 }
1218
1219 ret = curl_easy_perform( _curl );
1220#if CURLVERSION_AT_LEAST(7,19,4)
1221 // bnc#692260: If the client sends a request with an If-Modified-Since header
1222 // with a future date for the server, the server may respond 200 sending a
1223 // zero size file.
1224 // curl-7.19.4 introduces CURLINFO_CONDITION_UNMET to check this condition.
1225 if ( ftell(file) == 0 && ret == 0 )
1226 {
1227 long httpReturnCode = 33;
1228 if ( curl_easy_getinfo( _curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
1229 {
1230 long conditionUnmet = 33;
1231 if ( curl_easy_getinfo( _curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
1232 {
1233 WAR << "TIMECONDITION unmet - retry without." << endl;
1234 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1235 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1236 ret = curl_easy_perform( _curl );
1237 }
1238 }
1239 }
1240#endif
1241
1242 if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1243 WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1244 }
1245
1246 if ( ret != 0 )
1247 {
1248 ERR << "curl error: " << ret << ": " << _curlError
1249 << ", temp file size " << ftell(file)
1250 << " bytes." << endl;
1251
1252 // the timeout is determined by the progress data object
1253 // which holds whether the timeout was reached or not,
1254 // otherwise it would be a user cancel
1255 try {
1256
1257 if ( progressData.fileSizeExceeded )
1258 ZYPP_THROW(MediaFileSizeExceededException(url, progressData._expectedFileSize));
1259
1260 evaluateCurlCode( srcFile.filename(), ret, progressData.reached );
1261 }
1262 catch ( const MediaException &e ) {
1263 // some error, we are not sure about file existence, rethrw
1264 ZYPP_RETHROW(e);
1265 }
1266 }
1267
1268#if DETECT_DIR_INDEX
1269 if (!ret && detectDirIndex())
1270 {
1271 ZYPP_THROW(MediaNotAFileException(_url, filename));
1272 }
1273#endif // DETECT_DIR_INDEX
1274}
1275
1277
1278void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
1279{
1280 filesystem::DirContent content;
1281 getDirInfo( content, dirname, /*dots*/false );
1282
1283 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
1284 Pathname filename = dirname + it->name;
1285 int res = 0;
1286
1287 switch ( it->type ) {
1288 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
1290 getFile( OnMediaLocation( filename ) );
1291 break;
1292 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
1293 if ( recurse_r ) {
1294 getDir( filename, recurse_r );
1295 } else {
1296 res = assert_dir( localPath( filename ) );
1297 if ( res ) {
1298 WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
1299 }
1300 }
1301 break;
1302 default:
1303 // don't provide devices, sockets, etc.
1304 break;
1305 }
1306 }
1307}
1308
1310
1311void MediaCurl::getDirInfo( std::list<std::string> & retlist,
1312 const Pathname & dirname, bool dots ) const
1313{
1314 getDirectoryYast( retlist, dirname, dots );
1315}
1316
1318
1320 const Pathname & dirname, bool dots ) const
1321{
1322 getDirectoryYast( retlist, dirname, dots );
1323}
1324
1326//
1327int MediaCurl::aliveCallback( void *clientp, double /*dltotal*/, double dlnow, double /*ultotal*/, double /*ulnow*/ )
1328{
1329 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1330 if( pdata )
1331 {
1332 // Do not propagate dltotal in alive callbacks. MultiCurl uses this to
1333 // prevent a percentage raise while downloading a metalink file. Download
1334 // activity however is indicated by propagating the download rate (via dlnow).
1335 pdata->updateStats( 0.0, dlnow );
1336 return pdata->reportProgress();
1337 }
1338 return 0;
1339}
1340
1341int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow )
1342{
1343 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1344 if( pdata )
1345 {
1346 // work around curl bug that gives us old data
1347 long httpReturnCode = 0;
1348 if ( curl_easy_getinfo( pdata->curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
1349 return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
1350
1351 pdata->updateStats( dltotal, dlnow );
1352 return pdata->reportProgress();
1353 }
1354 return 0;
1355}
1356
1358{
1359 internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>(clientp);
1360 return pdata ? pdata->curl : 0;
1361}
1362
1364
1365std::string MediaCurl::getAuthHint() const
1366{
1367 long auth_info = CURLAUTH_NONE;
1368
1369 CURLcode infoRet =
1370 curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1371
1372 if(infoRet == CURLE_OK)
1373 {
1374 return CurlAuthData::auth_type_long2str(auth_info);
1375 }
1376
1377 return "";
1378}
1379
1384void MediaCurl::resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
1385{
1386 internal::ProgressData *data = reinterpret_cast<internal::ProgressData *>(clientp);
1387 if ( data ) {
1388 data->_expectedFileSize = expectedFileSize;
1389 }
1390}
1391
1393
1394bool MediaCurl::authenticate(const std::string & availAuthTypes, bool firstTry) const
1395{
1397 CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
1398 CurlAuthData_Ptr credentials;
1399
1400 // get stored credentials
1401 AuthData_Ptr cmcred = cm.getCred(_url);
1402
1403 if (cmcred && firstTry)
1404 {
1405 credentials.reset(new CurlAuthData(*cmcred));
1406 DBG << "got stored credentials:" << endl << *credentials << endl;
1407 }
1408 // if not found, ask user
1409 else
1410 {
1411
1412 CurlAuthData_Ptr curlcred;
1413 curlcred.reset(new CurlAuthData());
1415
1416 // preset the username if present in current url
1417 if (!_url.getUsername().empty() && firstTry)
1418 curlcred->setUsername(_url.getUsername());
1419 // if CM has found some credentials, preset the username from there
1420 else if (cmcred)
1421 curlcred->setUsername(cmcred->username());
1422
1423 // indicate we have no good credentials from CM
1424 cmcred.reset();
1425
1426 std::string prompt_msg = str::Format(_("Authentication required for '%s'")) % _url.asString();
1427
1428 // set available authentication types from the exception
1429 // might be needed in prompt
1430 curlcred->setAuthType(availAuthTypes);
1431
1432 // ask user
1433 if (auth_report->prompt(_url, prompt_msg, *curlcred))
1434 {
1435 DBG << "callback answer: retry" << endl
1436 << "CurlAuthData: " << *curlcred << endl;
1437
1438 if (curlcred->valid())
1439 {
1440 credentials = curlcred;
1441 // if (credentials->username() != _url.getUsername())
1442 // _url.setUsername(credentials->username());
1450 }
1451 }
1452 else
1453 {
1454 DBG << "callback answer: cancel" << endl;
1455 }
1456 }
1457
1458 // set username and password
1459 if (credentials)
1460 {
1461 // HACK, why is this const?
1462 const_cast<MediaCurl*>(this)->_settings.setUsername(credentials->username());
1463 const_cast<MediaCurl*>(this)->_settings.setPassword(credentials->password());
1464
1465 // set username and password
1466 CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _settings.userPassword().c_str());
1467 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
1468
1469 // set available authentication types from the exception
1470 if (credentials->authType() == CURLAUTH_NONE)
1471 credentials->setAuthType(availAuthTypes);
1472
1473 // set auth type (seems this must be set _after_ setting the userpwd)
1474 if (credentials->authType() != CURLAUTH_NONE)
1475 {
1476 // FIXME: only overwrite if not empty?
1477 const_cast<MediaCurl*>(this)->_settings.setAuthType(credentials->authTypeAsString());
1478 ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, credentials->authType());
1479 if ( ret != 0 ) ZYPP_THROW(MediaCurlSetOptException(_url, _curlError));
1480 }
1481
1482 if (!cmcred)
1483 {
1484 credentials->setUrl(_url);
1485 cm.addCred(*credentials);
1486 cm.save();
1487 }
1488
1489 return true;
1490 }
1491
1492 return false;
1493}
1494
1495//need a out of line definiton, otherwise vtable is emitted for every translation unit
1497
1498 } // namespace media
1499} // namespace zypp
1500//
#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:180
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.
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: 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:173
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:629
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:703
@ 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:645
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:1384
std::string _currentCookieFile
Definition: MediaCurl.h:167
static int aliveCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
Definition: MediaCurl.cc:1327
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:1341
virtual void getFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename) const override
Definition: MediaCurl.cc:654
virtual void doGetFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename, callback::SendReport< DownloadProgressReport > &_report, RequestOptions options=OPTION_NONE) const
Definition: MediaCurl.cc:1061
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:1311
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:1171
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:734
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:1278
bool authenticate(const std::string &availAuthTypes, bool firstTry) const
Definition: MediaCurl.cc:1394
static CURL * progressCallback_getcurl(void *clientp)
Definition: MediaCurl.cc:1357
virtual void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
Definition: MediaCurl.cc:624
virtual void disconnectFrom() override
Definition: MediaCurl.cc:605
virtual bool doGetDoesFileExist(const Pathname &filename) const
Definition: MediaCurl.cc:875
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:1365
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 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
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:301
AutoDispose<FILE*> calling ::fclose
Definition: AutoDispose.h:312
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