22#include <zypp-core/parser/Sysconfig> 
   28#include <zypp-curl/ProxyInfo> 
   29#include <zypp-curl/auth/CurlAuthData> 
   30#include <zypp-media/auth/CredentialManager> 
   31#include <zypp-curl/CurlConfig> 
   57    void updateStats( curl_off_t dltotal = 0.0, curl_off_t dlnow = 0.0 );
 
   80    { 
return _file.value(); }
 
 
 
  136    if ( dlnow && dlnow != 
_dnlNow )
 
 
  186    auto written = fwrite( ptr, 1, bytes, 
_file );
 
 
  198                                         const std::string & err_r,
 
  199                                         const std::string & msg_r )
 
 
 
  220#define SET_OPTION(opt,val) do { \ 
  221    ret = curl_easy_setopt ( curl, opt, val ); \ 
  223      ZYPP_THROW(MediaCurlSetOptException(_origin.at(rData.mirror).url(), _curlError)); \ 
 
  227#define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val) 
  228#define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val) 
  229#define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val) 
  232                     const Pathname & attach_point_hint_r )
 
  238  _multi = curl_multi_init();
 
  242  MIL << 
"MediaCurl::MediaCurl(" << origin_r.
authority().
url() << 
", " << attach_point_hint_r << 
")" << endl;
 
  250    char    *atemp = ::strdup( apath.
asString().c_str());
 
  253         atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
 
  255      WAR << 
"attach point " << ainfo.
path()
 
  259    else if( atest != NULL)
 
 
  269  try { 
release(); } 
catch(...) {}
 
  271    curl_multi_cleanup(
_multi);
 
 
  290  curl_version_info_data *curl_info = NULL;
 
  291  curl_info = curl_version_info(CURLVERSION_NOW);
 
  293  if (curl_info->protocols)
 
  295    const char * 
const *
proto = 
nullptr;
 
  296    std::string        scheme( 
url.getScheme());
 
  300      if( scheme == std::string((
const char *)*
proto))
 
  305      std::string msg(
"Unsupported protocol '");
 
 
  315  CURL *curl = rData.
curl;
 
  318  curl_easy_reset ( curl );
 
  323  CURLcode ret = curl_easy_setopt( curl, CURLOPT_ERRORBUFFER, 
_curlError );
 
  334    case 4: 
SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); 
break;
 
  335    case 6: 
SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); 
break;
 
  377#ifdef CURLSSLOPT_ALLOW_BEAST 
  379    ret = curl_easy_setopt( curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST );
 
  388    SET_OPTION(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
 
  406    if ( cred && cred->valid() ) {
 
  423    std::string use_auth = settings.
authType();
 
  424    if (use_auth.empty())
 
  425      use_auth = 
"digest,basic";        
 
  427    if( auth != CURLAUTH_NONE)
 
  429      DBG << 
"Enabling HTTP authentication methods: " << use_auth
 
  430          << 
" (CURLOPT_HTTPAUTH=" << auth << 
")" << std::endl;
 
  437    DBG << 
"Proxy: '" << settings.
proxy() << 
"'" << endl;
 
  439    SET_OPTION(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
 
  449    if ( proxyuserpwd.empty() )
 
  454        DBG << 
"Proxy: ~/.curlrc does not contain the proxy-user option" << endl;
 
  458        DBG << 
"Proxy: using proxy-user from ~/.curlrc" << endl;
 
  463      DBG << 
"Proxy: using provided proxy-user '" << settings.
proxyUsername() << 
"'" << endl;
 
  466    if ( ! proxyuserpwd.empty() )
 
  471#if CURLVERSION_AT_LEAST(7,19,4) 
  476    DBG << 
"Proxy: explicitly NOPROXY" << endl;
 
  482    DBG << 
"Proxy: not explicitly set" << endl;
 
  483    DBG << 
"Proxy: libcurl may look into the environment" << endl;
 
  494#if CURLVERSION_AT_LEAST(7,15,5) 
  506  const auto &cookieFileParam = 
_origin.at(rData.
mirror).url().getQueryParam( 
"cookies" );
 
  507  if ( !cookieFileParam.empty() && 
str::strToBool( cookieFileParam, 
true ) )
 
  510    MIL << 
"No cookies requested" << endl;
 
  515#if CURLVERSION_AT_LEAST(7,18,0) 
  525  for ( 
const auto &header : settings.
headers() ) {
 
 
  558  auto that = 
const_cast<MediaCurl *
>(
this);
 
  559  std::exception_ptr lastErr;
 
  561  for ( 
unsigned mirr : mirrOrder ) {
 
  563      return that->getFileCopyFromMirror ( mirr, srcFile, 
target );
 
 
  582  const auto &filename = srcFile.
filename();
 
  591  AutoDispose<CURL*> curl( curl_easy_init(), []( CURL *hdl ) { 
if ( hdl ) { curl_easy_cleanup(hdl); } }  );
 
  594  rData.mirror = mirror;
 
  595  rData.curl = curl.
value ();
 
  597  if( !myUrl.url().isValid() )
 
  600  if( myUrl.url().getHost().empty() )
 
  605  bool firstAuth = 
true;  
 
  606  unsigned internalTry = 0;
 
  607  static constexpr unsigned maxInternalTry = 3;
 
  614      if( assert_dir( dest.
dirname() ) )
 
  616        DBG << 
"assert_dir " << dest.
dirname() << 
" failed" << endl;
 
  626          ERR << 
"out of memory for temp file name" << endl;
 
  630        AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
 
  633          ERR << 
"mkstemp failed for file '" << destNew << 
"'" << endl;
 
  638        file = ::fdopen( tmp_fd, 
"we" );
 
  641          ERR << 
"fopen failed for file '" << destNew << 
"'" << endl;
 
  647      DBG << 
"dest: " << dest << endl;
 
  648      DBG << 
"temp: " << destNew << endl;
 
  655        curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
 
  656        curl_easy_setopt(curl, CURLOPT_TIMEVALUE, (
long)
PathInfo(
target).mtime());
 
  660        curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
 
  661        curl_easy_setopt(curl, CURLOPT_TIMEVALUE, 0L);
 
  665        curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
 
  666        curl_easy_setopt(curl, CURLOPT_TIMEVALUE, 0L);
 
  687      std::string urlBuffer( curlUrl.
asString());
 
  688      CURLcode ret = curl_easy_setopt( curl, CURLOPT_URL,
 
  697      ret = curl_easy_setopt( curl, CURLOPT_WRITEDATA,  &progressData  );
 
  707      report->start(fileurl, dest);
 
  709      if ( curl_easy_setopt( curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
 
  710        WAR << 
"Can't set CURLOPT_PROGRESSDATA: " << 
_curlError << endl;;
 
  718  #if CURLVERSION_AT_LEAST(7,19,4) 
  723      if ( ftell(file) == 0 && ret == 0 )
 
  725        long httpReturnCode = 33;
 
  726        if ( curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
 
  728          long conditionUnmet = 33;
 
  729          if ( curl_easy_getinfo( curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
 
  731            WAR << 
"TIMECONDITION unmet - retry without." << endl;
 
  732            curl_easy_setopt( curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
 
  733            curl_easy_setopt( curl, CURLOPT_TIMEVALUE, 0L);
 
  740      if ( curl_easy_setopt( curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
 
  741        WAR << 
"Can't unset CURLOPT_PROGRESSDATA: " << 
_curlError << endl;;
 
  746            << 
", temp file size " << ftell(file)
 
  747            << 
" bytes." << endl;
 
  759      long httpReturnCode = 0;
 
  760      CURLcode infoRet = curl_easy_getinfo(curl,
 
  761                                           CURLINFO_RESPONSE_CODE,
 
  763      bool modified = 
true;
 
  764      if (infoRet == CURLE_OK)
 
  767        if ( httpReturnCode == 304
 
  768             || ( httpReturnCode == 213 && (myUrl.url().getScheme() == 
"ftp" || myUrl.url().getScheme() == 
"tftp") ) ) 
 
  770          DBG << 
" Not modified.";
 
  777        WAR << 
"Could not get the response code." << endl;
 
  780      if (modified || infoRet != CURLE_OK)
 
  785          ERR << 
"Failed to chmod file " << destNew << endl;
 
  789        if ( ::fclose( file ) )
 
  791          ERR << 
"Fclose failed for file '" << destNew << 
"'" << endl;
 
  796        if ( rename( destNew, dest ) != 0 ) {
 
  797          ERR << 
"Rename failed" << endl;
 
  822        if ( internalTry < maxInternalTry ) {
 
 
  849  auto that = 
const_cast<MediaCurl *
>(
this);
 
  851  std::exception_ptr lastErr;
 
  854      return that->doGetDoesFileExist( i, filename );
 
 
  879                                 bool timeout_reached)
 const 
  885    if (filename.
empty())
 
  886      url = baseMirr.url();
 
  894      case CURLE_UNSUPPORTED_PROTOCOL:
 
  895          err = 
" Unsupported protocol";
 
  898            err += 
" or redirect (";
 
  903      case CURLE_URL_MALFORMAT:
 
  904      case CURLE_URL_MALFORMAT_USER:
 
  907      case CURLE_LOGIN_DENIED:
 
  911      case CURLE_HTTP_RETURNED_ERROR:
 
  913        long httpReturnCode = 0;
 
  914        CURLcode infoRet = curl_easy_getinfo( rData.
curl,
 
  915                                              CURLINFO_RESPONSE_CODE,
 
  917        if ( infoRet == CURLE_OK )
 
  919          std::string msg = 
"HTTP response: " + 
str::numstring( httpReturnCode );
 
  920          switch ( httpReturnCode )
 
  926              DBG << msg << 
" Login failed (URL: " << 
url.asString() << 
")" << std::endl;
 
  927            DBG << 
"MediaUnauthorizedException auth hint: '" << auth_hint << 
"'" << std::endl;
 
  942            if ( 
url.getHost().find(
".suse.com") != std::string::npos )
 
  943              msg403 = 
_(
"Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
 
  944            else if (
url.asString().find(
"novell.com") != std::string::npos)
 
  945              msg403 = 
_(
"Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
 
  953          DBG << msg << 
" (URL: " << 
url.asString() << 
")" << std::endl;
 
  958          std::string msg = 
"Unable to retrieve HTTP response:";
 
  959          DBG << msg << 
" (URL: " << 
url.asString() << 
")" << std::endl;
 
  964      case CURLE_FTP_COULDNT_RETR_FILE:
 
  965#if CURLVERSION_AT_LEAST(7,16,0) 
  966      case CURLE_REMOTE_FILE_NOT_FOUND:
 
  968      case CURLE_FTP_ACCESS_DENIED:
 
  969      case CURLE_TFTP_NOTFOUND:
 
  970        err = 
"File not found";
 
  973      case CURLE_BAD_PASSWORD_ENTERED:
 
  974      case CURLE_FTP_USER_PASSWORD_INCORRECT:
 
  975          err = 
"Login failed";
 
  977      case CURLE_COULDNT_RESOLVE_PROXY:
 
  978      case CURLE_COULDNT_RESOLVE_HOST:
 
  979      case CURLE_COULDNT_CONNECT:
 
  980      case CURLE_FTP_CANT_GET_HOST:
 
  981        err = 
"Connection failed";
 
  983      case CURLE_WRITE_ERROR:
 
  986      case CURLE_PARTIAL_FILE:
 
  987      case CURLE_OPERATION_TIMEDOUT:
 
  988        timeout_reached = 
true; 
 
  990      case CURLE_ABORTED_BY_CALLBACK:
 
  991         if( timeout_reached )
 
  993          err  = 
"Timeout reached";
 
 
 1023  AutoDispose<CURL*> curl( curl_easy_init(), []( CURL *hdl ) { 
if ( hdl ) { curl_easy_cleanup(hdl); } }  );
 
 1025  rData.mirror = mirror;
 
 1026  rData.curl = curl.
value ();
 
 1028  auto &myUrl = 
_origin[mirror];
 
 1030  if( !myUrl.url().isValid() )
 
 1033  if( myUrl.url().getHost().empty() )
 
 1038  DBG << 
"URL: " << 
url.asString() << endl;
 
 1053  std::string urlBuffer( curlUrl.
asString());
 
 1056  bool canRetry  = 
true;
 
 1057  bool firstAuth = 
true;
 
 1060  while ( canRetry ) {
 
 1064    CURLcode ret = curl_easy_setopt( curl, CURLOPT_URL,
 
 1065                                     urlBuffer.c_str() );
 
 1070    AutoFILE file { ::fopen( 
"/dev/null", 
"w" ) };
 
 1072      ERR << 
"fopen failed for /dev/null" << endl;
 
 1076    ret = curl_easy_setopt( curl, CURLOPT_WRITEDATA, (*file) );
 
 1087    const bool doHeadRequest = (myUrl.url().getScheme() == 
"http" || myUrl.url().getScheme() == 
"https") && settings.headRequestsAllowed();
 
 1088    if ( doHeadRequest ) {
 
 1089      curl_easy_setopt( curl, CURLOPT_NOBODY, 1L );
 
 1091      curl_easy_setopt( curl, CURLOPT_RANGE, 
"0-1" );
 
 1096      MIL << 
"perform code: " << ok << 
" [ " << curl_easy_strerror(ok) << 
" ]" << endl;
 
 1108      if ( 
authenticate( myUrl.url(), settings, e.hint(), firstAuth ) ) {
 
 1116    return ( ok == CURLE_OK );
 
 
 1144    long httpReturnCode = 0;
 
 1145    if ( curl_easy_getinfo( pdata->
curl(), CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 ) {
 
 1146      return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
 
 
 1158    return pdata->
writeBytes ( ptr, size * nmemb );
 
 
 1167  long auth_info = CURLAUTH_NONE;
 
 1170    curl_easy_getinfo(curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
 
 1172  if(infoRet == CURLE_OK)
 
 
 1199  CURL *curl = rData.
curl;
 
 1208  if ( curl_multi_add_handle( 
_multi, curl ) != CURLM_OK )
 
 1216  if (mcode != CURLM_OK)
 
 1219  bool canContinue = 
true;
 
 1220  while ( canContinue ) {
 
 1222    CURLMsg *msg = 
nullptr;
 
 1224    while ((msg = curl_multi_info_read( 
_multi, &nqueue)) != 0) {
 
 1225        if ( msg->msg != CURLMSG_DONE  ) 
continue;
 
 1226        if ( msg->easy_handle != curl ) 
continue;
 
 1228        return msg->data.result;
 
 1232    std::vector<GPollFD> requestedFds = _curlHelper.
socks;
 
 1241      if (mcode != CURLM_OK)
 
 1245      if (mcode != CURLM_OK)
 
 
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
reference value() const
Reference to the Tp object.
void resetDispose()
Set no dispose function.
Store and operate with byte count.
std::string asUserHistory() const
A single (multiline) string composed of asUserString and historyAsString.
void addHistory(const std::string &msg_r)
Add some message text to the history.
Manages a data source characterized by an authoritative URL and a list of mirror URLs.
const OriginEndpoint & authority() const
const zypp::Url & url() const
ProgressData()
Ctor no range [0,0](0).
std::string getScheme() const
Returns the scheme name of the URL.
std::string asString() const
Returns a default string representation of the Url object.
static ZConfig & instance()
Singleton ctor.
Wrapper class for stat/lstat.
const Pathname & path() const
Return current Pathname.
Pathname dirname() const
Return all but the last component od this path.
const char * c_str() const
String representation.
const std::string & asString() const
String representation.
bool empty() const
Test for an empty path.
#define EXPLICITLY_NO_PROXY
size_t log_redirects_curl(char *ptr, size_t size, size_t nmemb, void *userdata)
void globalInitCurlOnce()
std::string curlUnEscape(const std::string &text_r)
void setupZYPP_MEDIA_CURL_DEBUG(CURL *curl)
Setup CURLOPT_VERBOSE and CURLOPT_DEBUGFUNCTION according to env::ZYPP_MEDIA_CURL_DEBUG.
CURLcode setCurlRedirProtocols(CURL *curl)
int ZYPP_MEDIA_CURL_IPRESOLVE()
4/6 to force IPv4/v6
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
int assert_file_mode(const Pathname &path, unsigned mode)
Like assert_file but enforce mode even if the file already exists.
int unlink(const Pathname &path)
Like 'unlink'.
std::string numstring(char n, int w=0)
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
int zypp_poll(std::vector< GPollFD > &fds, int timeout)
Small wrapper around g_poll that additionally listens to the shutdown FD returned by ZYpp::shutdownSi...
Easy-to use interface to the ZYPP dependency resolver.
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
AutoDispose< void > OnScopeExit
CURLMcode handleSocketActions(const std::vector< GPollFD > &actionsFds, int first=0)
std::vector< GPollFD > socks
std::optional< long > timeout_ms
Bottleneck filtering all DownloadProgressReport issued from Media[Muli]Curl.
ByteCount bytesWritten() const
ByteCount _expectedFileSize
curl_off_t _dnlNow
Bytes downloaded now.
int _dnlPercent
Percent completed or 0 if _dnlTotal is unknown.
time_t _timeRcv
Start of no-data timeout.
ByteCount expectedFileSize() const
time_t _timeLast
Start last period(~1sec)
int reportProgress() const
double _drateLast
Download rate in last period.
bool timeoutReached() const
void expectedFileSize(ByteCount newval_r)
ByteCount _bytesWritten
Bytes actually written into the file.
curl_off_t _dnlLast
Bytes downloaded at period start.
bool fileSizeExceeded() const
void updateStats(curl_off_t dltotal=0.0, curl_off_t dlnow=0.0)
double _drateTotal
Download rate so far.
zypp::callback::SendReport< zypp::media::DownloadProgressReport > * report
size_t writeBytes(char *ptr, ByteCount bytes)
curl_off_t _dnlTotal
Bytes to download or 0 if unknown.
ProgressData(AutoFILE file, CURL *curl, time_t timeout=0, zypp::Url url=zypp::Url(), zypp::ByteCount expectedFileSize_r=0, zypp::callback::SendReport< zypp::media::DownloadProgressReport > *_report=nullptr)
time_t _timeStart
Start total stats.
AutoDispose<int> calling close
AutoDispose<FILE*> calling fclose
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.