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> 
   58    void updateStats( curl_off_t dltotal = 0.0, curl_off_t dlnow = 0.0 );
 
 
  121    if ( dlnow && dlnow != 
_dnlNow )
 
 
  174                                         const std::string & err_r,
 
  175                                         const std::string & msg_r )
 
 
 
  196#define SET_OPTION(opt,val) do { \ 
  197    ret = curl_easy_setopt ( _curl, opt, val ); \ 
  199      ZYPP_THROW(MediaCurlSetOptException(_url, _curlError)); \ 
 
  203#define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val) 
  204#define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val) 
  205#define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val) 
  208                      const Pathname & attach_point_hint_r )
 
  217  MIL << 
"MediaCurl::MediaCurl(" << url_r << 
", " << attach_point_hint_r << 
")" << endl;
 
  225    char    *atemp = ::strdup( apath.
asString().c_str());
 
  228         atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
 
  230      WAR << 
"attach point " << ainfo.
path()
 
  231          << 
" is not useable for " << url_r.
getScheme() << endl;
 
  234    else if( atest != NULL)
 
 
  263  curl_version_info_data *curl_info = NULL;
 
  264  curl_info = curl_version_info(CURLVERSION_NOW);
 
  266  if (curl_info->protocols)
 
  268    const char * 
const *
proto = 
nullptr;
 
  269    std::string        scheme( 
url.getScheme());
 
  273      if( scheme == std::string((
const char *)*
proto))
 
  278      std::string msg(
"Unsupported protocol '");
 
 
  292  CURLcode ret = curl_easy_setopt( 
_curl, CURLOPT_ERRORBUFFER, 
_curlError );
 
  305  if ( 
_url.getHost() == 
"download.opensuse.org" )
 
  334    case 4: 
SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); 
break;
 
  335    case 6: 
SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); 
break;
 
  356  if ( 
_url.getScheme() == 
"https" )
 
  368    if( ! 
_settings.clientCertificatePath().empty() )
 
  372    if( ! 
_settings.clientKeyPath().empty() )
 
  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;
 
  439    SET_OPTION(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
 
  447    std::string proxyuserpwd = 
_settings.proxyUserPassword();
 
  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 = 
_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) 
  521  for ( 
const auto &header : vol_settings.
headers() ) {
 
 
  537  if ( !
_url.isValid() )
 
  547  _curl = curl_easy_init();
 
 
  585    try { curl_easy_cleanup( 
_curl ); }
 
 
  612  const auto &filename = srcFile.
filename();
 
  620  bool firstAuth = 
true;  
 
  621  unsigned internalTry = 0;
 
  622  static constexpr unsigned maxInternalTry = 3;
 
  647        if ( internalTry < maxInternalTry ) {
 
 
  704                                  bool timeout_reached)
 const 
  709    if (filename.
empty())
 
  718      case CURLE_UNSUPPORTED_PROTOCOL:
 
  719          err = 
" Unsupported protocol";
 
  722            err += 
" or redirect (";
 
  727      case CURLE_URL_MALFORMAT:
 
  728      case CURLE_URL_MALFORMAT_USER:
 
  731      case CURLE_LOGIN_DENIED:
 
  735      case CURLE_HTTP_RETURNED_ERROR:
 
  737        long httpReturnCode = 0;
 
  738        CURLcode infoRet = curl_easy_getinfo( 
_curl,
 
  739                                              CURLINFO_RESPONSE_CODE,
 
  741        if ( infoRet == CURLE_OK )
 
  743          std::string msg = 
"HTTP response: " + 
str::numstring( httpReturnCode );
 
  744          switch ( httpReturnCode )
 
  750            DBG << msg << 
" Login failed (URL: " << 
url.asString() << 
")" << std::endl;
 
  751            DBG << 
"MediaUnauthorizedException auth hint: '" << auth_hint << 
"'" << std::endl;
 
  766            if ( 
url.getHost().find(
".suse.com") != std::string::npos )
 
  767              msg403 = 
_(
"Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
 
  768            else if (
url.asString().find(
"novell.com") != std::string::npos)
 
  769              msg403 = 
_(
"Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
 
  777          DBG << msg << 
" (URL: " << 
url.asString() << 
")" << std::endl;
 
  782          std::string msg = 
"Unable to retrieve HTTP response:";
 
  783          DBG << msg << 
" (URL: " << 
url.asString() << 
")" << std::endl;
 
  788      case CURLE_FTP_COULDNT_RETR_FILE:
 
  789#if CURLVERSION_AT_LEAST(7,16,0) 
  790      case CURLE_REMOTE_FILE_NOT_FOUND:
 
  792      case CURLE_FTP_ACCESS_DENIED:
 
  793      case CURLE_TFTP_NOTFOUND:
 
  794        err = 
"File not found";
 
  797      case CURLE_BAD_PASSWORD_ENTERED:
 
  798      case CURLE_FTP_USER_PASSWORD_INCORRECT:
 
  799          err = 
"Login failed";
 
  801      case CURLE_COULDNT_RESOLVE_PROXY:
 
  802      case CURLE_COULDNT_RESOLVE_HOST:
 
  803      case CURLE_COULDNT_CONNECT:
 
  804      case CURLE_FTP_CANT_GET_HOST:
 
  805        err = 
"Connection failed";
 
  807      case CURLE_WRITE_ERROR:
 
  810      case CURLE_PARTIAL_FILE:
 
  811      case CURLE_OPERATION_TIMEDOUT:
 
  812        timeout_reached = 
true; 
 
  814      case CURLE_ABORTED_BY_CALLBACK:
 
  815         if( timeout_reached )
 
  817          err  = 
"Timeout reached";
 
 
  850  if(
_url.getHost().empty())
 
  855  DBG << 
"URL: " << 
url.asString() << endl;
 
  871  std::string urlBuffer( curlUrl.
asString());
 
  872  CURLcode ret = curl_easy_setopt( 
_curl, CURLOPT_URL,
 
  885  struct TempSetHeadRequest
 
  887    TempSetHeadRequest(CURL *curl_r, 
bool doHttpHeadRequest_r)
 
  888      : 
_curl{curl_r}, _doHttpHeadRequest{doHttpHeadRequest_r} {
 
  889      if ( _doHttpHeadRequest ) {
 
  890        curl_easy_setopt( 
_curl, CURLOPT_NOBODY, 1L );
 
  892        curl_easy_setopt( 
_curl, CURLOPT_RANGE, 
"0-1" );
 
  895    TempSetHeadRequest(
const TempSetHeadRequest &) = 
delete;
 
  896    TempSetHeadRequest(TempSetHeadRequest &&) = 
delete;
 
  897    TempSetHeadRequest &operator=(
const TempSetHeadRequest &) = 
delete;
 
  898    TempSetHeadRequest &operator=(TempSetHeadRequest &&) = 
delete;
 
  899    ~TempSetHeadRequest() {
 
  900      if ( _doHttpHeadRequest ) {
 
  901        curl_easy_setopt( 
_curl, CURLOPT_NOBODY, 0L);
 
  907        curl_easy_setopt( 
_curl, CURLOPT_HTTPGET, 1L );
 
  909        curl_easy_setopt( 
_curl, CURLOPT_RANGE, NULL );
 
  914    bool   _doHttpHeadRequest;
 
  915  } _guard( 
_curl, (
_url.getScheme() == 
"http" || 
_url.getScheme() == 
"https") && 
_settings.headRequestsAllowed() );
 
  918  AutoFILE file { ::fopen( 
"/dev/null", 
"w" ) };
 
  920      ERR << 
"fopen failed for /dev/null" << endl;
 
  924  ret = curl_easy_setopt( 
_curl, CURLOPT_WRITEDATA, (*file) );
 
  930  MIL << 
"perform code: " << ok << 
" [ " << curl_easy_strerror(ok) << 
" ]" << endl;
 
  947  return ( ok == CURLE_OK );
 
 
  955    if( assert_dir( dest.
dirname() ) )
 
  957      DBG << 
"assert_dir " << dest.
dirname() << 
" failed" << endl;
 
  967        ERR << 
"out of memory for temp file name" << endl;
 
  971      AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
 
  974        ERR << 
"mkstemp failed for file '" << destNew << 
"'" << endl;
 
  979      file = ::fdopen( tmp_fd, 
"we" );
 
  982        ERR << 
"fopen failed for file '" << destNew << 
"'" << endl;
 
  985      tmp_fd.resetDispose();    
 
  988    DBG << 
"dest: " << dest << endl;
 
  989    DBG << 
"temp: " << destNew << endl;
 
  994      curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
 
  999      curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
 
 1000      curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
 
 1008      curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
 
 1009      curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
 
 1013    long httpReturnCode = 0;
 
 1014    CURLcode infoRet = curl_easy_getinfo(
_curl,
 
 1015                                         CURLINFO_RESPONSE_CODE,
 
 1017    bool modified = 
true;
 
 1018    if (infoRet == CURLE_OK)
 
 1021      if ( httpReturnCode == 304
 
 1022           || ( httpReturnCode == 213 && (
_url.getScheme() == 
"ftp" || 
_url.getScheme() == 
"tftp") ) ) 
 
 1024        DBG << 
" Not modified.";
 
 1031      WAR << 
"Could not get the response code." << endl;
 
 1034    if (modified || infoRet != CURLE_OK)
 
 1039        ERR << 
"Failed to chmod file " << destNew << endl;
 
 1043      if ( ::fclose( file ) )
 
 1045        ERR << 
"Fclose failed for file '" << destNew << 
"'" << endl;
 
 1050      if ( rename( destNew, dest ) != 0 ) {
 
 1051        ERR << 
"Rename failed" << endl;
 
 1054      destNew.resetDispose();   
 
 
 1069    if(
_url.getHost().empty())
 
 1074    DBG << 
"URL: " << 
url.asString() << endl;
 
 1090    std::string urlBuffer( curlUrl.
asString());
 
 1091    CURLcode ret = curl_easy_setopt( 
_curl, CURLOPT_URL,
 
 1092                                     urlBuffer.c_str() );
 
 1097    ret = curl_easy_setopt( 
_curl, CURLOPT_WRITEDATA, file );
 
 1105      report->start(
url, dest);
 
 1106    if ( curl_easy_setopt( 
_curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
 
 1107      WAR << 
"Can't set CURLOPT_PROGRESSDATA: " << 
_curlError << endl;;
 
 1111#if CURLVERSION_AT_LEAST(7,19,4) 
 1116    if ( ftell(file) == 0 && ret == 0 )
 
 1118      long httpReturnCode = 33;
 
 1119      if ( curl_easy_getinfo( 
_curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
 
 1121        long conditionUnmet = 33;
 
 1122        if ( curl_easy_getinfo( 
_curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
 
 1124          WAR << 
"TIMECONDITION unmet - retry without." << endl;
 
 1125          curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
 
 1126          curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
 
 1133    if ( curl_easy_setopt( 
_curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
 
 1134      WAR << 
"Can't unset CURLOPT_PROGRESSDATA: " << 
_curlError << endl;;
 
 1140          << 
", temp file size " << ftell(file)
 
 1141          << 
" bytes." << endl;
 
 
 1167  for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
 
 1168      Pathname filename = dirname + it->name;
 
 1171      switch ( it->type ) {
 
 1178          getDir( filename, recurse_r );
 
 1180          res = assert_dir( 
localPath( filename ) );
 
 1182            WAR << 
"Ignore error (" << res <<  
") on creating local directory '" << 
localPath( filename ) << 
"'" << endl;
 
 
 1196                               const Pathname & dirname, 
bool dots )
 const 
 
 1204                            const Pathname & dirname, 
bool dots )
 const 
 
 1231    long httpReturnCode = 0;
 
 1232    if ( curl_easy_getinfo( pdata->
curl(), CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
 
 1233      return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
 
 
 1244  return pdata ? pdata->
curl() : 0;
 
 
 1251  long auth_info = CURLAUTH_NONE;
 
 1254    curl_easy_getinfo(
_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
 
 1256  if(infoRet == CURLE_OK)
 
 
 1286    ,[](
auto &releaseMe ){ 
if (releaseMe._multi) curl_multi_cleanup(releaseMe._multi); }
 
 1289  if (!cMulti->_multi)
 
 1296  if ( curl_multi_add_handle( cMulti->_multi, 
_curl ) != CURLM_OK )
 
 1300  OnScopeExit autoRemove([&](){ curl_multi_remove_handle( cMulti->_multi, 
_curl ); });
 
 1304  if (mcode != CURLM_OK)
 
 1307  bool canContinue = 
true;
 
 1308  while ( canContinue ) {
 
 1310    CURLMsg *msg = 
nullptr;
 
 1312    while ((msg = curl_multi_info_read( cMulti->_multi, &nqueue)) != 0) {
 
 1313        if ( msg->msg != CURLMSG_DONE  ) 
continue;
 
 1314        if ( msg->easy_handle != 
_curl ) 
continue;
 
 1316        return msg->data.result;
 
 1320    std::vector<GPollFD> requestedFds = _curlHelper.
socks;
 
 1329      if (mcode != CURLM_OK)
 
 1333      if (mcode != CURLM_OK)
 
 
 1351  if (cmcred && firstTry)
 
 1354    DBG << 
"got stored credentials:" << endl << *credentials << endl;
 
 1365    if (!
_url.getUsername().empty() && firstTry)
 
 1366      curlcred->setUsername(
_url.getUsername());
 
 1369      curlcred->setUsername(cmcred->username());
 
 1374    std::string prompt_msg = 
str::Format(
_(
"Authentication required for '%s'")) % 
_url.asString();
 
 1378    curlcred->setAuthType(availAuthTypes);
 
 1381    if (auth_report->prompt(
_url, prompt_msg, *curlcred))
 
 1383      DBG << 
"callback answer: retry" << endl
 
 1384          << 
"CurlAuthData: " << *curlcred << endl;
 
 1386      if (curlcred->valid())
 
 1388        credentials = curlcred;
 
 1402      DBG << 
"callback answer: cancel" << endl;
 
 1414    CURLcode ret = curl_easy_setopt(
_curl, CURLOPT_USERPWD, 
_settings.userPassword().c_str());
 
 1418    if (credentials->authType() == CURLAUTH_NONE)
 
 1419      credentials->setAuthType(availAuthTypes);
 
 1422    if (credentials->authType() != CURLAUTH_NONE)
 
 1425      const_cast<MediaCurl*
>(
this)->
_settings.setAuthType(credentials->authTypeAsString());
 
 1426      ret = curl_easy_setopt(
_curl, CURLOPT_HTTPAUTH, credentials->authType());
 
 1432      credentials->setUrl(
_url);
 
 
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
void resetDispose()
Set no dispose function.
Store and operate with byte count.
Base class for Exception.
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.
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 std::string & asString() const
String representation.
bool empty() const
Test for an empty path.
Pathname absolutename() const
Return this path, adding a leading '/' if relative.
#define EXPLICITLY_NO_PROXY
void fillSettingsFromUrl(const Url &url, media::TransferSettings &s)
Fills the settings structure using options passed on the url for example ?timeout=x&proxy=foo.
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)
void fillSettingsSystemProxy(const Url &url, media::TransferSettings &s)
Reads the system proxy configuration and fills the settings structure proxy information.
Url clearQueryString(const Url &url)
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() ).
std::list< DirEntry > DirContent
Returned by readdir.
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 _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)
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
curl_off_t _dnlTotal
Bytes to download or 0 if unknown.
time_t _timeStart
Start total stats.
ProgressData(CURL *curl, time_t timeout=0, zypp::Url url=zypp::Url(), zypp::ByteCount expectedFileSize_r=0, zypp::callback::SendReport< zypp::media::DownloadProgressReport > *_report=nullptr)
AutoDispose<int> calling ::close
AutoDispose<FILE*> calling ::fclose
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.