Blob Blame History Raw
# package.py
# Module defining the dnf.Package class.
#
# Copyright (C) 2012-2016 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
# Public License for more details.  You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.  Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#

""" Contains the dnf.Package class. """

from __future__ import absolute_import
from __future__ import unicode_literals

from dnf.i18n import _

import binascii
import dnf.rpm
import dnf.yum.misc
import hawkey
import logging
import os

logger = logging.getLogger("dnf")


class Package(hawkey.Package):
    """ Represents a package. #:api """

    DEBUGINFO_SUFFIX = "-debuginfo"  # :api
    DEBUGSOURCE_SUFFIX = "-debugsource"  # :api

    def __init__(self, initobject, base):
        super(Package, self).__init__(initobject)
        self.base = base
        self._priv_chksum = None
        self._repo = None
        self._priv_size = None

    @property
    def _chksum(self):
        if self._priv_chksum:
            return self._priv_chksum
        if self._from_cmdline:
            chksum_type = dnf.yum.misc.get_default_chksum_type()
            chksum_val = dnf.yum.misc.checksum(chksum_type, self.location)
            return (hawkey.chksum_type(chksum_type),
                    binascii.unhexlify(chksum_val))
        return super(Package, self).chksum

    @_chksum.setter
    def _chksum(self, val):
        self._priv_chksum = val

    @property
    def _from_cmdline(self):
        return self.reponame == hawkey.CMDLINE_REPO_NAME

    @property
    def _from_system(self):
        return self.reponame == hawkey.SYSTEM_REPO_NAME

    @property
    def _from_repo(self):
        """
        For installed packages returns id of repository from which the package was installed
        prefixed with '@' (if such information is available in the history database). Otherwise
        returns id of repository the package belongs to (@System for installed packages of unknown
        origin)
        """
        pkgrepo = None
        if self._from_system:
            pkgrepo = self.base.history.repo(self)
        if pkgrepo:
            return '@' + pkgrepo
        return self.reponame

    @property
    def from_repo(self):
        # :api
        if self._from_system:
            return self.base.history.repo(self)
        return ""

    @property
    def _header(self):
        return dnf.rpm._header(self.localPkg())

    @property
    def _size(self):
        if self._priv_size:
            return self._priv_size
        return super(Package, self).size

    @_size.setter
    def _size(self, val):
        self._priv_size = val

    @property
    def _pkgid(self):
        if self.hdr_chksum is None:
            return None
        (_, chksum) = self.hdr_chksum
        return binascii.hexlify(chksum)

    @property
    def source_name(self):
        # :api
        """
        returns name of source package
        e.g. krb5-libs -> krb5
        """
        if self.sourcerpm is not None:
            # trim suffix first
            srcname = dnf.util.rtrim(self.sourcerpm, ".src.rpm")
            # sourcerpm should be in form of name-version-release now, so we
            # will strip the two rightmost parts separated by dash.
            # Using rtrim with version and release of self is not sufficient
            # because the package can have different version to the source
            # package.
            srcname = srcname.rsplit('-', 2)[0]
        else:
            srcname = None
        return srcname

    @property
    def debug_name(self):
        # :api
        """
        Returns name of the debuginfo package for this package.
        If this package is a debuginfo package, returns its name.
        If this package is a debugsource package, returns the debuginfo package
        for the base package.
        e.g. kernel-PAE -> kernel-PAE-debuginfo
        """
        if self.name.endswith(self.DEBUGINFO_SUFFIX):
            return self.name

        name = self.name
        if self.name.endswith(self.DEBUGSOURCE_SUFFIX):
            name = name[:-len(self.DEBUGSOURCE_SUFFIX)]

        return name + self.DEBUGINFO_SUFFIX

    @property
    def debugsource_name(self):
        # :api
        """
        Returns name of the debugsource package for this package.
        e.g. krb5-libs -> krb5-debugsource
        """
        # assuming self.source_name is None only for a source package
        src_name = self.source_name if self.source_name is not None else self.name
        return src_name + self.DEBUGSOURCE_SUFFIX

    @property
    def source_debug_name(self):
        # :api
        """
        returns name of debuginfo package for source package of given package
        e.g. krb5-libs -> krb5-debuginfo
        """
        # assuming self.source_name is None only for a source package
        src_name = self.source_name if self.source_name is not None else self.name
        return src_name + self.DEBUGINFO_SUFFIX

    @property # yum compatibility attribute
    def idx(self):
        """ Always type it to int, rpm bindings expect it like that. """
        return int(self.rpmdbid)

    @property # yum compatibility attribute
    def repoid(self):
        return self.reponame

    @property # yum compatibility attribute
    def pkgtup(self):
        return (self.name, self.arch, str(self.e), self.v, self.r)

    @property # yum compatibility attribute
    def repo(self):
        if self._repo:
            return self._repo
        return self.base.repos[self.reponame]

    @repo.setter
    def repo(self, val):
        self._repo = val

    @property
    def reason(self):
        if self.repoid != hawkey.SYSTEM_REPO_NAME:
            return None
        return self.base.history.rpm.get_reason_name(self)

    @property # yum compatibility attribute
    def relativepath(self):
        return self.location

    @property # yum compatibility attribute
    def a(self):
        return self.arch

    @property # yum compatibility attribute
    def e(self):
        return self.epoch

    @property # yum compatibility attribute
    def v(self):
        return self.version

    @property # yum compatibility attribute
    def r(self):
        return self.release

    @property # yum compatibility attribute
    def ui_from_repo(self):
        return self.reponame

    # yum compatibility method
    def evr_eq(self, pkg):
        return self.evr_cmp(pkg) == 0

    # yum compatibility method
    def evr_gt(self, pkg):
        return self.evr_cmp(pkg) > 0

    # yum compatibility method
    def evr_lt(self, pkg):
        return self.evr_cmp(pkg) < 0

    # yum compatibility method
    def getDiscNum(self):
        return self.medianr

    # yum compatibility method
    def localPkg(self):
        """ Package's location in the filesystem.

            For packages in remote repo returns where the package will be/has
            been downloaded.
        """
        if self._from_cmdline:
            return self.location
        loc = self.location
        if self.repo._repo.isLocal() and self.baseurl and self.baseurl.startswith('file://'):
            return os.path.join(self.get_local_baseurl(), loc.lstrip("/"))
        if not self._is_local_pkg():
            loc = os.path.basename(loc)
        return os.path.join(self.pkgdir, loc.lstrip("/"))

    def remote_location(self, schemes=('http', 'ftp', 'file', 'https')):
        # :api
        """
        The location from where the package can be downloaded from

        :param schemes: list of allowed protocols. Default is ('http', 'ftp', 'file', 'https')
        :return: location (string) or None
        """
        return self.repo.remote_location(self.location, schemes)

    def _is_local_pkg(self):
        if self.repoid == "@System":
            return True
        if '://' in self.location and not self.location.startswith('file://'):
            # the package has a remote URL as its location
            return False
        return self._from_cmdline or \
            (self.repo._repo.isLocal() and (not self.baseurl or self.baseurl.startswith('file://')))

    @property
    def pkgdir(self):
        if (self.repo._repo.isLocal() and not self._is_local_pkg()):
            return self.repo.cache_pkgdir()
        else:
            return self.repo.pkgdir

    # yum compatibility method
    def returnIdSum(self):
        """ Return the chksum type and chksum string how the legacy yum expects
            it.
        """
        if self._chksum is None:
            return (None, None)
        (chksum_type, chksum) = self._chksum
        return (hawkey.chksum_name(chksum_type), binascii.hexlify(chksum).decode())

    # yum compatibility method
    def verifyLocalPkg(self):
        if self._from_system:
            raise ValueError("Can not verify an installed package.")
        if self._from_cmdline:
            return True # local package always verifies against itself
        (chksum_type, chksum) = self.returnIdSum()
        real_sum = dnf.yum.misc.checksum(chksum_type, self.localPkg(),
                                         datasize=self._size)
        if real_sum != chksum:
            logger.debug(_('%s: %s check failed: %s vs %s'),
                         self, chksum_type, real_sum, chksum)
            return False
        return True