SET(LIBZYPP_MAJOR "14")
SET(LIBZYPP_COMPATMINOR "39")
SET(LIBZYPP_MINOR "45")
-SET(LIBZYPP_PATCH "0")
+SET(LIBZYPP_PATCH "10")
#
-# LAST RELEASED: 14.45.0 (39)
+# LAST RELEASED: 14.45.10 (39)
# (The number in parenthesis is LIBZYPP_COMPATMINOR)
#=======
<DD><DL>
<DT>version 0</DT>
<DD>General ability to provide and handle plugins.</DD>
+ <DT>version 0.1</DT>
+ <DD>Bugfix: don't reject header values containing a ':'.</DD>
</DL></DD>
<DT>plugin:commit</DT>
Obsoletes: yast2-packagemanager
# Features we provide (update doc/autoinclude/FeatureTest.doc):
-Provides: libzypp(plugin) = 0
+Provides: libzypp(plugin) = 0.1
Provides: libzypp(plugin:appdata) = 0
Provides: libzypp(plugin:commit) = 1
Provides: libzypp(plugin:services) = 1
-------------------------------------------------------------------
+Tue Feb 20 19:01:49 CET 2018 - ma@suse.de
+
+- Adapt loop mounting of iso images (bsc#1038132, bsc#1033236)
+- version 14.45.10 (39)
+
+-------------------------------------------------------------------
+Tue Feb 20 18:43:33 CET 2018 - ma@suse.de
+
+- RpmDb::checkPackage: fix parsing localized rpm output (bsc#1076415)
+- version 14.45.9 (39)
+
+-------------------------------------------------------------------
+Thu Jan 4 17:51:56 CET 2018 - ma@suse.de
+
+- plugin: don't reject header values containing a ':' (bsc#1074687)
+- version 14.45.8 (39)
+
+-------------------------------------------------------------------
+Thu Nov 23 12:43:57 CET 2017 - ma@suse.de
+
+- Don't filter procs with a different mnt namespace (bsc#1068708)
+- version 14.45.7 (39)
+
+-------------------------------------------------------------------
+Mon Sep 4 11:43:22 CEST 2017 - ma@suse.de
+
+- verifyFileSignature: Support signing with subkeys (bsc#1008325)
+- Fix gpg-pubkey relase (creation time) computation (bsc#1036659)
+- More specific exception message if GPG binary is missing (bsc#637791)
+- version 14.45.6 (39)
+
+-------------------------------------------------------------------
+Fri Jun 9 11:25:00 CEST 2017 - ma@suse.de
+
+- Fix potential crash if repo has no baseurl (bnc#1043218)
+- version 14.45.5 (39)
+
+-------------------------------------------------------------------
+Wed May 31 12:47:52 CEST 2017 - ma@suse.de
+
+- Testcase: add missing solver flags (bsc#1041889)
+- version 14.45.4 (39)
+
+-------------------------------------------------------------------
+Mon Mar 27 09:36:53 CEST 2017 - ma@suse.de
+
+- Fix X-libcurl-Empty-Header-Workaround (bsc#1030919)
+- version 14.45.3 (39)
+
+-------------------------------------------------------------------
+Tue Mar 21 13:40:30 CET 2017 - ma@suse.de
+
+- MediaCurl: Treat http response 410(Gone) like 404(Not Found)
+ (bsc#1030136)
+- version 14.45.2 (39)
+
+-------------------------------------------------------------------
+Tue Feb 14 12:10:58 CET 2017 - ma@suse.de
+
+- dumpAsXmlOnL: xml escape node content (bsc#1024909)
+- version 14.45.1 (39)
+
+-------------------------------------------------------------------
+Thu Dec 22 01:14:09 CET 2016 - ma@suse.de
+
+- Update zypp-po.tar.bz2
+
+-------------------------------------------------------------------
+Thu Dec 22 01:13:35 CET 2016 - ma@suse.de
+
+- Update sle-zypp-po.tar.bz2
+
+-------------------------------------------------------------------
Fri Dec 16 11:27:36 CET 2016 - ma@suse.de
- Merge branch 'bsc#985390' for zypper
base/GzStream.h
base/IOStream.h
base/InputStream.h
+ base/Iterable.h
base/Iterator.h
base/Json.h
+ base/LocaleGuard.h
base/LogControl.h
base/LogTools.h
base/Logger.h
<< timestamp() // 1 timestamp
<< _sep << HistoryActionID::REPO_ADD.asString(true) // 2 action
<< _sep << str::escape(repo.alias(), _sep) // 3 alias
- << _sep << *repo.baseUrlsBegin() // 4 primary URL
+ << _sep << str::escape(repo.url().asString(), _sep) // 4 primary URL
<< _sep << str::escape(ZConfig::instance().userData(), _sep) // 5 userdata
<< endl;
}
<< _sep << str::escape(ZConfig::instance().userData(), _sep) // 5 userdata
<< endl;
}
- if (*oldrepo.baseUrlsBegin() != *newrepo.baseUrlsBegin())
+ if ( oldrepo.url() != newrepo.url() )
{
_log
<< timestamp() // 1 timestamp
<< _sep << HistoryActionID::REPO_CHANGE_URL.asString(true) // 2 action
- << _sep << str::escape(oldrepo.alias(), _sep) // 3 old url
- << _sep << *newrepo.baseUrlsBegin() // 4 new url
+ << _sep << str::escape(oldrepo.url().asString(), _sep) // 3 old url
+ << _sep << str::escape(newrepo.url().asString(), _sep) // 4 new url
<< _sep << str::escape(ZConfig::instance().userData(), _sep) // 5 userdata
<< endl;
}
PublicKeyData KeyRing::Impl::publicKeyExists( const std::string & id, const Pathname & keyring )
{
MIL << "Searching key [" << id << "] in keyring " << keyring << endl;
- const std::list<PublicKeyData> & keys( publicKeyData( keyring ) );
- for_( it, keys.begin(), keys.end() )
+ PublicKeyData ret;
+ for ( const PublicKeyData & key : publicKeyData( keyring ) )
{
- if ( id == (*it).id() )
+ if ( key.providesKey( id ) )
{
- return *it;
+ ret = key;
+ break;
}
}
- return PublicKeyData();
+ return ret;
}
PublicKey KeyRing::Impl::exportKey( const PublicKeyData & keyData, const Pathname & keyring )
return res;
}
- // get the id of the signature
+ // get the id of the signature (it might be a subkey id!)
std::string id = readSignatureKeyId( signature );
- // doeskey exists in trusted keyring
+ // does key exists in trusted keyring
PublicKeyData trustedKeyData( publicKeyExists( id, trustedKeyRing() ) );
if ( trustedKeyData )
{
{
// bnc #393160: Comment #30: Compare at least the fingerprint
// in case an attacker created a key the the same id.
+ //
+ // FIXME: bsc#1008325: For keys using subkeys, we'd actually need
+ // to compare the subkey sets, to tell whether a key was updated.
+ // because created() remains unchanged if the primary key is not touched.
+ // For now we wait until a new subkey signs the data and treat it as a
+ // new key (else part below).
if ( trustedKeyData.fingerprint() == generalKeyData.fingerprint()
&& trustedKeyData.created() < generalKeyData.created() )
{
{
if ( key_r.find_first_of( ":\n" ) != std::string::npos )
ZYPP_THROW( PluginFrameException( "Illegal char in header key", key_r ) );
- if ( value_r.find_first_of( ":\n" ) != std::string::npos )
+ if ( value_r.find_first_of( "\n" ) != std::string::npos )
ZYPP_THROW( PluginFrameException( "Illegal char in header value", value_r ) );
return HeaderList::value_type( key_r, value_r );
}
///////////////////////////////////////////////////////////////////
namespace zypp
-{ /////////////////////////////////////////////////////////////////
+{
+ ///////////////////////////////////////////////////////////////////
+ namespace
+ {
+ inline bool isExpired( const Date & expires_r )
+ { return( expires_r && expires_r < Date::now() ); }
+
+ inline int hasDaysToLive( const Date & expires_r )
+ {
+ if ( expires_r )
+ {
+ Date exp( expires_r - Date::now() );
+ int ret = exp / Date::day;
+ if ( exp < 0 ) ret -= 1;
+ return ret;
+ }
+ return INT_MAX;
+ }
+
+ inline std::string expiresDetail( const Date & expires_r )
+ {
+ str::Str str;
+ if ( ! expires_r )
+ {
+ // translators: an annotation to a gpg keys expiry date
+ str << _("does not expire");
+ }
+ else if ( isExpired( expires_r ) )
+ {
+ // translators: an annotation to a gpg keys expiry date: "expired: 1999-04-12"
+ str << ( str::Format(_("expired: %1%") ) % expires_r.printDate() );
+ }
+ else
+ {
+ // translators: an annotation to a gpg keys expiry date: "expires: 2111-04-12"
+ str << ( str::Format(_("expires: %1%") ) % expires_r.printDate() );
+ }
+ return str;
+ }
+
+ inline std::string expiresDetailVerbose( const Date & expires_r )
+ {
+ if ( !expires_r )
+ { // translators: an annotation to a gpg keys expiry date
+ return _("(does not expire)");
+ }
+ std::string ret( expires_r.asString() );
+ int ttl( hasDaysToLive( expires_r ) );
+ if ( ttl <= 90 )
+ {
+ ret += " ";
+ if ( ttl < 0 )
+ { // translators: an annotation to a gpg keys expiry date
+ ret += _("(EXPIRED)");
+ }
+ else if ( ttl == 0 )
+ { // translators: an annotation to a gpg keys expiry date
+ ret += _("(expires within 24h)");
+ }
+ else
+ { // translators: an annotation to a gpg keys expiry date
+ ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
+ }
+ }
+ return ret;
+ }
+
+ } //namespace
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ /// \class PublicSubkeyData::Impl
+ /// \brief PublicSubkeyData implementation.
+ ///////////////////////////////////////////////////////////////////
+ struct PublicSubkeyData::Impl
+ {
+ std::string _id;
+ Date _created;
+ Date _expires;
+
+ public:
+ /** Offer default Impl. */
+ static shared_ptr<Impl> nullimpl()
+ {
+ static shared_ptr<Impl> _nullimpl( new Impl );
+ return _nullimpl;
+ }
+
+ private:
+ friend Impl * rwcowClone<Impl>( const Impl * rhs );
+ /** clone for RWCOW_pointer */
+ Impl * clone() const
+ { return new Impl( *this ); }
+ };
+
+ ///////////////////////////////////////////////////////////////////
+ /// class PublicSubkeyData
+ ///////////////////////////////////////////////////////////////////
+
+ PublicSubkeyData::PublicSubkeyData()
+ : _pimpl( Impl::nullimpl() )
+ {}
+
+ PublicSubkeyData::~PublicSubkeyData()
+ {}
+
+ PublicSubkeyData::operator bool() const
+ { return !_pimpl->_id.empty(); }
+
+ std::string PublicSubkeyData::id() const
+ { return _pimpl->_id; }
+
+ Date PublicSubkeyData::created() const
+ { return _pimpl->_created; }
+
+ Date PublicSubkeyData::expires() const
+ { return _pimpl->_expires; }
+
+ bool PublicSubkeyData::expired() const
+ { return isExpired( _pimpl->_expires ); }
+
+ int PublicSubkeyData::daysToLive() const
+ { return hasDaysToLive( _pimpl->_expires ); }
+
+ std::string PublicSubkeyData::asString() const
+ {
+ return str::Str() << id() << " " << created().printDate() << " [" << expiresDetail( expires() ) << "]";
+ }
///////////////////////////////////////////////////////////////////
/// \class PublicKeyData::Impl
Date _created;
Date _expires;
+ std::vector<PublicSubkeyData> _subkeys;
+
+ public:
+ bool hasSubkeyId( const std::string & id_r ) const
+ {
+ bool ret = false;
+ for ( const PublicSubkeyData & sub : _subkeys )
+ {
+ if ( sub.id() == id_r )
+ {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+ }
+
public:
/** Offer default Impl. */
static shared_ptr<Impl> nullimpl()
Impl * clone() const
{ return new Impl( *this ); }
};
- ///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
/// class PublicKeyData
{ return _pimpl->_expires; }
bool PublicKeyData::expired() const
- { return( _pimpl->_expires && _pimpl->_expires < Date::now() ); }
+ { return isExpired( _pimpl->_expires ); }
int PublicKeyData::daysToLive() const
- {
- if ( _pimpl->_expires )
- {
- Date exp( _pimpl->_expires - Date::now() );
- int ret = exp / Date::day;
- if ( exp < 0 ) ret -= 1;
- return ret;
- }
- return INT_MAX;
- }
+ { return hasDaysToLive( _pimpl->_expires ); }
std::string PublicKeyData::expiresAsString() const
- {
- if ( !_pimpl->_expires )
- { // translators: an annotation to a gpg keys expiry date
- return _("(does not expire)");
- }
- std::string ret( _pimpl->_expires.asString() );
- int ttl( daysToLive() );
- if ( ttl <= 90 )
- {
- ret += " ";
- if ( ttl < 0 )
- { // translators: an annotation to a gpg keys expiry date
- ret += _("(EXPIRED)");
- }
- else if ( ttl == 0 )
- { // translators: an annotation to a gpg keys expiry date
- ret += _("(expires within 24h)");
- }
- else
- { // translators: an annotation to a gpg keys expiry date
- ret += str::form( _PL("(expires in %d day)", "(expires in %d days)", ttl ), ttl );
- }
- }
- return ret;
- }
+ { return expiresDetailVerbose( _pimpl->_expires ); }
std::string PublicKeyData::gpgPubkeyVersion() const
{ return _pimpl->_id.empty() ? _pimpl->_id : str::toLower( _pimpl->_id.substr(8,8) ); }
std::string PublicKeyData::asString() const
{
- return str::form( "[%s-%s] [%s] [%s] [TTL %d]",
- _pimpl->_id.c_str(),
- gpgPubkeyRelease().c_str(),
- _pimpl->_name.c_str(),
- _pimpl->_fingerprint.c_str(),
- daysToLive() );
+ str::Str str;
+ str << "[" << _pimpl->_id << "-" << gpgPubkeyRelease();
+ for ( auto && sub : _pimpl->_subkeys )
+ str << ", " << sub.id();
+ return str << "] [" << _pimpl->_name.c_str() << "] [" << expiresDetail( _pimpl->_expires ) << "]";
}
+ bool PublicKeyData::hasSubkeys() const
+ { return !_pimpl->_subkeys.empty(); }
+
+ Iterable<PublicKeyData::SubkeyIterator> PublicKeyData::subkeys() const
+ { return makeIterable( &(*_pimpl->_subkeys.begin()), &(*_pimpl->_subkeys.end()) ); }
+
+ bool PublicKeyData::providesKey( const std::string & id_r ) const
+ { return( id_r == _pimpl->_id || _pimpl->hasSubkeyId( id_r ) ); }
+
std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj )
{
str << "[" << obj.name() << "]" << endl;
str << " cre " << Date::ValueType(obj.created()) << ' ' << obj.created() << endl;
str << " exp " << Date::ValueType(obj.expires()) << ' ' << obj.expiresAsString() << endl;
str << " ttl " << obj.daysToLive() << endl;
+ for ( auto && sub : obj._pimpl->_subkeys )
+ str << " sub " << sub << endl;
str << " rpm " << obj.gpgPubkeyVersion() << "-" << obj.gpgPubkeyRelease() << endl;
- str << "]";
return str;
}
///////////////////////////////////////////////////////////////////
struct PublicKeyScanner::Impl
{
- std::vector<std::string> _words;
- enum { pNONE, pPUB, pSIG, pFPR, pUID } _parseEntry;
- bool _parseOff; // no 'sub:' key parsing
+ enum { pNONE, pPUB, pSIG, pFPR, pUID, pSUB } _parseEntry;
+ std::vector<std::string> _words;
+ PublicKeyData::Impl * _keyDataPtr;
Impl()
: _parseEntry( pNONE )
- , _parseOff( false )
+ , _keyDataPtr( nullptr )
{}
void scan( std::string & line_r, std::list<PublicKeyData> & keys_r )
if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
{
_parseEntry = pPUB;
- _parseOff = false;
+ keys_r.push_back( PublicKeyData() ); // reset upon new key
+ _keyDataPtr = keys_r.back()._pimpl.get();
}
break;
if ( line_r[1] == 'i' && line_r[2] == 'g' && line_r[3] == ':' )
_parseEntry = pSIG;
else if ( line_r[1] == 'u' && line_r[2] == 'b' && line_r[3] == ':' )
- _parseOff = true;
+ _parseEntry = pSUB;
break;
default:
return;
}
- if ( _parseOff || _parseEntry == pNONE )
+ if ( _parseEntry == pNONE )
return;
+ if ( ! ( _keyDataPtr->_subkeys.empty() || _parseEntry == pSUB ) )
+ return; // collecting subkeys only
if ( line_r[line_r.size()-1] == '\n' )
line_r.erase( line_r.size()-1 );
- // DBG << line_r << endl;
+ //DBG << line_r << endl;
_words.clear();
str::splitFields( line_r, std::back_inserter(_words), ":" );
- PublicKeyData * key( &keys_r.back() );
-
switch ( _parseEntry )
{
case pPUB:
- keys_r.push_back( PublicKeyData() ); // reset upon new key
- key = &keys_r.back();
- key->_pimpl->_id = _words[4];
- key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
- key->_pimpl->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
- key->_pimpl->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
+ _keyDataPtr->_id = _words[4];
+ _keyDataPtr->_name = str::replaceAll( _words[9], "\\x3a", ":" );
+ _keyDataPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
+ _keyDataPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
break;
case pSIG:
// Update creation/modification date from signatures type "13x".
- if ( ( _words.size() > 10 && _words[10] == "13x" )
- || ( _words.size() > 12 && _words[12] == "13x" ) )
+ if ( ( _words.size() > 10 && _words[10] == "13x" && !_words[9].empty() && _words[9] != "[User ID not found]" )
+ || ( _words.size() > 12 && _words[12] == "13x" /* [selfsig] */) )
{
Date cdate(str::strtonum<Date::ValueType>(_words[5]));
- if ( key->_pimpl->_created < cdate )
- key->_pimpl->_created = cdate;
+ if ( _keyDataPtr->_created < cdate )
+ _keyDataPtr->_created = cdate;
}
break;
case pFPR:
- if ( key->_pimpl->_fingerprint.empty() )
- key->_pimpl->_fingerprint = _words[9];
+ if ( _keyDataPtr->_fingerprint.empty() )
+ _keyDataPtr->_fingerprint = _words[9];
break;
case pUID:
- if ( ! _words[9].empty() )
- key->_pimpl->_name = str::replaceAll( _words[9], "\\x3a", ":" );
+ if ( ! _words[9].empty() && _words[9] != "[User ID not found]" )
+ _keyDataPtr->_name = str::replaceAll( _words[9], "\\x3a", ":" );
+ break;
+
+ case pSUB:
+ _keyDataPtr->_subkeys.push_back( PublicSubkeyData() );
+ {
+ PublicSubkeyData::Impl * subPtr = _keyDataPtr->_subkeys.back()._pimpl.get();
+ subPtr->_id = _words[4];
+ subPtr->_created = Date(str::strtonum<Date::ValueType>(_words[5]));
+ subPtr->_expires = Date(str::strtonum<Date::ValueType>(_words[6]));
+ }
break;
case pNONE:
{
scanner.scan( line );
}
- prog.close();
+ int ret = prog.close();
switch ( scanner._keys.size() )
{
case 0:
- ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
+ if ( ret == 129 )
+ ZYPP_THROW( Exception( std::string("Can't read public key data: ") + GPG_BINARY + " is not installed!" ) );
+ else
+ ZYPP_THROW( BadKeyException( "File " + _dataFile.path().asString() + " doesn't contain public key data" , _dataFile.path() ) );
break;
case 1:
#include <set>
#include <string>
+#include "zypp/base/Iterable.h"
#include "zypp/base/PtrTypes.h"
#include "zypp/base/Exception.h"
#include "zypp/Pathname.h"
{
class TmpFile;
}
+ class PublicKeyData;
///////////////////////////////////////////////////////////////////
/// \class BadKeyException
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
+ /// \class PublicSubkeyData
+ /// \brief Class representing a GPG Public Keys subkeys.
+ /// \see \ref PublicKeyData.
+ ///////////////////////////////////////////////////////////////////
+ class PublicSubkeyData
+ {
+ public:
+ /** Default constructed: empty data. */
+ PublicSubkeyData();
+
+ ~PublicSubkeyData();
+
+ /** Whether this contains valid data (not default constructed). */
+ explicit operator bool() const;
+
+ public:
+ /** Subkey ID. */
+ std::string id() const;
+
+ /** Creation date. */
+ Date created() const;
+
+ /** Expiry date, or \c Date() if the key never expires. */
+ Date expires() const;
+
+ /** Whether the key has expired. */
+ bool expired() const;
+
+ /** Number of days (24h) until the key expires (or since it exired).
+ * A value of \c 0 means the key will expire within the next 24h.
+ * Negative values indicate the key has expired less than \c N days ago.
+ * For keys without expiration date \c INT_MAX is returned.
+ */
+ int daysToLive() const;
+
+ /** Simple string representation.
+ * Encodes \ref id, \ref created and \ref expires
+ * \code
+ * 640DB551 2016-04-12 [expires: 2019-04-12]
+ * \endcode
+ */
+ std::string asString() const;
+
+ private:
+ class Impl;
+ RWCOW_pointer<Impl> _pimpl;
+ friend class PublicKeyScanner;
+ friend std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj );
+ };
+ ///////////////////////////////////////////////////////////////////
+
+ /** \relates PublicSubkeyData Stream output */
+ inline std::ostream & operator<<( std::ostream & str, const PublicSubkeyData & obj )
+ { return str << obj.asString(); }
+
+ ///////////////////////////////////////////////////////////////////
/// \class PublicKeyData
/// \brief Class representing one GPG Public Keys data.
/// \ref PublicKeyData are provided e.g. by a \ref PublicKey or
~PublicKeyData();
- /** Scan data from 'gpg --with-colons' key listings. */
- friend class PublicKeyScanner;
-
/** Whether this contains valid data (not default constructed). */
explicit operator bool() const;
*/
std::string asString() const;
+ public:
+ typedef const PublicSubkeyData * SubkeyIterator;
+
+ /** Whether \ref subkeys is not empty. */
+ bool hasSubkeys() const;
+
+ /** Iterate any subkeys. */
+ Iterable<SubkeyIterator> subkeys() const;
+
+ /** Whether \a id_r is the \ref id of the primary key or of a subkey. */
+ bool providesKey( const std::string & id_r ) const;
+
private:
class Impl;
RWCOW_pointer<Impl> _pimpl;
+ friend class PublicKeyScanner;
+ friend std::ostream & dumpOn( std::ostream & str, const PublicKeyData & obj );
};
///////////////////////////////////////////////////////////////////
/** The public keys data (\see \ref PublicKeyData).*/
const PublicKeyData & keyData() const;
+ typedef PublicKeyData::SubkeyIterator SubkeyIterator;
+
bool isValid() const
{ return ! ( id().empty() || fingerprint().empty() ); }
std::string gpgPubkeyRelease() const; //!< \see \ref PublicKeyData
std::string asString() const; //!< \see \ref PublicKeyData
+ bool hasSubkeys() const ///!< \see \ref PublicKeyData
+ { return keyData().hasSubkeys(); }
+
+ Iterable<SubkeyIterator> subkeys() const ///!< \see \ref PublicKeyData
+ { return keyData().subkeys(); }
+
+ bool providesKey( const std::string & id_r ) const ///!< \see \ref PublicKeyData
+ { return keyData().providesKey( id_r ); }
+
public:
/** File containig the ASCII armored key. */
Pathname path() const;
if ( repokind == RepoType::RPMPLAINDIR )
{
- forPlainDirs.reset( new MediaMounter( *info.baseUrlsBegin() ) );
+ forPlainDirs.reset( new MediaMounter( info.url() ) );
// recusive for plaindir as 2nd arg!
cmd.push_back( "-R" );
// FIXME this does only work form dir: URLs
if ( _options.probe )
{
DBG << "unknown repository type, probing" << endl;
+ assert_urls(tosave);
- RepoType probedtype;
- probedtype = probe( *tosave.baseUrlsBegin(), info.path() );
- if ( tosave.baseUrlsSize() > 0 )
- {
- if ( probedtype == RepoType::NONE )
- ZYPP_THROW(RepoUnknownTypeException(info));
- else
- tosave.setType(probedtype);
- }
+ RepoType probedtype( probe( tosave.url(), info.path() ) );
+ if ( probedtype == RepoType::NONE )
+ ZYPP_THROW(RepoUnknownTypeException(info));
+ else
+ tosave.setType(probedtype);
}
progress.set(50);
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/Iterable.h
+ */
+#ifndef ZYPP_BASE_ITERABLE_H
+#define ZYPP_BASE_ITERABLE_H
+
+#include <iterator>
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+ ///////////////////////////////////////////////////////////////////
+ /// \class Iterable
+ /// \brief
+ /// \code
+ /// struct Foo
+ /// {
+ /// class Iterator;
+ /// typedef Iterable<Iterator> IterableType;
+ ///
+ /// Iterator myBegin();
+ /// Iterator myEnd();
+ ///
+ /// IterableType iterate() { return makeIterable( myBegin(), myEnd() ); }
+ /// };
+ /// \endcode
+ ///////////////////////////////////////////////////////////////////
+ template <class TIterator>
+ class Iterable
+ {
+ public:
+ typedef size_t size_type;
+ typedef TIterator iterator_type;
+ typedef typename std::iterator_traits<iterator_type>::value_type value_type;
+ typedef typename std::iterator_traits<iterator_type>::difference_type difference_type;
+ typedef typename std::iterator_traits<iterator_type>::pointer pointer;
+ typedef typename std::iterator_traits<iterator_type>::reference reference;
+ typedef typename std::iterator_traits<iterator_type>::iterator_category iterator_category;
+
+ /** Ctor taking the iterator pair */
+ Iterable()
+ {}
+
+ /** Ctor taking the iterator pair */
+ Iterable( iterator_type begin_r, iterator_type end_r )
+ : _begin( std::move(begin_r) )
+ , _end( std::move(end_r) )
+ {}
+
+ /** Ctor taking the iterator pair */
+ Iterable( std::pair<iterator_type,iterator_type> range_r )
+ : _begin( std::move(range_r.first) )
+ , _end( std::move(range_r.second) )
+ {}
+
+ iterator_type begin() const
+ { return _begin; }
+
+ iterator_type end() const
+ { return _end; }
+
+ bool empty() const
+ { return( _begin == _end ); }
+
+ size_type size() const
+ { size_type ret = 0; for ( iterator_type i = _begin; i != _end; ++i ) ++ret; return ret; }
+
+ bool contains( const value_type & val_r ) const
+ { return( find( val_r ) != _end ); }
+
+ iterator_type find( const value_type & val_r ) const
+ { iterator_type ret = _begin; for ( ; ret != _end; ++ret ) if ( *ret == val_r ) break; return ret; }
+
+ private:
+ iterator_type _begin;
+ iterator_type _end;
+ };
+
+ /** \relates Iterable convenient construction. */
+ template <class TIterator>
+ Iterable<TIterator> makeIterable( TIterator && begin_r, TIterator && end_r )
+ { return Iterable<TIterator>( std::forward<TIterator>(begin_r), std::forward<TIterator>(end_r) ); }
+
+ /** \relates Iterable convenient construction. */
+ template <class TIterator>
+ Iterable<TIterator> makeIterable( std::pair<TIterator,TIterator> && range_r )
+ { return Iterable<TIterator>( std::forward<std::pair<TIterator,TIterator>>(range_r) ); }
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_ITERABLE_H
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/base/LocaleGuard.h
+ */
+#ifndef ZYPP_BASE_LOCALEGUARD_H
+#define ZYPP_BASE_LOCALEGUARD_H
+
+#include <locale.h>
+#include <string>
+
+#include "zypp/base/Easy.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{
+ ///////////////////////////////////////////////////////////////////
+ /// \class LocaleGuard
+ /// \brief Temorarily change a locale category value
+ /// \ingroup g_RAII
+ ///////////////////////////////////////////////////////////////////
+ class LocaleGuard
+ {
+ NON_COPYABLE(LocaleGuard);
+ NON_MOVABLE(LocaleGuard);
+
+ public:
+ /** Ctor saving the current locale category value. */
+ LocaleGuard( int category_r, const std::string & value_r = "C" )
+ : _category( -1 )
+ {
+ const char * ovalue = ::setlocale( category_r, nullptr );
+ if ( ovalue && ovalue != value_r )
+ {
+ _category = category_r;
+ _value = ovalue;
+ ::setlocale( _category, value_r.c_str() );
+ }
+ }
+
+ /** Dtor asserts the saved locale category value is restored. */
+ ~LocaleGuard()
+ { restore(); }
+
+ /** immediately restore the saved locale category value. */
+ void restore()
+ {
+ if ( _category != -1 )
+ {
+ ::setlocale( _category, _value.c_str() );
+ _category = -1;
+ }
+ }
+
+ private:
+ int _category; ///< saved category or -1 if no restore needed
+ std::string _value; ///< saved category value
+ };
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_BASE_LOCALEGUARD_H
{
xmlout::Node guard( str, name_r, xmlout::Node::optionalContent );
const std::string & content( asString( obj ) );
- if ( ! content.empty() ) *guard << content;
+ if ( ! content.empty() ) *guard << xml::escape( content );
return str;
}
//@}
ZYPP_THROW(MediaForbiddenException(url, msg403));
}
case 404:
+ case 410:
ZYPP_THROW(MediaFileNotFoundException(_url, filename));
}
}
// differs
}
+ else // mixed cases:
+ {
+ // Type ISO: Since 11.1 mtab might contain the name of
+ // the loop device instead of the iso file:
+ if ( ref.mediaSource->type == "iso"
+ && str::hasPrefix( Pathname(e->src).asString(), "/dev/loop" )
+ && ref.attachPoint->path == Pathname(e->dir) )
+ {
+ DBG << "Found bound media "
+ << ref.mediaSource->asString()
+ << " in the mount table as " << e->src << std::endl;
+ _isAttached = true;
+ break;
+ }
+ }
}
if( !_isAttached)
#include "zypp/media/MediaISO.h"
-
-#define LOSETUP_TOOL_PATH "/sbin/losetup"
-
-using std::string;
using std::endl;
//////////////////////////////////////////////////////////////////////
}
// ---------------------------------------------------------------
- string MediaISO::findUnusedLoopDevice()
- {
- const char* argv[] =
- {
- LOSETUP_TOOL_PATH,
- "-f",
- NULL
- };
- ExternalProgram losetup(argv, ExternalProgram::Stderr_To_Stdout);
-
- string out = losetup.receiveLine();
- string device = out.substr(0, out.size() - 1); // remove the trailing endl
- for(; out.length(); out = losetup.receiveLine())
- DBG << "losetup: " << out;
-
- if (losetup.close() != 0)
- {
- ERR << LOSETUP_TOOL_PATH " failed to find an unused loop device." << std::endl;
- ZYPP_THROW(MediaNoLoopDeviceException(_url));
- }
-
- DBG << "found " << device << endl;
- return device;
- }
-
- // ---------------------------------------------------------------
void MediaISO::attachTo(bool next)
{
if(next)
ZYPP_THROW(MediaNotSupportedException(_url));
}
- //! \todo make this thread-safe - another thread might pick up the same device
- string loopdev = findUnusedLoopDevice(); // (bnc #428009)
-
- MediaSourceRef media( new MediaSource("iso", loopdev));
- PathInfo dinfo(loopdev);
- if( dinfo.isBlk())
- {
- media->maj_nr = dinfo.devMajor();
- media->min_nr = dinfo.devMinor();
- }
- else
- ERR << loopdev << " is not a block device" << endl;
+ MediaSourceRef media( new MediaSource("iso", isofile.asString() ) );
- AttachedMedia ret( findAttachedMedia( media));
+ AttachedMedia ret( findAttachedMedia(media));
if( ret.mediaSource &&
ret.attachPoint &&
!ret.attachPoint->empty())
setAttachPoint( mountpoint, true);
}
- std::string mountopts("ro,loop=" + loopdev);
+ std::string mountopts("ro,loop");
Mount mount;
mount.mount(isofile.asString(), mountpoint,
MediaAccessId _isosource;
std::string _filesystem;
- private:
- std::string findUnusedLoopDevice();
-
protected:
virtual void attachTo (bool next = false);
void TransferSettings::addHeader( const std::string &header )
{
+ if ( ! header.empty() )
_impl->_headers.push_back(header);
}
std::pair<std::string, std::string> values(*it);
// curl resets headers that are empty, so we use a workaround
if (values.second.empty()) {
- values.second = "\nX-libcurl-Empty-Header-Workaround: *";
+ values.second = "\r\nX-libcurl-Empty-Header-Workaround: *";
}
headers.insert(values);
}
/// \class FilterRunsInLXC
/// \brief Functor guessing whether \a PID is running in a container.
///
- /// Asumme a using different \c pid/mnt namespace than \c self.
+ /// Assumme using different \c pid namespace than \c self.
/////////////////////////////////////////////////////////////////
struct FilterRunsInLXC
{
bool operator()( pid_t pid_r ) const
- { return( nsIno( pid_r, "pid" ) != pidNS || nsIno( pid_r, "mnt" ) != mntNS ); }
+ { return( nsIno( pid_r, "pid" ) != pidNS ); }
FilterRunsInLXC()
: pidNS( nsIno( "self", "pid" ) )
- , mntNS( nsIno( "self", "mnt" ) )
{}
static inline ino_t nsIno( const std::string & pid_r, const std::string & ns_r )
{ return nsIno( asString(pid_r), ns_r ); }
ino_t pidNS;
- ino_t mntNS;
};
/////////////////////////////////////////////////////////////////
private:
std::string dumpFile; // Path of the generated testcase
std::ofstream *file;
+ bool _inSetup;
public:
HelixControl (const std::string & controlPath,
const LocaleSet &languages,
const target::Modalias::ModaliasList & modaliasList,
const std::set<std::string> & multiversionSpec,
- const std::string & systemPath,
- const bool forceResolve,
- const bool onlyRequires,
- const bool ignorealreadyrecommended);
+ const std::string & systemPath);
HelixControl ();
~HelixControl ();
+ void closeSetup()
+ {
+ if ( _inSetup )
+ {
+ *file << "</setup>" << endl << "<trial>" << endl;
+ _inSetup = false;
+ }
+ }
+
+ void addTagIf( const std::string & tag_r, bool yesno_r = true )
+ {
+ if ( yesno_r )
+ *file << (_inSetup ? TAB : "") << "<" << tag_r << "/>" << endl;
+ }
+
void installResolvable (const ResObject::constPtr &resObject,
const ResStatus &status);
void lockResolvable (const ResObject::constPtr &resObject,
void addDependencies (const CapabilitySet &capRequire, const CapabilitySet &capConflict);
void addUpgradeRepos( const std::set<Repository> & upgradeRepos_r );
- void distupgrade ();
- void verifySystem ();
- void update ();
-
std::string filename () { return dumpFile; }
};
const LocaleSet &languages,
const target::Modalias::ModaliasList & modaliasList,
const std::set<std::string> & multiversionSpec,
- const std::string & systemPath,
- const bool forceResolve,
- const bool onlyRequires,
- const bool ignorealreadyrecommended)
+ const std::string & systemPath)
:dumpFile (controlPath)
+ ,_inSetup( true )
{
file = new ofstream(controlPath.c_str());
if (!file) {
}
for_( it, modaliasList.begin(), modaliasList.end() ) {
- *file << TAB << "<modalias name=\"" << *it
+ *file << TAB << "<modalias name=\"" << xml_escape(*it)
<< "\" />" << endl;
}
<< "\" />" << endl;
}
- if (forceResolve)
- *file << TAB << "<forceResolve/>" << endl;
- if (onlyRequires)
- *file << TAB << "<onlyRequires/>" << endl;
- if (ignorealreadyrecommended)
- *file << TAB << "<ignorealreadyrecommended/>" << endl;
-
- *file << "</setup>" << endl
- << "<trial>" << endl;
+ // setup continued outside....
}
HelixControl::HelixControl()
HelixControl::~HelixControl()
{
+ closeSetup(); // in case it is still open
*file << "</trial>" << endl
<< "</test>" << endl;
delete(file);
}
}
-void HelixControl::distupgrade()
-{
- *file << "<distupgrade/>" << endl;
-}
-
-void HelixControl::verifySystem()
-{
- *file << "<verify/>" << endl;
-}
-
-void HelixControl::update()
-{
- *file << "<update/>" << endl;
-}
-
//---------------------------------------------------------------------------
Testcase::Testcase()
pool.getRequestedLocales(),
target::Modalias::instance().modaliasList(),
ZConfig::instance().multiversionSpec(),
- "solver-system.xml.gz",
- resolver.forceResolve(),
- resolver.onlyRequires(),
- resolver.ignoreAlreadyRecommended() );
+ "solver-system.xml.gz");
+
+ // In <setup>: resolver flags,...
+ control.addTagIf( "ignorealreadyrecommended", resolver.ignoreAlreadyRecommended() );
+ control.addTagIf( "onlyRequires", resolver.onlyRequires() );
+ control.addTagIf( "forceResolve", resolver.forceResolve() );
+
+ control.addTagIf( "cleandepsOnRemove", resolver.cleandepsOnRemove() );
+
+ control.addTagIf( "allowVendorChange", resolver.allowVendorChange() );
+
+ control.addTagIf( "dupAllowDowngrade", resolver.dupAllowDowngrade() );
+ control.addTagIf( "dupAllowNameChange", resolver.dupAllowNameChange() );
+ control.addTagIf( "dupAllowArchChange", resolver.dupAllowArchChange() );
+ control.addTagIf( "dupAllowVendorChange", resolver.dupAllowVendorChange() );
+
+ control.closeSetup();
+ // Entering <trial>...
for (PoolItemList::const_iterator iter = items_to_install.begin(); iter != items_to_install.end(); iter++) {
control.installResolvable (iter->resolvable(), iter->status());
SystemCheck::instance().conflictSystemCap());
control.addUpgradeRepos( resolver.upgradeRepos() );
- if (resolver.isUpgradeMode())
- control.distupgrade ();
- if (resolver.isUpdateMode())
- control.update();
- if (resolver.isVerifyingMode())
- control.verifySystem();
+ control.addTagIf( "distupgrade", resolver.isUpgradeMode() );
+ control.addTagIf( "update", resolver.isUpdateMode() );
+ control.addTagIf( "verify", resolver.isVerifyingMode() );
return true;
}
#include "zypp/base/Logger.h"
#include "zypp/base/String.h"
#include "zypp/base/Gettext.h"
+#include "zypp/base/LocaleGuard.h"
#include "zypp/Date.h"
#include "zypp/Pathname.h"
for_( it, rpmKeys.begin(), rpmKeys.end() )
{
- if ( keyEd == *it ) // quick test (Edition is IdStringType!)
+ // bsc#1008325: Keys using subkeys for signing don't get a higher release
+ // if new subkeys are added, because the primary key remains unchanged.
+ // For now always re-import keys with subkeys. Here we don't want to export the
+ // keys in the rpm database to check whether the subkeys are the same. The calling
+ // code should take care, we don't re-import the same kesy over and over again.
+ if ( keyEd == *it && !pubkey_r.hasSubkeys() ) // quick test (Edition is IdStringType!)
{
MIL << "Key " << pubkey_r << " is already in the rpm trusted keyring. (skip import)" << endl;
return;
qva.qva_flags = (VERIFY_DIGEST|VERIFY_SIGNATURE);
RpmlogCapture vresult;
+ LocaleGuard guard( LC_ALL, "C" ); // bsc#1076415: rpm log output is localized, but we need to parse it :(
int res = ::rpmVerifySignatures( &qva, ts, fd, path_r.basename().c_str() );
+ guard.restore();
ts = rpmtsFree(ts);
::Fclose( fd );