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