Imported Upstream version 17.6.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 #include <fstream>
15
16 #include "zypp/base/LogTools.h"
17 #include "zypp/base/DefaultIntegral.h"
18 #include "zypp/parser/xml/XmlEscape.h"
19
20 #include "zypp/ManagedFile.h"
21 #include "zypp/PublicKey.h"
22 #include "zypp/MediaSetAccess.h"
23 #include "zypp/RepoInfo.h"
24 #include "zypp/Glob.h"
25 #include "zypp/TriBool.h"
26 #include "zypp/Pathname.h"
27 #include "zypp/ZConfig.h"
28 #include "zypp/repo/RepoMirrorList.h"
29 #include "zypp/ExternalProgram.h"
30 #include "zypp/media/MediaAccess.h"
31
32 #include "zypp/base/IOStream.h"
33 #include "zypp/base/InputStream.h"
34 #include "zypp/parser/xml/Reader.h"
35
36
37 #include "zypp/base/StrMatcher.h"
38 #include "zypp/KeyRing.h"
39 #include "zypp/TmpPath.h"
40 #include "zypp/ZYppFactory.h"
41 #include "zypp/ZYppCallbacks.h"
42
43 using std::endl;
44 using zypp::xml::escape;
45
46 ///////////////////////////////////////////////////////////////////
47 namespace zypp
48 { /////////////////////////////////////////////////////////////////
49
50   ///////////////////////////////////////////////////////////////////
51   //
52   //    CLASS NAME : RepoInfo::Impl
53   //
54   /** RepoInfo implementation. */
55   struct RepoInfo::Impl
56   {
57     Impl()
58       : _rawGpgCheck( indeterminate )
59       , _rawRepoGpgCheck( indeterminate )
60       , _rawPkgGpgCheck( indeterminate )
61       , _validRepoSignature( indeterminate )
62       , keeppackages(indeterminate)
63       , _mirrorListForceMetalink(false)
64       , type(repo::RepoType::NONE_e)
65       , emptybaseurls(false)
66     {}
67
68     ~Impl()
69     {}
70
71   public:
72     static const unsigned defaultPriority = 99;
73     static const unsigned noPriority = unsigned(-1);
74
75     void setProbedType( const repo::RepoType & t ) const
76     {
77       if ( type == repo::RepoType::NONE
78            && t != repo::RepoType::NONE )
79       {
80         // lazy init!
81         const_cast<Impl*>(this)->type = t;
82       }
83     }
84
85   public:
86     /** Path to a license tarball in case it exists in the repo. */
87     Pathname licenseTgz( const std::string & name_r ) const
88     {
89       Pathname ret;
90       if ( !metadataPath().empty() )
91       {
92         std::string licenseStem( "license" );
93         if ( !name_r.empty() )
94         {
95           licenseStem += "-";
96           licenseStem += name_r;
97         }
98
99         filesystem::Glob g;
100         // TODO: REPOMD: this assumes we know the name of the tarball. In fact
101         // we'd need to get the file from repomd.xml (<data type="license[-name_r]">)
102         g.add( metadataPath() / path / ("repodata/*"+licenseStem+".tar.gz") );
103         if ( g.empty() )
104           g.add( metadataPath() / path / (licenseStem+".tar.gz") );
105
106         if ( !g.empty() )
107           ret = *g.begin();
108       }
109       return ret;
110     }
111
112     const RepoVariablesReplacedUrlList & baseUrls() const
113     {
114       const Url & mlurl( _mirrorListUrl.transformed() );        // Variables replaced!
115       if ( _baseUrls.empty() && ! mlurl.asString().empty() )
116       {
117         emptybaseurls = true;
118         DBG << "MetadataPath: " << metadataPath() << endl;
119         repo::RepoMirrorList rmurls( mlurl, metadataPath(), _mirrorListForceMetalink );
120         _baseUrls.raw().insert( _baseUrls.raw().end(), rmurls.getUrls().begin(), rmurls.getUrls().end() );
121       }
122       return _baseUrls;
123     }
124
125     RepoVariablesReplacedUrlList & baseUrls()
126     { return _baseUrls; }
127
128     bool baseurl2dump() const
129     { return !emptybaseurls && !_baseUrls.empty(); }
130
131
132     const RepoVariablesReplacedUrlList & gpgKeyUrls() const
133     { return _gpgKeyUrls; }
134
135     RepoVariablesReplacedUrlList & gpgKeyUrls()
136     { return _gpgKeyUrls; }
137
138
139     const std::set<std::string> & contentKeywords() const
140     { hasContent()/*init if not yet done*/; return _keywords.second; }
141
142     void addContent( const std::string & keyword_r )
143     { _keywords.second.insert( keyword_r ); if ( ! hasContent() ) _keywords.first = true; }
144
145     bool hasContent() const
146     {
147       if ( !_keywords.first && ! metadataPath().empty() )
148       {
149         // HACK directly check master index file until RepoManager offers
150         // some content probing and zypper uses it.
151         /////////////////////////////////////////////////////////////////
152         MIL << "Empty keywords...." << metadataPath() << endl;
153         Pathname master;
154         if ( PathInfo( (master=metadataPath()/"/repodata/repomd.xml") ).isFile() )
155         {
156           //MIL << "GO repomd.." << endl;
157           xml::Reader reader( master );
158           while ( reader.seekToNode( 2, "content" ) )
159           {
160             _keywords.second.insert( reader.nodeText().asString() );
161             reader.seekToEndNode( 2, "content" );
162           }
163           _keywords.first = true;       // valid content in _keywords even if empty
164         }
165         else if ( PathInfo( (master=metadataPath()/"/content") ).isFile() )
166         {
167           //MIL << "GO content.." << endl;
168           iostr::forEachLine( InputStream( master ),
169                             [this]( int num_r, std::string line_r )->bool
170                             {
171                               if ( str::startsWith( line_r, "REPOKEYWORDS" ) )
172                               {
173                                 std::vector<std::string> words;
174                                 if ( str::split( line_r, std::back_inserter(words) ) > 1
175                                   && words[0].length() == 12 /*"REPOKEYWORDS"*/ )
176                                 {
177                                   this->_keywords.second.insert( ++words.begin(), words.end() );
178                                 }
179                                 return true; // mult. occurrances are ok.
180                               }
181                               return( ! str::startsWith( line_r, "META " ) );   // no need to parse into META section.
182                             } );
183           _keywords.first = true;       // valid content in _keywords even if empty
184         }
185         /////////////////////////////////////////////////////////////////
186       }
187       return _keywords.first;
188     }
189
190     bool hasContent( const std::string & keyword_r ) const
191     { return( hasContent() && _keywords.second.find( keyword_r ) != _keywords.second.end() ); }
192
193     /** Signature check result needs to be stored/retrieved from _metadataPath.
194      * Don't call them from outside validRepoSignature/setValidRepoSignature
195      */
196     //@{
197     TriBool internalValidRepoSignature() const
198     {
199       if ( ! indeterminate(_validRepoSignature) )
200         return _validRepoSignature;
201       // check metadata:
202       if ( ! metadataPath().empty() )
203       {
204         // A missing ".repo_gpgcheck" might be plaindir(no Downloader) or not yet refreshed signed repo!
205         TriBool linkval = triBoolFromPath( metadataPath() / ".repo_gpgcheck" );
206         return linkval;
207       }
208       return indeterminate;
209     }
210
211     void internalSetValidRepoSignature( TriBool value_r )
212     {
213       if ( PathInfo(metadataPath()).isDir() )
214       {
215         Pathname gpgcheckFile( metadataPath() / ".repo_gpgcheck" );
216         if ( PathInfo(gpgcheckFile).isExist() )
217         {
218           TriBool linkval( indeterminate );
219           if ( triBoolFromPath( gpgcheckFile, linkval ) && linkval == value_r )
220             return;     // existing symlink fits value_r
221           else
222             filesystem::unlink( gpgcheckFile ); // will write a new one
223         }
224         filesystem::symlink( asString(value_r), gpgcheckFile );
225       }
226       _validRepoSignature = value_r;
227     }
228
229     /** We definitely have a symlink pointing to "indeterminate" (for repoGpgCheckIsMandatory)?
230      * I.e. user accepted the unsigned repo in Downloader. A test whether `internalValidRepoSignature`
231      * is indeterminate would include not yet checked repos, which is unwanted here.
232      */
233     bool internalUnsignedConfirmed() const
234     {
235       TriBool linkval( true );  // want to see it being switched to indeterminate
236       return triBoolFromPath( metadataPath() / ".repo_gpgcheck", linkval ) && indeterminate(linkval);
237     }
238
239     bool triBoolFromPath( const Pathname & path_r, TriBool & ret_r ) const
240     {
241       static const Pathname truePath( "true" );
242       static const Pathname falsePath( "false" );
243       static const Pathname indeterminatePath( "indeterminate" );
244
245       // Quiet readlink;
246       static const ssize_t bufsiz = 63;
247       static char buf[bufsiz+1];
248       ssize_t ret = ::readlink( path_r.c_str(), buf, bufsiz );
249       buf[ret == -1 ? 0 : ret] = '\0';
250
251       Pathname linkval( buf );
252
253       bool known = true;
254       if ( linkval == truePath )
255         ret_r = true;
256       else if ( linkval == falsePath )
257         ret_r = false;
258       else if ( linkval == indeterminatePath )
259         ret_r = indeterminate;
260       else
261         known = false;
262       return known;
263     }
264
265     TriBool triBoolFromPath( const Pathname & path_r ) const
266     { TriBool ret(indeterminate); triBoolFromPath( path_r, ret ); return ret; }
267
268     //@}
269
270   private:
271     TriBool _rawGpgCheck;       ///< default gpgcheck behavior: Y/N/ZConf
272     TriBool _rawRepoGpgCheck;   ///< need to check repo sign.: Y/N/(ZConf(Y/N/gpgCheck))
273     TriBool _rawPkgGpgCheck;    ///< need to check pkg sign.: Y/N/(ZConf(Y/N/gpgCheck))
274
275   public:
276     TriBool rawGpgCheck() const                 { return _rawGpgCheck; }
277     TriBool rawRepoGpgCheck() const             { return _rawRepoGpgCheck; }
278     TriBool rawPkgGpgCheck() const              { return _rawPkgGpgCheck; }
279
280     void rawGpgCheck( TriBool val_r )           { _rawGpgCheck = val_r; }
281     void rawRepoGpgCheck( TriBool val_r )       { _rawRepoGpgCheck = val_r; }
282     void rawPkgGpgCheck( TriBool val_r )        { _rawPkgGpgCheck = val_r; }
283
284     bool cfgGpgCheck() const
285     { return indeterminate(_rawGpgCheck) ? ZConfig::instance().gpgCheck() : (bool)_rawGpgCheck; }
286     TriBool cfgRepoGpgCheck() const
287     { return indeterminate(_rawGpgCheck) && indeterminate(_rawRepoGpgCheck) ? ZConfig::instance().repoGpgCheck() : _rawRepoGpgCheck; }
288     TriBool cfgPkgGpgCheck() const
289     { return indeterminate(_rawGpgCheck) && indeterminate(_rawPkgGpgCheck) ? ZConfig::instance().pkgGpgCheck() : _rawPkgGpgCheck; }
290
291   private:
292     TriBool _validRepoSignature;///< have  signed and valid repo metadata
293   public:
294     TriBool keeppackages;
295     RepoVariablesReplacedUrl _mirrorListUrl;
296     bool                     _mirrorListForceMetalink;
297     repo::RepoType type;
298     Pathname path;
299     std::string service;
300     std::string targetDistro;
301
302     void metadataPath( Pathname new_r )
303     { _metadataPath = std::move( new_r ); }
304
305     void packagesPath( Pathname new_r )
306     { _packagesPath = std::move( new_r ); }
307
308     bool usesAutoMethadataPaths() const
309     { return str::hasSuffix( _metadataPath.asString(), "/%AUTO%" ); }
310
311     Pathname metadataPath() const
312     {
313       if ( usesAutoMethadataPaths() )
314         return _metadataPath.dirname() / "%RAW%";
315       return _metadataPath;
316     }
317
318     Pathname packagesPath() const
319     {
320       if ( _packagesPath.empty() && usesAutoMethadataPaths() )
321         return _metadataPath.dirname() / "%PKG%";
322       return _packagesPath;
323     }
324
325     DefaultIntegral<unsigned,defaultPriority> priority;
326     mutable bool emptybaseurls;
327
328   private:
329     Pathname _metadataPath;
330     Pathname _packagesPath;
331
332     mutable RepoVariablesReplacedUrlList _baseUrls;
333     mutable std::pair<FalseBool, std::set<std::string> > _keywords;
334
335     RepoVariablesReplacedUrlList _gpgKeyUrls;
336
337     friend Impl * rwcowClone<Impl>( const Impl * rhs );
338     /** clone for RWCOW_pointer */
339     Impl * clone() const
340     { return new Impl( *this ); }
341   };
342   ///////////////////////////////////////////////////////////////////
343
344   /** \relates RepoInfo::Impl Stream output */
345   inline std::ostream & operator<<( std::ostream & str, const RepoInfo::Impl & obj )
346   {
347     return str << "RepoInfo::Impl";
348   }
349
350   ///////////////////////////////////////////////////////////////////
351   //
352   //    CLASS NAME : RepoInfo
353   //
354   ///////////////////////////////////////////////////////////////////
355
356   const RepoInfo RepoInfo::noRepo;
357
358   RepoInfo::RepoInfo()
359   : _pimpl( new Impl() )
360   {}
361
362   RepoInfo::~RepoInfo()
363   {}
364
365   unsigned RepoInfo::priority() const
366   { return _pimpl->priority; }
367
368   unsigned RepoInfo::defaultPriority()
369   { return Impl::defaultPriority; }
370
371   unsigned RepoInfo::noPriority()
372   { return Impl::noPriority; }
373
374   void RepoInfo::setPriority( unsigned newval_r )
375   { _pimpl->priority = newval_r ? newval_r : Impl::defaultPriority; }
376
377
378   bool RepoInfo::gpgCheck() const
379   { return _pimpl->cfgGpgCheck(); }
380
381   void RepoInfo::setGpgCheck( TriBool value_r )
382   { _pimpl->rawGpgCheck( value_r ); }
383
384   void RepoInfo::setGpgCheck( bool value_r ) // deprecated legacy and for squid
385   { setGpgCheck( TriBool(value_r) ); }
386
387
388   bool RepoInfo::repoGpgCheck() const
389   { return gpgCheck() || _pimpl->cfgRepoGpgCheck(); }
390
391   bool RepoInfo::repoGpgCheckIsMandatory() const
392   {
393     bool ret = ( gpgCheck() && indeterminate(_pimpl->cfgRepoGpgCheck()) ) || _pimpl->cfgRepoGpgCheck();
394     if ( ret && _pimpl->internalUnsignedConfirmed() )   // relax if unsigned repo was confirmed in the past
395       ret = false;
396     return ret;
397   }
398
399   void RepoInfo::setRepoGpgCheck( TriBool value_r )
400   { _pimpl->rawRepoGpgCheck( value_r ); }
401
402
403   bool RepoInfo::pkgGpgCheck() const
404   { return _pimpl->cfgPkgGpgCheck() || ( gpgCheck() && !bool(validRepoSignature())/*enforced*/ ) ; }
405
406   bool RepoInfo::pkgGpgCheckIsMandatory() const
407   { return _pimpl->cfgPkgGpgCheck() || ( gpgCheck() && indeterminate(_pimpl->cfgPkgGpgCheck()) && !bool(validRepoSignature())/*enforced*/ ); }
408
409   void RepoInfo::setPkgGpgCheck( TriBool value_r )
410   { _pimpl->rawPkgGpgCheck( value_r ); }
411
412
413   void RepoInfo::getRawGpgChecks( TriBool & g_r, TriBool & r_r, TriBool & p_r ) const
414   {
415     g_r = _pimpl->rawGpgCheck();
416     r_r = _pimpl->rawRepoGpgCheck();
417     p_r = _pimpl->rawPkgGpgCheck();
418   }
419
420
421   TriBool RepoInfo::validRepoSignature() const
422   {
423     TriBool ret( _pimpl->internalValidRepoSignature() );
424     if ( ret && !repoGpgCheck() ) ret = false;  // invalidate any old signature if repoGpgCheck is off
425     return ret;
426   }
427
428   void RepoInfo::setValidRepoSignature( TriBool value_r )
429   { _pimpl->internalSetValidRepoSignature( value_r ); }
430
431   ///////////////////////////////////////////////////////////////////
432   namespace
433   {
434     inline bool changeGpgCheckTo( TriBool & lhs, TriBool rhs )
435     { if ( ! sameTriboolState( lhs, rhs ) ) { lhs = rhs; return true; } return false; }
436
437     inline bool changeGpgCheckTo( TriBool ogpg[3], TriBool g, TriBool r, TriBool p )
438     {
439       bool changed = false;
440       if ( changeGpgCheckTo( ogpg[0], g ) ) changed = true;
441       if ( changeGpgCheckTo( ogpg[1], r ) ) changed = true;
442       if ( changeGpgCheckTo( ogpg[2], p ) ) changed = true;
443       return changed;
444     }
445   } // namespace
446   ///////////////////////////////////////////////////////////////////
447   bool RepoInfo::setGpgCheck( GpgCheck mode_r )
448   {
449     TriBool ogpg[3];    // Gpg RepoGpg PkgGpg
450     getRawGpgChecks( ogpg[0], ogpg[1], ogpg[2] );
451
452     bool changed = false;
453     switch ( mode_r )
454     {
455       case GpgCheck::On:
456         changed = changeGpgCheckTo( ogpg, true,          indeterminate, indeterminate );
457         break;
458       case GpgCheck::Strict:
459         changed = changeGpgCheckTo( ogpg, true,          true,          true          );
460         break;
461       case GpgCheck::AllowUnsigned:
462         changed = changeGpgCheckTo( ogpg, true,          false,         false         );
463         break;
464       case GpgCheck::AllowUnsignedRepo:
465         changed = changeGpgCheckTo( ogpg, true,          false,         indeterminate );
466         break;
467       case GpgCheck::AllowUnsignedPackage:
468         changed = changeGpgCheckTo( ogpg, true,          indeterminate, false         );
469         break;
470       case GpgCheck::Default:
471         changed = changeGpgCheckTo( ogpg, indeterminate, indeterminate, indeterminate );
472         break;
473       case GpgCheck::Off:
474         changed = changeGpgCheckTo( ogpg, false,         indeterminate, indeterminate );
475         break;
476       case GpgCheck::indeterminate:     // no change
477         break;
478     }
479
480     if ( changed )
481     {
482       setGpgCheck    ( ogpg[0] );
483       setRepoGpgCheck( ogpg[1] );
484       setPkgGpgCheck ( ogpg[2] );
485     }
486     return changed;
487   }
488
489   void RepoInfo::setMirrorListUrl( const Url & url_r )  // Raw
490   { _pimpl->_mirrorListUrl.raw() = url_r; _pimpl->_mirrorListForceMetalink = false; }
491
492   void  RepoInfo::setMetalinkUrl( const Url & url_r )   // Raw
493   { _pimpl->_mirrorListUrl.raw() = url_r; _pimpl->_mirrorListForceMetalink = true; }
494
495   void RepoInfo::setGpgKeyUrls( url_set urls )
496   { _pimpl->gpgKeyUrls().raw().swap( urls ); }
497
498   void RepoInfo::setGpgKeyUrl( const Url & url_r )
499   {
500     _pimpl->gpgKeyUrls().raw().clear();
501     _pimpl->gpgKeyUrls().raw().push_back( url_r );
502   }
503
504   Pathname RepoInfo::provideKey(const std::string &keyID_r, const Pathname &targetDirectory_r) const
505   {
506     if ( keyID_r.empty() )
507       return Pathname();
508
509     MIL << "Check for " << keyID_r << " at " << targetDirectory_r << endl;
510     std::string keyIDStr( keyID_r.size() > 8 ? keyID_r.substr( keyID_r.size()-8 ) : keyID_r );  // print short ID in Jobreports
511     filesystem::TmpDir tmpKeyRingDir;
512     KeyRing tempKeyRing(tmpKeyRingDir.path());
513
514     // translator: %1% is a gpg key ID like 3DBDC284
515     //             %2% is a cache directories path
516     JobReport::info( str::Format(_("Looking for gpg key ID %1% in cache %2%.") ) % keyIDStr % targetDirectory_r );
517     filesystem::dirForEach(targetDirectory_r,
518                            StrMatcher(".key", Match::STRINGEND),
519                            [&tempKeyRing]( const Pathname & dir_r, const std::string & str_r ){
520       try {
521
522         // deprecate a month old keys
523         PathInfo fileInfo ( dir_r/str_r );
524         if ( Date::now() - fileInfo.mtime() > Date::month ) {
525           //if unlink fails, the file will be overriden in the next step, no need
526           //to show a error
527           filesystem::unlink( dir_r/str_r );
528         } else {
529           tempKeyRing.multiKeyImport(dir_r/str_r, true);
530         }
531       } catch (const KeyRingException& e) {
532         ZYPP_CAUGHT(e);
533         ERR << "Error importing cached key from file '"<<dir_r/str_r<<"'."<<endl;
534       }
535       return true;
536     });
537
538     // no key in the cache is what we are looking for, lets download
539     // all keys specified in gpgkey= entries
540     if ( !tempKeyRing.isKeyTrusted(keyID_r) ) {
541       if ( ! gpgKeyUrlsEmpty() ) {
542         // translator: %1% is a gpg key ID like 3DBDC284
543         //             %2% is a repositories name
544         JobReport::info( str::Format(_("Looking for gpg key ID %1% in repository %2%.") ) % keyIDStr % asUserString() );
545         for ( const Url &url : gpgKeyUrls() ) {
546           try {
547             JobReport::info( "  gpgkey=" + url.asString() );
548             ManagedFile f = MediaSetAccess::provideOptionalFileFromUrl( url );
549             if ( f->empty() )
550               continue;
551
552             PublicKey key(f);
553             if ( !key.isValid() )
554               continue;
555
556             // import all keys into our temporary keyring
557             tempKeyRing.multiKeyImport(f, true);
558
559           } catch ( const std::exception & e ) {
560             //ignore and continue to next url
561             ZYPP_CAUGHT(e);
562             MIL << "Key import from url:'"<<url<<"' failed." << endl;
563           }
564         }
565       }
566       else {
567         // translator: %1% is a repositories name
568         JobReport::info( str::Format(_("Repository %1% does not define additional 'gpgkey=' URLs.") ) % asUserString() );
569       }
570     }
571
572     filesystem::assert_dir( targetDirectory_r );
573
574     //now write all keys into their own files in cache, override existing ones to always have
575     //up to date key data
576     for ( const auto & key: tempKeyRing.trustedPublicKeyData()) {
577       MIL << "KEY ID in KEYRING: " << key.id() << endl;
578
579       Pathname keyFile = targetDirectory_r/(str::Format("%1%.key") % key.rpmName()).asString();
580
581       std::ofstream fout( keyFile.c_str(), std::ios_base::out | std::ios_base::trunc );
582
583       if (!fout)
584         ZYPP_THROW(Exception(str::form("Cannot open file %s",keyFile.c_str())));
585
586       tempKeyRing.dumpTrustedPublicKey( key.id(), fout );
587     }
588
589     // key is STILL not known, we give up
590     if ( !tempKeyRing.isKeyTrusted(keyID_r) ) {
591       return Pathname();
592     }
593
594     PublicKeyData keyData( tempKeyRing.trustedPublicKeyData( keyID_r ) );
595     if ( !keyData ) {
596       ERR << "Error when exporting key from temporary keychain." << endl;
597       return Pathname();
598     }
599
600     return targetDirectory_r/(str::Format("%1%.key") % keyData.rpmName()).asString();
601   }
602
603   void RepoInfo::addBaseUrl( const Url & url_r )
604   {
605     for ( const auto & url : _pimpl->baseUrls().raw() ) // Raw unique!
606       if ( url == url_r )
607         return;
608     _pimpl->baseUrls().raw().push_back( url_r );
609   }
610
611   void RepoInfo::setBaseUrl( const Url & url_r )
612   {
613     _pimpl->baseUrls().raw().clear();
614     _pimpl->baseUrls().raw().push_back( url_r );
615   }
616
617   void RepoInfo::setBaseUrls( url_set urls )
618   { _pimpl->baseUrls().raw().swap( urls ); }
619
620   void RepoInfo::setPath( const Pathname &path )
621   { _pimpl->path = path; }
622
623   void RepoInfo::setType( const repo::RepoType &t )
624   { _pimpl->type = t; }
625
626   void RepoInfo::setProbedType( const repo::RepoType &t ) const
627   { _pimpl->setProbedType( t ); }
628
629
630   void RepoInfo::setMetadataPath( const Pathname &path )
631   { _pimpl->metadataPath( path ); }
632
633   void RepoInfo::setPackagesPath( const Pathname &path )
634   { _pimpl->packagesPath( path ); }
635
636   void RepoInfo::setKeepPackages( bool keep )
637   { _pimpl->keeppackages = keep; }
638
639   void RepoInfo::setService( const std::string& name )
640   { _pimpl->service = name; }
641
642   void RepoInfo::setTargetDistribution( const std::string & targetDistribution )
643   { _pimpl->targetDistro = targetDistribution; }
644
645   bool RepoInfo::keepPackages() const
646   { return indeterminate(_pimpl->keeppackages) ? false : (bool)_pimpl->keeppackages; }
647
648   Pathname RepoInfo::metadataPath() const
649   { return _pimpl->metadataPath(); }
650
651   Pathname RepoInfo::packagesPath() const
652   { return _pimpl->packagesPath(); }
653
654   bool RepoInfo::usesAutoMethadataPaths() const
655   { return _pimpl->usesAutoMethadataPaths(); }
656
657   repo::RepoType RepoInfo::type() const
658   { return _pimpl->type; }
659
660   Url RepoInfo::mirrorListUrl() const                   // Variables replaced!
661   { return _pimpl->_mirrorListUrl.transformed(); }
662
663   Url RepoInfo::rawMirrorListUrl() const                // Raw
664   { return _pimpl->_mirrorListUrl.raw(); }
665
666   bool RepoInfo::gpgKeyUrlsEmpty() const
667   { return _pimpl->gpgKeyUrls().empty(); }
668
669   RepoInfo::urls_size_type RepoInfo::gpgKeyUrlsSize() const
670   { return _pimpl->gpgKeyUrls().size(); }
671
672   RepoInfo::url_set RepoInfo::gpgKeyUrls() const        // Variables replaced!
673   { return _pimpl->gpgKeyUrls().transformed(); }
674
675   RepoInfo::url_set RepoInfo::rawGpgKeyUrls() const     // Raw
676   { return _pimpl->gpgKeyUrls().raw(); }
677
678   Url RepoInfo::gpgKeyUrl() const                       // Variables replaced!
679   { return( _pimpl->gpgKeyUrls().empty() ? Url() : *_pimpl->gpgKeyUrls().transformedBegin() ); }
680
681   Url RepoInfo::rawGpgKeyUrl() const                    // Raw
682   { return( _pimpl->gpgKeyUrls().empty() ? Url() : *_pimpl->gpgKeyUrls().rawBegin() ) ; }
683
684   RepoInfo::url_set RepoInfo::baseUrls() const          // Variables replaced!
685   { return _pimpl->baseUrls().transformed(); }
686
687   RepoInfo::url_set RepoInfo::rawBaseUrls() const       // Raw
688   { return _pimpl->baseUrls().raw(); }
689
690   Pathname RepoInfo::path() const
691   { return _pimpl->path; }
692
693   std::string RepoInfo::service() const
694   { return _pimpl->service; }
695
696   std::string RepoInfo::targetDistribution() const
697   { return _pimpl->targetDistro; }
698
699   Url RepoInfo::rawUrl() const
700   { return( _pimpl->baseUrls().empty() ? Url() : *_pimpl->baseUrls().rawBegin() ); }
701
702   RepoInfo::urls_const_iterator RepoInfo::baseUrlsBegin() const
703   { return _pimpl->baseUrls().transformedBegin(); }
704
705   RepoInfo::urls_const_iterator RepoInfo::baseUrlsEnd() const
706   { return _pimpl->baseUrls().transformedEnd(); }
707
708   RepoInfo::urls_size_type RepoInfo::baseUrlsSize() const
709   { return _pimpl->baseUrls().size(); }
710
711   bool RepoInfo::baseUrlsEmpty() const
712   { return _pimpl->baseUrls().empty(); }
713
714   bool RepoInfo::baseUrlSet() const
715   { return _pimpl->baseurl2dump(); }
716
717   const std::set<std::string> & RepoInfo::contentKeywords() const
718   { return _pimpl->contentKeywords(); }
719
720   void RepoInfo::addContent( const std::string & keyword_r )
721   { _pimpl->addContent( keyword_r ); }
722
723   bool RepoInfo::hasContent() const
724   { return _pimpl->hasContent(); }
725
726   bool RepoInfo::hasContent( const std::string & keyword_r ) const
727   { return _pimpl->hasContent( keyword_r ); }
728
729   ///////////////////////////////////////////////////////////////////
730
731   bool RepoInfo::hasLicense() const
732   { return hasLicense( std::string() ); }
733
734   bool RepoInfo::hasLicense( const std::string & name_r ) const
735   { return !_pimpl->licenseTgz( name_r ).empty(); }
736
737
738   bool RepoInfo::needToAcceptLicense() const
739   { return needToAcceptLicense( std::string() ); }
740
741   bool RepoInfo::needToAcceptLicense( const std::string & name_r ) const
742   {
743     const Pathname & licenseTgz( _pimpl->licenseTgz( name_r ) );
744     if ( licenseTgz.empty() )
745       return false;     // no licenses at all
746
747     ExternalProgram::Arguments cmd;
748     cmd.push_back( "tar" );
749     cmd.push_back( "-t" );
750     cmd.push_back( "-z" );
751     cmd.push_back( "-f" );
752     cmd.push_back( licenseTgz.asString() );
753     ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
754
755     bool accept = true;
756     static const std::string noAcceptanceFile = "no-acceptance-needed\n";
757     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
758     {
759       if ( output == noAcceptanceFile )
760       {
761         accept = false;
762       }
763     }
764     prog.close();
765     MIL << "License(" << name_r << ") in " << name() << " has to be accepted: " << (accept?"true":"false" ) << endl;
766     return accept;
767   }
768
769
770   std::string RepoInfo::getLicense( const Locale & lang_r )
771   { return const_cast<const RepoInfo *>(this)->getLicense( std::string(), lang_r ); }
772
773   std::string RepoInfo::getLicense( const Locale & lang_r ) const
774   { return getLicense( std::string(), lang_r ); }
775
776   std::string RepoInfo::getLicense( const std::string & name_r, const Locale & lang_r ) const
777   {
778     LocaleSet avlocales( getLicenseLocales( name_r ) );
779     if ( avlocales.empty() )
780       return std::string();
781
782     Locale getLang( Locale::bestMatch( avlocales, lang_r ) );
783     if ( !getLang && avlocales.find( Locale::noCode ) == avlocales.end() )
784     {
785       WAR << "License(" << name_r << ") in " << name() << " contains no fallback text!" << endl;
786       // Using the fist locale instead of returning no text at all.
787       // So the user might recognize that there is a license, even if he
788       // can't read it.
789       getLang = *avlocales.begin();
790     }
791
792     // now extract the license file.
793     static const std::string licenseFileFallback( "license.txt" );
794     std::string licenseFile( !getLang ? licenseFileFallback
795                                       : str::form( "license.%s.txt", getLang.c_str() ) );
796
797     ExternalProgram::Arguments cmd;
798     cmd.push_back( "tar" );
799     cmd.push_back( "-x" );
800     cmd.push_back( "-z" );
801     cmd.push_back( "-O" );
802     cmd.push_back( "-f" );
803     cmd.push_back( _pimpl->licenseTgz( name_r ).asString() ); // if it not exists, avlocales was empty.
804     cmd.push_back( licenseFile );
805
806     std::string ret;
807     ExternalProgram prog( cmd, ExternalProgram::Discard_Stderr );
808     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
809     {
810       ret += output;
811     }
812     prog.close();
813     return ret;
814   }
815
816
817   LocaleSet RepoInfo::getLicenseLocales() const
818   { return getLicenseLocales( std::string() ); }
819
820   LocaleSet RepoInfo::getLicenseLocales( const std::string & name_r ) const
821   {
822     const Pathname & licenseTgz( _pimpl->licenseTgz( name_r ) );
823     if ( licenseTgz.empty() )
824       return LocaleSet();
825
826     ExternalProgram::Arguments cmd;
827     cmd.push_back( "tar" );
828     cmd.push_back( "-t" );
829     cmd.push_back( "-z" );
830     cmd.push_back( "-f" );
831     cmd.push_back( licenseTgz.asString() );
832
833     LocaleSet ret;
834     ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
835     for ( std::string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() )
836     {
837       static const C_Str license( "license." );
838       static const C_Str dotTxt( ".txt\n" );
839       if ( str::hasPrefix( output, license ) && str::hasSuffix( output, dotTxt ) )
840       {
841         if ( output.size() <= license.size() +  dotTxt.size() ) // license.txt
842           ret.insert( Locale() );
843         else
844           ret.insert( Locale( std::string( output.c_str()+license.size(), output.size()- license.size() - dotTxt.size() ) ) );
845       }
846     }
847     prog.close();
848     return ret;
849   }
850
851   ///////////////////////////////////////////////////////////////////
852
853   std::ostream & RepoInfo::dumpOn( std::ostream & str ) const
854   {
855     RepoInfoBase::dumpOn(str);
856     if ( _pimpl->baseurl2dump() )
857     {
858       for ( const auto & url : _pimpl->baseUrls().raw() )
859       {
860         str << "- url         : " << url << std::endl;
861       }
862     }
863
864     // print if non empty value
865     auto strif( [&] ( const std::string & tag_r, const std::string & value_r ) {
866       if ( ! value_r.empty() )
867         str << tag_r << value_r << std::endl;
868     });
869
870     strif( (_pimpl->_mirrorListForceMetalink ? "- metalink    : " : "- mirrorlist  : "), rawMirrorListUrl().asString() );
871     strif( "- path        : ", path().asString() );
872     str << "- type        : " << type() << std::endl;
873     str << "- priority    : " << priority() << std::endl;
874
875     // Yes No Default(Y) Default(N)
876 #define OUTS(T,B) ( indeterminate(T) ? (std::string("D(")+(B?"Y":"N")+")") : ((bool)T?"Y":"N") )
877     str << "- gpgcheck    : " << OUTS(_pimpl->rawGpgCheck(),gpgCheck())
878                               << " repo" << OUTS(_pimpl->rawRepoGpgCheck(),repoGpgCheck()) << (repoGpgCheckIsMandatory() ? "* ": " " )
879                               << "sig" << asString( validRepoSignature(), "?", "Y", "N" )
880                               << " pkg" << OUTS(_pimpl->rawPkgGpgCheck(),pkgGpgCheck()) << (pkgGpgCheckIsMandatory() ? "* ": " " )
881                               << std::endl;
882 #undef OUTS
883
884     for ( const auto & url : _pimpl->gpgKeyUrls().raw() )
885     {
886       str << "- gpgkey      : " << url << std::endl;
887     }
888
889     if ( ! indeterminate(_pimpl->keeppackages) )
890       str << "- keeppackages: " << keepPackages() << std::endl;
891
892     strif( "- service     : ", service() );
893     strif( "- targetdistro: ", targetDistribution() );
894     strif( "- filePath:     ", filepath().asString() );
895     strif( "- metadataPath: ", metadataPath().asString() );
896     strif( "- packagesPath: ", packagesPath().asString() );
897
898     return str;
899   }
900
901   std::ostream & RepoInfo::dumpAsIniOn( std::ostream & str ) const
902   {
903     RepoInfoBase::dumpAsIniOn(str);
904
905     if ( _pimpl->baseurl2dump() )
906     {
907       str << "baseurl=";
908       std::string indent;
909       for ( const auto & url : _pimpl->baseUrls().raw() )
910       {
911         str << indent << url << endl;
912         if ( indent.empty() ) indent = "        ";      // "baseurl="
913       }
914     }
915
916     if ( ! _pimpl->path.empty() )
917       str << "path="<< path() << endl;
918
919     if ( ! (rawMirrorListUrl().asString().empty()) )
920       str << (_pimpl->_mirrorListForceMetalink ? "metalink=" : "mirrorlist=") << rawMirrorListUrl() << endl;
921
922     str << "type=" << type().asString() << endl;
923
924     if ( priority() != defaultPriority() )
925       str << "priority=" << priority() << endl;
926
927     if ( ! indeterminate(_pimpl->rawGpgCheck()) )
928       str << "gpgcheck=" << (_pimpl->rawGpgCheck() ? "1" : "0") << endl;
929
930     if ( ! indeterminate(_pimpl->rawRepoGpgCheck()) )
931       str << "repo_gpgcheck=" << (_pimpl->rawRepoGpgCheck() ? "1" : "0") << endl;
932
933     if ( ! indeterminate(_pimpl->rawPkgGpgCheck()) )
934       str << "pkg_gpgcheck=" << (_pimpl->rawPkgGpgCheck() ? "1" : "0") << endl;
935
936     {
937       std::string indent( "gpgkey=");
938       for ( const auto & url : _pimpl->gpgKeyUrls().raw() )
939       {
940         str << indent << url << endl;
941         if ( indent[0] != ' ' )
942           indent = "       ";
943       }
944     }
945
946     if (!indeterminate(_pimpl->keeppackages))
947       str << "keeppackages=" << keepPackages() << endl;
948
949     if( ! service().empty() )
950       str << "service=" << service() << endl;
951
952     return str;
953   }
954
955   std::ostream & RepoInfo::dumpAsXmlOn( std::ostream & str, const std::string & content ) const
956   {
957     std::string tmpstr;
958     str
959       << "<repo"
960       << " alias=\"" << escape(alias()) << "\""
961       << " name=\"" << escape(name()) << "\"";
962     if (type() != repo::RepoType::NONE)
963       str << " type=\"" << type().asString() << "\"";
964     str
965       << " priority=\"" << priority() << "\""
966       << " enabled=\"" << enabled() << "\""
967       << " autorefresh=\"" << autorefresh() << "\""
968       << " gpgcheck=\"" << gpgCheck() << "\""
969       << " repo_gpgcheck=\"" << repoGpgCheck() << "\""
970       << " pkg_gpgcheck=\"" << pkgGpgCheck() << "\"";
971     if (!(tmpstr = gpgKeyUrl().asString()).empty())
972       str << " gpgkey=\"" << escape(tmpstr) << "\"";
973     if (!(tmpstr = mirrorListUrl().asString()).empty())
974       str << (_pimpl->_mirrorListForceMetalink ? " metalink=\"" : " mirrorlist=\"") << escape(tmpstr) << "\"";
975     str << ">" << endl;
976
977     if ( _pimpl->baseurl2dump() )
978     {
979       for_( it, baseUrlsBegin(), baseUrlsEnd() )        // !transform iterator replaces variables
980         str << "<url>" << escape((*it).asString()) << "</url>" << endl;
981     }
982
983     str << "</repo>" << endl;
984     return str;
985   }
986
987
988   std::ostream & operator<<( std::ostream & str, const RepoInfo & obj )
989   {
990     return obj.dumpOn(str);
991   }
992
993   std::ostream & operator<<( std::ostream & str, const RepoInfo::GpgCheck & obj )
994   {
995     switch ( obj )
996     {
997 #define OUTS( V ) case RepoInfo::V: return str << #V; break
998       OUTS( GpgCheck::On );
999       OUTS( GpgCheck::Strict );
1000       OUTS( GpgCheck::AllowUnsigned );
1001       OUTS( GpgCheck::AllowUnsignedRepo );
1002       OUTS( GpgCheck::AllowUnsignedPackage );
1003       OUTS( GpgCheck::Default );
1004       OUTS( GpgCheck::Off );
1005       OUTS( GpgCheck::indeterminate );
1006 #undef OUTS
1007     }
1008     return str << "GpgCheck::UNKNOWN";
1009   }
1010
1011   /////////////////////////////////////////////////////////////////
1012 } // namespace zypp
1013 ///////////////////////////////////////////////////////////////////