libzypp  17.31.9
MediaCurl.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
13 #include <iostream>
14 #include <chrono>
15 #include <list>
16 
17 #include <zypp/base/Logger.h>
18 #include <zypp/ExternalProgram.h>
19 #include <zypp/base/String.h>
20 #include <zypp/base/Gettext.h>
21 #include <zypp-core/parser/Sysconfig>
22 #include <zypp/base/Gettext.h>
23 
24 #include <zypp/media/MediaCurl.h>
25 #include <zypp-curl/ProxyInfo>
26 #include <zypp-curl/auth/CurlAuthData>
27 #include <zypp-media/auth/CredentialManager>
28 #include <zypp-curl/CurlConfig>
30 #include <zypp/Target.h>
31 #include <zypp/ZYppFactory.h>
32 #include <zypp/ZConfig.h>
33 
34 #include <cstdlib>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/mount.h>
38 #include <errno.h>
39 #include <dirent.h>
40 #include <unistd.h>
41 
42 using std::endl;
43 
44 namespace internal {
45  using namespace zypp;
49  struct OptionalDownloadProgressReport : public callback::ReceiveReport<media::DownloadProgressReport>
50  {
51  using TimePoint = std::chrono::steady_clock::time_point;
52 
53  OptionalDownloadProgressReport( bool isOptional=false )
54  : _oldRec { Distributor::instance().getReceiver() }
55  , _isOptional { isOptional }
56  { connect(); }
57 
59  { if ( _oldRec ) Distributor::instance().setReceiver( *_oldRec ); else Distributor::instance().noReceiver(); }
60 
61  void reportbegin() override
62  { if ( _oldRec ) _oldRec->reportbegin(); }
63 
64  void reportend() override
65  { if ( _oldRec ) _oldRec->reportend(); }
66 
67  void report( const UserData & userData_r = UserData() ) override
68  { if ( _oldRec ) _oldRec->report( userData_r ); }
69 
70 
71  void start( const Url & file_r, Pathname localfile_r ) override
72  {
73  if ( not _oldRec ) return;
74  if ( _isOptional ) {
75  // delay start until first data are received.
76  _startFile = file_r;
77  _startLocalfile = std::move(localfile_r);
78  return;
79  }
80  _oldRec->start( file_r, localfile_r );
81  }
82 
83  bool progress( int value_r, const Url & file_r, double dbps_avg_r = -1, double dbps_current_r = -1 ) override
84  {
85  if ( not _oldRec ) return true;
86  if ( notStarted() ) {
87  if ( not ( value_r || dbps_avg_r || dbps_current_r ) )
88  return true;
89  sendStart();
90  }
91 
92  //static constexpr std::chrono::milliseconds minfequency { 1000 }; only needed if we'd avoid sending reports without change
93  static constexpr std::chrono::milliseconds maxfequency { 100 };
94  TimePoint now { TimePoint::clock::now() };
95  TimePoint::duration elapsed { now - _lastProgressSent };
96  if ( elapsed < maxfequency )
97  return true; // continue
98  _lastProgressSent = now;
99  return _oldRec->progress( value_r, file_r, dbps_avg_r, dbps_current_r );
100  }
101 
102  Action problem( const Url & file_r, Error error_r, const std::string & description_r ) override
103  {
104  if ( not _oldRec || notStarted() ) return ABORT;
105  return _oldRec->problem( file_r, error_r, description_r );
106  }
107 
108  void finish( const Url & file_r, Error error_r, const std::string & reason_r ) override
109  {
110  if ( not _oldRec || notStarted() ) return;
111  _oldRec->finish( file_r, error_r, reason_r );
112  }
113 
114  private:
115  // _isOptional also indicates the delayed start
116  bool notStarted() const
117  { return _isOptional; }
118 
119  void sendStart()
120  {
121  if ( _isOptional ) {
122  // we know _oldRec is valid...
123  _oldRec->start( std::move(_startFile), std::move(_startLocalfile) );
124  _isOptional = false;
125  }
126  }
127 
128  private:
129  Receiver *const _oldRec;
132  Pathname _startLocalfile;
134  };
135 
137  {
138  ProgressData( CURL *curl, time_t timeout = 0, const zypp::Url & url = zypp::Url(),
139  zypp::ByteCount expectedFileSize_r = 0,
141 
142  void updateStats( double dltotal = 0.0, double dlnow = 0.0 );
143 
144  int reportProgress() const;
145 
146  CURL * curl()
147  { return _curl; }
148 
149  bool timeoutReached() const
150  { return _timeoutReached; }
151 
152  bool fileSizeExceeded() const
153  { return _fileSizeExceeded; }
154 
156  { return _expectedFileSize; }
157 
158  void expectedFileSize( ByteCount newval_r )
159  { _expectedFileSize = newval_r; }
160 
161  private:
162  CURL * _curl;
164  time_t _timeout;
169 
170  time_t _timeStart = 0;
171  time_t _timeLast = 0;
172  time_t _timeRcv = 0;
173  time_t _timeNow = 0;
174 
175  double _dnlTotal = 0.0;
176  double _dnlLast = 0.0;
177  double _dnlNow = 0.0;
178 
179  int _dnlPercent= 0;
180 
181  double _drateTotal= 0.0;
182  double _drateLast = 0.0;
183  };
184 
185 
186 
187  ProgressData::ProgressData(CURL *curl, time_t timeout, const Url &url, ByteCount expectedFileSize_r, zypp::callback::SendReport< zypp::media::DownloadProgressReport> *_report)
188  : _curl( curl )
189  , _url( url )
190  , _timeout( timeout )
191  , _timeoutReached( false )
192  , _fileSizeExceeded ( false )
193  , _expectedFileSize( expectedFileSize_r )
194  , report( _report )
195  {}
196 
197  void ProgressData::updateStats(double dltotal, double dlnow)
198  {
199  time_t now = _timeNow = time(0);
200 
201  // If called without args (0.0), recompute based on the last values seen
202  if ( dltotal && dltotal != _dnlTotal )
203  _dnlTotal = dltotal;
204 
205  if ( dlnow && dlnow != _dnlNow )
206  {
207  _timeRcv = now;
208  _dnlNow = dlnow;
209  }
210 
211  // init or reset if time jumps back
212  if ( !_timeStart || _timeStart > now )
213  _timeStart = _timeLast = _timeRcv = now;
214 
215  // timeout condition
216  if ( _timeout )
217  _timeoutReached = ( (now - _timeRcv) > _timeout );
218 
219  // check if the downloaded data is already bigger than what we expected
220  _fileSizeExceeded = _expectedFileSize > 0 && _expectedFileSize < static_cast<ByteCount::SizeType>(_dnlNow);
221 
222  // percentage:
223  if ( _dnlTotal )
224  _dnlPercent = int(_dnlNow * 100 / _dnlTotal);
225 
226  // download rates:
227  _drateTotal = _dnlNow / std::max( int(now - _timeStart), 1 );
228 
229  if ( _timeLast < now )
230  {
231  _drateLast = (_dnlNow - _dnlLast) / int(now - _timeLast);
232  // start new period
233  _timeLast = now;
234  _dnlLast = _dnlNow;
235  }
236  else if ( _timeStart == _timeLast )
238  }
239 
241  {
242  if ( _fileSizeExceeded )
243  return 1;
244  if ( _timeoutReached )
245  return 1; // no-data timeout
246  if ( report && !(*report)->progress( _dnlPercent, _url, _drateTotal, _drateLast ) )
247  return 1; // user requested abort
248  return 0;
249  }
250 
251  const char * anonymousIdHeader()
252  {
253  // we need to add the release and identifier to the
254  // agent string.
255  // The target could be not initialized, and then this information
256  // is guessed.
257  static const std::string _value(
259  "X-ZYpp-AnonymousId: %s",
260  Target::anonymousUniqueId( Pathname()/*guess root*/ ).c_str() ) )
261  );
262  return _value.c_str();
263  }
264 
266  {
267  // we need to add the release and identifier to the
268  // agent string.
269  // The target could be not initialized, and then this information
270  // is guessed.
271  static const std::string _value(
273  "X-ZYpp-DistributionFlavor: %s",
274  Target::distributionFlavor( Pathname()/*guess root*/ ).c_str() ) )
275  );
276  return _value.c_str();
277  }
278 
279  const char * agentString()
280  {
281  // we need to add the release and identifier to the
282  // agent string.
283  // The target could be not initialized, and then this information
284  // is guessed.
285  static const std::string _value(
286  str::form(
287  "ZYpp " LIBZYPP_VERSION_STRING " (curl %s) %s"
288  , curl_version_info(CURLVERSION_NOW)->version
289  , Target::targetDistribution( Pathname()/*guess root*/ ).c_str()
290  )
291  );
292  return _value.c_str();
293  }
294 
298  {
299  public:
301  const std::string & err_r,
302  const std::string & msg_r )
303  : media::MediaCurlException( url_r, err_r, msg_r )
304  {}
305  //~MediaCurlExceptionMayRetryInternaly() noexcept {}
306  };
307 
308 }
309 
310 
311 using namespace internal;
312 using namespace zypp::base;
313 
314 namespace zypp {
315 
316  namespace media {
317 
318 Pathname MediaCurl::_cookieFile = "/var/lib/YaST2/cookies";
319 
320 // we use this define to unbloat code as this C setting option
321 // and catching exception is done frequently.
323 #define SET_OPTION(opt,val) do { \
324  ret = curl_easy_setopt ( _curl, opt, val ); \
325  if ( ret != 0) { \
326  ZYPP_THROW(MediaCurlSetOptException(_url, _curlError)); \
327  } \
328  } while ( false )
329 
330 #define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val)
331 #define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val)
332 #define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val)
333 
334 MediaCurl::MediaCurl( const Url & url_r,
335  const Pathname & attach_point_hint_r )
336  : MediaNetworkCommonHandler( url_r, attach_point_hint_r,
337  "/", // urlpath at attachpoint
338  true ), // does_download
339  _curl( NULL ),
340  _customHeaders(0L)
341 {
342  _curlError[0] = '\0';
343 
344  MIL << "MediaCurl::MediaCurl(" << url_r << ", " << attach_point_hint_r << ")" << endl;
345 
347 
348  if( !attachPoint().empty())
349  {
350  PathInfo ainfo(attachPoint());
351  Pathname apath(attachPoint() + "XXXXXX");
352  char *atemp = ::strdup( apath.asString().c_str());
353  char *atest = NULL;
354  if( !ainfo.isDir() || !ainfo.userMayRWX() ||
355  atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
356  {
357  WAR << "attach point " << ainfo.path()
358  << " is not useable for " << url_r.getScheme() << endl;
359  setAttachPoint("", true);
360  }
361  else if( atest != NULL)
362  ::rmdir(atest);
363 
364  if( atemp != NULL)
365  ::free(atemp);
366  }
367 }
368 
370 {
372 }
373 
374 void MediaCurl::setCookieFile( const Pathname &fileName )
375 {
376  _cookieFile = fileName;
377 }
378 
380 
381 void MediaCurl::checkProtocol(const Url &url) const
382 {
383  curl_version_info_data *curl_info = NULL;
384  curl_info = curl_version_info(CURLVERSION_NOW);
385  // curl_info does not need any free (is static)
386  if (curl_info->protocols)
387  {
388  const char * const *proto;
389  std::string scheme( url.getScheme());
390  bool found = false;
391  for(proto=curl_info->protocols; !found && *proto; ++proto)
392  {
393  if( scheme == std::string((const char *)*proto))
394  found = true;
395  }
396  if( !found)
397  {
398  std::string msg("Unsupported protocol '");
399  msg += scheme;
400  msg += "'";
402  }
403  }
404 }
405 
407 {
409 
410  curl_easy_setopt(_curl, CURLOPT_HEADERFUNCTION, log_redirects_curl);
411  curl_easy_setopt(_curl, CURLOPT_HEADERDATA, &_lastRedirect);
412  CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
413  if ( ret != 0 ) {
414  ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
415  }
416 
417  SET_OPTION(CURLOPT_FAILONERROR, 1L);
418  SET_OPTION(CURLOPT_NOSIGNAL, 1L);
419 
420  // create non persistant settings
421  // so that we don't add headers twice
422  TransferSettings vol_settings(_settings);
423 
424  // add custom headers for download.opensuse.org (bsc#955801)
425  if ( _url.getHost() == "download.opensuse.org" )
426  {
427  vol_settings.addHeader(anonymousIdHeader());
428  vol_settings.addHeader(distributionFlavorHeader());
429  }
430  vol_settings.addHeader("Pragma:");
431 
432  _settings.setTimeout(ZConfig::instance().download_transfer_timeout());
434 
436 
437  // fill some settings from url query parameters
438  try
439  {
441  }
442  catch ( const MediaException &e )
443  {
444  disconnectFrom();
445  ZYPP_RETHROW(e);
446  }
447  // if the proxy was not set (or explicitly unset) by url, then look...
448  if ( _settings.proxy().empty() )
449  {
450  // ...at the system proxy settings
452  }
453 
455  switch ( env::ZYPP_MEDIA_CURL_IPRESOLVE() )
456  {
457  case 4: SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); break;
458  case 6: SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); break;
459  }
460 
464  SET_OPTION(CURLOPT_CONNECTTIMEOUT, _settings.connectTimeout());
465  // If a transfer timeout is set, also set CURLOPT_TIMEOUT to an upper limit
466  // just in case curl does not trigger its progress callback frequently
467  // enough.
468  if ( _settings.timeout() )
469  {
470  SET_OPTION(CURLOPT_TIMEOUT, 3600L);
471  }
472 
473  // follow any Location: header that the server sends as part of
474  // an HTTP header (#113275)
475  SET_OPTION(CURLOPT_FOLLOWLOCATION, 1L);
476  // 3 redirects seem to be too few in some cases (bnc #465532)
477  SET_OPTION(CURLOPT_MAXREDIRS, 6L);
478 
479  if ( _url.getScheme() == "https" )
480  {
481 #if CURLVERSION_AT_LEAST(7,19,4)
482  // restrict following of redirections from https to https only
483  if ( _url.getHost() == "download.opensuse.org" )
484  SET_OPTION( CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
485  else
486  SET_OPTION( CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS );
487 #endif
488 
491  {
493  }
494 
496  {
497  SET_OPTION(CURLOPT_SSLCERT, _settings.clientCertificatePath().c_str());
498  }
499  if( ! _settings.clientKeyPath().empty() )
500  {
501  SET_OPTION(CURLOPT_SSLKEY, _settings.clientKeyPath().c_str());
502  }
503 
504 #ifdef CURLSSLOPT_ALLOW_BEAST
505  // see bnc#779177
506  ret = curl_easy_setopt( _curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST );
507  if ( ret != 0 ) {
508  disconnectFrom();
510  }
511 #endif
512  SET_OPTION(CURLOPT_SSL_VERIFYPEER, _settings.verifyPeerEnabled() ? 1L : 0L);
513  SET_OPTION(CURLOPT_SSL_VERIFYHOST, _settings.verifyHostEnabled() ? 2L : 0L);
514  // bnc#903405 - POODLE: libzypp should only talk TLS
515  SET_OPTION(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
516  }
517 
518  SET_OPTION(CURLOPT_USERAGENT, _settings.userAgentString().c_str() );
519 
520  /* Fixes bsc#1174011 "auth=basic ignored in some cases"
521  * We should proactively add the password to the request if basic auth is configured
522  * and a password is available in the credentials but not in the URL.
523  *
524  * We will be a bit paranoid here and require that the URL has a user embedded, otherwise we go the default route
525  * and ask the server first about the auth method
526  */
527  if ( _settings.authType() == "basic"
528  && _settings.username().size()
529  && !_settings.password().size() ) {
530 
531  CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
532  const auto cred = cm.getCred( _url );
533  if ( cred && cred->valid() ) {
534  if ( !_settings.username().size() )
535  _settings.setUsername(cred->username());
536  _settings.setPassword(cred->password());
537  }
538  }
539 
540  /*---------------------------------------------------------------*
541  CURLOPT_USERPWD: [user name]:[password]
542 
543  Url::username/password -> CURLOPT_USERPWD
544  If not provided, anonymous FTP identification
545  *---------------------------------------------------------------*/
546 
547  if ( _settings.userPassword().size() )
548  {
549  SET_OPTION(CURLOPT_USERPWD, _settings.userPassword().c_str());
550  std::string use_auth = _settings.authType();
551  if (use_auth.empty())
552  use_auth = "digest,basic"; // our default
553  long auth = CurlAuthData::auth_type_str2long(use_auth);
554  if( auth != CURLAUTH_NONE)
555  {
556  DBG << "Enabling HTTP authentication methods: " << use_auth
557  << " (CURLOPT_HTTPAUTH=" << auth << ")" << std::endl;
558  SET_OPTION(CURLOPT_HTTPAUTH, auth);
559  }
560  }
561 
562  if ( _settings.proxyEnabled() && ! _settings.proxy().empty() )
563  {
564  DBG << "Proxy: '" << _settings.proxy() << "'" << endl;
565  SET_OPTION(CURLOPT_PROXY, _settings.proxy().c_str());
566  SET_OPTION(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
567  /*---------------------------------------------------------------*
568  * CURLOPT_PROXYUSERPWD: [user name]:[password]
569  *
570  * Url::option(proxyuser and proxypassword) -> CURLOPT_PROXYUSERPWD
571  * If not provided, $HOME/.curlrc is evaluated
572  *---------------------------------------------------------------*/
573 
574  std::string proxyuserpwd = _settings.proxyUserPassword();
575 
576  if ( proxyuserpwd.empty() )
577  {
578  CurlConfig curlconf;
579  CurlConfig::parseConfig(curlconf); // parse ~/.curlrc
580  if ( curlconf.proxyuserpwd.empty() )
581  DBG << "Proxy: ~/.curlrc does not contain the proxy-user option" << endl;
582  else
583  {
584  proxyuserpwd = curlconf.proxyuserpwd;
585  DBG << "Proxy: using proxy-user from ~/.curlrc" << endl;
586  }
587  }
588  else
589  {
590  DBG << "Proxy: using provided proxy-user '" << _settings.proxyUsername() << "'" << endl;
591  }
592 
593  if ( ! proxyuserpwd.empty() )
594  {
595  SET_OPTION(CURLOPT_PROXYUSERPWD, curlUnEscape( proxyuserpwd ).c_str());
596  }
597  }
598 #if CURLVERSION_AT_LEAST(7,19,4)
599  else if ( _settings.proxy() == EXPLICITLY_NO_PROXY )
600  {
601  // Explicitly disabled in URL (see fillSettingsFromUrl()).
602  // This should also prevent libcurl from looking into the environment.
603  DBG << "Proxy: explicitly NOPROXY" << endl;
604  SET_OPTION(CURLOPT_NOPROXY, "*");
605  }
606 #endif
607  else
608  {
609  DBG << "Proxy: not explicitly set" << endl;
610  DBG << "Proxy: libcurl may look into the environment" << endl;
611  }
612 
614  if ( _settings.minDownloadSpeed() != 0 )
615  {
616  SET_OPTION(CURLOPT_LOW_SPEED_LIMIT, _settings.minDownloadSpeed());
617  // default to 10 seconds at low speed
618  SET_OPTION(CURLOPT_LOW_SPEED_TIME, 60L);
619  }
620 
621 #if CURLVERSION_AT_LEAST(7,15,5)
622  if ( _settings.maxDownloadSpeed() != 0 )
623  SET_OPTION_OFFT(CURLOPT_MAX_RECV_SPEED_LARGE, _settings.maxDownloadSpeed());
624 #endif
625 
626  /*---------------------------------------------------------------*
627  *---------------------------------------------------------------*/
628 
631  if ( str::strToBool( _url.getQueryParam( "cookies" ), true ) )
632  SET_OPTION(CURLOPT_COOKIEFILE, _currentCookieFile.c_str() );
633  else
634  MIL << "No cookies requested" << endl;
635  SET_OPTION(CURLOPT_COOKIEJAR, _currentCookieFile.c_str() );
636  SET_OPTION(CURLOPT_PROGRESSFUNCTION, &progressCallback );
637  SET_OPTION(CURLOPT_NOPROGRESS, 0L);
638 
639 #if CURLVERSION_AT_LEAST(7,18,0)
640  // bnc #306272
641  SET_OPTION(CURLOPT_PROXY_TRANSFER_MODE, 1L );
642 #endif
643  // append settings custom headers to curl
644  for ( const auto &header : vol_settings.headers() )
645  {
646  // MIL << "HEADER " << *it << std::endl;
647 
648  _customHeaders = curl_slist_append(_customHeaders, header.c_str());
649  if ( !_customHeaders )
651  }
652 
653  SET_OPTION(CURLOPT_HTTPHEADER, _customHeaders);
654 }
655 
657 
658 
659 void MediaCurl::attachTo (bool next)
660 {
661  if ( next )
663 
664  if ( !_url.isValid() )
666 
669  {
671  }
672 
673  disconnectFrom(); // clean _curl if needed
674  _curl = curl_easy_init();
675  if ( !_curl ) {
677  }
678  try
679  {
680  setupEasy();
681  }
682  catch (Exception & ex)
683  {
684  disconnectFrom();
685  ZYPP_RETHROW(ex);
686  }
687 
688  // FIXME: need a derived class to propelly compare url's
690  setMediaSource(media);
691 }
692 
693 bool
695 {
696  return MediaHandler::checkAttachPoint( apoint, true, true);
697 }
698 
700 
702 {
703  if ( _customHeaders )
704  {
705  curl_slist_free_all(_customHeaders);
706  _customHeaders = 0L;
707  }
708 
709  if ( _curl )
710  {
711  // bsc#1201092: Strange but within global_dtors we may exceptions here.
712  try { curl_easy_cleanup( _curl ); }
713  catch (...) { ; }
714  _curl = NULL;
715  }
716 }
717 
719 
720 void MediaCurl::releaseFrom( const std::string & ejectDev )
721 {
722  disconnect();
723 }
724 
726 
727 void MediaCurl::getFile( const OnMediaLocation &file ) const
728 {
729  // Use absolute file name to prevent access of files outside of the
730  // hierarchy below the attach point.
731  getFileCopy( file, localPath(file.filename()).absolutename() );
732 }
733 
735 
736 void MediaCurl::getFileCopy( const OnMediaLocation & srcFile , const Pathname & target ) const
737 {
738 
739  const auto &filename = srcFile.filename();
740 
741  // Optional files will send no report until data are actually received (we know it exists).
742  OptionalDownloadProgressReport reportfilter( srcFile.optional() );
744 
745  Url fileurl(getFileUrl(filename));
746 
747  bool retry = false;
748  unsigned internalTry = 0;
749  static constexpr unsigned maxInternalTry = 3;
750 
751  do
752  {
753  retry = false;
754  try
755  {
756  doGetFileCopy( srcFile, target, report );
757  }
758  // retry with proper authentication data
759  catch (MediaUnauthorizedException & ex_r)
760  {
761  if(authenticate(ex_r.hint(), !retry))
762  retry = true;
763  else
764  {
765  report->finish(fileurl, zypp::media::DownloadProgressReport::ACCESS_DENIED, ex_r.asUserHistory());
766  ZYPP_RETHROW(ex_r);
767  }
768  }
769  // unexpected exception
770  catch (MediaException & excpt_r)
771  {
772  if ( typeid(excpt_r) == typeid( MediaCurlExceptionMayRetryInternaly ) ) {
773  ++internalTry;
774  if ( internalTry < maxInternalTry ) {
775  // just report (NO_ERROR); no interactive request to the user
776  report->problem(fileurl, media::DownloadProgressReport::NO_ERROR, excpt_r.asUserHistory()+_("Will try again..."));
777  retry = true;
778  continue;
779  }
780  excpt_r.addHistory( str::Format(_("Giving up after %1% attempts.")) % maxInternalTry );
781  }
782 
784  if( typeid(excpt_r) == typeid( media::MediaFileNotFoundException ) ||
785  typeid(excpt_r) == typeid( media::MediaNotAFileException ) )
786  {
788  }
789  report->finish(fileurl, reason, excpt_r.asUserHistory());
790  ZYPP_RETHROW(excpt_r);
791  }
792  }
793  while (retry);
794 
795  report->finish(fileurl, zypp::media::DownloadProgressReport::NO_ERROR, "");
796 }
797 
799 
800 bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
801 {
802  bool retry = false;
803 
804  do
805  {
806  try
807  {
808  return doGetDoesFileExist( filename );
809  }
810  // authentication problem, retry with proper authentication data
811  catch (MediaUnauthorizedException & ex_r)
812  {
813  if(authenticate(ex_r.hint(), !retry))
814  retry = true;
815  else
816  ZYPP_RETHROW(ex_r);
817  }
818  // unexpected exception
819  catch (MediaException & excpt_r)
820  {
821  ZYPP_RETHROW(excpt_r);
822  }
823  }
824  while (retry);
825 
826  return false;
827 }
828 
830 
832  CURLcode code,
833  bool timeout_reached) const
834 {
835  if ( code != 0 )
836  {
837  Url url;
838  if (filename.empty())
839  url = _url;
840  else
841  url = getFileUrl(filename);
842 
843  std::string err;
844  {
845  switch ( code )
846  {
847  case CURLE_UNSUPPORTED_PROTOCOL:
848  err = " Unsupported protocol";
849  if ( !_lastRedirect.empty() )
850  {
851  err += " or redirect (";
852  err += _lastRedirect;
853  err += ")";
854  }
855  break;
856  case CURLE_URL_MALFORMAT:
857  case CURLE_URL_MALFORMAT_USER:
858  err = " Bad URL";
859  break;
860  case CURLE_LOGIN_DENIED:
861  ZYPP_THROW(
862  MediaUnauthorizedException(url, "Login failed.", _curlError, ""));
863  break;
864  case CURLE_HTTP_RETURNED_ERROR:
865  {
866  long httpReturnCode = 0;
867  CURLcode infoRet = curl_easy_getinfo( _curl,
868  CURLINFO_RESPONSE_CODE,
869  &httpReturnCode );
870  if ( infoRet == CURLE_OK )
871  {
872  std::string msg = "HTTP response: " + str::numstring( httpReturnCode );
873  switch ( httpReturnCode )
874  {
875  case 401:
876  {
877  std::string auth_hint = getAuthHint();
878 
879  DBG << msg << " Login failed (URL: " << url.asString() << ")" << std::endl;
880  DBG << "MediaUnauthorizedException auth hint: '" << auth_hint << "'" << std::endl;
881 
883  url, "Login failed.", _curlError, auth_hint
884  ));
885  }
886 
887  case 502: // bad gateway (bnc #1070851)
888  case 503: // service temporarily unavailable (bnc #462545)
890  case 504: // gateway timeout
892  case 403:
893  {
894  std::string msg403;
895  if ( url.getHost().find(".suse.com") != std::string::npos )
896  msg403 = _("Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
897  else if (url.asString().find("novell.com") != std::string::npos)
898  msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
900  }
901  case 404:
902  case 410:
904  }
905 
906  DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
908  }
909  else
910  {
911  std::string msg = "Unable to retrieve HTTP response:";
912  DBG << msg << " (URL: " << url.asString() << ")" << std::endl;
914  }
915  }
916  break;
917  case CURLE_FTP_COULDNT_RETR_FILE:
918 #if CURLVERSION_AT_LEAST(7,16,0)
919  case CURLE_REMOTE_FILE_NOT_FOUND:
920 #endif
921  case CURLE_FTP_ACCESS_DENIED:
922  case CURLE_TFTP_NOTFOUND:
923  err = "File not found";
925  break;
926  case CURLE_BAD_PASSWORD_ENTERED:
927  case CURLE_FTP_USER_PASSWORD_INCORRECT:
928  err = "Login failed";
929  break;
930  case CURLE_COULDNT_RESOLVE_PROXY:
931  case CURLE_COULDNT_RESOLVE_HOST:
932  case CURLE_COULDNT_CONNECT:
933  case CURLE_FTP_CANT_GET_HOST:
934  err = "Connection failed";
935  break;
936  case CURLE_WRITE_ERROR:
937  err = "Write error";
938  break;
939  case CURLE_PARTIAL_FILE:
940  case CURLE_OPERATION_TIMEDOUT:
941  timeout_reached = true; // fall though to TimeoutException
942  // fall though...
943  case CURLE_ABORTED_BY_CALLBACK:
944  if( timeout_reached )
945  {
946  err = "Timeout reached";
948  }
949  else
950  {
951  err = "User abort";
952  }
953  break;
954 
955  // Attempt to work around certain issues by autoretry in MediaCurl::getFileCopy
956  case CURLE_HTTP2:
957  case CURLE_HTTP2_STREAM:
958  err = "Curl error " + str::numstring( code );
960  break;
961 
962  default:
963  err = "Curl error " + str::numstring( code );
964  break;
965  }
966 
967  // uhm, no 0 code but unknown curl exception
969  }
970  }
971  else
972  {
973  // actually the code is 0, nothing happened
974  }
975 }
976 
978 
979 bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
980 {
981  DBG << filename.asString() << endl;
982 
983  if(!_url.isValid())
985 
986  if(_url.getHost().empty())
988 
989  Url url(getFileUrl(filename));
990 
991  DBG << "URL: " << url.asString() << endl;
992  // Use URL without options and without username and passwd
993  // (some proxies dislike them in the URL).
994  // Curl seems to need the just scheme, hostname and a path;
995  // the rest was already passed as curl options (in attachTo).
996  Url curlUrl( clearQueryString(url) );
997 
998  //
999  // See also Bug #154197 and ftp url definition in RFC 1738:
1000  // The url "ftp://user@host/foo/bar/file" contains a path,
1001  // that is relative to the user's home.
1002  // The url "ftp://user@host//foo/bar/file" (or also with
1003  // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1004  // contains an absolute path.
1005  //
1006  _lastRedirect.clear();
1007  std::string urlBuffer( curlUrl.asString());
1008  CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1009  urlBuffer.c_str() );
1010  if ( ret != 0 ) {
1012  }
1013 
1014  // If no head requests allowed (?head_requests=no):
1015  // Instead of returning no data with NOBODY, we return
1016  // little data, that works with broken servers, and
1017  // works for ftp as well, because retrieving only headers
1018  // ftp will return always OK code ?
1019  // See http://curl.haxx.se/docs/knownbugs.html #58
1021  struct TempSetHeadRequest
1022  {
1023  TempSetHeadRequest( CURL * curl_r, bool doHttpHeadRequest_r )
1024  : _curl { curl_r }
1025  , _doHttpHeadRequest { doHttpHeadRequest_r }
1026  {
1027  if ( _doHttpHeadRequest ) {
1028  curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
1029  } else {
1030  curl_easy_setopt( _curl, CURLOPT_RANGE, "0-1" );
1031  }
1032  }
1033  ~TempSetHeadRequest() {
1034  if ( _doHttpHeadRequest ) {
1035  curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
1036  /* yes, this is why we never got to get NOBODY working before,
1037  because setting it changes this option too, and we also*
1038  need to reset it
1039  See: http://curl.haxx.se/mail/archive-2005-07/0073.html
1040  */
1041  curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
1042  } else {
1043  curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1044  }
1045  }
1046  private:
1047  CURL * _curl;
1048  bool _doHttpHeadRequest;
1049  } _guard( _curl, (_url.getScheme() == "http" || _url.getScheme() == "https") && _settings.headRequestsAllowed() );
1050 
1051 
1052  AutoFILE file { ::fopen( "/dev/null", "w" ) };
1053  if ( !file ) {
1054  ERR << "fopen failed for /dev/null" << endl;
1055  ZYPP_THROW(MediaWriteException("/dev/null"));
1056  }
1057 
1058  ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, (*file) );
1059  if ( ret != 0 ) {
1061  }
1062 
1063  CURLcode ok = curl_easy_perform( _curl );
1064  MIL << "perform code: " << ok << " [ " << curl_easy_strerror(ok) << " ]" << endl;
1065 
1066  // as we are not having user interaction, the user can't cancel
1067  // the file existence checking, a callback or timeout return code
1068  // will be always a timeout.
1069  try {
1070  evaluateCurlCode( filename, ok, true /* timeout */);
1071  }
1072  catch ( const MediaFileNotFoundException &e ) {
1073  // if the file did not exist then we can return false
1074  return false;
1075  }
1076  catch ( const MediaException &e ) {
1077  // some error, we are not sure about file existence, rethrw
1078  ZYPP_RETHROW(e);
1079  }
1080  // exists
1081  return ( ok == CURLE_OK );
1082 }
1083 
1085 
1086 
1087 #if DETECT_DIR_INDEX
1088 bool MediaCurl::detectDirIndex() const
1089 {
1090  if(_url.getScheme() != "http" && _url.getScheme() != "https")
1091  return false;
1092  //
1093  // try to check the effective url and set the not_a_file flag
1094  // if the url path ends with a "/", what usually means, that
1095  // we've received a directory index (index.html content).
1096  //
1097  // Note: This may be dangerous and break file retrieving in
1098  // case of some server redirections ... ?
1099  //
1100  bool not_a_file = false;
1101  char *ptr = NULL;
1102  CURLcode ret = curl_easy_getinfo( _curl,
1103  CURLINFO_EFFECTIVE_URL,
1104  &ptr);
1105  if ( ret == CURLE_OK && ptr != NULL)
1106  {
1107  try
1108  {
1109  Url eurl( ptr);
1110  std::string path( eurl.getPathName());
1111  if( !path.empty() && path != "/" && *path.rbegin() == '/')
1112  {
1113  DBG << "Effective url ("
1114  << eurl
1115  << ") seems to provide the index of a directory"
1116  << endl;
1117  not_a_file = true;
1118  }
1119  }
1120  catch( ... )
1121  {}
1122  }
1123  return not_a_file;
1124 }
1125 #endif
1126 
1128 
1129 void MediaCurl::doGetFileCopy( const OnMediaLocation &srcFile , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1130 {
1131  Pathname dest = target.absolutename();
1132  if( assert_dir( dest.dirname() ) )
1133  {
1134  DBG << "assert_dir " << dest.dirname() << " failed" << endl;
1135  ZYPP_THROW( MediaSystemException(getFileUrl(srcFile.filename()), "System error on " + dest.dirname().asString()) );
1136  }
1137 
1138  ManagedFile destNew { target.extend( ".new.zypp.XXXXXX" ) };
1139  AutoFILE file;
1140  {
1141  AutoFREE<char> buf { ::strdup( (*destNew).c_str() ) };
1142  if( ! buf )
1143  {
1144  ERR << "out of memory for temp file name" << endl;
1145  ZYPP_THROW(MediaSystemException(getFileUrl(srcFile.filename()), "out of memory for temp file name"));
1146  }
1147 
1148  AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
1149  if( tmp_fd == -1 )
1150  {
1151  ERR << "mkstemp failed for file '" << destNew << "'" << endl;
1152  ZYPP_THROW(MediaWriteException(destNew));
1153  }
1154  destNew = ManagedFile( (*buf), filesystem::unlink );
1155 
1156  file = ::fdopen( tmp_fd, "we" );
1157  if ( ! file )
1158  {
1159  ERR << "fopen failed for file '" << destNew << "'" << endl;
1160  ZYPP_THROW(MediaWriteException(destNew));
1161  }
1162  tmp_fd.resetDispose(); // don't close it here! ::fdopen moved ownership to file
1163  }
1164 
1165  DBG << "dest: " << dest << endl;
1166  DBG << "temp: " << destNew << endl;
1167 
1168  // set IFMODSINCE time condition (no download if not modified)
1169  if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
1170  {
1171  curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1172  curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
1173  }
1174  else
1175  {
1176  curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1177  curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1178  }
1179  try
1180  {
1181  doGetFileCopyFile( srcFile, dest, file, report, options);
1182  }
1183  catch (Exception &e)
1184  {
1185  curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1186  curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1187  ZYPP_RETHROW(e);
1188  }
1189 
1190  long httpReturnCode = 0;
1191  CURLcode infoRet = curl_easy_getinfo(_curl,
1192  CURLINFO_RESPONSE_CODE,
1193  &httpReturnCode);
1194  bool modified = true;
1195  if (infoRet == CURLE_OK)
1196  {
1197  DBG << "HTTP response: " + str::numstring(httpReturnCode);
1198  if ( httpReturnCode == 304
1199  || ( httpReturnCode == 213 && (_url.getScheme() == "ftp" || _url.getScheme() == "tftp") ) ) // not modified
1200  {
1201  DBG << " Not modified.";
1202  modified = false;
1203  }
1204  DBG << endl;
1205  }
1206  else
1207  {
1208  WAR << "Could not get the response code." << endl;
1209  }
1210 
1211  if (modified || infoRet != CURLE_OK)
1212  {
1213  // apply umask
1214  if ( ::fchmod( ::fileno(file), filesystem::applyUmaskTo( 0644 ) ) )
1215  {
1216  ERR << "Failed to chmod file " << destNew << endl;
1217  }
1218 
1219  file.resetDispose(); // we're going to close it manually here
1220  if ( ::fclose( file ) )
1221  {
1222  ERR << "Fclose failed for file '" << destNew << "'" << endl;
1223  ZYPP_THROW(MediaWriteException(destNew));
1224  }
1225 
1226  // move the temp file into dest
1227  if ( rename( destNew, dest ) != 0 ) {
1228  ERR << "Rename failed" << endl;
1230  }
1231  destNew.resetDispose(); // no more need to unlink it
1232  }
1233 
1234  DBG << "done: " << PathInfo(dest) << endl;
1235 }
1236 
1238 
1239 void MediaCurl::doGetFileCopyFile( const OnMediaLocation & srcFile, const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
1240 {
1241  DBG << srcFile.filename().asString() << endl;
1242 
1243  if(!_url.isValid())
1245 
1246  if(_url.getHost().empty())
1248 
1249  Url url(getFileUrl(srcFile.filename()));
1250 
1251  DBG << "URL: " << url.asString() << endl;
1252  // Use URL without options and without username and passwd
1253  // (some proxies dislike them in the URL).
1254  // Curl seems to need the just scheme, hostname and a path;
1255  // the rest was already passed as curl options (in attachTo).
1256  Url curlUrl( clearQueryString(url) );
1257 
1258  //
1259  // See also Bug #154197 and ftp url definition in RFC 1738:
1260  // The url "ftp://user@host/foo/bar/file" contains a path,
1261  // that is relative to the user's home.
1262  // The url "ftp://user@host//foo/bar/file" (or also with
1263  // encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
1264  // contains an absolute path.
1265  //
1266  _lastRedirect.clear();
1267  std::string urlBuffer( curlUrl.asString());
1268  CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
1269  urlBuffer.c_str() );
1270  if ( ret != 0 ) {
1272  }
1273 
1274  ret = curl_easy_setopt( _curl, CURLOPT_WRITEDATA, file );
1275  if ( ret != 0 ) {
1277  }
1278 
1279  // Set callback and perform.
1280  internal::ProgressData progressData(_curl, _settings.timeout(), url, srcFile.downloadSize(), &report);
1281  if (!(options & OPTION_NO_REPORT_START))
1282  report->start(url, dest);
1283  if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1284  WAR << "Can't set CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1285  }
1286 
1287  ret = curl_easy_perform( _curl );
1288 #if CURLVERSION_AT_LEAST(7,19,4)
1289  // bnc#692260: If the client sends a request with an If-Modified-Since header
1290  // with a future date for the server, the server may respond 200 sending a
1291  // zero size file.
1292  // curl-7.19.4 introduces CURLINFO_CONDITION_UNMET to check this condition.
1293  if ( ftell(file) == 0 && ret == 0 )
1294  {
1295  long httpReturnCode = 33;
1296  if ( curl_easy_getinfo( _curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
1297  {
1298  long conditionUnmet = 33;
1299  if ( curl_easy_getinfo( _curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
1300  {
1301  WAR << "TIMECONDITION unmet - retry without." << endl;
1302  curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1303  curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
1304  ret = curl_easy_perform( _curl );
1305  }
1306  }
1307  }
1308 #endif
1309 
1310  if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1311  WAR << "Can't unset CURLOPT_PROGRESSDATA: " << _curlError << endl;;
1312  }
1313 
1314  if ( ret != 0 )
1315  {
1316  ERR << "curl error: " << ret << ": " << _curlError
1317  << ", temp file size " << ftell(file)
1318  << " bytes." << endl;
1319 
1320  // the timeout is determined by the progress data object
1321  // which holds whether the timeout was reached or not,
1322  // otherwise it would be a user cancel
1323  try {
1324 
1325  if ( progressData.fileSizeExceeded() )
1326  ZYPP_THROW(MediaFileSizeExceededException(url, progressData.expectedFileSize()));
1327 
1328  evaluateCurlCode( srcFile.filename(), ret, progressData.timeoutReached() );
1329  }
1330  catch ( const MediaException &e ) {
1331  // some error, we are not sure about file existence, rethrw
1332  ZYPP_RETHROW(e);
1333  }
1334  }
1335 
1336 #if DETECT_DIR_INDEX
1337  if (!ret && detectDirIndex())
1338  {
1340  }
1341 #endif // DETECT_DIR_INDEX
1342 }
1343 
1345 
1346 void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
1347 {
1348  filesystem::DirContent content;
1349  getDirInfo( content, dirname, /*dots*/false );
1350 
1351  for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
1352  Pathname filename = dirname + it->name;
1353  int res = 0;
1354 
1355  switch ( it->type ) {
1356  case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
1357  case filesystem::FT_FILE:
1358  getFile( OnMediaLocation( filename ) );
1359  break;
1360  case filesystem::FT_DIR: // newer directory.yast contain at least directory info
1361  if ( recurse_r ) {
1362  getDir( filename, recurse_r );
1363  } else {
1364  res = assert_dir( localPath( filename ) );
1365  if ( res ) {
1366  WAR << "Ignore error (" << res << ") on creating local directory '" << localPath( filename ) << "'" << endl;
1367  }
1368  }
1369  break;
1370  default:
1371  // don't provide devices, sockets, etc.
1372  break;
1373  }
1374  }
1375 }
1376 
1378 
1379 void MediaCurl::getDirInfo( std::list<std::string> & retlist,
1380  const Pathname & dirname, bool dots ) const
1381 {
1382  getDirectoryYast( retlist, dirname, dots );
1383 }
1384 
1386 
1388  const Pathname & dirname, bool dots ) const
1389 {
1390  getDirectoryYast( retlist, dirname, dots );
1391 }
1392 
1394 //
1395 int MediaCurl::aliveCallback( void *clientp, double /*dltotal*/, double dlnow, double /*ultotal*/, double /*ulnow*/ )
1396 {
1397  internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1398  if( pdata )
1399  {
1400  // Do not propagate dltotal in alive callbacks. MultiCurl uses this to
1401  // prevent a percentage raise while downloading a metalink file. Download
1402  // activity however is indicated by propagating the download rate (via dlnow).
1403  pdata->updateStats( 0.0, dlnow );
1404  return pdata->reportProgress();
1405  }
1406  return 0;
1407 }
1408 
1409 int MediaCurl::progressCallback( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow )
1410 {
1411  internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>( clientp );
1412  if( pdata )
1413  {
1414  // work around curl bug that gives us old data
1415  long httpReturnCode = 0;
1416  if ( curl_easy_getinfo( pdata->curl(), CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
1417  return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
1418 
1419  pdata->updateStats( dltotal, dlnow );
1420  return pdata->reportProgress();
1421  }
1422  return 0;
1423 }
1424 
1426 {
1427  internal::ProgressData *pdata = reinterpret_cast<internal::ProgressData *>(clientp);
1428  return pdata ? pdata->curl() : 0;
1429 }
1430 
1432 
1433 std::string MediaCurl::getAuthHint() const
1434 {
1435  long auth_info = CURLAUTH_NONE;
1436 
1437  CURLcode infoRet =
1438  curl_easy_getinfo(_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1439 
1440  if(infoRet == CURLE_OK)
1441  {
1442  return CurlAuthData::auth_type_long2str(auth_info);
1443  }
1444 
1445  return "";
1446 }
1447 
1452 void MediaCurl::resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
1453 {
1454  internal::ProgressData *data = reinterpret_cast<internal::ProgressData *>(clientp);
1455  if ( data ) {
1456  data->expectedFileSize( expectedFileSize );
1457  }
1458 }
1459 
1461 
1462 bool MediaCurl::authenticate(const std::string & availAuthTypes, bool firstTry) const
1463 {
1465  CredentialManager cm(CredManagerOptions(ZConfig::instance().repoManagerRoot()));
1466  CurlAuthData_Ptr credentials;
1467 
1468  // get stored credentials
1469  AuthData_Ptr cmcred = cm.getCred(_url);
1470 
1471  if (cmcred && firstTry)
1472  {
1473  credentials.reset(new CurlAuthData(*cmcred));
1474  DBG << "got stored credentials:" << endl << *credentials << endl;
1475  }
1476  // if not found, ask user
1477  else
1478  {
1479 
1480  CurlAuthData_Ptr curlcred;
1481  curlcred.reset(new CurlAuthData());
1483 
1484  // preset the username if present in current url
1485  if (!_url.getUsername().empty() && firstTry)
1486  curlcred->setUsername(_url.getUsername());
1487  // if CM has found some credentials, preset the username from there
1488  else if (cmcred)
1489  curlcred->setUsername(cmcred->username());
1490 
1491  // indicate we have no good credentials from CM
1492  cmcred.reset();
1493 
1494  std::string prompt_msg = str::Format(_("Authentication required for '%s'")) % _url.asString();
1495 
1496  // set available authentication types from the exception
1497  // might be needed in prompt
1498  curlcred->setAuthType(availAuthTypes);
1499 
1500  // ask user
1501  if (auth_report->prompt(_url, prompt_msg, *curlcred))
1502  {
1503  DBG << "callback answer: retry" << endl
1504  << "CurlAuthData: " << *curlcred << endl;
1505 
1506  if (curlcred->valid())
1507  {
1508  credentials = curlcred;
1509  // if (credentials->username() != _url.getUsername())
1510  // _url.setUsername(credentials->username());
1518  }
1519  }
1520  else
1521  {
1522  DBG << "callback answer: cancel" << endl;
1523  }
1524  }
1525 
1526  // set username and password
1527  if (credentials)
1528  {
1529  // HACK, why is this const?
1530  const_cast<MediaCurl*>(this)->_settings.setUsername(credentials->username());
1531  const_cast<MediaCurl*>(this)->_settings.setPassword(credentials->password());
1532 
1533  // set username and password
1534  CURLcode ret = curl_easy_setopt(_curl, CURLOPT_USERPWD, _settings.userPassword().c_str());
1536 
1537  // set available authentication types from the exception
1538  if (credentials->authType() == CURLAUTH_NONE)
1539  credentials->setAuthType(availAuthTypes);
1540 
1541  // set auth type (seems this must be set _after_ setting the userpwd)
1542  if (credentials->authType() != CURLAUTH_NONE)
1543  {
1544  // FIXME: only overwrite if not empty?
1545  const_cast<MediaCurl*>(this)->_settings.setAuthType(credentials->authTypeAsString());
1546  ret = curl_easy_setopt(_curl, CURLOPT_HTTPAUTH, credentials->authType());
1548  }
1549 
1550  if (!cmcred)
1551  {
1552  credentials->setUrl(_url);
1553  cm.addCred(*credentials);
1554  cm.save();
1555  }
1556 
1557  return true;
1558  }
1559 
1560  return false;
1561 }
1562 
1563 //need a out of line definiton, otherwise vtable is emitted for every translation unit
1565 
1566  } // namespace media
1567 } // namespace zypp
1568 //
std::string getScheme() const
Returns the scheme name of the URL.
Definition: Url.cc:533
long timeout() const
transfer timeout
void globalInitCurlOnce()
Definition: curlhelper.cc:62
Url getFileUrl(const Pathname &filename) const
concatenate the attach url and the filename to a complete download url
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:694
const Pathname & certificateAuthoritiesPath() const
SSL certificate authorities path ( default: /etc/ssl/certs )
int assert_dir(const Pathname &path, unsigned mode)
Like &#39;mkdir -p&#39;.
Definition: PathInfo.cc:319
MediaCurlExceptionMayRetryInternaly(const Url &url_r, const std::string &err_r, const std::string &msg_r)
Definition: MediaCurl.cc:300
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:330
#define MIL
Definition: Logger.h:96
const Pathname & clientCertificatePath() const
SSL client certificate file.
bool progress(int value_r, const Url &file_r, double dbps_avg_r=-1, double dbps_current_r=-1) override
Download progress.
Definition: MediaCurl.cc:83
void addHeader(std::string &&val_r)
add a header, on the form "Foo: Bar"
#define _(MSG)
Definition: Gettext.h:37
size_t log_redirects_curl(char *ptr, size_t size, size_t nmemb, void *userdata)
Definition: curlhelper.cc:134
ProgressData()
Ctor no range [0,0](0).
Definition: progressdata.h:174
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:922
std::string proxyUserPassword() const
returns the proxy user and password as a user:pass string
void checkProtocol(const Url &url) const
check the url is supported by the curl library
Definition: MediaCurl.cc:381
bool authenticate(const std::string &availAuthTypes, bool firstTry) const
Definition: MediaCurl.cc:1462
Implementation class for FTP, HTTP and HTTPS MediaHandler.
Definition: MediaCurl.h:31
void setPassword(const std::string &val_r)
sets the auth password
std::chrono::steady_clock::time_point TimePoint
Definition: MediaCurl.cc:51
ByteCount _expectedFileSize
Definition: MediaCurl.cc:167
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:265
Store and operate with byte count.
Definition: ByteCount.h:30
long maxDownloadSpeed() const
Maximum download speed (bytes per second)
to not add a IFMODSINCE header if target exists
Definition: MediaCurl.h:43
Holds transfer setting.
void save()
Saves any unsaved credentials added via addUserCred() or addGlobalCred() methods. ...
const std::string & authType() const
get the allowed authentication types
bool verifyHostEnabled() const
Whether to verify host for ssl.
const std::string & proxyUsername() const
proxy auth username
static int progressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
Callback reporting download progress.
Definition: MediaCurl.cc:1409
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
Definition: Pathname.h:173
int reportProgress() const
Definition: MediaCurl.cc:240
Url clearQueryString(const Url &url)
Definition: curlhelper.cc:364
void setAttachPoint(const Pathname &path, bool temp)
Set a new attach point.
void start(const Url &file_r, Pathname localfile_r) override
Definition: MediaCurl.cc:71
const char * anonymousIdHeader()
Definition: MediaCurl.cc:251
const char * c_str() const
String representation.
Definition: Pathname.h:110
void addHistory(const std::string &msg_r)
Add some message text to the history.
Definition: Exception.cc:140
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...
void setUsername(const std::string &val_r)
sets the auth username
Action problem(const Url &file_r, Error error_r, const std::string &description_r) override
Definition: MediaCurl.cc:102
virtual void getFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename) const override
Definition: MediaCurl.cc:736
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:1395
time_t _timeNow
Now.
Definition: MediaCurl.cc:173
AuthData_Ptr getCred(const Url &url)
Get credentials for the specified url.
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition: ManagedFile.h:27
void setConnectTimeout(long t)
set the connect timeout
virtual void setupEasy()
initializes the curl easy handle with the data from the url
Definition: MediaCurl.cc:406
bool timeoutReached() const
Definition: MediaCurl.cc:149
Convenient building of std::string with boost::format.
Definition: String.h:252
Structure holding values of curlrc options.
Definition: curlconfig.h:26
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:300
std::string _currentCookieFile
Definition: MediaCurl.h:160
const std::string & password() const
auth password
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:1346
virtual void getFile(const OnMediaLocation &file) const override
Call concrete handler to provide file below attach point.
Definition: MediaCurl.cc:727
#define ERR
Definition: Logger.h:98
const std::string & username() const
auth username
bool fileSizeExceeded() const
Definition: MediaCurl.cc:152
Pathname localPath(const Pathname &pathname) const
Files provided will be available at &#39;localPath(filename)&#39;.
const Headers & headers() const
returns a list of all added headers
int ZYPP_MEDIA_CURL_IPRESOLVE()
4/6 to force IPv4/v6
Definition: curlhelper.cc:43
static void setCookieFile(const Pathname &)
Definition: MediaCurl.cc:374
virtual void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
Definition: MediaCurl.cc:720
bool verifyPeerEnabled() const
Whether to verify peer for ssl.
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:1452
const std::string & hint() const
comma separated list of available authentication types
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
static int parseConfig(CurlConfig &config, const std::string &filename="")
Parse a curlrc file and store the result in the config structure.
Definition: curlconfig.cc:24
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:175
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
Bottleneck filtering all DownloadProgressReport issued from Media[Muli]Curl.
Definition: MediaCurl.cc:49
Url clearQueryString(const Url &url) const
Definition: MediaCurl.cc:369
void setAuthType(const std::string &val_r)
set the allowed authentication types
std::string trim(const std::string &s, const Trim trim_r)
Definition: String.cc:223
void finish(const Url &file_r, Error error_r, const std::string &reason_r) override
Definition: MediaCurl.cc:108
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
Just inherits Exception to separate media exceptions.
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:831
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
Definition: Exception.cc:91
const ByteCount & downloadSize() const
The size of the resource on the server.
void disconnect()
Use concrete handler to isconnect media.
long connectTimeout() const
connection timeout
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:176
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.
void fillSettingsFromUrl(const Url &url, media::TransferSettings &s)
Fills the settings structure using options passed on the url for example ?timeout=x&proxy=foo.
Definition: curlhelper.cc:174
void setTimeout(long t)
set the transfer timeout
Receiver * _oldRec
std::string proxyuserpwd
Definition: curlconfig.h:49
bool isValid() const
Verifies the Url.
Definition: Url.cc:489
virtual bool doGetDoesFileExist(const Pathname &filename) const
Definition: MediaCurl.cc:979
const Pathname & clientKeyPath() const
SSL client key file.
void expectedFileSize(ByteCount newval_r)
Definition: MediaCurl.cc:158
shared_ptr< CurlAuthData > CurlAuthData_Ptr
Definition: curlauthdata.h:102
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:659
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:800
const char * agentString()
Definition: MediaCurl.cc:279
void resetDispose()
Set no dispose function.
Definition: AutoDispose.h:180
void doGetFileCopyFile(const OnMediaLocation &srcFile, const Pathname &dest, FILE *file, callback::SendReport< DownloadProgressReport > &report, RequestOptions options=OPTION_NONE) const
Definition: MediaCurl.cc:1239
Media source internally used by MediaManager and MediaHandler.
Definition: MediaSource.h:36
static std::string auth_type_long2str(long auth_type)
Converts a long of ORed CURLAUTH_* identifiers into a string of comma separated list of authenticatio...
long minDownloadSpeed() const
Minimum download speed (bytes per second) until the connection is dropped.
int _dnlPercent
Percent completed or 0 if _dnlTotal is unknown.
Definition: MediaCurl.cc:179
curl_slist * _customHeaders
Definition: MediaCurl.h:168
bool proxyEnabled() const
proxy is enabled
shared_ptr< AuthData > AuthData_Ptr
Definition: authdata.h:79
int rmdir(const Pathname &path)
Like &#39;rmdir&#39;.
Definition: PathInfo.cc:366
#define SET_OPTION(opt, val)
Definition: MediaCurl.cc:323
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:163
Url url() const
Url used.
Definition: MediaHandler.h:503
double _drateLast
Download rate in last period.
Definition: MediaCurl.cc:182
std::string curlUnEscape(std::string text_r)
Definition: curlhelper.cc:357
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:170
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:1379
void setupZYPP_MEDIA_CURL_DEBUG(CURL *curl)
Setup CURLOPT_VERBOSE and CURLOPT_DEBUGFUNCTION according to env::ZYPP_MEDIA_CURL_DEBUG.
Definition: curlhelper.cc:121
virtual void disconnectFrom() override
Definition: MediaCurl.cc:701
static CURL * progressCallback_getcurl(void *clientp)
Definition: MediaCurl.cc:1425
Typesafe passing of user data via callbacks.
Definition: UserData.h:38
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
Definition: String.h:429
static long auth_type_str2long(std::string &auth_type_str)
Converts a string of comma separated list of authetication type names into a long of ORed CURLAUTH_* ...
Definition: curlauthdata.cc:50
double _dnlNow
Bytes downloaded now.
Definition: MediaCurl.cc:177
void setUserAgentString(std::string &&val_r)
sets the user agent ie: "Mozilla v3"
Wrapper class for ::stat/::lstat.
Definition: PathInfo.h:220
void report(const UserData &userData_r=UserData()) override
The most generic way of sending/receiving data.
Definition: MediaCurl.cc:67
AutoDispose<FILE*> calling ::fclose
Definition: AutoDispose.h:311
void updateStats(double dltotal=0.0, double dlnow=0.0)
Definition: MediaCurl.cc:197
static Pathname _cookieFile
Definition: MediaCurl.h:161
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
Definition: curlhelper.cc:322
double _drateTotal
Download rate so far.
Definition: MediaCurl.cc:181
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 userPassword() const
returns the user and password as a user:pass string
std::string getAuthHint() const
Return a comma separated list of available authentication methods supported by server.
Definition: MediaCurl.cc:1433
time_t _timeRcv
Start of no-data timeout.
Definition: MediaCurl.cc:172
#define EXPLICITLY_NO_PROXY
Definition: curlhelper_p.h:25
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:1129
void addCred(const AuthData &cred)
Add new credentials with user callbacks.
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:1
Curl HTTP authentication data.
Definition: curlauthdata.h:22
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
OptionalDownloadProgressReport(bool isOptional=false)
Definition: MediaCurl.cc:53
char _curlError[CURL_ERROR_SIZE]
Definition: MediaCurl.h:167
ByteCount expectedFileSize() const
Definition: MediaCurl.cc:155
#define CONNECT_TIMEOUT
Definition: curlhelper_p.h:21
const std::string & proxy() const
proxy host
MediaCurlException(const Url &url_r, const std::string &err_r, const std::string &msg_r)
bool optional() const
Whether this is an optional resource.
time_t _timeLast
Start last period(~1sec)
Definition: MediaCurl.cc:171
const char * c_str() const
Definition: IdStringType.h:105
TrueBool _guard
Definition: TargetImpl.cc:1563
bool userMayRWX() const
Definition: PathInfo.h:353
const std::string & userAgentString() const
user agent string
Url manipulation class.
Definition: Url.h:91
bool headRequestsAllowed() const
whether HEAD requests are allowed
#define DBG
Definition: Logger.h:95
Attempt to work around certain issues by autoretry in MediaCurl::getFileCopy E.g. ...
Definition: MediaCurl.cc:297
zypp::callback::SendReport< zypp::media::DownloadProgressReport > * report
Definition: MediaCurl.cc:168
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
Definition: Url.cc:572