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