Imported Upstream version 15.10.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 && avlocales.find( Locale::noCode ) == avlocales.end() )
470     {
471       WAR << "License.tar.gz contains no fallback text! " << *this << endl;
472       // Using the fist locale instead of returning no text at all.
473       // So the user might recognize that there is a license, even if he
474       // can't read it.
475       getLang = *avlocales.begin();
476     }
477
478     // now extract the license file.
479     static const std::string licenseFileFallback( "license.txt" );
480     std::string licenseFile( !getLang ? licenseFileFallback
481                                       : str::form( "license.%s.txt", getLang.c_str() ) );
482
483     ExternalProgram::Arguments cmd;
484     cmd.push_back( "tar" );
485     cmd.push_back( "-x" );
486     cmd.push_back( "-z" );
487     cmd.push_back( "-O" );
488     cmd.push_back( "-f" );
489     cmd.push_back( _pimpl->licenseTgz().asString() ); // if it not exists, avlocales was empty.
490     cmd.push_back( licenseFile );
491
492     std::string ret;
493     ExternalProgram prog( cmd, ExternalProgram::Discard_Stderr );
494     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
495     {
496       ret += output;
497     }
498     prog.close();
499     return ret;
500   }
501
502   LocaleSet RepoInfo::getLicenseLocales() const
503   {
504     Pathname licenseTgz( _pimpl->licenseTgz() );
505     if ( licenseTgz.empty() || ! PathInfo( licenseTgz ).isFile() )
506       return LocaleSet();
507
508     ExternalProgram::Arguments cmd;
509     cmd.push_back( "tar" );
510     cmd.push_back( "-t" );
511     cmd.push_back( "-z" );
512     cmd.push_back( "-f" );
513     cmd.push_back( licenseTgz.asString() );
514
515     LocaleSet ret;
516     ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
517     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
518     {
519       static const C_Str license( "license." );
520       static const C_Str dotTxt( ".txt\n" );
521       if ( str::hasPrefix( output, license ) && str::hasSuffix( output, dotTxt ) )
522       {
523         if ( output.size() <= license.size() +  dotTxt.size() ) // license.txt
524           ret.insert( Locale() );
525         else
526           ret.insert( Locale( std::string( output.c_str()+license.size(), output.size()- license.size() - dotTxt.size() ) ) );
527       }
528     }
529     prog.close();
530     return ret;
531   }
532
533   ///////////////////////////////////////////////////////////////////
534
535   std::ostream & RepoInfo::dumpOn( std::ostream & str ) const
536   {
537     RepoInfoBase::dumpOn(str);
538     if ( _pimpl->baseurl2dump() )
539     {
540       for ( const auto & url : _pimpl->baseUrls().raw() )
541       {
542         str << "- url         : " << url << std::endl;
543       }
544     }
545
546     // print if non empty value
547     auto strif( [&] ( const std::string & tag_r, const std::string & value_r ) {
548       if ( ! value_r.empty() )
549         str << tag_r << value_r << std::endl;
550     });
551
552     strif( (_pimpl->_mirrorListForceMetalink ? "- metalink    : " : "- mirrorlist  : "), rawMirrorListUrl().asString() );
553     strif( "- path        : ", path().asString() );
554     str << "- type        : " << type() << std::endl;
555     str << "- priority    : " << priority() << std::endl;
556
557     // Yes No Default(Y) Default(N)
558 #define OUTS(T,B) ( indeterminate(T) ? (std::string("D(")+(B?"Y":"N")+")") : ((bool)T?"Y":"N") )
559     str << "- gpgcheck    : " << OUTS(_pimpl->_gpgCheck,gpgCheck())
560                               << " repo" << OUTS(_pimpl->_repoGpgCheck,repoGpgCheck())
561                               << " sig" << asString( validRepoSignature(), "?", "Y", "N" )
562                               << " pkg" << OUTS(_pimpl->_pkgGpgCheck,pkgGpgCheck())
563                               << std::endl;
564 #undef OUTS
565
566     strif( "- gpgkey      : ", rawGpgKeyUrl().asString() );
567
568     if ( ! indeterminate(_pimpl->keeppackages) )
569       str << "- keeppackages: " << keepPackages() << std::endl;
570
571     strif( "- service     : ", service() );
572     strif( "- targetdistro: ", targetDistribution() );
573     strif( "- metadataPath: ", metadataPath().asString() );
574     strif( "- packagesPath: ", packagesPath().asString() );
575
576     return str;
577   }
578
579   std::ostream & RepoInfo::dumpAsIniOn( std::ostream & str ) const
580   {
581     RepoInfoBase::dumpAsIniOn(str);
582
583     if ( _pimpl->baseurl2dump() )
584     {
585       str << "baseurl=";
586       std::string indent;
587       for ( const auto & url : _pimpl->baseUrls().raw() )
588       {
589         str << indent << url << endl;
590         if ( indent.empty() ) indent = "        ";      // "baseurl="
591       }
592     }
593
594     if ( ! _pimpl->path.empty() )
595       str << "path="<< path() << endl;
596
597     if ( ! (rawMirrorListUrl().asString().empty()) )
598       str << (_pimpl->_mirrorListForceMetalink ? "metalink=" : "mirrorlist=") << rawMirrorListUrl() << endl;
599
600     str << "type=" << type().asString() << endl;
601
602     if ( priority() != defaultPriority() )
603       str << "priority=" << priority() << endl;
604
605     if ( ! indeterminate(_pimpl->_gpgCheck) )
606       str << "gpgcheck=" << (_pimpl->_gpgCheck ? "1" : "0") << endl;
607
608     if ( ! indeterminate(_pimpl->_repoGpgCheck) )
609       str << "repo_gpgcheck=" << (_pimpl->_repoGpgCheck ? "1" : "0") << endl;
610
611     if ( ! indeterminate(_pimpl->_pkgGpgCheck) )
612       str << "pkg_gpgcheck=" << (_pimpl->_pkgGpgCheck ? "1" : "0") << endl;
613
614     if ( ! (rawGpgKeyUrl().asString().empty()) )
615       str << "gpgkey=" << rawGpgKeyUrl() << endl;
616
617     if (!indeterminate(_pimpl->keeppackages))
618       str << "keeppackages=" << keepPackages() << endl;
619
620     if( ! service().empty() )
621       str << "service=" << service() << endl;
622
623     return str;
624   }
625
626   std::ostream & RepoInfo::dumpAsXmlOn( std::ostream & str, const std::string & content ) const
627   {
628     std::string tmpstr;
629     str
630       << "<repo"
631       << " alias=\"" << escape(alias()) << "\""
632       << " name=\"" << escape(name()) << "\"";
633     if (type() != repo::RepoType::NONE)
634       str << " type=\"" << type().asString() << "\"";
635     str
636       << " priority=\"" << priority() << "\""
637       << " enabled=\"" << enabled() << "\""
638       << " autorefresh=\"" << autorefresh() << "\""
639       << " gpgcheck=\"" << gpgCheck() << "\""
640       << " repo_gpgcheck=\"" << repoGpgCheck() << "\""
641       << " pkg_gpgcheck=\"" << pkgGpgCheck() << "\"";
642     if (!(tmpstr = gpgKeyUrl().asString()).empty())
643       str << " gpgkey=\"" << escape(tmpstr) << "\"";
644     if (!(tmpstr = mirrorListUrl().asString()).empty())
645       str << (_pimpl->_mirrorListForceMetalink ? " metalink=\"" : " mirrorlist=\"") << escape(tmpstr) << "\"";
646     str << ">" << endl;
647
648     if ( _pimpl->baseurl2dump() )
649     {
650       for_( it, baseUrlsBegin(), baseUrlsEnd() )        // !transform iterator replaces variables
651         str << "<url>" << escape((*it).asString()) << "</url>" << endl;
652     }
653
654     str << "</repo>" << endl;
655     return str;
656   }
657
658
659   std::ostream & operator<<( std::ostream & str, const RepoInfo & obj )
660   {
661     return obj.dumpOn(str);
662   }
663
664
665   /////////////////////////////////////////////////////////////////
666 } // namespace zypp
667 ///////////////////////////////////////////////////////////////////