# This file is a portion of the Red Hat Network Panel Applet
#
# Copyright (C) 1999-2002 Red Hat, Inc. All Rights Reserved.
# Distributed under GPL version 2.
#
# Author: Chip Turner, Daniel Veillard
#
# $Id: rhn_applet_rpm.py,v 1.16 2004/01/21 15:04:57 veillard Exp $

import rpm

if not hasattr(rpm, "opendb"):
    rpm_interf=410
else:
    rpm_interf=400

import string
import os
import time

import gettext
import rhn_utils

GETTEXT_DOMAIN = "rhn-applet"
gettext.bindtextdomain (GETTEXT_DOMAIN, "/usr/share/locale")
gettext.textdomain (GETTEXT_DOMAIN)
try:
    gettext.install(GETTEXT_DOMAIN, "/usr/share/locale", 1)
except IOError:
    import __builtin__
    __builtin__.__dict__['_'] = unicode
except AttributeError: # older gettext modules don't have an install() method
    from gettext import _

class rhnAppletRPM:
    def __init__(self, fatal = 0):
        self.latest_installed_package_cache = None

        self.fatal = fatal
        self.last_rpmdb_mtime = 0
        self.change_number = 0

    def rpmdb_mtime(self):
        return os.stat('/var/lib/rpm/Packages')[8]

    def __find_by_iter(self, iter_name, *values):
        rhn_utils.log_debug("Beginning RPM iter (%s, %s)" % (
            iter_name, values,))
	ret = []

	if rpm_interf == 400:
	    try:
		rpmdb = rpm.opendb()
	    except rpm.error:
		if self.fatal:
		    raise rhn_utils.rhnAppletException(
_("cannot open RPM database, perhaps it is not readable as this user, or already open?"))
		else:
		    rhn_utils.log_debug("Unable to open rpm database. Perhaps another application has "+\
			      "it open?")
		    # placeholder value that gets overwritten when the db can be read again
		    return []

	    method = getattr(rpmdb, iter_name)
	    iterator = apply(method, values)

	else: # RPM library interface 410 or later
	    try:
		ts = rpm.TransactionSet()
                ts.setVSFlags(-1)
		if values == None or values[0] == None or values[0] == 0:
		    iterator = ts.dbMatch()
		else:
		    if iter_name == "findbyfile":
			iterator = ts.dbMatch('basenames', values[0])
		    elif iter_name == "findbyname":
			iterator = ts.dbMatch('name', values[0])
		    elif iter_name == "findbyprovides":
			iterator = ts.dbMatch('providename', values[0])
		    else:
			rhn_utils.log_debug("Unable to scan rpm database for iter_name = %s" % (iter_name))
			return []
	    except:
		raise rhn_utils.rhnAppletException(
_("cannot open RPM database, perhaps it is not readable as this user, or already open?"))
	        
	    
	if type(iterator) == type([]):
	    for index in iterator:
		i_tmp = {}
		for field in ("name", "version", "release", "epoch"):
		    i_tmp[field] = rpmdb[index][field]

		ret.append(i_tmp)
		
	else:
	    try:
		h = iterator.next()
		while(h):
		    i_tmp = {}
		    for field in ("name", "version", "release", "epoch"):
			i_tmp[field] = h[field]

		    ret.append(i_tmp)
		    h = iterator.next()
	    except StopIteration:
	        pass

	rhn_utils.log_debug("Finished RPM query (%s, %s)" % (
	                    iter_name, values,))
	    
        return ret

    def find_latest_by_name(self, name):
        h = self.latest_installed_packages()
        for i in h.keys():
            if h[i]["name"] == name:
                return h[i]
        return

    def find_provides(self, name):
        return self.__find_by_iter("findbyprovides", name)
            
    def find_provides_by_file(self, filename):
        return self.__find_by_iter("findbyfile", filename)
            
    def refresh(self, force=0):
        mtime = self.rpmdb_mtime()
        rhn_utils.log_debug("rpm db mtimes: %s, %s, %s" % (self.last_rpmdb_mtime, mtime, time.time()))

        #
	# force a rescan if there is no cache
	#
	if self.latest_installed_package_cache == None:
	    force = 1

	#
	# block processing while there is ongoing operations
	# to avoid the rpm DB reentrancy problems
	#
	if not force and mtime + 5 > time.time():
	    return
        
	#
	# No need to do a scan to refresh the cached state
	#
        if not force and self.last_rpmdb_mtime == mtime:
            return
        
        latest = {}
    
        all_packages = self.__find_by_iter("match", 0)
    
        for h in all_packages:
            name = h['name']
            epoch = h['epoch']
            if epoch == None:
                epoch = ""
            version = h['version']
            release = h['release']

            # if we've never seen the package, or if the one we see is
            # NEWER than the one we've seen before, keep track of it.
            # the net result is a latest holding only the latest vre
            # of each package

            if not latest.has_key(name):
                latest[name] = { 'name' : name,
                                 'version' : version,
                                 'release' : release,
                                 'epoch': epoch }
            else:
                if self.compare_vre(latest[name]['version'],
                                    latest[name]['release'],
                                    latest[name]['epoch'],
                                    version,
                                    release,
                                    epoch) < 0:
                    latest[name] = { 'name' : name,
                                     'version' : version,
                                     'release' : release,
                                     'epoch': epoch }

        rhn_utils.log_debug("Ended RPM query for installed packages")

        self.latest_installed_package_cache = latest
        self.last_rpmdb_mtime = self.rpmdb_mtime()

        self.change_number = self.change_number + 1

        return latest
    
        
    def latest_installed_packages(self):
        return self.latest_installed_package_cache
    
    def compare_vre(self, v1, r1, e1, v2, r2, e2):
	if e1 == "" or e1 == 0 or e1 == "0" or e1 == None:
	    e1 = None
        else:
            e1 = str(e1)
            
	if e2 == "" or e2 == 0 or e2 == "0" or e2 == None:
	    e2 = None
        else:
            e2 = str(e2)
            
        cmp = rpm.labelCompare((e1, str(v1), str(r1)),
                               (e2, str(v2), str(r2)))

        return cmp
