Imported Upstream version 15.3.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/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
298   TriBool RepoInfo::validRepoSignature() const
299   {
300     TriBool ret = _pimpl->internalValidRepoSignature();
301     // keep indeterminate(=unsigned) but invalidate any signature if !repoGpgCheck
302     if ( !indeterminate(ret) && !repoGpgCheck() )
303       ret = false;
304     return ret;
305   }
306
307   void RepoInfo::setValidRepoSignature( TriBool value_r )
308   { _pimpl->internalSetValidRepoSignature( value_r ); }
309
310
311   void RepoInfo::setMirrorListUrl( const Url & url_r )  // Raw
312   { _pimpl->_mirrorListUrl.raw() = url_r; _pimpl->_mirrorListForceMetalink = false; }
313
314   void  RepoInfo::setMetalinkUrl( const Url & url_r )   // Raw
315   { _pimpl->_mirrorListUrl.raw() = url_r; _pimpl->_mirrorListForceMetalink = true; }
316
317   void RepoInfo::setGpgKeyUrl( const Url & url_r )
318   { _pimpl->_gpgKeyUrl.raw() = url_r; }
319
320   void RepoInfo::addBaseUrl( const Url & url_r )
321   {
322     for ( const auto & url : _pimpl->baseUrls().raw() ) // Raw unique!
323       if ( url == url_r )
324         return;
325     _pimpl->baseUrls().raw().push_back( url_r );
326   }
327
328   void RepoInfo::setBaseUrl( const Url & url_r )
329   {
330     _pimpl->baseUrls().raw().clear();
331     _pimpl->baseUrls().raw().push_back( url_r );
332   }
333
334   void RepoInfo::setPath( const Pathname &path )
335   { _pimpl->path = path; }
336
337   void RepoInfo::setType( const repo::RepoType &t )
338   { _pimpl->type = t; }
339
340   void RepoInfo::setProbedType( const repo::RepoType &t ) const
341   { _pimpl->setProbedType( t ); }
342
343
344   void RepoInfo::setMetadataPath( const Pathname &path )
345   { _pimpl->metadatapath = path; }
346
347   void RepoInfo::setPackagesPath( const Pathname &path )
348   { _pimpl->packagespath = path; }
349
350   void RepoInfo::setKeepPackages( bool keep )
351   { _pimpl->keeppackages = keep; }
352
353   void RepoInfo::setService( const std::string& name )
354   { _pimpl->service = name; }
355
356   void RepoInfo::setTargetDistribution( const std::string & targetDistribution )
357   { _pimpl->targetDistro = targetDistribution; }
358
359   bool RepoInfo::keepPackages() const
360   { return indeterminate(_pimpl->keeppackages) ? false : (bool)_pimpl->keeppackages; }
361
362   Pathname RepoInfo::metadataPath() const
363   { return _pimpl->metadatapath; }
364
365   Pathname RepoInfo::packagesPath() const
366   { return _pimpl->packagespath; }
367
368   repo::RepoType RepoInfo::type() const
369   { return _pimpl->type; }
370
371   Url RepoInfo::mirrorListUrl() const                   // Variables replaced!
372   { return _pimpl->_mirrorListUrl.transformed(); }
373
374   Url RepoInfo::rawMirrorListUrl() const                // Raw
375   { return _pimpl->_mirrorListUrl.raw(); }
376
377   Url RepoInfo::gpgKeyUrl() const                       // Variables replaced!
378   { return _pimpl->_gpgKeyUrl.transformed(); }
379
380   Url RepoInfo::rawGpgKeyUrl() const                    // Raw
381   {  return _pimpl->_gpgKeyUrl.raw(); }
382
383   RepoInfo::url_set RepoInfo::baseUrls() const          // Variables replaced!
384   { return _pimpl->baseUrls().transformed(); }
385
386   RepoInfo::url_set RepoInfo::rawBaseUrls() const       // Raw
387   { return _pimpl->baseUrls().raw(); }
388
389   Pathname RepoInfo::path() const
390   { return _pimpl->path; }
391
392   std::string RepoInfo::service() const
393   { return _pimpl->service; }
394
395   std::string RepoInfo::targetDistribution() const
396   { return _pimpl->targetDistro; }
397
398   Url RepoInfo::rawUrl() const
399   { return( _pimpl->baseUrls().empty() ? Url() : *_pimpl->baseUrls().rawBegin() ); }
400
401   RepoInfo::urls_const_iterator RepoInfo::baseUrlsBegin() const
402   { return _pimpl->baseUrls().transformedBegin(); }
403
404   RepoInfo::urls_const_iterator RepoInfo::baseUrlsEnd() const
405   { return _pimpl->baseUrls().transformedEnd(); }
406
407   RepoInfo::urls_size_type RepoInfo::baseUrlsSize() const
408   { return _pimpl->baseUrls().size(); }
409
410   bool RepoInfo::baseUrlsEmpty() const
411   { return _pimpl->baseUrls().empty(); }
412
413   bool RepoInfo::baseUrlSet() const
414   { return _pimpl->baseurl2dump(); }
415
416
417   void RepoInfo::addContent( const std::string & keyword_r )
418   { _pimpl->addContent( keyword_r ); }
419
420   bool RepoInfo::hasContent( const std::string & keyword_r ) const
421   { return _pimpl->hasContent( keyword_r ); }
422
423   ///////////////////////////////////////////////////////////////////
424
425   bool RepoInfo::hasLicense() const
426   {
427     Pathname licenseTgz( _pimpl->licenseTgz() );
428     return ! licenseTgz.empty() &&  PathInfo(licenseTgz).isFile();
429   }
430
431   bool RepoInfo::needToAcceptLicense() const
432   {
433     static const std::string noAcceptanceFile = "no-acceptance-needed\n";
434     bool accept = true;
435
436     Pathname licenseTgz( _pimpl->licenseTgz() );
437     if ( licenseTgz.empty() || ! PathInfo( licenseTgz ).isFile() )
438       return false;     // no licenses at all
439
440     ExternalProgram::Arguments cmd;
441     cmd.push_back( "tar" );
442     cmd.push_back( "-t" );
443     cmd.push_back( "-z" );
444     cmd.push_back( "-f" );
445     cmd.push_back( licenseTgz.asString() );
446
447     ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
448     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
449     {
450       if ( output == noAcceptanceFile )
451       {
452         accept = false;
453       }
454     }
455     MIL << "License for " << name() << " has to be accepted: " << (accept?"true":"false" ) << endl;
456     return accept;
457   }
458
459   std::string RepoInfo::getLicense( const Locale & lang_r )
460   { return const_cast<const RepoInfo *>(this)->getLicense( lang_r );  }
461
462   std::string RepoInfo::getLicense( const Locale & lang_r ) const
463   {
464     LocaleSet avlocales( getLicenseLocales() );
465     if ( avlocales.empty() )
466       return std::string();
467
468     Locale getLang( Locale::bestMatch( avlocales, lang_r ) );
469     if ( getLang == Locale::noCode
470          && avlocales.find( Locale::noCode ) == avlocales.end() )
471     {
472       WAR << "License.tar.gz contains no fallback text! " << *this << endl;
473       // Using the fist locale instead of returning no text at all.
474       // So the user might recognize that there is a license, even if he
475       // can't read it.
476       getLang = *avlocales.begin();
477     }
478
479     // now extract the license file.
480     static const std::string licenseFileFallback( "license.txt" );
481     std::string licenseFile( getLang == Locale::noCode
482                              ? licenseFileFallback
483                              : str::form( "license.%s.txt", getLang.code().c_str() ) );
484
485     ExternalProgram::Arguments cmd;
486     cmd.push_back( "tar" );
487     cmd.push_back( "-x" );
488     cmd.push_back( "-z" );
489     cmd.push_back( "-O" );
490     cmd.push_back( "-f" );
491     cmd.push_back( _pimpl->licenseTgz().asString() ); // if it not exists, avlocales was empty.
492     cmd.push_back( licenseFile );
493
494     std::string ret;
495     ExternalProgram prog( cmd, ExternalProgram::Discard_Stderr );
496     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
497     {
498       ret += output;
499     }
500     prog.close();
501     return ret;
502   }
503
504   LocaleSet RepoInfo::getLicenseLocales() const
505   {
506     Pathname licenseTgz( _pimpl->licenseTgz() );
507     if ( licenseTgz.empty() || ! PathInfo( licenseTgz ).isFile() )
508       return LocaleSet();
509
510     ExternalProgram::Arguments cmd;
511     cmd.push_back( "tar" );
512     cmd.push_back( "-t" );
513     cmd.push_back( "-z" );
514     cmd.push_back( "-f" );
515     cmd.push_back( licenseTgz.asString() );
516
517     LocaleSet ret;
518     ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
519     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
520     {
521       static const C_Str license( "license." );
522       static const C_Str dotTxt( ".txt\n" );
523       if ( str::hasPrefix( output, license ) && str::hasSuffix( output, dotTxt ) )
524       {
525         if ( output.size() <= license.size() +  dotTxt.size() ) // license.txt
526           ret.insert( Locale() );
527         else
528           ret.insert( Locale( std::string( output.c_str()+license.size(), output.size()- license.size() - dotTxt.size() ) ) );
529       }
530     }
531     prog.close();
532     return ret;
533   }
534
535   ///////////////////////////////////////////////////////////////////
536
537   std::ostream & RepoInfo::dumpOn( std::ostream & str ) const
538   {
539     RepoInfoBase::dumpOn(str);
540     if ( _pimpl->baseurl2dump() )
541     {
542       for ( const auto & url : _pimpl->baseUrls().raw() )
543       {
544         str << "- url         : " << url << std::endl;
545       }
546     }
547
548     // print if non empty value
549     auto strif( [&] ( const std::string & tag_r, const std::string & value_r ) {
550       if ( ! value_r.empty() )
551         str << tag_r << value_r << std::endl;
552     });
553
554     strif( (_pimpl->_mirrorListForceMetalink ? "- metalink    : " : "- mirrorlist  : "), rawMirrorListUrl().asString() );
555     strif( "- path        : ", path().asString() );
556     str << "- type        : " << type() << std::endl;
557     str << "- priority    : " << priority() << std::endl;
558
559     // Yes No Default(Y) Default(N)
560 #define OUTS(T,B) ( indeterminate(T) ? (std::string("D(")+(B?"Y":"N")+")") : ((bool)T?"Y":"N") )
561     str << "- gpgcheck    : " << OUTS(_pimpl->_gpgCheck,gpgCheck())
562                               << " repo" << OUTS(_pimpl->_repoGpgCheck,repoGpgCheck())
563                               << " sig" << asString( validRepoSignature(), "?", "Y", "N" )
564                               << " pkg" << OUTS(_pimpl->_pkgGpgCheck,pkgGpgCheck())
565                               << std::endl;
566 #undef OUTS
567
568     strif( "- gpgkey      : ", rawGpgKeyUrl().asString() );
569
570     if ( ! indeterminate(_pimpl->keeppackages) )
571       str << "- keeppackages: " << keepPackages() << std::endl;
572
573     strif( "- service     : ", service() );
574     strif( "- targetdistro: ", targetDistribution() );
575     strif( "- metadataPath: ", metadataPath().asString() );
576     strif( "- packagesPath: ", packagesPath().asString() );
577
578     return str;
579   }
580
581   std::ostream & RepoInfo::dumpAsIniOn( std::ostream & str ) const
582   {
583     RepoInfoBase::dumpAsIniOn(str);
584
585     if ( _pimpl->baseurl2dump() )
586     {
587       str << "baseurl=";
588       std::string indent;
589       for ( const auto & url : _pimpl->baseUrls().raw() )
590       {
591         str << indent << url << endl;
592         if ( indent.empty() ) indent = "        ";      // "baseurl="
593       }
594     }
595
596     if ( ! _pimpl->path.empty() )
597       str << "path="<< path() << endl;
598
599     if ( ! (rawMirrorListUrl().asString().empty()) )
600       str << (_pimpl->_mirrorListForceMetalink ? "metalink=" : "mirrorlist=") << rawMirrorListUrl() << endl;
601
602     str << "type=" << type().asString() << endl;
603
604     if ( priority() != defaultPriority() )
605       str << "priority=" << priority() << endl;
606
607     if ( ! indeterminate(_pimpl->_gpgCheck) )
608       str << "gpgcheck=" << (_pimpl->_gpgCheck ? "1" : "0") << endl;
609
610     if ( ! indeterminate(_pimpl->_repoGpgCheck) )
611       str << "repo_gpgcheck=" << (_pimpl->_repoGpgCheck ? "1" : "0") << endl;
612
613     if ( ! indeterminate(_pimpl->_pkgGpgCheck) )
614       str << "pkg_gpgcheck=" << (_pimpl->_pkgGpgCheck ? "1" : "0") << endl;
615
616     if ( ! (rawGpgKeyUrl().asString().empty()) )
617       str << "gpgkey=" << rawGpgKeyUrl() << endl;
618
619     if (!indeterminate(_pimpl->keeppackages))
620       str << "keeppackages=" << keepPackages() << endl;
621
622     if( ! service().empty() )
623       str << "service=" << service() << endl;
624
625     return str;
626   }
627
628   std::ostream & RepoInfo::dumpAsXmlOn( std::ostream & str, const std::string & content ) const
629   {
630     std::string tmpstr;
631     str
632       << "<repo"
633       << " alias=\"" << escape(alias()) << "\""
634       << " name=\"" << escape(name()) << "\"";
635     if (type() != repo::RepoType::NONE)
636       str << " type=\"" << type().asString() << "\"";
637     str
638       << " priority=\"" << priority() << "\""
639       << " enabled=\"" << enabled() << "\""
640       << " autorefresh=\"" << autorefresh() << "\""
641       << " gpgcheck=\"" << gpgCheck() << "\""
642       << " repo_gpgcheck=\"" << repoGpgCheck() << "\""
643       << " pkg_gpgcheck=\"" << pkgGpgCheck() << "\"";
644     if (!(tmpstr = gpgKeyUrl().asString()).empty())
645       str << " gpgkey=\"" << escape(tmpstr) << "\"";
646     if (!(tmpstr = mirrorListUrl().asString()).empty())
647       str << (_pimpl->_mirrorListForceMetalink ? " metalink=\"" : " mirrorlist=\"") << escape(tmpstr) << "\"";
648     str << ">" << endl;
649
650     if ( _pimpl->baseurl2dump() )
651     {
652       for_( it, baseUrlsBegin(), baseUrlsEnd() )        // !transform iterator replaces variables
653         str << "<url>" << escape((*it).asString()) << "</url>" << endl;
654     }
655
656     str << "</repo>" << endl;
657     return str;
658   }
659
660
661   std::ostream & operator<<( std::ostream & str, const RepoInfo & obj )
662   {
663     return obj.dumpOn(str);
664   }
665
666
667   /////////////////////////////////////////////////////////////////
668 } // namespace zypp
669 ///////////////////////////////////////////////////////////////////