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