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