add mirrorlist-support
[platform/upstream/libzypp.git] / zypp / RepoInfo.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/RepoInfo.cc
10  *
11 */
12 #include <iostream>
13 #include <vector>
14
15 #include "zypp/base/Logger.h"
16 #include "zypp/base/DefaultIntegral.h"
17 #include "zypp/parser/xml/XmlEscape.h"
18
19 #include "zypp/RepoInfo.h"
20 #include "zypp/repo/RepoInfoBaseImpl.h"
21 #include "zypp/repo/RepoMirrorList.h"
22 #include "zypp/ExternalProgram.h"
23 #include "zypp/media/MediaAccess.h"
24
25 using namespace std;
26 using zypp::xml::escape;
27
28 ///////////////////////////////////////////////////////////////////
29 namespace zypp
30 { /////////////////////////////////////////////////////////////////
31
32   ///////////////////////////////////////////////////////////////////
33   //
34   //    CLASS NAME : RepoInfo::Impl
35   //
36   /** RepoInfo implementation. */
37   struct RepoInfo::Impl : public repo::RepoInfoBase::Impl
38   {
39     Impl()
40       : repo::RepoInfoBase::Impl()
41       , gpgcheck(indeterminate)
42       , keeppackages(indeterminate)
43       , type(repo::RepoType::NONE_e)
44       , emptybaseurls(false)
45     {}
46
47     ~Impl()
48     {}
49
50   public:
51     static const unsigned defaultPriority = 99;
52
53     void setProbedType( const repo::RepoType & t ) const
54     {
55       if ( type == repo::RepoType::NONE
56            && t != repo::RepoType::NONE )
57       {
58         // lazy init!
59         const_cast<Impl*>(this)->type = t;
60       }
61     }
62
63   public:
64     Pathname licenseTgz() const
65     { return metadatapath.empty() ? Pathname() : metadatapath / path / "license.tar.gz"; }
66
67     Url getmirrorListUrl() const
68     {
69       repo::RepoVariablesUrlReplacer replacer;
70       return replacer(mirrorlist_url);
71     }
72
73     Url &setmirrorListUrl()
74     {
75       return mirrorlist_url;
76     }
77
78     const std::set<Url> &baseUrls() const
79     {
80       if ( _baseUrls.empty() && ! (getmirrorListUrl().asString().empty()) )
81       {
82         emptybaseurls = true;
83         repo::RepoMirrorList rmirrorlist (getmirrorListUrl());
84         std::vector<Url> rmurls = rmirrorlist.getUrls();
85         _baseUrls.insert(rmurls.begin(), rmurls.end());
86       }
87       return _baseUrls;
88     }
89
90     std::set<Url> &baseUrls()
91     {
92       return _baseUrls;
93     }
94
95     bool baseurl2dump() const
96     {
97       return !emptybaseurls && !_baseUrls.empty();
98     }
99
100
101   public:
102     TriBool gpgcheck;
103     TriBool keeppackages;
104     Url gpgkey_url;
105     repo::RepoType type;
106     Pathname path;
107     std::string service;
108     std::string targetDistro;
109     Pathname metadatapath;
110     Pathname packagespath;
111     DefaultIntegral<unsigned,defaultPriority> priority;
112     mutable bool emptybaseurls;
113
114   private:
115     Url mirrorlist_url;
116     mutable std::set<Url> _baseUrls;
117
118     friend Impl * rwcowClone<Impl>( const Impl * rhs );
119     /** clone for RWCOW_pointer */
120     Impl * clone() const
121     { return new Impl( *this ); }
122   };
123   ///////////////////////////////////////////////////////////////////
124
125   /** \relates RepoInfo::Impl Stream output */
126   inline std::ostream & operator<<( std::ostream & str, const RepoInfo::Impl & obj )
127   {
128     return str << "RepoInfo::Impl";
129   }
130
131   ///////////////////////////////////////////////////////////////////
132   //
133   //    CLASS NAME : RepoInfo
134   //
135   ///////////////////////////////////////////////////////////////////
136
137   const RepoInfo RepoInfo::noRepo;
138
139   ///////////////////////////////////////////////////////////////////
140   //
141   //    METHOD NAME : RepoInfo::RepoInfo
142   //    METHOD TYPE : Ctor
143   //
144   RepoInfo::RepoInfo()
145   : _pimpl( new Impl() )
146   {}
147
148   ///////////////////////////////////////////////////////////////////
149   //
150   //    METHOD NAME : RepoInfo::~RepoInfo
151   //    METHOD TYPE : Dtor
152   //
153   RepoInfo::~RepoInfo()
154   {
155     //MIL << std::endl;
156   }
157
158   unsigned RepoInfo::priority() const
159   { return _pimpl->priority; }
160   unsigned RepoInfo::defaultPriority()
161   { return Impl::defaultPriority; }
162   void RepoInfo::setPriority( unsigned newval_r )
163   {
164     _pimpl->priority = newval_r ? newval_r : Impl::defaultPriority;
165   }
166
167   void RepoInfo::setGpgCheck( bool check )
168   {
169     _pimpl->gpgcheck = check;
170   }
171
172   void RepoInfo::setMirrorListUrl( const Url &url )
173   {
174     _pimpl->setmirrorListUrl() = url;
175   }
176
177   void RepoInfo::setGpgKeyUrl( const Url &url )
178   {
179     _pimpl->gpgkey_url = url;
180   }
181
182   void RepoInfo::addBaseUrl( const Url &url )
183   {
184     _pimpl->baseUrls().insert(url);
185   }
186
187   void RepoInfo::setBaseUrl( const Url &url )
188   {
189     _pimpl->baseUrls().clear();
190     addBaseUrl(url);
191   }
192
193   void RepoInfo::setPath( const Pathname &path )
194   {
195     _pimpl->path = path;
196   }
197
198   void RepoInfo::setType( const repo::RepoType &t )
199   {
200     _pimpl->type = t;
201   }
202
203   void RepoInfo::setProbedType( const repo::RepoType &t ) const
204   { _pimpl->setProbedType( t ); }
205
206
207   void RepoInfo::setMetadataPath( const Pathname &path )
208   {
209     _pimpl->metadatapath = path;
210   }
211
212   void RepoInfo::setPackagesPath( const Pathname &path )
213   {
214     _pimpl->packagespath = path;
215   }
216
217   void RepoInfo::setKeepPackages( bool keep )
218   {
219     _pimpl->keeppackages = keep;
220   }
221
222   void RepoInfo::setService( const std::string& name )
223   {
224     _pimpl->service = name;
225   }
226
227   void RepoInfo::setTargetDistribution(
228       const std::string & targetDistribution)
229   {
230     _pimpl->targetDistro = targetDistribution;
231   }
232
233   bool RepoInfo::gpgCheck() const
234   { return indeterminate(_pimpl->gpgcheck) ? true : (bool) _pimpl->gpgcheck; }
235
236   Pathname RepoInfo::metadataPath() const
237   { return _pimpl->metadatapath; }
238
239   Pathname RepoInfo::packagesPath() const
240   { return _pimpl->packagespath; }
241
242   repo::RepoType RepoInfo::type() const
243   { return _pimpl->type; }
244
245   Url RepoInfo::mirrorListUrl() const
246   {
247     return _pimpl->getmirrorListUrl();
248   }
249
250   Url RepoInfo::gpgKeyUrl() const
251   { return _pimpl->gpgkey_url; }
252
253   std::set<Url> RepoInfo::baseUrls() const
254   {
255     RepoInfo::url_set replaced_urls;
256     repo::RepoVariablesUrlReplacer replacer;
257     for ( url_set::const_iterator it = _pimpl->baseUrls().begin();
258           it != _pimpl->baseUrls().end();
259           ++it )
260     {
261       replaced_urls.insert(replacer(*it));
262     }
263     return replaced_urls;
264   }
265
266   Pathname RepoInfo::path() const
267   { return _pimpl->path; }
268
269   std::string RepoInfo::service() const
270   { return _pimpl->service; }
271
272   std::string RepoInfo::targetDistribution() const
273   { return _pimpl->targetDistro; }
274
275   RepoInfo::urls_const_iterator RepoInfo::baseUrlsBegin() const
276   {
277     return make_transform_iterator( _pimpl->baseUrls().begin(),
278                                     repo::RepoVariablesUrlReplacer() );
279     //return _pimpl->baseUrls.begin();
280   }
281
282   RepoInfo::urls_const_iterator RepoInfo::baseUrlsEnd() const
283   {
284     //return _pimpl->baseUrls.end();
285     return make_transform_iterator( _pimpl->baseUrls().end(),
286                                     repo::RepoVariablesUrlReplacer() );
287   }
288
289   RepoInfo::urls_size_type RepoInfo::baseUrlsSize() const
290   { return _pimpl->baseUrls().size(); }
291
292   bool RepoInfo::baseUrlsEmpty() const
293   { return _pimpl->baseUrls().empty(); }
294
295   // false by default (if not set by setKeepPackages)
296   bool RepoInfo::keepPackages() const
297   {
298     if (indeterminate(_pimpl->keeppackages))
299     {
300       if (_pimpl->baseUrls().empty())
301         return false;
302       else if ( baseUrlsBegin()->schemeIsDownloading() )
303         return true;
304       else
305         return false;
306     }
307
308     return (bool) _pimpl->keeppackages;
309   }
310
311   ///////////////////////////////////////////////////////////////////
312
313   bool RepoInfo::hasLicense() const
314   {
315     Pathname licenseTgz( _pimpl->licenseTgz() );
316     SEC << licenseTgz << endl;
317     SEC << PathInfo(licenseTgz) << endl;
318
319     return ! licenseTgz.empty() &&  PathInfo(licenseTgz).isFile();
320   }
321
322   std::string RepoInfo::getLicense( const Locale & lang_r )
323   {
324     LocaleSet avlocales( getLicenseLocales() );
325     if ( avlocales.empty() )
326       return std::string();
327
328     Locale getLang( Locale::bestMatch( avlocales, lang_r ) );
329     if ( getLang == Locale::noCode
330          && avlocales.find( Locale::noCode ) == avlocales.end() )
331     {
332       WAR << "License.tar.gz contains no fallback text! " << *this << endl;
333       // Using the fist locale instead of returning no text at all.
334       // So the user might recognize that there is a license, even if he
335       // can't read it.
336       getLang = *avlocales.begin();
337     }
338
339     // now extract the license file.
340     static const std::string licenseFileFallback( "license.txt" );
341     std::string licenseFile( getLang == Locale::noCode
342                              ? licenseFileFallback
343                              : str::form( "license.%s.txt", getLang.code().c_str() ) );
344
345     ExternalProgram::Arguments cmd;
346     cmd.push_back( "tar" );
347     cmd.push_back( "-x" );
348     cmd.push_back( "-z" );
349     cmd.push_back( "-O" );
350     cmd.push_back( "-f" );
351     cmd.push_back( _pimpl->licenseTgz().asString() ); // if it not exists, avlocales was empty.
352     cmd.push_back( licenseFile );
353
354     std::string ret;
355     ExternalProgram prog( cmd, ExternalProgram::Discard_Stderr );
356     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
357     {
358       ret += output;
359     }
360     prog.close();
361     return ret;
362   }
363
364   LocaleSet RepoInfo::getLicenseLocales() const
365   {
366     Pathname licenseTgz( _pimpl->licenseTgz() );
367     if ( licenseTgz.empty() || ! PathInfo( licenseTgz ).isFile() )
368       return LocaleSet();
369
370     ExternalProgram::Arguments cmd;
371     cmd.push_back( "tar" );
372     cmd.push_back( "-t" );
373     cmd.push_back( "-z" );
374     cmd.push_back( "-f" );
375     cmd.push_back( licenseTgz.asString() );
376
377     LocaleSet ret;
378     ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
379     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
380     {
381       static const C_Str license( "license." );
382       static const C_Str dotTxt( ".txt\n" );
383       if ( str::hasPrefix( output, license ) && str::hasSuffix( output, dotTxt ) )
384       {
385         if ( output.size() <= license.size() +  dotTxt.size() ) // license.txt
386           ret.insert( Locale() );
387         else
388           ret.insert( Locale( std::string( output.c_str()+license.size(), output.size()- license.size() - dotTxt.size() ) ) );
389       }
390       else
391       {
392         WAR << "  " << output;
393       }
394     }
395     prog.close();
396     return ret;
397   }
398
399   ///////////////////////////////////////////////////////////////////
400
401   std::ostream & RepoInfo::dumpOn( std::ostream & str ) const
402   {
403     RepoInfoBase::dumpOn(str);
404     if ( _pimpl->baseurl2dump() )
405     {
406       for ( urls_const_iterator it = baseUrlsBegin();
407             it != baseUrlsEnd();
408             ++it )
409       {
410         str << "- url         : " << *it << std::endl;
411       }
412     }
413     if ( ! (_pimpl->getmirrorListUrl().asString().empty())  )
414     {
415       str << "- mirrorlist  : " << _pimpl->getmirrorListUrl() << std::endl;
416     }
417     str << "- path        : " << path() << std::endl;
418     str << "- type        : " << type() << std::endl;
419     str << "- priority    : " << priority() << std::endl;
420
421     str << "- gpgcheck    : " << gpgCheck() << std::endl;
422     str << "- gpgkey      : " << gpgKeyUrl() << std::endl;
423     str << "- keeppackages: " << keepPackages() << std::endl;
424     str << "- service     : " << service() << std::endl;
425
426     if (!targetDistribution().empty())
427       str << "- targetdistro: " << targetDistribution() << std::endl;
428
429     if (!metadataPath().empty())
430       str << "- metadataPath: " << metadataPath() << std::endl;
431
432     if (!packagesPath().empty())
433       str << "- packagesPath: " << packagesPath() << std::endl;
434
435     return str;
436   }
437
438   std::ostream & RepoInfo::dumpAsIniOn( std::ostream & str ) const
439   {
440     RepoInfoBase::dumpAsIniOn(str);
441
442     if ( _pimpl->baseurl2dump() )
443     {
444       str << "baseurl=";
445       for ( url_set::const_iterator it = _pimpl->baseUrls().begin();
446             it != _pimpl->baseUrls().end();
447             ++it )
448       {
449         str << *it << endl;
450       }
451     }
452
453     if ( ! _pimpl->path.empty() )
454       str << "path="<< path() << endl;
455
456     if ( ! (_pimpl->getmirrorListUrl().asString().empty()) )
457       str << "mirrorlist=" << _pimpl->getmirrorListUrl() << endl;
458
459     str << "type=" << type().asString() << endl;
460
461     if ( priority() != defaultPriority() )
462       str << "priority=" << priority() << endl;
463
464     if (!indeterminate(_pimpl->gpgcheck))
465       str << "gpgcheck=" << (gpgCheck() ? "1" : "0") << endl;
466     if ( ! (gpgKeyUrl().asString().empty()) )
467       str << "gpgkey=" <<gpgKeyUrl() << endl;
468
469     if (!indeterminate(_pimpl->keeppackages))
470       str << "keeppackages=" << keepPackages() << endl;
471
472     if( ! service().empty() )
473       str << "service=" << service() << endl;
474
475     return str;
476   }
477
478   std::ostream & RepoInfo::dumpAsXMLOn( std::ostream & str) const
479   { return dumpAsXMLOn(str, ""); }
480
481   std::ostream & RepoInfo::dumpAsXMLOn( std::ostream & str, const std::string & content) const
482   {
483     string tmpstr;
484     str
485       << "<repo"
486       << " alias=\"" << escape(alias()) << "\""
487       << " name=\"" << escape(name()) << "\"";
488     if (type() != repo::RepoType::NONE)
489       str << " type=\"" << type().asString() << "\"";
490     str
491       << " enabled=\"" << enabled() << "\""
492       << " autorefresh=\"" << autorefresh() << "\""
493       << " gpgcheck=\"" << gpgCheck() << "\"";
494     if (!(tmpstr = gpgKeyUrl().asString()).empty())
495       str << " gpgkey=\"" << escape(tmpstr) << "\"";
496     if (!(tmpstr = mirrorListUrl().asString()).empty())
497       str << " mirrorlist=\"" << escape(tmpstr) << "\"";
498     str << ">" << endl;
499
500     if ( _pimpl->baseurl2dump() )
501     {
502       for (RepoInfo::urls_const_iterator urlit = baseUrlsBegin();
503            urlit != baseUrlsEnd(); ++urlit)
504         str << "<url>" << escape(urlit->asString()) << "</url>" << endl;
505     }
506
507     str << "</repo>" << endl;
508     return str;
509   }
510
511
512   std::ostream & operator<<( std::ostream & str, const RepoInfo & obj )
513   {
514     return obj.dumpOn(str);
515   }
516
517
518   /////////////////////////////////////////////////////////////////
519 } // namespace zypp
520 ///////////////////////////////////////////////////////////////////