#!/usr/bin/python -u
#
# Command line wrapper for Update Agent
# Copyright (c) 1999, 2000 Red Hat, Inc.  Distributed under GPL.
#
# Author: Preston Brown <pbrown@redhat.com>

import sys, os
import rpm
import getopt

sys.path.append("/usr/share/rhn/up2date")

import up2date


def showHelp():
    print "Usage: up2date [options] [package names]"
    print ""
    print "Available command line options:"
    print """-a, --allpackages                 - make available all packages, not just
                                    those installed on your system"""
    print "-l, --list                        - list packages available for retrieval"
    print "-h, --help                        - this help"
    print "    --nosig                       - do not use GPG to check package signatures"
    print """-r, --register                    - register as an anonymous user with
                                    Red Hat Network.  You can register as a
                                    full user later by running rhn_register"""
    print "-u, --update                      - update system with all relevant packages"
    print "-v, --version                     - show program version information"
    print ""
    print """When operating in command line mode, specifying package names as arguments to
the program will attempt to retrieve (and possibly install, based on your
configuration) those packages.  Version, release, and architecture details will
be determined by the Update Agent automatically."""


def selectPackages(pkgNames, availablePackages):
    selectedPackages = []
    
    for pkg in availablePackages:
        if pkg[0] in pkgNames:
            selectedPackages.append(pkg)

    return selectedPackages

def selectPackages2(pkgNames):
    selPackages = []
    try:
        allPackages = up2date.getAvailablePackageList()
    except:
        print "Error getting available package list"
        return selPackages

    for pkg in allPackages:
        if pkg[0] in pkgNames:
            selPackages.append(pkg)

    return selPackages


def printit(a):
    print "\n" + a + "..."


def percent(amount, total):
    if total != 0:
        i = (amount + 1.0) / total
        if i > 1:
            i = 1.0
        elif i < 0:
            i = 0
    else:
        i = 0

    print "%.5s%%\r" % (i * 100),


def rpmCallback(what, amount, total, hdr, path):
    if what == rpm.RPMCALLBACK_INST_OPEN_FILE:
        fileName = "%s/%s-%s-%s.%s.rpm" % (path,
                                           hdr['name'],
                                           hdr['version'],
                                           hdr['release'],
                                           hdr['arch'])
        try:
            fd = os.open(fileName, os.O_RDONLY)
        except:
            raise up2date.RpmError("Error opening %s" % fileName)

        return fd
    elif what == rpm.RPMCALLBACK_INST_START:
        fileName = "%s/%s-%s-%s.%s.rpm" % (path,
                                           hdr['name'],
                                           hdr['version'],
                                           hdr['release'],
                                           hdr['arch'])
        printit("Installing %s" % fileName)
    elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE:
        name = hdr['name']
        version = hdr['version']
        release = hdr['release']
        epoch = hdr['epoch']
        if epoch == None:
            epoch = ""
        up2date.remoteAddPackage((name, version, release, epoch))
    elif what == rpm.RPMCALLBACK_INST_PROGRESS:
        percent(amount, total)
    elif what == rpm.RPMCALLBACK_UNINST_STOP:
        uninstHdr = rpm.getTransactionCallbackHeader()
        name = uninstHdr['name']
        version = uninstHdr['version']
        release = uninstHdr['release']
        epoch = uninstHdr['epoch']
        if epoch == None:
            epoch = ""
        up2date.remoteDelPackage((name, version, release, epoch))

def showVersion(hasGui):
    if hasGui:
        import gui
        gui.showAbout()
    else:        
        print "Red Hat Update Agent v2.0.6"
        print "Copyright (c) 1999, 2000 Red Hat, Inc."
        print "Licensed under terms of the GPL."

def rootWarning(hasGui):
    if hasGui:
        import gui
        gui.rootWarning()
    else:
        print "You must run the Update Agent as root."


def batchRun(onlyList, pkgNames, fullUpdate = 0, fromDaemon = 0):
    if not fromDaemon:
        try:
            updated, skipped = up2date.getUpdatedPackageList(printit, percent)
        except up2date.RpmError, e:
            print e
            return 1
        except up2date.CommunicationError, e:
            print "There was a fatal error communicating with the server.  The message was:\n" + e.errmsg
            return 1

        if not len(updated) + len(skipped):
            print "There are no packages available for update."
            return 1


        if onlyList:
            print """
Name                                    Version        Release
--------------------------------------------------------------"""

            for pkg in updated:
                print "%-40s%-15s%-20s" % (pkg[0], pkg[1], pkg[2])

            if (len(skipped)):
                print "\nThe following Packages were marked to be skipped by your configuration:\n"
                print """
Name                                    Version        Release
--------------------------------------------------------------"""
                for pkg in skipped:
                    print "%-40s%-15s%-20s" % (pkg[0], pkg[1], pkg[2])
            
            return 0

    # if we are in full update mode, append all available relevant packages
    if fullUpdate:
        pkgNames = []
        for pkg in updated:
            pkgNames.append(pkg[0])

    if len(pkgNames):
        if not fromDaemon:
            selPkgList = selectPackages(pkgNames, updated + skipped)
        else:
            selPkgList = selectPackages2(pkgNames)

        if not len(selPkgList):
            print "None of the packages you requested were found, or they are already updated."
            return 1
            
        totalSize = 0
        for pkg in selPkgList:
            totalSize = totalSize + up2date.getHeader(pkg)['size']

        if totalSize > up2date.freeDiskSpace():
            print "The total size of the selected packages (%d kB) exceeds your free disk space (%d kB)." % (totalSize / 1024, up2date.freeDiskSpace() / 1024)
            return 1
            
        try:
            depPackages = up2date.dryRun(selPkgList, printit, percent)
        except up2date.CommunicationError, e:
            print "There was a fatal error communicating with the server.  The message was:\n" + e.errmsg
            return 1
        except up2date.RpmError, e:
            print "There was a fatal RPM error.  The message was:\n" + e.errmsg
            return 1
        except up2date.DependencyError, e:
            found = 0
            for index in range(updated):
                if updated[index][0] == e.pkgName:
                    print "%s\nSelecting %s for update." % e.errmsg, e.pkgName
                    selPkgList.append(updated[index])
                    depPackages = []
                    found = 1
                    break
            if not found:
                print "There was a fatal RPM error.  The message was:\n" + e.errmsg
                return 1

        if len(depPackages):
            print "The following packages were added to your selection to satify dependencies:\n"
            print """
Name                                    Version        Release
--------------------------------------------------------------"""
            for pair in depPackages:
                print "%-40s%-15s%-20s" % (pair[1][0], pair[1][1], pair[1][2])

        
        for pkg in selPkgList:
            print "Retrieving selected packages."
            up2date.getPackage(pkg, printit, percent)

            # do signature checking only if GPG installation checks out,
            # and GPG checking is enabled.
            if up2date.cfg.readEntry("useGPG") and not up2date.checkGPGInstallation():
                res = up2date.hasBadSignature(pkg)
                if res == 1:
                    print "The package %s is not signed with a GPG signature.  Aborting..." % up2date.pkgToString(pkg)
                    return 1
                if res == 2:
                    "The package %s does not have a valid GPG signature.\nIt has been tampered with or corrupted.  Aborting..." % up2date.pkgToString(pkg)
                    return 1


        if not up2date.cfg.readEntry("retrieveOnly"):
            try:
                up2date.installPackages(selPkgList, rpmCallback)
            except up2date.RpmError, e:
                print e
                return 1

            if not up2date.cfg.readEntry("keepAfterInstall"):
                for pkg in selPkgList:
                    try:
                        up2date.removePackage(pkg)
                    except up2date.FileError, e:
                        print e
                        return 1

        return 0
    
            
def main(arglist = [], fromDaemon = 0):
    onlyList = 0
    hasGui = 0
    pkgNames = []
    test = 0
    anonyRegister = 0
    fullUpdate = 0
    
    if not len(arglist):
        arglist = sys.argv[1:]
    try:
        optlist, args = getopt.getopt(arglist,
                                      'ahlruvt', ['allpackages', 'help',
                                                 'list', 'nosig','register',
                                                 'update', 'version', 'test'])
    except getopt.error, e:
        print "Error parsing command line arguments: %s" % e
        showHelp()
        if fromDaemon:
            return 1
        else:
            sys.exit(1)
    
    if args:
        pkgNames = args

    try:
        if os.access("/usr/share/rhn/up2date/gui.py", os.R_OK):
            if os.environ["DISPLAY"] != "":
                hasGui = 1
    except:        
        pass

    for opt in optlist:
        if opt[0] == "-a" or opt[0] == "--allpackages":
            up2date.cfg.writeEntry("retrieveNew", 1)
        if opt[0] == "-h" or opt[0] == "--help":
            showHelp()
            if fromDaemon:
                return 0
            else:
                sys.exit(0)
        if opt[0] == "-l" or opt[0] == "--list":
            onlyList = 1
        if opt[0] == "--nosig":
            up2date.cfg.writeEntry("useGPG", 0)
        if opt[0] == "-r" or opt[0] == "--register":
            anonyRegister = 1
        if opt[0] == "-u" or opt[0] == "--update":
            fullUpdate = 1
        if opt[0] == "-v" or opt[0] == "--version":
            showVersion(hasGui)
            if fromDaemon:
                return 0
            else:
                sys.exit(0)
        if opt[0] == "-t" or opt[0] == "--test":
            test = 1

    if os.geteuid() != 0 and not test:
        rootWarning(hasGui)
        if fromDaemon:
            return 1
        else:
            sys.exit(1)

    if up2date.getSystemId() == None and not anonyRegister and not hasGui:
        print """You are not registered with Red Hat Network.  To use Update Agent,
You must be registered.

However, you can continue now with an anonymous registration, and only
your Red Hat Linux version and computer architecture will be sent to
redhat.com.  You are free to use the rhn_register later to take full
advantage of the benefits offered by the Red Hat Network.

To register anonymously, specify \"--register\" as a command line option."""
    elif anonyRegister:
        try:
            anony = up2date.anonymousRegister()
        except up2date.CommunicationError, e:
            print "There was a fatal error communicating with the server.  The message was:\n" + e.errmsg
            if fromDaemon:
                return 1
            else:
                sys.exit(1)

        if anony == 0:
            print "There was some sort of error writing your anonymous registration key\nto disk."
            if fromDaemon:
                return 1
            else:
                sys.exit(1)

    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 1 and not hasGui:
        print """GPG does not appear to be installed.  Without it, you will be unable to
verify that packages Update Agent downloads are securely signed by a
trusted source.

Your Update Agent options specify that you want to use GPG.  Aborting."""
        if fromDaemon:
            return 1
        else:
            sys.exit(1)

    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 2 and not hasGui:
        print """Your GPG keyring does not contain the Red Hat, Inc. public key.
Without it, you will be unable to verify that packages Update Agent downloads
are securely signed by Red Hat.

Your Update Agent options specify that you want to use GPG.  Aborting."""
        if fromDaemon:
            return 1
        else:
            sys.exit(1)

    if onlyList or len(pkgNames) or fullUpdate:
        if fromDaemon:
            return batchRun(onlyList, pkgNames, fullUpdate, fromDaemon)
        else:
            sys.exit(batchRun(onlyList, pkgNames, fullUpdate))
    else:
        if hasGui:
            import gui
            gui.main()
            sys.exit(0)
        else:
            print "No interactive mode available; up2date-gnome not installed."
            print "Please specify either -l, -u, or package names as command line arguments."
            showHelp()
            if fromDaemon:
                return 1
            else:
                sys.exit(1)
        
        
if __name__ == "__main__":
    main()
