Imported Upstream version 17.11.3
[platform/upstream/libzypp.git] / zypp / media / MediaCurl.cc
index 0457ac8..0427191 100644 (file)
@@ -114,13 +114,11 @@ namespace
     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)
@@ -129,10 +127,21 @@ namespace
       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;
       }
 
@@ -184,19 +193,24 @@ namespace zypp {
     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)
@@ -240,6 +254,9 @@ namespace zypp {
        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);
@@ -260,6 +277,8 @@ namespace zypp {
 
       int reportProgress() const
       {
+        if ( fileSizeExceeded )
+          return 1;
        if ( reached )
          return 1;     // no-data timeout
        if ( report && !(*report)->progress( _dnlPercent, url, _drateTotal, _drateLast ) )
@@ -657,6 +676,7 @@ void MediaCurl::setupEasy()
   }
 
   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"));
@@ -965,16 +985,16 @@ Url MediaCurl::getFileUrl( const Pathname & filename_r ) const
 
 ///////////////////////////////////////////////////////////////////
 
-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;
 
@@ -986,7 +1006,7 @@ void MediaCurl::getFileCopy( const Pathname & filename , const Pathname & target
   {
     try
     {
-      doGetFileCopy(filename, target, report);
+      doGetFileCopy(filename, target, report, expectedFileSize_r);
       retry = false;
     }
     // retry with proper authentication data
@@ -1051,9 +1071,9 @@ bool MediaCurl::getDoesFileExist( const Pathname & filename ) const
 
 ///////////////////////////////////////////////////////////////////
 
-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 )
   {
@@ -1062,11 +1082,20 @@ void MediaCurl::evaluateCurlCode( const Pathname &filename,
       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";
@@ -1098,6 +1127,7 @@ void MediaCurl::evaluateCurlCode( const Pathname &filename,
                            ));
           }
 
+          case 502: // bad gateway (bnc #1070851)
           case 503: // service temporarily unavailable (bnc #462545)
             ZYPP_THROW(MediaTemporaryProblemException(url));
           case 504: // gateway timeout
@@ -1105,7 +1135,9 @@ void MediaCurl::evaluateCurlCode( const Pathname &filename,
           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));
           }
@@ -1207,6 +1239,7 @@ bool MediaCurl::doGetDoesFileExist( const Pathname & filename ) const
     // 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() );
@@ -1370,7 +1403,7 @@ bool MediaCurl::detectDirIndex() const
 
 ///////////////////////////////////////////////////////////////////
 
-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() ) )
@@ -1422,7 +1455,7 @@ void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & targ
     }
     try
     {
-      doGetFileCopyFile(filename, dest, file, report, options);
+      doGetFileCopyFile(filename, dest, file, report, expectedFileSize_r, options);
     }
     catch (Exception &e)
     {
@@ -1484,7 +1517,7 @@ void MediaCurl::doGetFileCopy( const Pathname & filename , const Pathname & targ
 
 ///////////////////////////////////////////////////////////////////
 
-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;
 
@@ -1511,6 +1544,7 @@ void MediaCurl::doGetFileCopyFile( const Pathname & filename , const Pathname &
     // 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() );
@@ -1524,7 +1558,7 @@ void MediaCurl::doGetFileCopyFile( const Pathname & filename , const Pathname &
     }
 
     // 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 ) {
@@ -1568,7 +1602,11 @@ void MediaCurl::doGetFileCopyFile( const Pathname & filename , const Pathname &
       // 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
@@ -1598,7 +1636,7 @@ void MediaCurl::getDir( const Pathname & dirname, bool recurse_r ) const
       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 ) {
@@ -1688,6 +1726,18 @@ string MediaCurl::getAuthHint() const
   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