Imported Upstream version 17.0.0
[platform/upstream/libzypp.git] / zypp / repo / susetags / Downloader.cc
1
2 #include <iostream>
3 #include <fstream>
4
5 #include "zypp/base/LogTools.h"
6 #include "zypp/base/Gettext.h"
7 #include "zypp/base/String.h"
8 #include "zypp/base/Regex.h"
9 #include "zypp/OnMediaLocation.h"
10 #include "zypp/MediaSetAccess.h"
11 #include "zypp/Fetcher.h"
12 #include "zypp/Locale.h"
13 #include "zypp/ZConfig.h"
14 #include "zypp/repo/MediaInfoDownloader.h"
15 #include "zypp/repo/susetags/Downloader.h"
16 #include "zypp/parser/ParseException.h"
17 #include "zypp/parser/susetags/RepoIndex.h"
18 #include "zypp/base/UserRequestException.h"
19
20 using namespace std;
21 using namespace zypp::parser;
22 using namespace zypp::parser::susetags;
23
24 namespace zypp
25 {
26 namespace repo
27 {
28 namespace susetags
29 {
30
31 Downloader::Downloader( const RepoInfo &repoinfo, const Pathname &delta_dir )
32   : repo::Downloader(repoinfo), _delta_dir(delta_dir)
33 {
34 }
35
36 RepoStatus Downloader::status( MediaSetAccess &media )
37 {
38   RepoStatus ret( media.provideOptionalFile( repoInfo().path() + "/content") );
39   if ( !ret.empty() )   // else: mandatory master index is missing
40     ret = ret && RepoStatus( media.provideOptionalFile( "/media.1/media" ) );
41   // else: mandatory master index is missing -> stay empty
42   return ret;
43 }
44
45 // search old repository file file to run the delta algorithm on
46 static Pathname search_deltafile( const Pathname &dir, const Pathname &file )
47 {
48   Pathname deltafile(dir + file.basename());
49   if (PathInfo(deltafile).isExist())
50     return deltafile;
51   return Pathname();
52 }
53
54
55 /** \todo: Downloading/sigcheck of master index shoudl be common in base class */
56 void Downloader::download( MediaSetAccess &media,
57                            const Pathname &dest_dir,
58                            const ProgressData::ReceiverFnc & progress )
59 {
60   downloadMediaInfo( dest_dir, media );
61
62   Pathname masterIndex( repoInfo().path() / "/content" );
63   defaultDownloadMasterIndex( media, dest_dir, masterIndex );
64
65   // Content file first to get the repoindex
66   {
67     Pathname inputfile( dest_dir / masterIndex );
68     ContentFileReader content;
69     content.setRepoIndexConsumer( bind( &Downloader::consumeIndex, this, _1 ) );
70     content.parse( inputfile );
71   }
72   if ( ! _repoindex )
73   {
74     ZYPP_THROW( ParseException( (dest_dir+repoInfo().path()).asString() + ": " + "No repository index in content file." ) );
75   }
76   MIL << "RepoIndex: " << _repoindex << endl;
77   if ( _repoindex->metaFileChecksums.empty() )
78   {
79     ZYPP_THROW( ParseException( (dest_dir+repoInfo().path()).asString() + ": " + "No metadata checksums in content file." ) );
80   }
81   if ( _repoindex->signingKeys.empty() )
82   {
83     WAR << "No signing keys defined." << endl;
84   }
85
86   // Prepare parsing
87   Pathname descr_dir = _repoindex->descrdir; // path below reporoot
88   //_datadir  = _repoIndex->datadir;  // path below reporoot
89
90   std::map<std::string,RepoIndex::FileChecksumMap::const_iterator> availablePackageTranslations;
91
92   for_( it, _repoindex->metaFileChecksums.begin(), _repoindex->metaFileChecksums.end() )
93   {
94     // omit unwanted translations
95     if ( str::hasPrefix( it->first, "packages" ) )
96     {
97       static const str::regex rx_packages( "^packages((.gz)?|(.([^.]*))(.gz)?)$" );
98       str::smatch what;
99       if ( str::regex_match( it->first, what, rx_packages ) )
100       {
101         if ( what[4].empty() // packages(.gz)?
102           || what[4] == "DU"
103           || what[4] == "en" )
104         { ; /* always downloaded */ }
105         else if ( what[4] == "FL" )
106         { continue; /* never downloaded */ }
107         else
108         {
109           // remember and decide later
110           availablePackageTranslations[what[4]] = it;
111           continue;
112         }
113       }
114       else
115         continue; // discard
116     }
117     else if ( it->first == "patterns.pat"
118               || it->first == "patterns.pat.gz" )
119     {
120       // take all patterns in one go
121     }
122     else if ( str::endsWith( it->first, ".pat" )
123               || str::endsWith( it->first, ".pat.gz" ) )
124     {
125
126       // *** see also zypp/parser/susetags/RepoParser.cc ***
127
128       // omit unwanted patterns, see https://bugzilla.novell.com/show_bug.cgi?id=298716
129       // expect "<name>.<arch>.pat[.gz]", <name> might contain additional dots
130       // split at dots, take .pat or .pat.gz into account
131
132       std::vector<std::string> patparts;
133       unsigned archpos = 2;
134       // expect "<name>.<arch>.pat[.gz]", <name> might contain additional dots
135       unsigned count = str::split( it->first, std::back_inserter(patparts), "." );
136       if ( patparts[count-1] == "gz" )
137           archpos++;
138
139       if ( count > archpos )
140       {
141         try                             // might by an invalid architecture
142         {
143           Arch patarch( patparts[count-archpos] );
144           if ( !patarch.compatibleWith( ZConfig::instance().systemArchitecture() ) )
145           {
146             // discard, if not compatible
147             MIL << "Discarding pattern " << it->first << endl;
148             continue;
149           }
150         }
151         catch ( const Exception & excpt )
152         {
153           WAR << "Pattern file name does not contain recognizable architecture: " << it->first << endl;
154           // keep .pat file if it doesn't contain an recognizable arch
155         }
156       }
157     }
158     MIL << "adding job " << it->first << endl;
159     OnMediaLocation location( repoInfo().path() + descr_dir + it->first, 1 );
160     location.setChecksum( it->second );
161     enqueueDigested(location, FileChecker(), search_deltafile(_delta_dir + descr_dir, it->first));
162   }
163
164   // check whether to download more package translations:
165   {
166     auto fnc_checkTransaltions( [&]( const Locale & locale_r ) {
167       for ( Locale toGet( locale_r ); toGet; toGet = toGet.fallback() )
168       {
169         auto it( availablePackageTranslations.find( toGet.code() ) );
170         if ( it != availablePackageTranslations.end() )
171         {
172           auto mit( it->second );
173           MIL << "adding job " << mit->first << endl;
174           OnMediaLocation location( repoInfo().path() + descr_dir + mit->first, 1 );
175           location.setChecksum( mit->second );
176           enqueueDigested(location, FileChecker(), search_deltafile(_delta_dir + descr_dir, mit->first));
177           break;
178         }
179       }
180     });
181     for ( const Locale & it : ZConfig::instance().repoRefreshLocales() )
182     {
183       fnc_checkTransaltions( it );
184     }
185     fnc_checkTransaltions( ZConfig::instance().textLocale() );
186   }
187
188   for_( it, _repoindex->mediaFileChecksums.begin(), _repoindex->mediaFileChecksums.end() )
189   {
190     // Repo adopts license files listed in HASH
191     if ( it->first != "license.tar.gz" )
192       continue;
193
194     MIL << "adding job " << it->first << endl;
195     OnMediaLocation location( repoInfo().path() + it->first, 1 );
196     location.setChecksum( it->second );
197     enqueueDigested(location, FileChecker(), search_deltafile(_delta_dir, it->first));
198   }
199
200   for_( it, _repoindex->signingKeys.begin(),_repoindex->signingKeys.end() )
201   {
202     MIL << "adding job " << it->first << endl;
203     OnMediaLocation location( repoInfo().path() + it->first, 1 );
204     location.setChecksum( it->second );
205     enqueueDigested(location);
206   }
207
208   start( dest_dir, media );
209 }
210
211 void Downloader::consumeIndex( const RepoIndex_Ptr & data_r )
212 {
213   MIL << "Consuming repo index" << endl;
214   _repoindex = data_r;
215 }
216
217 }// ns susetags
218 }// ns source
219 } // ns zypp