#
# install.py - methods related to doing installation
#
# Copyright 2002, 2003 Red Hat, Inc.
#
# This software may be freely redistributed under the terms of the GNU
# General Public License.
#
# 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., 675 Mass Ave, Cambridge, MA 02139, USA.
#

import os, sys, string
import rpm

from constants import *
import GroupSet

from rhpl.log import log
from rhpl.translate import _, N_


class InstallErrorException(Exception):
    def __init__ (self, reason):
        self.reason = reason

class InstallCallback:
    def cb(self, what, amount, total, h, (param)):
	if (what == rpm.RPMCALLBACK_TRANS_START):
	    # step 6 is the bulk of the transaction set
	    # processing time
	    if amount == 6:
                self.dialog.prepare_block (1.0, _("Preparing to install"))
                self.saved_total = total

 	elif (what == rpm.RPMCALLBACK_TRANS_PROGRESS):
            if self.saved_total != 0:

                self.dialog.update (float (amount)/self.saved_total),

        elif (what == rpm.RPMCALLBACK_TRANS_STOP):
            self.dialog.clear ()

        elif (what == rpm.RPMCALLBACK_UNINST_START):
            self.dialog.prepare_block (1.0/self.package_len,
                                       _("Removing packages"))

        elif (what == rpm.RPMCALLBACK_UNINST_STOP):
            self.dialog.update (1.0)
            self.removed_packages += 1

	elif (what == rpm.RPMCALLBACK_INST_OPEN_FILE):
            self.dialog.prepare_block (1.0/self.package_len,
                                       "%s-%s-%s" % (h[rpm.RPMTAG_NAME],
                                                     h[rpm.RPMTAG_VERSION],
                                                     h[rpm.RPMTAG_RELEASE]))

	    self.rpmFD = -1
            self.size = h[rpm.RPMTAG_SIZE]
	    while self.rpmFD < 0:
                fn = self.comps.hdrlist[GroupSet.nevra(h)].getFilename(self.pkgTimer)
		try:
#                    ret = checksig(fn)
#                    log("return of checksig for %s is %s" %(fn, ret))
#                    if ret != 0:
#                        raise SystemError                    
		    self.rpmFD = os.open(fn, os.O_RDONLY)
		    # Make sure this package seems valid
		    try:
                        hdr = self.ts.hdrFromFdno(self.rpmFD)
			os.lseek(self.rpmFD, 0, 0)
                        
                        # if we don't have a valid package, throw an error
                        if not hdr:
                            raise SystemError
		    except Exception, e:
			try:
			    os.close(self.rpmFD)
			except Exception, e:
			    pass
			self.rpmFD = -1
			raise SystemError
		except Exception, e:
                    sys.stderr.write(
			_("The file %s cannot be opened. This is due to "
			  "a missing file, a bad package, or bad media. "
			  "Press <return> to try again.") % fn)
                    raise InstallErrorException("fileopen")

	    fn = self.method.unlinkFilename(fn)
	    return self.rpmFD
	elif (what == rpm.RPMCALLBACK_INST_PROGRESS):
            if total != 100:
                self.dialog.update (float (amount)/self.size)
            else:
                self.dialog.update (1.0)
	elif (what == rpm.RPMCALLBACK_INST_CLOSE_FILE):
            self.dialog.update (1.0)
	    os.close (self.rpmFD)
            self.installed_packages += 1

        elif ((what == rpm.RPMCALLBACK_UNPACK_ERROR) or
              (what == rpm.RPMCALLBACK_CPIO_ERROR)):
            # we may want to make this error more fine-grained at some
            # point
            pkg = "%s-%s-%s" % (h[rpm.RPMTAG_NAME],
                                h[rpm.RPMTAG_VERSION],
                                h[rpm.RPMTAG_RELEASE])
#            self.messageWindow(_("Error Installing Package"),
            sys.stderr.write(
                               _("There was an error installing %s.  This "
                                 "can indicate media failure, lack of disk "
                                 "space, and/or hardware problems.  This is "
                                 "a fatal error and your install will be "
                                 "aborted.  Please verify your media and try "
                                 "your install again.\n\n"
                                 "Press the OK button to reboot "
                                 "your system.") % (pkg,))
            if what == rpm.RPMCALLBACK_UNPACK_ERROR:
                msg = "unpack"
            else:
                msg = "cpio"
            InstallErrorException(msg)
	else:
	    pass

    def __init__(self, method, dialog, package_len, comps, ts):
        self.removed_packages = 0
        self.installed_packages = 0
        self.saved_total = 0
	self.method = method
        self.dialog = dialog
        self.comps = comps
        self.package_len = package_len
        self.ts = ts
        assert (package_len > 0)
        # we should log the output for our install somewhere
        self.pkgTimer = None


def checksig(fileName):
    # RPM spews to stdout/stderr.  Redirect.
    # stolen from up2date/up2date.py
    saveStdout = os.dup(1)
    saveStderr = os.dup(2)
    redirStdout = os.open("/dev/null", os.O_WRONLY | os.O_APPEND)
    redirStderr = os.open("/dev/null", os.O_WRONLY | os.O_APPEND)
    os.dup2(redirStdout, 1)
    os.dup2(redirStderr, 2)
    # now do the rpm thing
    ret = rpm.checksig(fileName, rpm.CHECKSIG_MD5)
    # restore normal stdout and stderr
    os.dup2(saveStdout, 1)
    os.dup2(saveStderr, 2)
    # Clean up
    os.close(redirStdout)
    os.close(redirStderr)
    os.close(saveStdout)
    os.close(saveStderr)
    return ret    


def sortPackages(first, second):
    # install packages in cd order (cd tag is 1000002, num is 1000003)
    onenum = first[1000003]
    twonum = second[1000003]

    onedisc = first[1000002]
    twodisc = second[1000002]

    # this code assumes a lot, specifically for the case where we have packages
    # depending on packages in the base.  we should really split up into
    # separate transaction sets to order sanely, but 

    # if we have a package order for both, compare them
    if onenum and twonum:
        one = onenum
        two = twonum
    # if we have a package order for one and not the other, that one is "first"
    elif onenum and not twonum:
        return -1
    elif twonum and not onenum:
        return 1
    # okay, we don't have a package order for either; try going off of disc #
    elif onedisc and twodisc:
        one = onedisc
        two = twodisc
    elif onedisc and not twodisc:
        return -1
    elif twodisc and not onedisc:
        return 1
    # we have no real ordering information... we'll just go off of name :/
    else:
        one = 0
        two = 0
    
    if one < two:
	return -1
    elif one > two:
	return 1
    elif (string.lower(first[rpm.RPMTAG_NAME])
          < string.lower(second[rpm.RPMTAG_NAME])):
	return -1
    elif (string.lower(first[rpm.RPMTAG_NAME])
          > string.lower(second[rpm.RPMTAG_NAME])):
	return 1

    return 0

