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