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