Imported Upstream version 17.0.0
[platform/upstream/libzypp.git] / zypp / repo / yum / Downloader.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9
10 #include <fstream>
11 #include "zypp/base/String.h"
12 #include "zypp/base/LogTools.h"
13 #include "zypp/base/Function.h"
14 #include "zypp/ZConfig.h"
15
16 #include "zypp/parser/yum/RepomdFileReader.h"
17 #include "zypp/parser/yum/PatchesFileReader.h"
18 #include "Downloader.h"
19 #include "zypp/repo/MediaInfoDownloader.h"
20 #include "zypp/base/UserRequestException.h"
21 #include "zypp/parser/xml/Reader.h"
22
23 using namespace std;
24 using namespace zypp::xml;
25 using namespace zypp::parser::yum;
26
27 namespace zypp
28 {
29 namespace repo
30 {
31 namespace yum
32 {
33
34 Downloader::Downloader( const RepoInfo &repoinfo , const Pathname &delta_dir)
35   : repo::Downloader(repoinfo), _delta_dir(delta_dir), _media_ptr(0L)
36 {}
37
38 RepoStatus Downloader::status( MediaSetAccess &media )
39 {
40   RepoStatus ret( media.provideFile( repoInfo().path() / "/repodata/repomd.xml" ) );
41   if ( !ret.empty() )   // else: mandatory master index is missing
42     ret = ret && RepoStatus( media.provideOptionalFile( "/media.1/media" ) );
43   // else: mandatory master index is missing -> stay empty
44   return ret;
45 }
46
47 static OnMediaLocation loc_with_path_prefix( const OnMediaLocation & loc, const Pathname & prefix )
48 {
49   if (prefix.empty() || prefix == "/")
50     return loc;
51
52   OnMediaLocation loc_with_path(loc);
53   loc_with_path.changeFilename(prefix / loc.filename());
54   return loc_with_path;
55 }
56
57 // search old repository file file to run the delta algorithm on
58 static Pathname search_deltafile( const Pathname & dir, const Pathname & file )
59 {
60   Pathname deltafile;
61   if (!PathInfo(dir).isDir())
62     return deltafile;
63   string base = file.basename();
64   size_t hypoff = base.find("-");
65   if (hypoff != string::npos)
66     base.replace(0, hypoff + 1, "");
67   size_t basesize = base.size();
68   std::list<Pathname> retlist;
69   if (!filesystem::readdir(retlist, dir, false))
70   {
71     for_( it, retlist.begin(), retlist.end() )
72     {
73       string fn = it->asString();
74       if (fn.size() >= basesize && fn.substr(fn.size() - basesize, basesize) == base)
75         deltafile = *it;
76     }
77   }
78   return deltafile;
79 }
80
81 bool Downloader::patches_Callback( const OnMediaLocation & loc_r, const string & id_r )
82 {
83   OnMediaLocation loc_with_path(loc_with_path_prefix(loc_r, repoInfo().path()));
84   MIL << id_r << " : " << loc_with_path << endl;
85   this->enqueueDigested(loc_with_path,  FileChecker(), search_deltafile(_delta_dir + "repodata", loc_r.filename()));
86   return true;
87 }
88
89
90 //bool repomd_Callback2( const OnMediaLocation &loc, const ResourceType &dtype, const std::string &typestr, UserData & userData_r );
91
92 ///////////////////////////////////////////////////////////////////
93 namespace
94 {
95   ///////////////////////////////////////////////////////////////////
96   /// \class Impl
97   /// \brief Helper filtering the files offered by a RepomdFileReader
98   ///
99   /// Clumsy construct; basically an Impl class for Downloader, maintained
100   /// in Downloader::download only while parsing a repomd.xml.
101   ///
102   /// Introduced because Downloader itself lacks an Impl class, thus can't
103   /// be extended to provide more data to the callbacks without losing
104   /// binary compatibility.
105   ///////////////////////////////////////////////////////////////////
106   struct RepomdFileReaderCallback2
107   {
108     RepomdFileReaderCallback2( const RepomdFileReader::ProcessResource & origCallback_r )
109     : _origCallback( origCallback_r )
110     {
111       addWantedLocale( ZConfig::instance().textLocale() );
112       for ( const Locale & it : ZConfig::instance().repoRefreshLocales() )
113         addWantedLocale( it );
114     }
115
116     /** The callback invoked by the RepomdFileReader */
117     bool repomd_Callback2( const OnMediaLocation & loc_r, const ResourceType & dtype_r, const std::string & typestr_r )
118     {
119       // filter well known resource types
120       if ( dtype_r == ResourceType::OTHER || dtype_r == ResourceType::FILELISTS )
121         return true;    // skip it
122
123       // filter custom resource types (by string)
124       if ( dtype_r == ResourceType::NONE )
125       {
126         // susedata.LANG
127         if ( str::hasPrefix( typestr_r, "susedata." ) && ! wantLocale( Locale(typestr_r.c_str()+9) ) )
128           return true;  // skip it
129       }
130
131       // take it
132       return( _origCallback ? _origCallback( loc_r, dtype_r ) : true );
133     }
134
135   private:
136     bool wantLocale( const Locale & locale_r ) const
137     { return _wantedLocales.count( locale_r ); }
138
139     void addWantedLocale( Locale locale_r )
140     {
141       while ( locale_r )
142       {
143         _wantedLocales.insert( locale_r );
144         locale_r = locale_r.fallback();
145       }
146     }
147
148   private:
149     RepomdFileReader::ProcessResource _origCallback;    ///< Original Downloader callback
150     LocaleSet _wantedLocales;                           ///< Locales do download
151
152   };
153 } // namespace
154 ///////////////////////////////////////////////////////////////////
155
156 bool Downloader::repomd_Callback( const OnMediaLocation & loc_r, const ResourceType & dtype_r )
157 {
158   // NOTE: Filtering of unwanted files is done in RepomdFileReaderCallback2!
159
160   // schedule file for download
161   const OnMediaLocation & loc_with_path(loc_with_path_prefix(loc_r, repoInfo().path()));
162   this->enqueueDigested(loc_with_path, FileChecker(), search_deltafile(_delta_dir + "repodata", loc_r.filename()));
163
164   // We got a patches file we need to read, to add patches listed
165   // there, so we transfer what we have in the queue, and
166   // queue the patches in the patches callback
167   if ( dtype_r == ResourceType::PATCHES )
168   {
169     this->start( _dest_dir, *_media_ptr );
170     // now the patches.xml file must exists
171     PatchesFileReader( _dest_dir + repoInfo().path() + loc_r.filename(),
172                        bind( &Downloader::patches_Callback, this, _1, _2));
173   }
174   return true;
175 }
176
177 void Downloader::download( MediaSetAccess & media, const Pathname & dest_dir, const ProgressData::ReceiverFnc & progressrcv )
178 {
179   downloadMediaInfo( dest_dir, media );
180
181   Pathname masterIndex( repoInfo().path() / "/repodata/repomd.xml" );
182   defaultDownloadMasterIndex( media, dest_dir, masterIndex );
183
184   // init the data stored in Downloader itself
185   _media_ptr = (&media);
186   _dest_dir = dest_dir;
187
188   // init the extended data
189   RepomdFileReaderCallback2 pimpl( bind(&Downloader::repomd_Callback, this, _1, _2) );
190
191   // setup parser
192   RepomdFileReader( dest_dir / masterIndex,
193                     RepomdFileReader::ProcessResource2( bind(&RepomdFileReaderCallback2::repomd_Callback2, &pimpl, _1, _2, _3) ) );
194
195   // ready, go!
196   start( dest_dir, media );
197 }
198
199 } // namespace yum
200 } // namespace repo
201 } // namespace zypp
202
203
204