libzypp  17.31.1
MediaCurl.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
13 #include <iostream>
14 #include <list>
15 
16 #include <zypp/base/Logger.h>
17 #include <zypp/ExternalProgram.h>
18 #include <zypp/base/String.h>
19 #include <zypp/base/Gettext.h>
20 #include <zypp-core/parser/Sysconfig>
21 #include <zypp/base/Gettext.h>
22 
23 #include <zypp/media/MediaCurl.h>
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 
41 using std::endl;
42 
43 namespace internal {
44  using namespace zypp;
45  struct ProgressData
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)
79  double drate_period;
80  // bytes downloaded at the start of the last period
81  double dload_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;
143  _dnlLast = _dnlNow;
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 
206 using namespace internal;
207 using namespace zypp::base;
208 
209 namespace zypp {
210 
211  namespace media {
212 
213 Pathname 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 
229 MediaCurl::MediaCurl( const Url & url_r,
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 
270 void MediaCurl::setCookieFile( const Pathname &fileName )
271 {
272  _cookieFile = fileName;
273 }
274 
276 
277 void 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  {
348  disconnectFrom();
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 ) {
412  disconnectFrom();
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 
563 void 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  {
588  disconnectFrom();
589  ZYPP_RETHROW(ex);
590  }
591 
592  // FIXME: need a derived class to propelly compare url's
594  setMediaSource(media);
595 }
596 
597 bool
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 
624 void MediaCurl::releaseFrom( const std::string & ejectDev )
625 {
626  disconnect();
627 }
628 
629 Url 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 
645 void 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 
654 void 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 
703 bool 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:
764  ZYPP_THROW(
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 
875 bool 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
1020 bool 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 
1061 void 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 
1171 void 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 
1278 void 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
1289  case filesystem::FT_FILE:
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 
1311 void 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 //
1327 int 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 
1341 int 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 
1365 std::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 
1384 void 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 
1394 bool 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
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 * agentString()
Definition: MediaCurl.cc:188
const char * distributionFlavorHeader()
Definition: MediaCurl.cc:174
const char * anonymousIdHeader()
Definition: MediaCurl.cc:160
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
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