return 0;
}
- static size_t
- log_redirects_curl(
- void *ptr, size_t size, size_t nmemb, void *stream)
+ static size_t log_redirects_curl( char *ptr, size_t size, size_t nmemb, void *userdata)
{
- // INT << "got header: " << string((char *)ptr, ((char*)ptr) + size*nmemb) << endl;
+ // INT << "got header: " << string(ptr, ptr + size*nmemb) << endl;
- char * lstart = (char *)ptr, * lend = (char *)ptr;
+ char * lstart = ptr, * lend = ptr;
size_t pos = 0;
size_t max = size * nmemb;
while (pos + 1 < max)
for (lstart = lend; *lend != '\n' && pos < max; ++lend, ++pos);
// look for "Location"
- string line(lstart, lend);
- if (line.find("Location") != string::npos)
+ if ( lstart[0] == 'L'
+ && lstart[1] == 'o'
+ && lstart[2] == 'c'
+ && lstart[3] == 'a'
+ && lstart[4] == 't'
+ && lstart[5] == 'i'
+ && lstart[6] == 'o'
+ && lstart[7] == 'n'
+ && lstart[8] == ':' )
{
+ std::string line { lstart, *(lend-1)=='\r' ? lend-1 : lend };
DBG << "redirecting to " << line << endl;
+ if ( userdata ) {
+ *reinterpret_cast<std::string *>( userdata ) = line;
+ }
return max;
}
struct ProgressData
{
ProgressData( CURL *_curl, time_t _timeout = 0, const Url & _url = Url(),
+ ByteCount expectedFileSize_r = 0,
callback::SendReport<DownloadProgressReport> *_report = nullptr )
: curl( _curl )
, url( _url )
, timeout( _timeout )
, reached( false )
+ , fileSizeExceeded ( false )
, report( _report )
+ , _expectedFileSize( expectedFileSize_r )
{}
CURL *curl;
Url url;
time_t timeout;
bool reached;
+ bool fileSizeExceeded;
callback::SendReport<DownloadProgressReport> *report;
+ ByteCount _expectedFileSize;
time_t _timeStart = 0; ///< Start total stats
time_t _timeLast = 0; ///< Start last period(~1sec)
if ( timeout )
reached = ( (now - _timeRcv) > timeout );
+ // check if the downloaded data is already bigger than what we expected
+ fileSizeExceeded = _expectedFileSize > 0 && _expectedFileSize < static_cast<ByteCount::SizeType>(_dnlNow);
+
// percentage:
if ( _dnlTotal )
_dnlPercent = int(_dnlNow * 100 / _dnlTotal);
int reportProgress() const
{
+ if ( fileSizeExceeded )
+ return 1;
if ( reached )
return 1; // no-data timeout
if ( report && !(*report)->progress( _dnlPercent, url, _drateTotal, _drateLast ) )
}
curl_easy_setopt(_curl, CURLOPT_HEADERFUNCTION, log_redirects_curl);
+ curl_easy_setopt(_curl, CURLOPT_HEADERDATA, &_lastRedirect);
CURLcode ret = curl_easy_setopt( _curl, CURLOPT_ERRORBUFFER, _curlError );
if ( ret != 0 ) {
ZYPP_THROW(MediaCurlSetOptException(_url, "Error setting error buffer"));
///////////////////////////////////////////////////////////////////
-void MediaCurl::getFile( const Pathname & filename ) const
+void MediaCurl::getFile(const Pathname & filename , const ByteCount &expectedFileSize_r) const
{
// Use absolute file name to prevent access of files outside of the
// hierarchy below the attach point.
- getFileCopy(filename, localPath(filename).absolutename());
+ getFileCopy(filename, localPath(filename).absolutename(), expectedFileSize_r);
}
///////////////////////////////////////////////////////////////////
-void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target) const
+void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target, const ByteCount &expectedFileSize_r ) const
{
callback::SendReport<DownloadProgressReport> report;
{
try
{
- doGetFileCopy(filename, target, report);
+ doGetFileCopy(filename, target, report, expectedFileSize_r);
retry = false;
}
// retry with proper authentication data
///////////////////////////////////////////////////////////////////
-void MediaCurl::evaluateCurlCode( const Pathname &filename,
+void MediaCurl::evaluateCurlCode(const Pathname &filename,
CURLcode code,
- bool timeout_reached ) const
+ bool timeout_reached) const
{
if ( code != 0 )
{
url = _url;
else
url = getFileUrl(filename);
+
std::string err;
{
switch ( code )
{
case CURLE_UNSUPPORTED_PROTOCOL:
+ err = " Unsupported protocol";
+ if ( !_lastRedirect.empty() )
+ {
+ err += " or redirect (";
+ err += _lastRedirect;
+ err += ")";
+ }
+ break;
case CURLE_URL_MALFORMAT:
case CURLE_URL_MALFORMAT_USER:
err = " Bad URL";
));
}
+ case 502: // bad gateway (bnc #1070851)
case 503: // service temporarily unavailable (bnc #462545)
ZYPP_THROW(MediaTemporaryProblemException(url));
case 504: // gateway timeout
case 403:
{
string msg403;
- if (url.asString().find("novell.com") != string::npos)
+ if ( url.getHost().find(".suse.com") != string::npos )
+ msg403 = _("Visit the SUSE Customer Center to check whether your registration is valid and has not expired.");
+ else if (url.asString().find("novell.com") != string::npos)
msg403 = _("Visit the Novell Customer Center to check whether your registration is valid and has not expired.");
ZYPP_THROW(MediaForbiddenException(url, msg403));
}
// encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
// contains an absolute path.
//
+ _lastRedirect.clear();
string urlBuffer( curlUrl.asString());
CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
urlBuffer.c_str() );
///////////////////////////////////////////////////////////////////
-void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
+void MediaCurl::doGetFileCopy(const Pathname & filename , const Pathname & target, callback::SendReport<DownloadProgressReport> & report, const ByteCount &expectedFileSize_r, RequestOptions options ) const
{
Pathname dest = target.absolutename();
if( assert_dir( dest.dirname() ) )
}
try
{
- doGetFileCopyFile(filename, dest, file, report, options);
+ doGetFileCopyFile(filename, dest, file, report, expectedFileSize_r, options);
}
catch (Exception &e)
{
///////////////////////////////////////////////////////////////////
-void MediaCurl::doGetFileCopyFile( const Pathname & filename , const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, RequestOptions options ) const
+void MediaCurl::doGetFileCopyFile(const Pathname & filename , const Pathname & dest, FILE *file, callback::SendReport<DownloadProgressReport> & report, const ByteCount &expectedFileSize_r, RequestOptions options ) const
{
DBG << filename.asString() << endl;
// encoded slash as %2f) "ftp://user@host/%2ffoo/bar/file"
// contains an absolute path.
//
+ _lastRedirect.clear();
string urlBuffer( curlUrl.asString());
CURLcode ret = curl_easy_setopt( _curl, CURLOPT_URL,
urlBuffer.c_str() );
}
// Set callback and perform.
- ProgressData progressData(_curl, _settings.timeout(), url, &report);
+ ProgressData progressData(_curl, _settings.timeout(), url, expectedFileSize_r, &report);
if (!(options & OPTION_NO_REPORT_START))
report->start(url, dest);
if ( curl_easy_setopt( _curl, CURLOPT_PROGRESSDATA, &progressData ) != 0 ) {
// which holds whether the timeout was reached or not,
// otherwise it would be a user cancel
try {
- evaluateCurlCode( filename, ret, progressData.reached);
+
+ if ( progressData.fileSizeExceeded )
+ ZYPP_THROW(MediaFileSizeExceededException(url, progressData._expectedFileSize));
+
+ evaluateCurlCode( filename, ret, progressData.reached );
}
catch ( const MediaException &e ) {
// some error, we are not sure about file existence, rethrw
switch ( it->type ) {
case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
case filesystem::FT_FILE:
- getFile( filename );
+ getFile( filename, 0 );
break;
case filesystem::FT_DIR: // newer directory.yast contain at least directory info
if ( recurse_r ) {
return "";
}
+/**
+ * MediaMultiCurl needs to reset the expected filesize in case a metalink file is downloaded
+ * otherwise this function should not be called
+ */
+void MediaCurl::resetExpectedFileSize(void *clientp, const ByteCount &expectedFileSize)
+{
+ ProgressData *data = reinterpret_cast<ProgressData *>(clientp);
+ if ( data ) {
+ data->_expectedFileSize = expectedFileSize;
+ }
+}
+
///////////////////////////////////////////////////////////////////
bool MediaCurl::authenticate(const string & availAuthTypes, bool firstTry) const