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