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 //
std::string getScheme() const
Returns the scheme name of the URL.
Definition: Url.cc:533
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
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:319
std::string targetDistribution() const
This is register.target attribute of the installed base product.
Definition: Target.cc:102
#define SET_OPTION_OFFT(opt, val)
Definition: MediaCurl.cc:225
#define MIL
Definition: Logger.h:96
#define _(MSG)
Definition: Gettext.h:37
ProgressData()
Ctor no range [0,0](0).
Definition: progressdata.h:171
const Pathname & path() const
Return current Pathname.
Definition: PathInfo.h:246
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:428
Describes a resource file located on a medium.
static ZConfig & instance()
Singleton ctor.
Definition: ZConfig.cc:823
void checkProtocol(const Url &url) const
check the url is supported by the curl library
Definition: MediaCurl.cc:277
bool authenticate(const std::string &availAuthTypes, bool firstTry) const
Definition: MediaCurl.cc:1392
Implementation class for FTP, HTTP and HTTPS MediaHandler.
Definition: MediaCurl.h:31
virtual bool checkAttachPoint(const Pathname &apoint) const
Verify if the specified directory as attach point (root) as requires by the particular media handler ...
const char * distributionFlavorHeader()
Definition: MediaCurl.cc:174
Store and operate with byte count.
Definition: ByteCount.h:30
to not add a IFMODSINCE header if target exists
Definition: MediaCurl.h:43
static int progressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
Callback reporting download progress.
Definition: MediaCurl.cc:1339
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition: Pathname.h:170
int reportProgress() const
Definition: MediaCurl.cc:149
void setAttachPoint(const Pathname &path, bool temp)
Set a new attach point.
const char * anonymousIdHeader()
Definition: MediaCurl.cc:160
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...
virtual void getFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename) const override
Definition: MediaCurl.cc:652
static int aliveCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
Callback sending just an alive trigger to the UI, without stats (e.g.
Definition: MediaCurl.cc:1325
time_t _timeNow
Now.
Definition: MediaCurl.cc:62
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition: ManagedFile.h:27
virtual void setupEasy()
initializes the curl easy handle with the data from the url
Definition: MediaCurl.cc:302
Convenient building of std::string with boost::format.
Definition: String.h:252
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition: String.cc:36
Edition * _value
Definition: SysContent.cc:311
AutoDispose<int> calling ::close
Definition: AutoDispose.h:296
std::string _currentCookieFile
Definition: MediaCurl.h:167
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
virtual void getFile(const OnMediaLocation &file) const override
Call concrete handler to provide file below attach point.
Definition: MediaCurl.cc:643
#define ERR
Definition: Logger.h:98
Url getFileUrl(const Pathname &filename) const
concatenate the attach url and the filename to a complete download url
Definition: MediaCurl.cc:627
Pathname localPath(const Pathname &pathname) const
Files provided will be available at &#39;localPath(filename)&#39;.
static void setCookieFile(const Pathname &)
Definition: MediaCurl.cc:270
virtual void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
Definition: MediaCurl.cc:622
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
bool empty() const
Test for an empty path.
Definition: Pathname.h:114
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition: Exception.h:440
bool detectDirIndex() const
void setPathName(const std::string &path, EEncoding eflag=zypp::url::E_DECODED)
Set the path name.
Definition: Url.cc:764
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
double _dnlTotal
Bytes to download or 0 if unknown.
Definition: MediaCurl.cc:64
std::string asString() const
Returns a default string representation of the Url object.
Definition: Url.cc:497
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
Url clearQueryString(const Url &url) const
Definition: MediaCurl.cc:265
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:223
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:700
const Url _url
Url to handle.
Definition: MediaHandler.h:113
void setMediaSource(const MediaSourceRef &ref)
Set new media source reference.
const std::string & asString() const
String representation.
Definition: Pathname.h:91
int rename(const Pathname &oldpath, const Pathname &newpath)
Like &#39;rename&#39;.
Definition: PathInfo.cc:742
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
const ByteCount & downloadSize() const
The size of the resource on the server.
void disconnect()
Use concrete handler to isconnect media.
Pathname dirname() const
Return all but the last component od this path.
Definition: Pathname.h:124
do not send a start ProgressReport
Definition: MediaCurl.h:45
#define WAR
Definition: Logger.h:97
double _dnlLast
Bytes downloaded at period start.
Definition: MediaCurl.cc:65
std::list< DirEntry > DirContent
Returned by readdir.
Definition: PathInfo.h:518
void getDirectoryYast(std::list< std::string > &retlist, const Pathname &dirname, bool dots=true) const
Retrieve and if available scan dirname/directory.yast.
bool isValid() const
Verifies the Url.
Definition: Url.cc:489
virtual bool doGetDoesFileExist(const Pathname &filename) const
Definition: MediaCurl.cc:873
const Pathname & filename() const
The path to the resource on the medium.
virtual void attachTo(bool next=false) override
Call concrete handler to attach the media.
Definition: MediaCurl.cc:563
std::string numstring(char n, int w=0)
Definition: String.h:289
Common baseclass for MediaCurl and MediaNetwork.
virtual bool getDoesFileExist(const Pathname &filename) const override
Repeatedly calls doGetDoesFileExist() until it successfully returns, fails unexpectedly, or user cancels the operation.
Definition: MediaCurl.cc:701
const char * agentString()
Definition: MediaCurl.cc:188
void resetDispose()
Set no dispose function.
Definition: AutoDispose.h:176
void doGetFileCopyFile(const OnMediaLocation &srcFile, const Pathname &dest, FILE *file, callback::SendReport< DownloadProgressReport > &report, RequestOptions options=OPTION_NONE) const
Definition: MediaCurl.cc:1169
Media source internally used by MediaManager and MediaHandler.
Definition: MediaSource.h:36
zypp::ByteCount _expectedFileSize
Definition: MediaCurl.cc:57
int _dnlPercent
Percent completed or 0 if _dnlTotal is unknown.
Definition: MediaCurl.cc:68
curl_slist * _customHeaders
Definition: MediaCurl.h:175
int rmdir(const Pathname &path)
Like &#39;rmdir&#39;.
Definition: PathInfo.cc:366
#define SET_OPTION(opt, val)
Definition: MediaCurl.cc:218
Pathname absolutename() const
Return this path, adding a leading &#39;/&#39; if relative.
Definition: Pathname.h:139
Base class for Exception.
Definition: Exception.h:145
Pathname attachPoint() const
Return the currently used attach point.
std::string _lastRedirect
to log/report redirections
Definition: MediaCurl.h:170
Url url() const
Url used.
Definition: MediaHandler.h:503
double _drateLast
Download rate in last period.
Definition: MediaCurl.cc:71
std::string getPathName(EEncoding eflag=zypp::url::E_DECODED) const
Returns the path name from the URL.
Definition: Url.cc:604
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
Definition: Url.cc:588
time_t _timeStart
Start total stats.
Definition: MediaCurl.cc:59
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
virtual void disconnectFrom() override
Definition: MediaCurl.cc:605
static CURL * progressCallback_getcurl(void *clientp)
Definition: MediaCurl.cc:1355
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition: String.h:429
double _dnlNow
Bytes downloaded now.
Definition: MediaCurl.cc:66
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition: AutoDispose.h:93
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
AutoDispose<FILE*> calling ::fclose
Definition: AutoDispose.h:307
void updateStats(double dltotal=0.0, double dlnow=0.0)
Definition: MediaCurl.cc:106
static Pathname _cookieFile
Definition: MediaCurl.h:168
double _drateTotal
Download rate so far.
Definition: MediaCurl.cc:70
std::string anonymousUniqueId() const
anonymous unique id
Definition: Target.cc:132
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
Definition: PathInfo.h:789
std::string getAuthHint() const
Return a comma separated list of available authentication methods supported by server.
Definition: MediaCurl.cc:1363
time_t _timeRcv
Start of no-data timeout.
Definition: MediaCurl.cc:61
Pathname createAttachPoint() const
Try to create a default / temporary attach point.
virtual void doGetFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename, callback::SendReport< DownloadProgressReport > &_report, RequestOptions options=OPTION_NONE) const
Definition: MediaCurl.cc:1059
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
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
char _curlError[CURL_ERROR_SIZE]
Definition: MediaCurl.h:174
time_t _timeLast
Start last period(~1sec)
Definition: MediaCurl.cc:60
const char * c_str() const
Definition: IdStringType.h:105
bool userMayRWX() const
Definition: PathInfo.h:353
Url manipulation class.
Definition: Url.h:91
#define DBG
Definition: Logger.h:95
zypp::callback::SendReport< zypp::media::DownloadProgressReport > * report
Definition: MediaCurl.cc:56
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:572