+ /** Path to a license tarball in case it exists in the repo. */
+ Pathname licenseTgz( const std::string & name_r ) const
+ {
+ Pathname ret;
+ if ( !metadataPath().empty() )
+ {
+ std::string licenseStem( "license" );
+ if ( !name_r.empty() )
+ {
+ licenseStem += "-";
+ licenseStem += name_r;
+ }
+
+ filesystem::Glob g;
+ // TODO: REPOMD: this assumes we know the name of the tarball. In fact
+ // we'd need to get the file from repomd.xml (<data type="license[-name_r]">)
+ g.add( metadataPath() / path / ("repodata/*"+licenseStem+".tar.gz") );
+ if ( g.empty() )
+ g.add( metadataPath() / path / (licenseStem+".tar.gz") );
+
+ if ( !g.empty() )
+ ret = *g.begin();
+ }
+ return ret;
+ }
+
+ const RepoVariablesReplacedUrlList & baseUrls() const
+ {
+ const Url & mlurl( _mirrorListUrl.transformed() ); // Variables replaced!
+ if ( _baseUrls.empty() && ! mlurl.asString().empty() )
+ {
+ emptybaseurls = true;
+ DBG << "MetadataPath: " << metadataPath() << endl;
+ repo::RepoMirrorList rmurls( mlurl, metadataPath(), _mirrorListForceMetalink );
+ _baseUrls.raw().insert( _baseUrls.raw().end(), rmurls.getUrls().begin(), rmurls.getUrls().end() );
+ }
+ return _baseUrls;
+ }
+
+ RepoVariablesReplacedUrlList & baseUrls()
+ { return _baseUrls; }
+
+ bool baseurl2dump() const
+ { return !emptybaseurls && !_baseUrls.empty(); }
+
+
+ const RepoVariablesReplacedUrlList & gpgKeyUrls() const
+ { return _gpgKeyUrls; }
+
+ RepoVariablesReplacedUrlList & gpgKeyUrls()
+ { return _gpgKeyUrls; }
+
+
+ const std::set<std::string> & contentKeywords() const
+ { hasContent()/*init if not yet done*/; return _keywords.second; }
+
+ void addContent( const std::string & keyword_r )
+ { _keywords.second.insert( keyword_r ); if ( ! hasContent() ) _keywords.first = true; }
+
+ bool hasContent() const
+ {
+ if ( !_keywords.first && ! metadataPath().empty() )
+ {
+ // HACK directly check master index file until RepoManager offers
+ // some content probing and zypper uses it.
+ /////////////////////////////////////////////////////////////////
+ MIL << "Empty keywords...." << metadataPath() << endl;
+ Pathname master;
+ if ( PathInfo( (master=metadataPath()/"/repodata/repomd.xml") ).isFile() )
+ {
+ //MIL << "GO repomd.." << endl;
+ xml::Reader reader( master );
+ while ( reader.seekToNode( 2, "content" ) )
+ {
+ _keywords.second.insert( reader.nodeText().asString() );
+ reader.seekToEndNode( 2, "content" );
+ }
+ _keywords.first = true; // valid content in _keywords even if empty
+ }
+ else if ( PathInfo( (master=metadataPath()/"/content") ).isFile() )
+ {
+ //MIL << "GO content.." << endl;
+ iostr::forEachLine( InputStream( master ),
+ [this]( int num_r, std::string line_r )->bool
+ {
+ if ( str::startsWith( line_r, "REPOKEYWORDS" ) )
+ {
+ std::vector<std::string> words;
+ if ( str::split( line_r, std::back_inserter(words) ) > 1
+ && words[0].length() == 12 /*"REPOKEYWORDS"*/ )
+ {
+ this->_keywords.second.insert( ++words.begin(), words.end() );
+ }
+ return true; // mult. occurrances are ok.
+ }
+ return( ! str::startsWith( line_r, "META " ) ); // no need to parse into META section.
+ } );
+ _keywords.first = true; // valid content in _keywords even if empty
+ }
+ /////////////////////////////////////////////////////////////////
+ }
+ return _keywords.first;
+ }
+
+ bool hasContent( const std::string & keyword_r ) const
+ { return( hasContent() && _keywords.second.find( keyword_r ) != _keywords.second.end() ); }
+
+ /** Signature check result needs to be stored/retrieved from _metadataPath.
+ * Don't call them from outside validRepoSignature/setValidRepoSignature
+ */
+ //@{
+ TriBool internalValidRepoSignature() const
+ {
+ if ( ! indeterminate(_validRepoSignature) )
+ return _validRepoSignature;
+ // check metadata:
+ if ( ! metadataPath().empty() )
+ {
+ // A missing ".repo_gpgcheck" might be plaindir(no Downloader) or not yet refreshed signed repo!
+ TriBool linkval = triBoolFromPath( metadataPath() / ".repo_gpgcheck" );
+ return linkval;
+ }
+ return indeterminate;
+ }
+
+ void internalSetValidRepoSignature( TriBool value_r )
+ {
+ if ( PathInfo(metadataPath()).isDir() )
+ {
+ Pathname gpgcheckFile( metadataPath() / ".repo_gpgcheck" );
+ if ( PathInfo(gpgcheckFile).isExist() )
+ {
+ TriBool linkval( indeterminate );
+ if ( triBoolFromPath( gpgcheckFile, linkval ) && linkval == value_r )
+ return; // existing symlink fits value_r
+ else
+ filesystem::unlink( gpgcheckFile ); // will write a new one
+ }
+ filesystem::symlink( asString(value_r), gpgcheckFile );
+ }
+ _validRepoSignature = value_r;
+ }
+
+ /** We definitely have a symlink pointing to "indeterminate" (for repoGpgCheckIsMandatory)?
+ * I.e. user accepted the unsigned repo in Downloader. A test whether `internalValidRepoSignature`
+ * is indeterminate would include not yet checked repos, which is unwanted here.
+ */
+ bool internalUnsignedConfirmed() const
+ {
+ TriBool linkval( true ); // want to see it being switched to indeterminate
+ return triBoolFromPath( metadataPath() / ".repo_gpgcheck", linkval ) && indeterminate(linkval);
+ }
+
+ bool triBoolFromPath( const Pathname & path_r, TriBool & ret_r ) const
+ {
+ static const Pathname truePath( "true" );
+ static const Pathname falsePath( "false" );
+ static const Pathname indeterminatePath( "indeterminate" );
+
+ // Quiet readlink;
+ static const ssize_t bufsiz = 63;
+ static char buf[bufsiz+1];
+ ssize_t ret = ::readlink( path_r.c_str(), buf, bufsiz );
+ buf[ret == -1 ? 0 : ret] = '\0';
+
+ Pathname linkval( buf );
+
+ bool known = true;
+ if ( linkval == truePath )
+ ret_r = true;
+ else if ( linkval == falsePath )
+ ret_r = false;
+ else if ( linkval == indeterminatePath )
+ ret_r = indeterminate;
+ else
+ known = false;
+ return known;
+ }
+
+ TriBool triBoolFromPath( const Pathname & path_r ) const
+ { TriBool ret(indeterminate); triBoolFromPath( path_r, ret ); return ret; }
+
+ //@}
+
+ private:
+ TriBool _rawGpgCheck; ///< default gpgcheck behavior: Y/N/ZConf
+ TriBool _rawRepoGpgCheck; ///< need to check repo sign.: Y/N/(ZConf(Y/N/gpgCheck))
+ TriBool _rawPkgGpgCheck; ///< need to check pkg sign.: Y/N/(ZConf(Y/N/gpgCheck))
+
+ public:
+ TriBool rawGpgCheck() const { return _rawGpgCheck; }
+ TriBool rawRepoGpgCheck() const { return _rawRepoGpgCheck; }
+ TriBool rawPkgGpgCheck() const { return _rawPkgGpgCheck; }
+
+ void rawGpgCheck( TriBool val_r ) { _rawGpgCheck = val_r; }
+ void rawRepoGpgCheck( TriBool val_r ) { _rawRepoGpgCheck = val_r; }
+ void rawPkgGpgCheck( TriBool val_r ) { _rawPkgGpgCheck = val_r; }
+
+ bool cfgGpgCheck() const
+ { return indeterminate(_rawGpgCheck) ? ZConfig::instance().gpgCheck() : (bool)_rawGpgCheck; }
+ TriBool cfgRepoGpgCheck() const
+ { return indeterminate(_rawGpgCheck) && indeterminate(_rawRepoGpgCheck) ? ZConfig::instance().repoGpgCheck() : _rawRepoGpgCheck; }
+ TriBool cfgPkgGpgCheck() const
+ { return indeterminate(_rawGpgCheck) && indeterminate(_rawPkgGpgCheck) ? ZConfig::instance().pkgGpgCheck() : _rawPkgGpgCheck; }
+
+ private:
+ TriBool _validRepoSignature;///< have signed and valid repo metadata
+ public: