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