#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
{
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" ) )
+ 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()) )
- 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