Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / repo / RepoProvideFile.cc
index 90107fa..45e2b42 100644 (file)
 #include "zypp/base/Gettext.h"
 #include "zypp/base/Logger.h"
 #include "zypp/base/String.h"
+#include "zypp/base/UserRequestException.h"
 #include "zypp/repo/RepoProvideFile.h"
 #include "zypp/ZYppCallbacks.h"
 #include "zypp/MediaSetAccess.h"
 #include "zypp/ZConfig.h"
+#include "zypp/ZYppFactory.h"
 #include "zypp/repo/SUSEMediaVerifier.h"
 #include "zypp/repo/RepoException.h"
 
 #include "zypp/repo/SUSEMediaVerifier.h"
 #include "zypp/repo/RepoException.h"
+#include "zypp/FileChecker.h"
+#include "zypp/Fetcher.h"
 
 using std::endl;
 using std::set;
@@ -47,39 +51,80 @@ namespace zypp
     namespace
     { /////////////////////////////////////////////////////////////////
 
-      /** Hack to extract progress information from source::DownloadFileReport.
-       * We redirect the static report triggered from Repository::provideFile
-       * to feed the ProvideFilePolicy callbacks.
+      /** Hack to extract progress information from media::DownloadProgressReport.
+       * We redirect the static report triggered from RepoInfo::provideFile
+       * to feed the ProvideFilePolicy callbacks in addition to any connected
+       * media::DownloadProgressReport.
       */
-      struct DownloadFileReportHack : public callback::ReceiveReport<repo::RepoReport>
+      struct DownloadFileReportHack : public callback::ReceiveReport<media::DownloadProgressReport>
       {
-        virtual bool progress( const ProgressData &progress )
-        {
+       typedef callback::ReceiveReport<ReportType> BaseType;
+       typedef function<bool(int)>                 RedirectType;
+
+       DownloadFileReportHack( RedirectType redirect_r )
+       : _oldRec( Distributor::instance().getReceiver() )
+       , _redirect( redirect_r )
+       { connect(); }
+       ~DownloadFileReportHack()
+       { if ( _oldRec ) Distributor::instance().setReceiver( *_oldRec ); else Distributor::instance().noReceiver(); }
+
+       virtual void start( const Url & file, Pathname localfile )
+       {
+         if ( _oldRec )
+           _oldRec->start( file, localfile );
+         else
+           BaseType::start( file, localfile );
+       }
+
+       virtual bool progress( int value, const Url & file, double dbps_avg = -1, double dbps_current = -1 )
+       {
+         bool ret = true;
+         if ( _oldRec )
+           ret &= _oldRec->progress( value, file, dbps_avg, dbps_current );
           if ( _redirect )
-            return _redirect( progress.val() );
-          return true;
-        }
-        function<bool ( int )> _redirect;
+            ret &= _redirect( value );
+         return ret;
+       }
+
+       virtual Action problem( const Url & file, Error error, const std::string & description )
+       {
+         if ( _oldRec )
+           return _oldRec->problem( file, error, description );
+         return BaseType::problem( file, error, description );
+       }
+       virtual void finish( const Url & file, Error error, const std::string & reason )
+       {
+         if ( _oldRec )
+           _oldRec->finish( file, error, reason );
+         else
+           BaseType::finish( file, error, reason );
+       }
+
+       private:
+         Receiver * _oldRec;
+         RedirectType _redirect;
       };
 
       /////////////////////////////////////////////////////////////////
     } // namespace
     ///////////////////////////////////////////////////////////////////
 
-    ManagedFile provideFile( Repository repo_r,
+    ManagedFile provideFile( RepoInfo repo_r,
                              const OnMediaLocation & loc_r,
                              const ProvideFilePolicy & policy_r )
     {
       RepoMediaAccess access;
       return access.provideFile(repo_r, loc_r, policy_r );
     }
-    
+
+    ///////////////////////////////////////////////////////////////////
     class RepoMediaAccess::Impl
     {
     public:
-      Impl()
+      Impl( const ProvideFilePolicy & defaultPolicy_r )
+        : _defaultPolicy( defaultPolicy_r )
       {}
-      
+
       ~Impl()
       {
         std::map<Url, shared_ptr<MediaSetAccess> >::iterator it;
@@ -90,8 +135,15 @@ namespace zypp
           it->second->release();
         }
       }
-      
-      shared_ptr<MediaSetAccess> mediaAccessForUrl( const Url &url )
+
+      /** Provide a MediaSetAccess for \c url with label and verifyer adjusted.
+       *
+       * As the same url (e.g. \c 'dvd:///' ) might be used for multiple repos
+       * we must always adjust the repo specific data (label,verifyer).
+       *
+       * \todo This mixture of media and repos specific data is fragile.
+      */
+      shared_ptr<MediaSetAccess> mediaAccessForUrl( const Url &url, RepoInfo repo )
       {
         std::map<Url, shared_ptr<MediaSetAccess> >::const_iterator it;
         it = _medias.find(url);
@@ -105,167 +157,189 @@ namespace zypp
           media.reset( new MediaSetAccess(url) );
           _medias[url] = media;
         }
+        setVerifierForRepo( repo, media );
         return media;
       }
-      
-      void setVerifierForRepo( Repository repo, shared_ptr<MediaSetAccess> media )
-      {
-        RepoInfo info = repo.info();
-        // set a verifier if the repository has it
-        Pathname mediafile = info.metadataPath() + "/media.1/media";
-        if ( ! mediafile.empty() )
+
+      private:
+        void setVerifierForRepo( RepoInfo repo, shared_ptr<MediaSetAccess> media )
         {
-          if ( PathInfo(mediafile).isExist() )
+          // Always set the MediaSetAccess label.
+          media->setLabel( repo.name() );
+
+          // set a verifier if the repository has it
+
+          Pathname mediafile = repo.metadataPath() + "/media.1/media";
+          if ( ! repo.metadataPath().empty() )
           {
-            std::map<shared_ptr<MediaSetAccess>, Repository>::const_iterator it;
-            it = _verifier.find(media);
-            if ( it != _verifier.end() )
+            if ( PathInfo(mediafile).isExist() )
             {
-              if ( it->second == repo )
+              std::map<shared_ptr<MediaSetAccess>, RepoInfo>::const_iterator it;
+              it = _verifier.find(media);
+              if ( it != _verifier.end() )
               {
-                // this media is already using this repo verifier
-                return;
+                if ( it->second.alias() == repo.alias() )
+                {
+                  // this media is already using this repo verifier
+                  return;
+                }
               }
-            }
 
-            std::ifstream str(mediafile.asString().c_str());
-            std::string vendor;
-            std::string mediaid;
-            std::string buffer;
-            if ( str )
-            {
-              getline(str, vendor);
-              getline(str, mediaid);
-              getline(str, buffer);
-              
-              unsigned media_nr = str::strtonum<unsigned>(buffer);
-              MIL << "Repository '" << info.alias() << "' has " << media_nr << " medias"<< endl;
-              
-              for ( int i=1; i <= media_nr; ++i )
+              std::ifstream str(mediafile.asString().c_str());
+              std::string vendor;
+              std::string mediaid;
+              std::string buffer;
+              if ( str )
               {
-                media::MediaVerifierRef verifier( new repo::SUSEMediaVerifier(vendor,
-                                           mediaid,
-                                           i));
-                
-                media->setVerifier( i, verifier);
+                getline(str, vendor);
+                getline(str, mediaid);
+                getline(str, buffer);
+
+                unsigned media_nr = str::strtonum<unsigned>(buffer);
+                MIL << "Repository '" << repo.alias() << "' has " << media_nr << " medias"<< endl;
+
+                for ( unsigned i=1; i <= media_nr; ++i )
+                {
+                  media::MediaVerifierRef verifier( new repo::SUSEMediaVerifier( vendor, mediaid, i ) );
+
+                  media->setVerifier( i, verifier);
+                }
+                _verifier[media] = repo;
+              }
+              else
+              {
+                ZYPP_THROW(RepoMetadataException(repo));
               }
-              _verifier[media] = repo;
             }
             else
             {
-              ZYPP_THROW(RepoMetadataException(info));
+              WAR << "No media verifier for repo '" << repo.alias() << "' media/media.1 does not exist in '" << repo.metadataPath() << "'" << endl;
             }
           }
           else
           {
-            WAR << "No media verifier for repo '" << info.alias() << endl;
+            WAR << "'" << repo.alias() << "' metadata path is empty. Can't set verifier. Probably this repository does not come from RepoManager." << endl;
           }
         }
-        else
-        {
-          MIL << "Unknown metadata path for repo '" << info.alias() << "'. Can't set media verifier."<< endl;
-        }
-      }
-      
-      std::map<shared_ptr<MediaSetAccess>, Repository> _verifier;
-      std::map<Url, shared_ptr<MediaSetAccess> > _medias;
+
+      private:
+        std::map<shared_ptr<MediaSetAccess>, RepoInfo> _verifier;
+        std::map<Url, shared_ptr<MediaSetAccess> > _medias;
+
+      public:
+        ProvideFilePolicy _defaultPolicy;
     };
-    
-    
-    
-    RepoMediaAccess::RepoMediaAccess()
-      : _impl( new Impl() )
-    {
-    }
-    
+    ///////////////////////////////////////////////////////////////////
+
+
+    RepoMediaAccess::RepoMediaAccess( const ProvideFilePolicy & defaultPolicy_r )
+      : _impl( new Impl( defaultPolicy_r ) )
+    {}
+
     RepoMediaAccess::~RepoMediaAccess()
-    {
-    
-    }
-    
-    ManagedFile RepoMediaAccess::provideFile( Repository repo_r,
+    {}
+
+    void RepoMediaAccess::setDefaultPolicy( const ProvideFilePolicy & policy_r )
+    { _impl->_defaultPolicy = policy_r; }
+
+    const ProvideFilePolicy & RepoMediaAccess::defaultPolicy() const
+    { return _impl->_defaultPolicy; }
+
+    ManagedFile RepoMediaAccess::provideFile( RepoInfo repo_r,
                                               const OnMediaLocation & loc_r,
                                               const ProvideFilePolicy & policy_r )
     {
-      MIL << "provideFile " << loc_r << endl;
+      MIL << loc_r << endl;
       // Arrange DownloadFileReportHack to recieve the source::DownloadFileReport
       // and redirect download progress triggers to call the ProvideFilePolicy
       // callback.
-      DownloadFileReportHack dumb;
-      dumb._redirect = bind( mem_fun_ref( &ProvideFilePolicy::progress ),
-                             ref( policy_r ), _1 );
-      callback::TempConnect<repo::RepoReport> temp( dumb );
-
-      Url url;
-      RepoInfo info = repo_r.info();
-      set<Url> urls = info.baseUrls();
-      if ( urls.empty() )
-        ZYPP_THROW(Exception(_("No url in repository.")));
-      
-      for ( RepoInfo::urls_const_iterator it = urls.begin();
-            it != urls.end();
-            ++it )
+      DownloadFileReportHack dumb( bind( mem_fun_ref( &ProvideFilePolicy::progress ), ref( policy_r ), _1 ) );
+
+      RepoException repo_excpt(repo_r,
+                              str::form(_("Can't provide file '%s' from repository '%s'"),
+                               loc_r.filename().c_str(),
+                               repo_r.alias().c_str() ) );
+
+      if ( repo_r.baseUrlsEmpty() )
+      {
+        repo_excpt.remember(RepoException(_("No url in repository.")));
+        ZYPP_THROW(repo_excpt);
+      }
+
+      Fetcher fetcher;
+      fetcher.addCachePath( repo_r.packagesPath() );
+      MIL << "Added cache path " << repo_r.packagesPath() << endl;
+
+      // Test whether download destination is writable, if not
+      // switch into the tmpspace (e.g. bnc#755239, download and
+      // install srpms as user).
+      Pathname destinationDir( repo_r.packagesPath() );
+
+      PathInfo pi( destinationDir );
+      if ( ! pi.isExist() )
+      {
+       // try to create it...
+       assert_dir( destinationDir );
+       pi();
+      }
+      if ( geteuid() != 0 && ! pi.userMayW() )
+      {
+        WAR << "Destination dir '" << destinationDir << "' is not user writable, using tmp space." << endl;
+        destinationDir = getZYpp()->tmpPath() / destinationDir;
+       assert_dir( destinationDir );
+        fetcher.addCachePath( destinationDir );
+        MIL << "Added cache path " << destinationDir << endl;
+      }
+
+      // Suppress (interactive) media::MediaChangeReport if we in have multiple basurls (>1)
+      media::ScopedDisableMediaChangeReport guard( repo_r.baseUrlsSize() > 1 );
+
+      for ( RepoInfo::urls_const_iterator it = repo_r.baseUrlsBegin();
+            it != repo_r.baseUrlsEnd();
+            /* incremented in the loop */ )
       {
-        url = *it;
+        Url url( *it );
+        ++it;
         try
         {
-          MIL << "Providing file of repo '" << info.alias() 
-              << "' from " << url << endl;
-          shared_ptr<MediaSetAccess> access = _impl->mediaAccessForUrl(url);
-          _impl->setVerifierForRepo(repo_r, access);
+          MIL << "Providing file of repo '" << repo_r.alias() << "' from " << url << endl;
+          shared_ptr<MediaSetAccess> access = _impl->mediaAccessForUrl( url, repo_r );
 
-          ManagedFile ret( access->provideFile(loc_r) );
+         fetcher.enqueue( loc_r );
+         fetcher.start( destinationDir, *access );
 
-          std::string scheme( url.getScheme() );
-          if ( scheme == "http" || scheme == "https" || scheme == "ftp" )
+         // reached if no exception has been thrown, so this is the correct file
+          ManagedFile ret( destinationDir + loc_r.filename() );
+          if ( !repo_r.keepPackages() )
           {
             ret.setDispose( filesystem::unlink );
           }
 
-          if ( loc_r.checksum().empty() )
-          {
-            // no checksum in metadata
-            WAR << "No checksum in metadata " << loc_r << endl;
-          }
-          else
-          {
-            std::ifstream input( ret->asString().c_str() );
-            CheckSum retChecksum( loc_r.checksum().type(), input );
-            input.close();
-
-            if ( loc_r.checksum() != retChecksum )
-            {
-              // failed integity check
-              std::ostringstream err;
-              err << "File " << ret << " fails integrity check. Expected: [" << loc_r.checksum() << "] Got: [";
-              if ( retChecksum.empty() )
-                err << "Failed to compute checksum";
-              else
-                err << retChecksum;
-              err << "]";
-
-              if ( policy_r.failOnChecksumError() )
-                ZYPP_THROW( Exception( err.str() ) );
-              else
-                WAR << "NO failOnChecksumError: " << err.str() << endl;
-            }
-          }
-
           MIL << "provideFile at " << ret << endl;
           return ret;
         }
+        catch ( const UserRequestException & excpt )
+       {
+         ZYPP_CAUGHT( excpt );
+         ZYPP_RETHROW( excpt );
+       }
+        catch ( const FileCheckException & excpt )
+       {
+         ZYPP_CAUGHT( excpt );
+         ZYPP_RETHROW( excpt );
+       }
         catch ( const Exception &e )
         {
           ZYPP_CAUGHT( e );
+
+          repo_excpt.remember(e);
+
           WAR << "Trying next url" << endl;
           continue;
         }
       } // iteration over urls
 
-      ZYPP_THROW(Exception(str::form(_("Can't provide file %s from repository %s"),
-                                       loc_r.filename().c_str(),
-                                       info.alias().c_str() ) ) );
-      
+      ZYPP_THROW(repo_excpt);
       return ManagedFile(); // not reached
     }