c4718836eb788976e5e5b0f54865aff639ed4390
[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/ZConfig.h"
23 #include "zypp/repo/RepoMirrorList.h"
24 #include "zypp/ExternalProgram.h"
25 #include "zypp/media/MediaAccess.h"
26
27 #include "zypp/base/IOStream.h"
28 #include "zypp/base/InputStream.h"
29 #include "zypp/parser/xml/Reader.h"
30
31 using std::endl;
32 using zypp::xml::escape;
33
34 ///////////////////////////////////////////////////////////////////
35 namespace zypp
36 { /////////////////////////////////////////////////////////////////
37
38   ///////////////////////////////////////////////////////////////////
39   //
40   //    CLASS NAME : RepoInfo::Impl
41   //
42   /** RepoInfo implementation. */
43   struct RepoInfo::Impl
44   {
45     Impl()
46       : _rawGpgCheck( indeterminate )
47       , _rawRepoGpgCheck( indeterminate )
48       , _rawPkgGpgCheck( indeterminate )
49       , _validRepoSignature( indeterminate )
50       , keeppackages(indeterminate)
51       , type(repo::RepoType::NONE_e)
52       , emptybaseurls(false)
53     {}
54
55     ~Impl()
56     {}
57
58   public:
59     static const unsigned defaultPriority = 99;
60
61     void setProbedType( const repo::RepoType & t ) const
62     {
63       if ( type == repo::RepoType::NONE
64            && t != repo::RepoType::NONE )
65       {
66         // lazy init!
67         const_cast<Impl*>(this)->type = t;
68       }
69     }
70
71   public:
72     Pathname licenseTgz() const
73     { return metadatapath.empty() ? Pathname() : metadatapath / path / "license.tar.gz"; }
74
75     const RepoVariablesReplacedUrlList & baseUrls() const
76     {
77       const Url & mlurl( _mirrorListUrl.transformed() );        // Variables replaced!
78       if ( _baseUrls.empty() && ! mlurl.asString().empty() )
79       {
80         emptybaseurls = true;
81         DBG << "MetadataPath: " << metadatapath << endl;
82         repo::RepoMirrorList rmurls( mlurl, metadatapath );
83         _baseUrls.raw().insert( _baseUrls.raw().end(), rmurls.getUrls().begin(), rmurls.getUrls().end() );
84       }
85       return _baseUrls;
86     }
87
88     RepoVariablesReplacedUrlList & baseUrls()
89     { return _baseUrls; }
90
91     bool baseurl2dump() const
92     { return !emptybaseurls && !_baseUrls.empty(); }
93
94
95     const RepoVariablesReplacedUrlList & gpgKeyUrls() const
96     { return _gpgKeyUrls; }
97
98     RepoVariablesReplacedUrlList & gpgKeyUrls()
99     { return _gpgKeyUrls; }
100
101     void addContent( const std::string & keyword_r )
102     { _keywords.insert( keyword_r ); }
103
104     bool hasContent( const std::string & keyword_r ) const
105     {
106       if ( _keywords.empty() && ! metadatapath.empty() )
107       {
108         // HACK directly check master index file until RepoManager offers
109         // some content probing ans zypepr uses it.
110         /////////////////////////////////////////////////////////////////
111         MIL << "Empty keywords...." << metadatapath << endl;
112         Pathname master;
113         if ( PathInfo( (master=metadatapath/"/repodata/repomd.xml") ).isFile() )
114         {
115           //MIL << "GO repomd.." << endl;
116           xml::Reader reader( master );
117           while ( reader.seekToNode( 2, "content" ) )
118           {
119             _keywords.insert( reader.nodeText().asString() );
120             reader.seekToEndNode( 2, "content" );
121           }
122           _keywords.insert( "" );       // valid content in _keywords even if empty
123         }
124         else if ( PathInfo( (master=metadatapath/"/content") ).isFile() )
125         {
126           //MIL << "GO content.." << endl;
127           iostr::forEachLine( InputStream( master ),
128                             [this]( int num_r, std::string line_r )->bool
129                             {
130                               if ( str::startsWith( line_r, "REPOKEYWORDS" ) )
131                               {
132                                 std::vector<std::string> words;
133                                 if ( str::split( line_r, std::back_inserter(words) ) > 1
134                                   && words[0].length() == 12 /*"REPOKEYWORDS"*/ )
135                                 {
136                                   this->_keywords.insert( ++words.begin(), words.end() );
137                                 }
138                                 return true; // mult. occurrances are ok.
139                               }
140                               return( ! str::startsWith( line_r, "META " ) );   // no need to parse into META section.
141                             } );
142           _keywords.insert( "" );
143         }
144         /////////////////////////////////////////////////////////////////
145       }
146       return( _keywords.find( keyword_r ) != _keywords.end() );
147     }
148
149     /** Signature check result needs to be stored/retrieved from _metadatapath.
150      * Don't call them from outside validRepoSignature/setValidRepoSignature
151      */
152     //@{
153     TriBool internalValidRepoSignature() const
154     {
155       if ( ! indeterminate(_validRepoSignature) )
156         return _validRepoSignature;
157       // check metadata:
158       if ( ! metadatapath.empty() )
159       {
160         // A missing ".repo_gpgcheck" might be plaindir(no Downloader) or not yet refreshed signed repo!
161         TriBool linkval = triBoolFromPath( metadatapath / ".repo_gpgcheck" );
162         return linkval;
163       }
164       return indeterminate;
165     }
166
167     void internalSetValidRepoSignature( TriBool value_r )
168     {
169       if ( PathInfo(metadatapath).isDir() )
170       {
171         Pathname gpgcheckFile( metadatapath / ".repo_gpgcheck" );
172         if ( PathInfo(gpgcheckFile).isExist() )
173         {
174           TriBool linkval( indeterminate );
175           if ( triBoolFromPath( gpgcheckFile, linkval ) && linkval == value_r )
176             return;     // existing symlink fits value_r
177           else
178             filesystem::unlink( gpgcheckFile ); // will write a new one
179         }
180         filesystem::symlink( asString(value_r), gpgcheckFile );
181       }
182       _validRepoSignature = value_r;
183     }
184
185     /** We definitely have a symlink pointing to "indeterminate" (for repoGpgCheckIsMandatory)?
186      * I.e. user accepted the unsigned repo in Downloader. A test whether `internalValidRepoSignature`
187      * is indeterminate would include not yet checked repos, which is unwanted here.
188      */
189     bool internalUnsignedConfirmed() const
190     {
191       TriBool linkval( true );  // want to see it being switched to indeterminate
192       return triBoolFromPath( metadatapath / ".repo_gpgcheck", linkval ) && indeterminate(linkval);
193     }
194
195     bool triBoolFromPath( const Pathname & path_r, TriBool & ret_r ) const
196     {
197       static const Pathname truePath( "true" );
198       static const Pathname falsePath( "false" );
199       static const Pathname indeterminatePath( "indeterminate" );
200
201       // Quiet readlink;
202       static const ssize_t bufsiz = 63;
203       static char buf[bufsiz+1];
204       ssize_t ret = ::readlink( path_r.c_str(), buf, bufsiz );
205       buf[ret == -1 ? 0 : ret] = '\0';
206
207       Pathname linkval( buf );
208
209       bool known = true;
210       if ( linkval == truePath )
211         ret_r = true;
212       else if ( linkval == falsePath )
213         ret_r = false;
214       else if ( linkval == indeterminatePath )
215         ret_r = indeterminate;
216       else
217         known = false;
218       return known;
219     }
220
221     TriBool triBoolFromPath( const Pathname & path_r ) const
222     { TriBool ret(indeterminate); triBoolFromPath( path_r, ret ); return ret; }
223
224     //@}
225
226   private:
227     TriBool _rawGpgCheck;       ///< default gpgcheck behavior: Y/N/ZConf
228     TriBool _rawRepoGpgCheck;   ///< need to check repo sign.: Y/N/(ZConf(Y/N/gpgCheck))
229     TriBool _rawPkgGpgCheck;    ///< need to check pkg sign.: Y/N/(ZConf(Y/N/gpgCheck))
230
231   public:
232     TriBool rawGpgCheck() const                 { return _rawGpgCheck; }
233     TriBool rawRepoGpgCheck() const             { return _rawRepoGpgCheck; }
234     TriBool rawPkgGpgCheck() const              { return _rawPkgGpgCheck; }
235
236     void rawGpgCheck( TriBool val_r )           { _rawGpgCheck = val_r; }
237     void rawRepoGpgCheck( TriBool val_r )       { _rawRepoGpgCheck = val_r; }
238     void rawPkgGpgCheck( TriBool val_r )        { _rawPkgGpgCheck = val_r; }
239
240     bool cfgGpgCheck() const
241     { return indeterminate(_rawGpgCheck) ? ZConfig::instance().gpgCheck() : (bool)_rawGpgCheck; }
242     TriBool cfgRepoGpgCheck() const
243     { return indeterminate(_rawGpgCheck) && indeterminate(_rawRepoGpgCheck) ? ZConfig::instance().repoGpgCheck() : _rawRepoGpgCheck; }
244     TriBool cfgPkgGpgCheck() const
245     { return indeterminate(_rawGpgCheck) && indeterminate(_rawPkgGpgCheck) ? ZConfig::instance().pkgGpgCheck() : _rawPkgGpgCheck; }
246
247   private:
248     TriBool _validRepoSignature;///< have  signed and valid repo metadata
249   public:
250     TriBool keeppackages;
251     RepoVariablesReplacedUrl _mirrorListUrl;
252     repo::RepoType type;
253     Pathname path;
254     std::string service;
255     std::string targetDistro;
256     Pathname metadatapath;
257     Pathname packagespath;
258     DefaultIntegral<unsigned,defaultPriority> priority;
259     mutable bool emptybaseurls;
260     repo::RepoVariablesUrlReplacer replacer;
261
262   private:
263     mutable RepoVariablesReplacedUrlList _baseUrls;
264     mutable std::set<std::string> _keywords;
265
266     RepoVariablesReplacedUrlList _gpgKeyUrls;
267
268     friend Impl * rwcowClone<Impl>( const Impl * rhs );
269     /** clone for RWCOW_pointer */
270     Impl * clone() const
271     { return new Impl( *this ); }
272   };
273   ///////////////////////////////////////////////////////////////////
274
275   /** \relates RepoInfo::Impl Stream output */
276   inline std::ostream & operator<<( std::ostream & str, const RepoInfo::Impl & obj )
277   {
278     return str << "RepoInfo::Impl";
279   }
280
281   ///////////////////////////////////////////////////////////////////
282   //
283   //    CLASS NAME : RepoInfo
284   //
285   ///////////////////////////////////////////////////////////////////
286
287   const RepoInfo RepoInfo::noRepo;
288
289   RepoInfo::RepoInfo()
290   : _pimpl( new Impl() )
291   {}
292
293   RepoInfo::~RepoInfo()
294   {}
295
296   unsigned RepoInfo::priority() const
297   { return _pimpl->priority; }
298
299   unsigned RepoInfo::defaultPriority()
300   { return Impl::defaultPriority; }
301
302   void RepoInfo::setPriority( unsigned newval_r )
303   { _pimpl->priority = newval_r ? newval_r : Impl::defaultPriority; }
304
305
306   bool RepoInfo::gpgCheck() const
307   { return _pimpl->cfgGpgCheck(); }
308
309   void RepoInfo::setGpgCheck( TriBool value_r )
310   { _pimpl->rawGpgCheck( value_r ); }
311
312   void RepoInfo::setGpgCheck( bool value_r ) // deprecated legacy and for squid
313   { setGpgCheck( TriBool(value_r) ); }
314
315
316   bool RepoInfo::repoGpgCheck() const
317   { return gpgCheck() || _pimpl->cfgRepoGpgCheck(); }
318
319   bool RepoInfo::repoGpgCheckIsMandatory() const
320   {
321     bool ret = ( gpgCheck() && indeterminate(_pimpl->cfgRepoGpgCheck()) ) || _pimpl->cfgRepoGpgCheck();
322     if ( ret && _pimpl->internalUnsignedConfirmed() )   // relax if unsigned repo was confirmed in the past
323       ret = false;
324     return ret;
325   }
326
327   void RepoInfo::setRepoGpgCheck( TriBool value_r )
328   { _pimpl->rawRepoGpgCheck( value_r ); }
329
330
331   bool RepoInfo::pkgGpgCheck() const
332   { return _pimpl->cfgPkgGpgCheck() || ( gpgCheck() && !bool(validRepoSignature())/*enforced*/ ) ; }
333
334   bool RepoInfo::pkgGpgCheckIsMandatory() const
335   { return _pimpl->cfgPkgGpgCheck() || ( gpgCheck() && indeterminate(_pimpl->cfgPkgGpgCheck()) && !bool(validRepoSignature())/*enforced*/ ); }
336
337   void RepoInfo::setPkgGpgCheck( TriBool value_r )
338   { _pimpl->rawPkgGpgCheck( value_r ); }
339
340
341   void RepoInfo::getRawGpgChecks( TriBool & g_r, TriBool & r_r, TriBool & p_r ) const
342   {
343     g_r = _pimpl->rawGpgCheck();
344     r_r = _pimpl->rawRepoGpgCheck();
345     p_r = _pimpl->rawPkgGpgCheck();
346   }
347
348
349   TriBool RepoInfo::validRepoSignature() const
350   {
351     TriBool ret( _pimpl->internalValidRepoSignature() );
352     if ( ret && !repoGpgCheck() ) ret = false;  // invalidate any old signature if repoGpgCheck is off
353     return ret;
354   }
355
356   void RepoInfo::setValidRepoSignature( TriBool value_r )
357   { _pimpl->internalSetValidRepoSignature( value_r ); }
358
359   ///////////////////////////////////////////////////////////////////
360   namespace
361   {
362     inline bool changeGpgCheckTo( TriBool & lhs, TriBool rhs )
363     { if ( ! sameTriboolState( lhs, rhs ) ) { lhs = rhs; return true; } return false; }
364
365     inline bool changeGpgCheckTo( TriBool ogpg[3], TriBool g, TriBool r, TriBool p )
366     {
367       bool changed = false;
368       if ( changeGpgCheckTo( ogpg[0], g ) ) changed = true;
369       if ( changeGpgCheckTo( ogpg[1], r ) ) changed = true;
370       if ( changeGpgCheckTo( ogpg[2], p ) ) changed = true;
371       return changed;
372     }
373   } // namespace
374   ///////////////////////////////////////////////////////////////////
375   bool RepoInfo::setGpgCheck( GpgCheck mode_r )
376   {
377     TriBool ogpg[3];    // Gpg RepoGpg PkgGpg
378     getRawGpgChecks( ogpg[0], ogpg[1], ogpg[2] );
379
380     bool changed = false;
381     switch ( mode_r.asEnum() )
382     {
383       case GpgCheck::On:
384         changed = changeGpgCheckTo( ogpg, true,          indeterminate, indeterminate );
385         break;
386       case GpgCheck::Strict:
387         changed = changeGpgCheckTo( ogpg, true,          true,          true          );
388         break;
389       case GpgCheck::AllowUnsigned:
390         changed = changeGpgCheckTo( ogpg, true,          false,         false         );
391         break;
392       case GpgCheck::AllowUnsignedRepo:
393         changed = changeGpgCheckTo( ogpg, true,          false,         indeterminate );
394         break;
395       case GpgCheck::AllowUnsignedPackage:
396         changed = changeGpgCheckTo( ogpg, true,          indeterminate, false         );
397         break;
398       case GpgCheck::Default:
399         changed = changeGpgCheckTo( ogpg, indeterminate, indeterminate, indeterminate );
400         break;
401       case GpgCheck::Off:
402         changed = changeGpgCheckTo( ogpg, false,         indeterminate, indeterminate );
403         break;
404       case GpgCheck::indeterminate:     // no change
405         break;
406     }
407
408     if ( changed )
409     {
410       setGpgCheck    ( ogpg[0] );
411       setRepoGpgCheck( ogpg[1] );
412       setPkgGpgCheck ( ogpg[2] );
413     }
414     return changed;
415   }
416
417   void RepoInfo::setMirrorListUrl( const Url & url_r )  // Raw
418   { _pimpl->_mirrorListUrl.raw() = url_r; }
419
420   void RepoInfo::setGpgKeyUrls( url_set urls )
421   { _pimpl->gpgKeyUrls().raw().swap( urls ); }
422
423   void RepoInfo::setGpgKeyUrl( const Url & url_r )
424   {
425     _pimpl->gpgKeyUrls().raw().clear();
426     _pimpl->gpgKeyUrls().raw().push_back( url_r );
427   }
428
429   void RepoInfo::addBaseUrl( const Url & url_r )
430   {
431     for ( const auto & url : _pimpl->baseUrls().raw() ) // Raw unique!
432       if ( url == url_r )
433         return;
434     _pimpl->baseUrls().raw().push_back( url_r );
435   }
436
437   void RepoInfo::setBaseUrl( const Url & url_r )
438   {
439     _pimpl->baseUrls().raw().clear();
440     _pimpl->baseUrls().raw().push_back( url_r );
441   }
442
443   void RepoInfo::setBaseUrls( url_set urls )
444   { _pimpl->baseUrls().raw().swap( urls ); }
445
446   void RepoInfo::setPath( const Pathname &path )
447   { _pimpl->path = path; }
448
449   void RepoInfo::setType( const repo::RepoType &t )
450   { _pimpl->type = t; }
451
452   void RepoInfo::setProbedType( const repo::RepoType &t ) const
453   { _pimpl->setProbedType( t ); }
454
455
456   void RepoInfo::setMetadataPath( const Pathname &path )
457   { _pimpl->metadatapath = path; }
458
459   void RepoInfo::setPackagesPath( const Pathname &path )
460   { _pimpl->packagespath = path; }
461
462   void RepoInfo::setKeepPackages( bool keep )
463   { _pimpl->keeppackages = keep; }
464
465   void RepoInfo::setService( const std::string& name )
466   { _pimpl->service = name; }
467
468   void RepoInfo::setTargetDistribution( const std::string & targetDistribution )
469   { _pimpl->targetDistro = targetDistribution; }
470
471   bool RepoInfo::keepPackages() const
472   { return indeterminate(_pimpl->keeppackages) ? false : (bool)_pimpl->keeppackages; }
473
474   Pathname RepoInfo::metadataPath() const
475   { return _pimpl->metadatapath; }
476
477   Pathname RepoInfo::packagesPath() const
478   { return _pimpl->packagespath; }
479
480   repo::RepoType RepoInfo::type() const
481   { return _pimpl->type; }
482
483   Url RepoInfo::mirrorListUrl() const                   // Variables replaced!
484   { return _pimpl->_mirrorListUrl.transformed(); }
485
486   Url RepoInfo::rawMirrorListUrl() const                // Raw
487   { return _pimpl->_mirrorListUrl.raw(); }
488
489   bool RepoInfo::gpgKeyUrlsEmpty() const
490   { return _pimpl->gpgKeyUrls().empty(); }
491
492   RepoInfo::urls_size_type RepoInfo::gpgKeyUrlsSize() const
493   { return _pimpl->gpgKeyUrls().size(); }
494
495   RepoInfo::url_set RepoInfo::gpgKeyUrls() const        // Variables replaced!
496   { return _pimpl->gpgKeyUrls().transformed(); }
497
498   RepoInfo::url_set RepoInfo::rawGpgKeyUrls() const     // Raw
499   { return _pimpl->gpgKeyUrls().raw(); }
500
501   Url RepoInfo::gpgKeyUrl() const                       // Variables replaced!
502   { return( _pimpl->gpgKeyUrls().empty() ? Url() : *_pimpl->gpgKeyUrls().transformedBegin() ); }
503
504   Url RepoInfo::rawGpgKeyUrl() const                    // Raw
505   { return( _pimpl->gpgKeyUrls().empty() ? Url() : *_pimpl->gpgKeyUrls().rawBegin() ) ; }
506
507   RepoInfo::url_set RepoInfo::baseUrls() const          // Variables replaced!
508   { return _pimpl->baseUrls().transformed(); }
509
510   RepoInfo::url_set RepoInfo::rawBaseUrls() const       // Raw
511   { return _pimpl->baseUrls().raw(); }
512
513   Pathname RepoInfo::path() const
514   { return _pimpl->path; }
515
516   std::string RepoInfo::service() const
517   { return _pimpl->service; }
518
519   std::string RepoInfo::targetDistribution() const
520   { return _pimpl->targetDistro; }
521
522   Url RepoInfo::rawUrl() const
523   { return( _pimpl->baseUrls().empty() ? Url() : *_pimpl->baseUrls().rawBegin() ); }
524
525   RepoInfo::urls_const_iterator RepoInfo::baseUrlsBegin() const
526   { return _pimpl->baseUrls().transformedBegin(); }
527
528   RepoInfo::urls_const_iterator RepoInfo::baseUrlsEnd() const
529   { return _pimpl->baseUrls().transformedEnd(); }
530
531   RepoInfo::urls_size_type RepoInfo::baseUrlsSize() const
532   { return _pimpl->baseUrls().size(); }
533
534   bool RepoInfo::baseUrlsEmpty() const
535   { return _pimpl->baseUrls().empty(); }
536
537   bool RepoInfo::baseUrlSet() const
538   { return _pimpl->baseurl2dump(); }
539
540
541   void RepoInfo::addContent( const std::string & keyword_r )
542   { _pimpl->addContent( keyword_r ); }
543
544   bool RepoInfo::hasContent( const std::string & keyword_r ) const
545   { return _pimpl->hasContent( keyword_r ); }
546
547   ///////////////////////////////////////////////////////////////////
548
549   bool RepoInfo::hasLicense() const
550   {
551     Pathname licenseTgz( _pimpl->licenseTgz() );
552     return ! licenseTgz.empty() &&  PathInfo(licenseTgz).isFile();
553   }
554
555   bool RepoInfo::needToAcceptLicense() const
556   {
557     static const std::string noAcceptanceFile = "no-acceptance-needed\n";
558     bool accept = true;
559
560     Pathname licenseTgz( _pimpl->licenseTgz() );
561     if ( licenseTgz.empty() || ! PathInfo( licenseTgz ).isFile() )
562       return false;     // no licenses at all
563
564     ExternalProgram::Arguments cmd;
565     cmd.push_back( "tar" );
566     cmd.push_back( "-t" );
567     cmd.push_back( "-z" );
568     cmd.push_back( "-f" );
569     cmd.push_back( licenseTgz.asString() );
570
571     ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
572     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
573     {
574       if ( output == noAcceptanceFile )
575       {
576         accept = false;
577       }
578     }
579     prog.close();
580     MIL << "License for " << name() << " has to be accepted: " << (accept?"true":"false" ) << endl;
581     return accept;
582   }
583
584   std::string RepoInfo::getLicense( const Locale & lang_r )
585   { return const_cast<const RepoInfo *>(this)->getLicense( lang_r );  }
586
587   std::string RepoInfo::getLicense( const Locale & lang_r ) const
588   {
589     LocaleSet avlocales( getLicenseLocales() );
590     if ( avlocales.empty() )
591       return std::string();
592
593     Locale getLang( Locale::bestMatch( avlocales, lang_r ) );
594     if ( getLang == Locale::noCode
595          && avlocales.find( Locale::noCode ) == avlocales.end() )
596     {
597       WAR << "License.tar.gz contains no fallback text! " << *this << endl;
598       // Using the fist locale instead of returning no text at all.
599       // So the user might recognize that there is a license, even if he
600       // can't read it.
601       getLang = *avlocales.begin();
602     }
603
604     // now extract the license file.
605     static const std::string licenseFileFallback( "license.txt" );
606     std::string licenseFile( getLang == Locale::noCode
607                              ? licenseFileFallback
608                              : str::form( "license.%s.txt", getLang.code().c_str() ) );
609
610     ExternalProgram::Arguments cmd;
611     cmd.push_back( "tar" );
612     cmd.push_back( "-x" );
613     cmd.push_back( "-z" );
614     cmd.push_back( "-O" );
615     cmd.push_back( "-f" );
616     cmd.push_back( _pimpl->licenseTgz().asString() ); // if it not exists, avlocales was empty.
617     cmd.push_back( licenseFile );
618
619     std::string ret;
620     ExternalProgram prog( cmd, ExternalProgram::Discard_Stderr );
621     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
622     {
623       ret += output;
624     }
625     prog.close();
626     return ret;
627   }
628
629   LocaleSet RepoInfo::getLicenseLocales() const
630   {
631     Pathname licenseTgz( _pimpl->licenseTgz() );
632     if ( licenseTgz.empty() || ! PathInfo( licenseTgz ).isFile() )
633       return LocaleSet();
634
635     ExternalProgram::Arguments cmd;
636     cmd.push_back( "tar" );
637     cmd.push_back( "-t" );
638     cmd.push_back( "-z" );
639     cmd.push_back( "-f" );
640     cmd.push_back( licenseTgz.asString() );
641
642     LocaleSet ret;
643     ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
644     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
645     {
646       static const C_Str license( "license." );
647       static const C_Str dotTxt( ".txt\n" );
648       if ( str::hasPrefix( output, license ) && str::hasSuffix( output, dotTxt ) )
649       {
650         if ( output.size() <= license.size() +  dotTxt.size() ) // license.txt
651           ret.insert( Locale() );
652         else
653           ret.insert( Locale( std::string( output.c_str()+license.size(), output.size()- license.size() - dotTxt.size() ) ) );
654       }
655     }
656     prog.close();
657     return ret;
658   }
659
660   ///////////////////////////////////////////////////////////////////
661
662   std::ostream & RepoInfo::dumpOn( std::ostream & str ) const
663   {
664     RepoInfoBase::dumpOn(str);
665     if ( _pimpl->baseurl2dump() )
666     {
667       for ( const auto & url : _pimpl->baseUrls().raw() )
668       {
669         str << "- url         : " << url << std::endl;
670       }
671     }
672
673     // print if non empty value
674     auto strif( [&] ( const std::string & tag_r, const std::string & value_r ) {
675       if ( ! value_r.empty() )
676         str << tag_r << value_r << std::endl;
677     });
678
679     strif( "- mirrorlist  : ", rawMirrorListUrl().asString() );
680     strif( "- path        : ", path().asString() );
681     str << "- type        : " << type() << std::endl;
682     str << "- priority    : " << priority() << std::endl;
683
684     // Yes No Default(Y) Default(N)
685 #define OUTS(T,B) ( indeterminate(T) ? (std::string("D(")+(B?"Y":"N")+")") : ((bool)T?"Y":"N") )
686     str << "- gpgcheck    : " << OUTS(_pimpl->rawGpgCheck(),gpgCheck())
687                               << " repo" << OUTS(_pimpl->rawRepoGpgCheck(),repoGpgCheck()) << (repoGpgCheckIsMandatory() ? "* ": " " )
688                               << "sig" << asString( validRepoSignature(), "?", "Y", "N" )
689                               << " pkg" << OUTS(_pimpl->rawPkgGpgCheck(),pkgGpgCheck()) << (pkgGpgCheckIsMandatory() ? "* ": " " )
690                               << std::endl;
691 #undef OUTS
692
693     for ( const auto & url : _pimpl->gpgKeyUrls().raw() )
694     {
695       str << "- gpgkey      : " << url << std::endl;
696     }
697
698     if ( ! indeterminate(_pimpl->keeppackages) )
699       str << "- keeppackages: " << keepPackages() << std::endl;
700
701     strif( "- service     : ", service() );
702     strif( "- targetdistro: ", targetDistribution() );
703     strif( "- metadataPath: ", metadataPath().asString() );
704     strif( "- packagesPath: ", packagesPath().asString() );
705
706     return str;
707   }
708
709   std::ostream & RepoInfo::dumpAsIniOn( std::ostream & str ) const
710   {
711     RepoInfoBase::dumpAsIniOn(str);
712
713     if ( _pimpl->baseurl2dump() )
714     {
715       str << "baseurl=";
716       std::string indent;
717       for ( const auto & url : _pimpl->baseUrls().raw() )
718       {
719         str << indent << url << endl;
720         if ( indent.empty() ) indent = "        ";      // "baseurl="
721       }
722     }
723
724     if ( ! _pimpl->path.empty() )
725       str << "path="<< path() << endl;
726
727     if ( ! (rawMirrorListUrl().asString().empty()) )
728       str << "mirrorlist=" << rawMirrorListUrl() << endl;
729
730     str << "type=" << type().asString() << endl;
731
732     if ( priority() != defaultPriority() )
733       str << "priority=" << priority() << endl;
734
735     if ( ! indeterminate(_pimpl->rawGpgCheck()) )
736       str << "gpgcheck=" << (_pimpl->rawGpgCheck() ? "1" : "0") << endl;
737
738     if ( ! indeterminate(_pimpl->rawRepoGpgCheck()) )
739       str << "repo_gpgcheck=" << (_pimpl->rawRepoGpgCheck() ? "1" : "0") << endl;
740
741     if ( ! indeterminate(_pimpl->rawPkgGpgCheck()) )
742       str << "pkg_gpgcheck=" << (_pimpl->rawPkgGpgCheck() ? "1" : "0") << endl;
743
744     {
745       std::string indent( "gpgkey=");
746       for ( const auto & url : _pimpl->gpgKeyUrls().raw() )
747       {
748         str << indent << url << endl;
749         if ( indent[0] != ' ' )
750           indent = "       ";
751       }
752     }
753
754     if (!indeterminate(_pimpl->keeppackages))
755       str << "keeppackages=" << keepPackages() << endl;
756
757     if( ! service().empty() )
758       str << "service=" << service() << endl;
759
760     return str;
761   }
762
763   std::ostream & RepoInfo::dumpAsXmlOn( std::ostream & str, const std::string & content ) const
764   {
765     std::string tmpstr;
766     str
767       << "<repo"
768       << " alias=\"" << escape(alias()) << "\""
769       << " name=\"" << escape(name()) << "\"";
770     if (type() != repo::RepoType::NONE)
771       str << " type=\"" << type().asString() << "\"";
772     str
773       << " priority=\"" << priority() << "\""
774       << " enabled=\"" << enabled() << "\""
775       << " autorefresh=\"" << autorefresh() << "\""
776       << " gpgcheck=\"" << gpgCheck() << "\""
777       << " repo_gpgcheck=\"" << repoGpgCheck() << "\""
778       << " pkg_gpgcheck=\"" << pkgGpgCheck() << "\"";
779     if (!(tmpstr = gpgKeyUrl().asString()).empty())
780       str << " gpgkey=\"" << escape(tmpstr) << "\"";
781     if (!(tmpstr = mirrorListUrl().asString()).empty())
782       str << " mirrorlist=\"" << escape(tmpstr) << "\"";
783     str << ">" << endl;
784
785     if ( _pimpl->baseurl2dump() )
786     {
787       for_( it, baseUrlsBegin(), baseUrlsEnd() )        // !transform iterator replaces variables
788         str << "<url>" << escape((*it).asString()) << "</url>" << endl;
789     }
790
791     str << "</repo>" << endl;
792     return str;
793   }
794
795
796   std::ostream & operator<<( std::ostream & str, const RepoInfo & obj )
797   {
798     return obj.dumpOn(str);
799   }
800
801   std::ostream & operator<<( std::ostream & str, const RepoInfo::GpgCheck & obj )
802   {
803     switch ( obj.asEnum() )
804     {
805 #define OUTS( V ) case RepoInfo::V: return str << #V; break
806       OUTS( GpgCheck::On );
807       OUTS( GpgCheck::Strict );
808       OUTS( GpgCheck::AllowUnsigned );
809       OUTS( GpgCheck::AllowUnsignedRepo );
810       OUTS( GpgCheck::AllowUnsignedPackage );
811       OUTS( GpgCheck::Default );
812       OUTS( GpgCheck::Off );
813       OUTS( GpgCheck::indeterminate );
814 #undef OUTS
815     }
816     return str << "GpgCheck::UNKNOWN";
817   }
818
819   /////////////////////////////////////////////////////////////////
820 } // namespace zypp
821 ///////////////////////////////////////////////////////////////////