16#include <zypp/base/Logger.h>
17#include <zypp/ExternalProgram.h>
18#include <zypp/base/String.h>
19#include <zypp/base/Gettext.h>
20#include <zypp-core/parser/Sysconfig>
21#include <zypp/base/Gettext.h>
24#include <zypp-curl/ProxyInfo>
25#include <zypp-curl/auth/CurlAuthData>
26#include <zypp-media/auth/CredentialManager>
27#include <zypp-curl/CurlConfig>
28#include <zypp-curl/private/curlhelper_p.h>
78 bool progress(
int value_r,
const Url & file_r,
double dbps_avg_r = -1,
double dbps_current_r = -1 )
override
82 if ( not ( value_r || dbps_avg_r || dbps_current_r ) )
86 return _oldRec->
progress( value_r, file_r, dbps_avg_r, dbps_current_r );
95 void finish(
const Url & file_r,
Error error_r,
const std::string & reason_r )
override
150 void updateStats(
double dltotal = 0.0,
double dlnow = 0.0 );
176 , timeout( _timeout )
178 , fileSizeExceeded ( false )
180 , _expectedFileSize( expectedFileSize_r )
191 if ( dlnow && dlnow !=
_dnlNow )
243 static const std::string
_value(
245 "X-ZYpp-AnonymousId: %s",
257 static const std::string
_value(
259 "X-ZYpp-DistributionFlavor: %s",
271 static const std::string
_value(
273 "ZYpp " LIBZYPP_VERSION_STRING
" (curl %s) %s"
274 , curl_version_info(CURLVERSION_NOW)->version
295#define SET_OPTION(opt,val) do { \
296 ret = curl_easy_setopt ( _curl, opt, val ); \
298 ZYPP_THROW(MediaCurlSetOptException(_url, _curlError)); \
302#define SET_OPTION_OFFT(opt,val) SET_OPTION(opt,(curl_off_t)val)
303#define SET_OPTION_LONG(opt,val) SET_OPTION(opt,(long)val)
304#define SET_OPTION_VOID(opt,val) SET_OPTION(opt,(void*)val)
307 const Pathname & attach_point_hint_r )
317 MIL <<
"MediaCurl::MediaCurl(" << url_r <<
", " << attach_point_hint_r <<
")" << endl;
319 globalInitCurlOnce();
325 char *atemp = ::strdup( apath.
asString().c_str());
328 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
330 WAR <<
"attach point " << ainfo.
path()
331 <<
" is not useable for " << url_r.
getScheme() << endl;
334 else if( atest != NULL)
344 return internal::clearQueryString(
url);
356 curl_version_info_data *curl_info = NULL;
357 curl_info = curl_version_info(CURLVERSION_NOW);
359 if (curl_info->protocols)
361 const char *
const *proto;
364 for(proto=curl_info->protocols; !found && *proto; ++proto)
366 if( scheme == std::string((
const char *)*proto))
371 std::string msg(
"Unsupported protocol '");
385 curl_easy_setopt(
_curl, CURLOPT_VERBOSE, 1L);
386 curl_easy_setopt(
_curl, CURLOPT_DEBUGFUNCTION, log_curl);
391 curl_easy_setopt(
_curl, CURLOPT_HEADERFUNCTION, log_redirects_curl);
393 CURLcode ret = curl_easy_setopt(
_curl, CURLOPT_ERRORBUFFER,
_curlError );
395 ZYPP_THROW(MediaCurlSetOptException(
_url,
"Error setting error buffer"));
403 TransferSettings vol_settings(
_settings);
411 vol_settings.addHeader(
"Pragma:");
414 _settings.setConnectTimeout(CONNECT_TIMEOUT);
423 catch (
const MediaException &e )
436 switch ( env::ZYPP_MEDIA_CURL_IPRESOLVE() )
438 case 4:
SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
break;
439 case 6:
SET_OPTION(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
break;
462#if CURLVERSION_AT_LEAST(7,19,4)
465 SET_OPTION( CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS );
467 SET_OPTION( CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS );
476 if( !
_settings.clientCertificatePath().empty() )
480 if( !
_settings.clientKeyPath().empty() )
485#ifdef CURLSSLOPT_ALLOW_BEAST
487 ret = curl_easy_setopt(
_curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST );
496 SET_OPTION(CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
513 const auto cred = cm.getCred(
_url );
514 if ( cred && cred->valid() ) {
531 std::string use_auth =
_settings.authType();
532 if (use_auth.empty())
533 use_auth =
"digest,basic";
534 long auth = CurlAuthData::auth_type_str2long(use_auth);
535 if( auth != CURLAUTH_NONE)
537 DBG <<
"Enabling HTTP authentication methods: " << use_auth
538 <<
" (CURLOPT_HTTPAUTH=" << auth <<
")" << std::endl;
547 SET_OPTION(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
555 std::string proxyuserpwd =
_settings.proxyUserPassword();
557 if ( proxyuserpwd.empty() )
560 CurlConfig::parseConfig(curlconf);
561 if ( curlconf.proxyuserpwd.empty() )
562 DBG <<
"Proxy: ~/.curlrc does not contain the proxy-user option" << endl;
565 proxyuserpwd = curlconf.proxyuserpwd;
566 DBG <<
"Proxy: using proxy-user from ~/.curlrc" << endl;
571 DBG <<
"Proxy: using provided proxy-user '" <<
_settings.proxyUsername() <<
"'" << endl;
574 if ( ! proxyuserpwd.empty() )
576 SET_OPTION(CURLOPT_PROXYUSERPWD, curlUnEscape( proxyuserpwd ).c_str());
579#if CURLVERSION_AT_LEAST(7,19,4)
580 else if (
_settings.proxy() == EXPLICITLY_NO_PROXY )
584 DBG <<
"Proxy: explicitly NOPROXY" << endl;
590 DBG <<
"Proxy: not explicitly set" << endl;
591 DBG <<
"Proxy: libcurl may look into the environment" << endl;
602#if CURLVERSION_AT_LEAST(7,15,5)
615 MIL <<
"No cookies requested" << endl;
620#if CURLVERSION_AT_LEAST(7,18,0)
625 for (
const auto &header : vol_settings.headers() )
655 _curl = curl_easy_init();
693 try { curl_easy_cleanup(
_curl ); }
720 const auto &filename = srcFile.
filename();
738 catch (MediaUnauthorizedException & ex_r)
749 catch (MediaException & excpt_r)
752 if(
typeid(excpt_r) ==
typeid( media::MediaFileNotFoundException ) ||
753 typeid(excpt_r) ==
typeid( media::MediaNotAFileException ) )
757 report->finish(fileurl, reason, excpt_r.asUserHistory());
779 catch (MediaUnauthorizedException & ex_r)
787 catch (MediaException & excpt_r)
801 bool timeout_reached)
const
806 if (filename.
empty())
815 case CURLE_UNSUPPORTED_PROTOCOL:
816 err =
" Unsupported protocol";
819 err +=
" or redirect (";
824 case CURLE_URL_MALFORMAT:
825 case CURLE_URL_MALFORMAT_USER:
828 case CURLE_LOGIN_DENIED:
830 MediaUnauthorizedException(
url,
"Login failed.",
_curlError,
""));
832 case CURLE_HTTP_RETURNED_ERROR:
834 long httpReturnCode = 0;
835 CURLcode infoRet = curl_easy_getinfo(
_curl,
836 CURLINFO_RESPONSE_CODE,
838 if ( infoRet == CURLE_OK )
840 std::string msg =
"HTTP response: " +
str::numstring( httpReturnCode );
841 switch ( httpReturnCode )
847 DBG << msg <<
" Login failed (URL: " <<
url.
asString() <<
")" << std::endl;
848 DBG <<
"MediaUnauthorizedException auth hint: '" << auth_hint <<
"'" << std::endl;
863 if (
url.
getHost().find(
".suse.com") != std::string::npos )
864 msg403 =
_(
"Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
865 else if (
url.
asString().find(
"novell.com") != std::string::npos)
866 msg403 =
_(
"Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
879 std::string msg =
"Unable to retrieve HTTP response:";
885 case CURLE_FTP_COULDNT_RETR_FILE:
886#if CURLVERSION_AT_LEAST(7,16,0)
887 case CURLE_REMOTE_FILE_NOT_FOUND:
889 case CURLE_FTP_ACCESS_DENIED:
890 case CURLE_TFTP_NOTFOUND:
891 err =
"File not found";
894 case CURLE_BAD_PASSWORD_ENTERED:
895 case CURLE_FTP_USER_PASSWORD_INCORRECT:
896 err =
"Login failed";
898 case CURLE_COULDNT_RESOLVE_PROXY:
899 case CURLE_COULDNT_RESOLVE_HOST:
900 case CURLE_COULDNT_CONNECT:
901 case CURLE_FTP_CANT_GET_HOST:
902 err =
"Connection failed";
904 case CURLE_WRITE_ERROR:
907 case CURLE_PARTIAL_FILE:
908 case CURLE_OPERATION_TIMEDOUT:
909 timeout_reached =
true;
911 case CURLE_ABORTED_BY_CALLBACK:
912 if( timeout_reached )
914 err =
"Timeout reached";
922 case CURLE_SSL_PEER_CERTIFICATE:
968 std::string urlBuffer( curlUrl.
asString());
969 CURLcode ret = curl_easy_setopt(
_curl, CURLOPT_URL,
982 struct TempSetHeadRequest
984 TempSetHeadRequest( CURL * curl_r,
bool doHttpHeadRequest_r )
986 , _doHttpHeadRequest { doHttpHeadRequest_r }
988 if ( _doHttpHeadRequest ) {
989 curl_easy_setopt( _curl, CURLOPT_NOBODY, 1L );
991 curl_easy_setopt( _curl, CURLOPT_RANGE,
"0-1" );
994 ~TempSetHeadRequest() {
995 if ( _doHttpHeadRequest ) {
996 curl_easy_setopt( _curl, CURLOPT_NOBODY, 0L);
1002 curl_easy_setopt( _curl, CURLOPT_HTTPGET, 1L );
1004 curl_easy_setopt( _curl, CURLOPT_RANGE, NULL );
1009 bool _doHttpHeadRequest;
1013 AutoFILE file { ::fopen(
"/dev/null",
"w" ) };
1015 ERR <<
"fopen failed for /dev/null" << endl;
1016 ZYPP_THROW(MediaWriteException(
"/dev/null"));
1019 ret = curl_easy_setopt(
_curl, CURLOPT_WRITEDATA, (*file) );
1024 CURLcode ok = curl_easy_perform(
_curl );
1025 MIL <<
"perform code: " << ok <<
" [ " << curl_easy_strerror(ok) <<
" ]" << endl;
1033 catch (
const MediaFileNotFoundException &e ) {
1037 catch (
const MediaException &e ) {
1042 return ( ok == CURLE_OK );
1061 bool not_a_file =
false;
1063 CURLcode ret = curl_easy_getinfo(
_curl,
1064 CURLINFO_EFFECTIVE_URL,
1066 if ( ret == CURLE_OK && ptr != NULL)
1071 std::string path( eurl.getPathName());
1072 if( !path.empty() && path !=
"/" && *path.rbegin() ==
'/')
1074 DBG <<
"Effective url ("
1076 <<
") seems to provide the index of a directory"
1093 if( assert_dir( dest.
dirname() ) )
1095 DBG <<
"assert_dir " << dest.
dirname() <<
" failed" << endl;
1105 ERR <<
"out of memory for temp file name" << endl;
1109 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
1112 ERR <<
"mkstemp failed for file '" << destNew <<
"'" << endl;
1117 file = ::fdopen( tmp_fd,
"we" );
1120 ERR <<
"fopen failed for file '" << destNew <<
"'" << endl;
1123 tmp_fd.resetDispose();
1126 DBG <<
"dest: " << dest << endl;
1127 DBG <<
"temp: " << destNew << endl;
1132 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1133 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, (
long)
PathInfo(target).mtime());
1137 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1138 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1146 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1147 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1151 long httpReturnCode = 0;
1152 CURLcode infoRet = curl_easy_getinfo(
_curl,
1153 CURLINFO_RESPONSE_CODE,
1155 bool modified =
true;
1156 if (infoRet == CURLE_OK)
1159 if ( httpReturnCode == 304
1162 DBG <<
" Not modified.";
1169 WAR <<
"Could not get the response code." << endl;
1172 if (modified || infoRet != CURLE_OK)
1177 ERR <<
"Failed to chmod file " << destNew << endl;
1181 if ( ::fclose( file ) )
1183 ERR <<
"Fclose failed for file '" << destNew <<
"'" << endl;
1188 if ( rename( destNew, dest ) != 0 ) {
1189 ERR <<
"Rename failed" << endl;
1192 destNew.resetDispose();
1228 std::string urlBuffer( curlUrl.
asString());
1229 CURLcode ret = curl_easy_setopt(
_curl, CURLOPT_URL,
1230 urlBuffer.c_str() );
1235 ret = curl_easy_setopt(
_curl, CURLOPT_WRITEDATA, file );
1243 report->start(
url, dest);
1244 if ( curl_easy_setopt(
_curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1245 WAR <<
"Can't set CURLOPT_PROGRESSDATA: " <<
_curlError << endl;;
1248 ret = curl_easy_perform(
_curl );
1249#if CURLVERSION_AT_LEAST(7,19,4)
1254 if ( ftell(file) == 0 && ret == 0 )
1256 long httpReturnCode = 33;
1257 if ( curl_easy_getinfo(
_curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
1259 long conditionUnmet = 33;
1260 if ( curl_easy_getinfo(
_curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
1262 WAR <<
"TIMECONDITION unmet - retry without." << endl;
1263 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1264 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1265 ret = curl_easy_perform(
_curl );
1271 if ( curl_easy_setopt(
_curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1272 WAR <<
"Can't unset CURLOPT_PROGRESSDATA: " <<
_curlError << endl;;
1278 <<
", temp file size " << ftell(file)
1279 <<
" bytes." << endl;
1291 catch (
const MediaException &e ) {
1312 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
1313 Pathname filename = dirname + it->name;
1316 switch ( it->type ) {
1323 getDir( filename, recurse_r );
1325 res = assert_dir(
localPath( filename ) );
1327 WAR <<
"Ignore error (" << res <<
") on creating local directory '" <<
localPath( filename ) <<
"'" << endl;
1341 const Pathname & dirname,
bool dots )
const
1349 const Pathname & dirname,
bool dots )
const
1376 long httpReturnCode = 0;
1377 if ( curl_easy_getinfo( pdata->
curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
1378 return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
1389 return pdata ? pdata->
curl : 0;
1396 long auth_info = CURLAUTH_NONE;
1399 curl_easy_getinfo(
_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1401 if(infoRet == CURLE_OK)
1403 return CurlAuthData::auth_type_long2str(auth_info);
1426 CredentialManager cm(CredManagerOptions(
ZConfig::instance().repoManagerRoot()));
1427 CurlAuthData_Ptr credentials;
1430 AuthData_Ptr cmcred = cm.getCred(
_url);
1432 if (cmcred && firstTry)
1434 credentials.reset(
new CurlAuthData(*cmcred));
1435 DBG <<
"got stored credentials:" << endl << *credentials << endl;
1441 CurlAuthData_Ptr curlcred;
1442 curlcred.reset(
new CurlAuthData());
1450 curlcred->setUsername(cmcred->username());
1459 curlcred->setAuthType(availAuthTypes);
1462 if (auth_report->prompt(
_url, prompt_msg, *curlcred))
1464 DBG <<
"callback answer: retry" << endl
1465 <<
"CurlAuthData: " << *curlcred << endl;
1467 if (curlcred->valid())
1469 credentials = curlcred;
1483 DBG <<
"callback answer: cancel" << endl;
1495 CURLcode ret = curl_easy_setopt(
_curl, CURLOPT_USERPWD,
_settings.userPassword().c_str());
1499 if (credentials->authType() == CURLAUTH_NONE)
1500 credentials->setAuthType(availAuthTypes);
1503 if (credentials->authType() != CURLAUTH_NONE)
1506 const_cast<MediaCurl*
>(
this)->
_settings.setAuthType(credentials->authTypeAsString());
1507 ret = curl_easy_setopt(
_curl, CURLOPT_HTTPAUTH, credentials->authType());
1513 credentials->setUrl(
_url);
1514 cm.addCred(*credentials);
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.
const char * c_str() const
ProgressData()
Ctor no range [0,0](0).
std::string distributionFlavor() const
This is flavor attribute of the installed base product but does not require the target to be loaded a...
std::string anonymousUniqueId() const
anonymous unique id
std::string targetDistribution() const
This is register.target attribute of the installed base product.
std::string getScheme() const
Returns the scheme name of the URL.
std::string asString() const
Returns a default string representation of the Url object.
std::string getUsername(EEncoding eflag=zypp::url::E_DECODED) const
Returns the username from the URL authority.
std::string getQueryParam(const std::string ¶m, EEncoding eflag=zypp::url::E_DECODED) const
Return the value for the specified query parameter.
std::string getHost(EEncoding eflag=zypp::url::E_DECODED) const
Returns the hostname or IP from the URL authority.
bool isValid() const
Verifies the Url.
static ZConfig & instance()
Singleton ctor.
Typesafe passing of user data via callbacks.
Wrapper class for stat/lstat.
const Pathname & path() const
Return current Pathname.
Pathname extend(const std::string &r) const
Append string r to the last component of the path.
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.
const char * anonymousIdHeader()
const char * distributionFlavorHeader()
const char * agentString()
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
int unlink(const Pathname &path)
Like 'unlink'.
int assert_file_mode(const Pathname &path, unsigned mode)
Like assert_file but enforce mode even if the file already exists.
std::list< DirEntry > DirContent
Returned by readdir.
std::string numstring(char n, int w=0)
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
std::string trim(const std::string &s, const Trim trim_r)
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.
Optional files will send no report until data are actually received (we know it exists).
void report(const UserData &userData_r=UserData()) override
The most generic way of sending/receiving data.
~OptionalDownloadProgressReport()
void reportbegin() override
void reportend() override
Action problem(const Url &file_r, Error error_r, const std::string &description_r) override
OptionalDownloadProgressReport(bool isOptional=false)
void finish(const Url &file_r, Error error_r, const std::string &reason_r) override
bool progress(int value_r, const Url &file_r, double dbps_avg_r=-1, double dbps_current_r=-1) override
Download progress.
void start(const Url &file_r, Pathname localfile_r) override
zypp::ByteCount _expectedFileSize
void updateStats(double dltotal=0.0, double dlnow=0.0)
int _dnlPercent
Percent completed or 0 if _dnlTotal is unknown.
time_t _timeRcv
Start of no-data timeout.
time_t _timeLast
Start last period(~1sec)
int reportProgress() const
double _drateLast
Download rate in last period.
double _dnlTotal
Bytes to download or 0 if unknown.
double _dnlNow
Bytes downloaded now.
double _drateTotal
Download rate so far.
zypp::callback::SendReport< zypp::media::DownloadProgressReport > * report
double _dnlLast
Bytes downloaded at period start.
time_t _timeStart
Start total stats.
AutoDispose<int> calling ::close
AutoDispose<FILE*> calling ::fclose
static DistributeReport & instance()
void setReceiver(Receiver &rec_r)
virtual void reportbegin()
virtual void report(const UserData &userData_r=UserData())
The most generic way of sending/receiving data.
callback::UserData UserData
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.