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 ret = curl_easy_setopt(
_curl, CURLOPT_NOBODY, 1L );
984 ret = curl_easy_setopt(
_curl, CURLOPT_RANGE,
"0-1" );
987 curl_easy_setopt(
_curl, CURLOPT_NOBODY, 0L);
988 curl_easy_setopt(
_curl, CURLOPT_RANGE, NULL );
994 curl_easy_setopt(
_curl, CURLOPT_HTTPGET, 1L );
998 AutoFILE file { ::fopen(
"/dev/null",
"w" ) };
1000 ERR <<
"fopen failed for /dev/null" << endl;
1001 curl_easy_setopt(
_curl, CURLOPT_NOBODY, 0L);
1002 curl_easy_setopt(
_curl, CURLOPT_RANGE, NULL );
1008 curl_easy_setopt(
_curl, CURLOPT_HTTPGET, 1L );
1012 ZYPP_THROW(MediaWriteException(
"/dev/null"));
1015 ret = curl_easy_setopt(
_curl, CURLOPT_WRITEDATA, (*file) );
1018 curl_easy_setopt(
_curl, CURLOPT_RANGE, NULL );
1019 curl_easy_setopt(
_curl, CURLOPT_NOBODY, 0L);
1025 curl_easy_setopt(
_curl, CURLOPT_HTTPGET, 1L );
1032 CURLcode ok = curl_easy_perform(
_curl );
1033 MIL <<
"perform code: " << ok <<
" [ " << curl_easy_strerror(ok) <<
" ]" << endl;
1038 curl_easy_setopt(
_curl, CURLOPT_NOBODY, 0L);
1048 curl_easy_setopt(
_curl, CURLOPT_HTTPGET, 1L);
1057 curl_easy_setopt(
_curl, CURLOPT_RANGE, NULL);
1069 catch (
const MediaFileNotFoundException &e ) {
1073 catch (
const MediaException &e ) {
1078 return ( ok == CURLE_OK );
1097 bool not_a_file =
false;
1099 CURLcode ret = curl_easy_getinfo(
_curl,
1100 CURLINFO_EFFECTIVE_URL,
1102 if ( ret == CURLE_OK && ptr != NULL)
1107 std::string path( eurl.getPathName());
1108 if( !path.empty() && path !=
"/" && *path.rbegin() ==
'/')
1110 DBG <<
"Effective url ("
1112 <<
") seems to provide the index of a directory"
1129 if( assert_dir( dest.
dirname() ) )
1131 DBG <<
"assert_dir " << dest.
dirname() <<
" failed" << endl;
1141 ERR <<
"out of memory for temp file name" << endl;
1145 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
1148 ERR <<
"mkstemp failed for file '" << destNew <<
"'" << endl;
1153 file = ::fdopen( tmp_fd,
"we" );
1156 ERR <<
"fopen failed for file '" << destNew <<
"'" << endl;
1159 tmp_fd.resetDispose();
1162 DBG <<
"dest: " << dest << endl;
1163 DBG <<
"temp: " << destNew << endl;
1168 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
1169 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, (
long)
PathInfo(target).mtime());
1173 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1174 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1182 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1183 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1187 long httpReturnCode = 0;
1188 CURLcode infoRet = curl_easy_getinfo(
_curl,
1189 CURLINFO_RESPONSE_CODE,
1191 bool modified =
true;
1192 if (infoRet == CURLE_OK)
1195 if ( httpReturnCode == 304
1198 DBG <<
" Not modified.";
1205 WAR <<
"Could not get the response code." << endl;
1208 if (modified || infoRet != CURLE_OK)
1213 ERR <<
"Failed to chmod file " << destNew << endl;
1217 if ( ::fclose( file ) )
1219 ERR <<
"Fclose failed for file '" << destNew <<
"'" << endl;
1224 if ( rename( destNew, dest ) != 0 ) {
1225 ERR <<
"Rename failed" << endl;
1228 destNew.resetDispose();
1264 std::string urlBuffer( curlUrl.
asString());
1265 CURLcode ret = curl_easy_setopt(
_curl, CURLOPT_URL,
1266 urlBuffer.c_str() );
1271 ret = curl_easy_setopt(
_curl, CURLOPT_WRITEDATA, file );
1279 report->start(
url, dest);
1280 if ( curl_easy_setopt(
_curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
1281 WAR <<
"Can't set CURLOPT_PROGRESSDATA: " <<
_curlError << endl;;
1284 ret = curl_easy_perform(
_curl );
1285#if CURLVERSION_AT_LEAST(7,19,4)
1290 if ( ftell(file) == 0 && ret == 0 )
1292 long httpReturnCode = 33;
1293 if ( curl_easy_getinfo(
_curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
1295 long conditionUnmet = 33;
1296 if ( curl_easy_getinfo(
_curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
1298 WAR <<
"TIMECONDITION unmet - retry without." << endl;
1299 curl_easy_setopt(
_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
1300 curl_easy_setopt(
_curl, CURLOPT_TIMEVALUE, 0L);
1301 ret = curl_easy_perform(
_curl );
1307 if ( curl_easy_setopt(
_curl, CURLOPT_PROGRESSDATA, NULL ) != 0 ) {
1308 WAR <<
"Can't unset CURLOPT_PROGRESSDATA: " <<
_curlError << endl;;
1314 <<
", temp file size " << ftell(file)
1315 <<
" bytes." << endl;
1327 catch (
const MediaException &e ) {
1348 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it ) {
1349 Pathname filename = dirname + it->name;
1352 switch ( it->type ) {
1359 getDir( filename, recurse_r );
1361 res = assert_dir(
localPath( filename ) );
1363 WAR <<
"Ignore error (" << res <<
") on creating local directory '" <<
localPath( filename ) <<
"'" << endl;
1377 const Pathname & dirname,
bool dots )
const
1385 const Pathname & dirname,
bool dots )
const
1412 long httpReturnCode = 0;
1413 if ( curl_easy_getinfo( pdata->
curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) != CURLE_OK || httpReturnCode == 0 )
1414 return aliveCallback( clientp, dltotal, dlnow, ultotal, ulnow );
1425 return pdata ? pdata->
curl : 0;
1432 long auth_info = CURLAUTH_NONE;
1435 curl_easy_getinfo(
_curl, CURLINFO_HTTPAUTH_AVAIL, &auth_info);
1437 if(infoRet == CURLE_OK)
1439 return CurlAuthData::auth_type_long2str(auth_info);
1462 CredentialManager cm(CredManagerOptions(
ZConfig::instance().repoManagerRoot()));
1463 CurlAuthData_Ptr credentials;
1466 AuthData_Ptr cmcred = cm.getCred(
_url);
1468 if (cmcred && firstTry)
1470 credentials.reset(
new CurlAuthData(*cmcred));
1471 DBG <<
"got stored credentials:" << endl << *credentials << endl;
1477 CurlAuthData_Ptr curlcred;
1478 curlcred.reset(
new CurlAuthData());
1486 curlcred->setUsername(cmcred->username());
1495 curlcred->setAuthType(availAuthTypes);
1498 if (auth_report->prompt(
_url, prompt_msg, *curlcred))
1500 DBG <<
"callback answer: retry" << endl
1501 <<
"CurlAuthData: " << *curlcred << endl;
1503 if (curlcred->valid())
1505 credentials = curlcred;
1519 DBG <<
"callback answer: cancel" << endl;
1531 CURLcode ret = curl_easy_setopt(
_curl, CURLOPT_USERPWD,
_settings.userPassword().c_str());
1535 if (credentials->authType() == CURLAUTH_NONE)
1536 credentials->setAuthType(availAuthTypes);
1539 if (credentials->authType() != CURLAUTH_NONE)
1542 const_cast<MediaCurl*
>(
this)->
_settings.setAuthType(credentials->authTypeAsString());
1543 ret = curl_easy_setopt(
_curl, CURLOPT_HTTPAUTH, credentials->authType());
1549 credentials->setUrl(
_url);
1550 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.