Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / repo / susetags / Downloader.cc
index 831c736..aadfc5e 100644 (file)
@@ -2,19 +2,24 @@
 #include <iostream>
 #include <fstream>
 
-#include "zypp/base/Logger.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Gettext.h"
 #include "zypp/base/String.h"
+#include "zypp/base/Regex.h"
 #include "zypp/OnMediaLocation.h"
 #include "zypp/MediaSetAccess.h"
 #include "zypp/Fetcher.h"
 #include "zypp/Locale.h"
 #include "zypp/ZConfig.h"
 #include "zypp/repo/MediaInfoDownloader.h"
-
 #include "zypp/repo/susetags/Downloader.h"
+#include "zypp/parser/ParseException.h"
+#include "zypp/parser/susetags/RepoIndex.h"
 #include "zypp/base/UserRequestException.h"
 
 using namespace std;
+using namespace zypp::parser;
+using namespace zypp::parser::susetags;
 
 namespace zypp
 {
@@ -23,122 +28,191 @@ namespace repo
 namespace susetags
 {
 
-Downloader::Downloader(const Pathname &path )
-    : _path(path)
+Downloader::Downloader( const RepoInfo &repoinfo, const Pathname &delta_dir )
+  : repo::Downloader(repoinfo), _delta_dir(delta_dir)
 {
-
 }
 
 RepoStatus Downloader::status( MediaSetAccess &media )
 {
-  Pathname content = media.provideFile( _path + "/content");
-  return RepoStatus(content);
+  Pathname content = media.provideFile( repoInfo().path() + "/content");
+  // the media.1 is always in the root of the media, not like the content
+  // file which is in the path() location
+  Pathname mediafile = media.provideFile( "/media.1/media" );
+
+  return RepoStatus(content) && RepoStatus(mediafile);
+}
+
+// search old repository file file to run the delta algorithm on
+static Pathname search_deltafile( const Pathname &dir, const Pathname &file )
+{
+  Pathname deltafile(dir + file.basename());
+  if (PathInfo(deltafile).isExist())
+    return deltafile;
+  return Pathname();
 }
 
+
+/** \todo: Downloading/sigcheck of master index shoudl be common in base class */
 void Downloader::download( MediaSetAccess &media,
                            const Pathname &dest_dir,
                            const ProgressData::ReceiverFnc & progress )
 {
   downloadMediaInfo( dest_dir, media );
 
-  SignatureFileChecker sigchecker;
+  Pathname masterIndex( repoInfo().path() / "/content" );
+  defaultDownloadMasterIndex( media, dest_dir, masterIndex );
 
-  Pathname sig = _path + "/content.asc";
-  if ( media.doesFileExist(sig) )
+  // Content file first to get the repoindex
   {
-    this->enqueue( OnMediaLocation( sig, 1 ) );
-    this->start( dest_dir, media );
-    this->reset();
-
-    sigchecker = SignatureFileChecker( dest_dir + sig );
+    Pathname inputfile( dest_dir / masterIndex );
+    ContentFileReader content;
+    content.setRepoIndexConsumer( bind( &Downloader::consumeIndex, this, _1 ) );
+    content.parse( inputfile );
   }
-
-  Pathname key = _path + "/content.key";
-  if ( media.doesFileExist(key) )
+  if ( ! _repoindex )
   {
-    this->enqueue( OnMediaLocation( key, 1 ) );
-    this->start( dest_dir, media );
-    this->reset();
-    sigchecker.addPublicKey(dest_dir + key);
+    ZYPP_THROW( ParseException( (dest_dir+repoInfo().path()).asString() + ": " + "No repository index in content file." ) );
+  }
+  MIL << "RepoIndex: " << _repoindex << endl;
+  if ( _repoindex->metaFileChecksums.empty() )
+  {
+    ZYPP_THROW( ParseException( (dest_dir+repoInfo().path()).asString() + ": " + "No metadata checksums in content file." ) );
+  }
+  if ( _repoindex->signingKeys.empty() )
+  {
+    WAR << "No signing keys defined." << endl;
   }
 
+  // Prepare parsing
+  Pathname descr_dir = _repoindex->descrdir; // path below reporoot
+  //_datadir  = _repoIndex->datadir;  // path below reporoot
 
-  this->enqueue( OnMediaLocation( _path + "/content", 1 ), sigchecker );
-  this->start( dest_dir, media );
-  this->reset();
-
-  std::ifstream file((dest_dir +  _path + "/content").asString().c_str());
-  std::string buffer;
-  Pathname descr_dir;
+  std::map<std::string,RepoIndex::FileChecksumMap::const_iterator> availablePackageTranslations;
 
-  // FIXME Note this code assumes DESCR comes before as META
-  string value;
-  while (file && !file.eof())
+  for_( it, _repoindex->metaFileChecksums.begin(), _repoindex->metaFileChecksums.end() )
   {
-    getline(file, buffer);
-    if ( buffer.substr( 0, 5 ) == "DESCR" )
+    // omit unwanted translations
+    if ( str::hasPrefix( it->first, "packages" ) )
     {
-      std::vector<std::string> words;
-      if ( str::split( buffer, std::back_inserter(words) ) != 2 )
+      static const str::regex rx_packages( "^packages((.gz)?|(.([^.]*))(.gz)?)$" );
+      str::smatch what;
+      if ( str::regex_match( it->first, what, rx_packages ) )
       {
-        // error
-        ZYPP_THROW(Exception("bad DESCR line"));
+       if ( what[4].empty() // packages(.gz)?
+         || what[4] == "DU"
+         || what[4] == "en" )
+       { ; /* always downloaded */ }
+       else if ( what[4] == "FL" )
+       { continue; /* never downloaded */ }
+       else
+       {
+         // remember and decide later
+         availablePackageTranslations[what[4]] = it;
+         continue;
+       }
       }
-      descr_dir = words[1];
+      else
+       continue; // discard
     }
-    else if ( buffer.substr( 0, 4 ) == "META" )
+    else if ( it->first == "patterns.pat"
+              || it->first == "patterns.pat.gz" )
     {
-      std::vector<std::string> words;
-      if ( str::split( buffer, std::back_inserter(words) ) != 4 )
-      {
-        // error
-        ZYPP_THROW(Exception("bad META line"));
-      }
-      // omit unwanted translations
-      if ( str::hasPrefix( words[3], "packages" ) )
+      // take all patterns in one go
+    }
+    else if ( str::endsWith( it->first, ".pat" )
+              || str::endsWith( it->first, ".pat.gz" ) )
+    {
+
+      // *** see also zypp/parser/susetags/RepoParser.cc ***
+
+      // omit unwanted patterns, see https://bugzilla.novell.com/show_bug.cgi?id=298716
+      // expect "<name>.<arch>.pat[.gz]", <name> might contain additional dots
+      // split at dots, take .pat or .pat.gz into account
+
+      std::vector<std::string> patparts;
+      unsigned archpos = 2;
+      // expect "<name>.<arch>.pat[.gz]", <name> might contain additional dots
+      unsigned count = str::split( it->first, std::back_inserter(patparts), "." );
+      if ( patparts[count-1] == "gz" )
+          archpos++;
+
+      if ( count > archpos )
       {
-        std::string rest( str::stripPrefix( words[3], "packages" ) );
-        if ( ! (   rest.empty()
-                || rest == ".DU"
-                || rest == ".en"
-                || rest == ".gz"
-                || rest == ".DU.gz"
-                || rest == ".en.gz" ) )
+        try                            // might by an invalid architecture
         {
-          // Not 100% correct as we take each fallback of textLocale
-          Locale toParse( ZConfig::instance().textLocale() );
-          while ( toParse != Locale::noCode )
-          {
-            if ( rest == ("."+toParse.code()) || (rest == ("."+toParse.code()+".gz")) )
-              break;
-            toParse = toParse.fallback();
-          }
-          if ( toParse == Locale::noCode )
+          Arch patarch( patparts[count-archpos] );
+          if ( !patarch.compatibleWith( ZConfig::instance().systemArchitecture() ) )
           {
-            // discard
+            // discard, if not compatible
+            MIL << "Discarding pattern " << it->first << endl;
             continue;
           }
         }
+        catch ( const Exception & excpt )
+        {
+          WAR << "Pattern file name does not contain recognizable architecture: " << it->first << endl;
+          // keep .pat file if it doesn't contain an recognizable arch
+        }
       }
-      OnMediaLocation location( _path + descr_dir + words[3], 1 );
-      location.setChecksum( CheckSum( words[1], words[2] ) );
-      this->enqueueDigested(location);
     }
-    else if (buffer.substr( 0, 3 ) == "KEY")
-    {
-      std::vector<std::string> words;
-      if ( str::split( buffer, std::back_inserter(words) ) != 4 )
+    MIL << "adding job " << it->first << endl;
+    OnMediaLocation location( repoInfo().path() + descr_dir + it->first, 1 );
+    location.setChecksum( it->second );
+    enqueueDigested(location, FileChecker(), search_deltafile(_delta_dir + descr_dir, it->first));
+  }
+
+  // check whether to download more package translations:
+  {
+    auto fnc_checkTransaltions( [&]( const Locale & locale_r ) {
+      for ( Locale toGet( locale_r ); toGet; toGet = toGet.fallback() )
       {
-        // error
-        ZYPP_THROW(Exception("bad KEY line"));
+       auto it( availablePackageTranslations.find( toGet.code() ) );
+       if ( it != availablePackageTranslations.end() )
+       {
+         auto mit( it->second );
+         MIL << "adding job " << mit->first << endl;
+         OnMediaLocation location( repoInfo().path() + descr_dir + mit->first, 1 );
+         location.setChecksum( mit->second );
+         enqueueDigested(location, FileChecker(), search_deltafile(_delta_dir + descr_dir, mit->first));
+         break;
+       }
       }
-      OnMediaLocation location( _path + words[3], 1 );
-      location.setChecksum( CheckSum( words[1], words[2] ) );
-      this->enqueueDigested(location);
+    });
+    for ( const Locale & it : ZConfig::instance().repoRefreshLocales() )
+    {
+      fnc_checkTransaltions( it );
     }
+    fnc_checkTransaltions( ZConfig::instance().textLocale() );
   }
-  file.close();
-  this->start( dest_dir, media );
+
+  for_( it, _repoindex->mediaFileChecksums.begin(), _repoindex->mediaFileChecksums.end() )
+  {
+    // Repo adopts license files listed in HASH
+    if ( it->first != "license.tar.gz" )
+      continue;
+
+    MIL << "adding job " << it->first << endl;
+    OnMediaLocation location( repoInfo().path() + it->first, 1 );
+    location.setChecksum( it->second );
+    enqueueDigested(location, FileChecker(), search_deltafile(_delta_dir, it->first));
+  }
+
+  for_( it, _repoindex->signingKeys.begin(),_repoindex->signingKeys.end() )
+  {
+    MIL << "adding job " << it->first << endl;
+    OnMediaLocation location( repoInfo().path() + it->first, 1 );
+    location.setChecksum( it->second );
+    enqueueDigested(location);
+  }
+
+  start( dest_dir, media );
+}
+
+void Downloader::consumeIndex( const RepoIndex_Ptr & data_r )
+{
+  MIL << "Consuming repo index" << endl;
+  _repoindex = data_r;
 }
 
 }// ns susetags