#!/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")

from translate import _, N_, cat

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 _("    --configure        - graphically configure Update Agent options")
    print _("""-d, --download         - download packages only, even if configuration says
                         to install.""")
    print _("""-i, --install          - install packages, even if configuration says to
                         download only.""")
    print _("-l, --list             - list packages available for retrieval")
    print _("-h, --help             - this help")
    print _("    --nosig            - do not use GPG to check package signatures")
    print _("""-p, --packages         - update packages associated with this System Profile
                         on Red Hat Network""")
    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 _("    --tmpdir=<dir>     - where to store temporary files / RPMs")
    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, throwaway=0):
    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),

fd = 0
def rpmCallback(what, amount, total, hdr, path):
    if what == rpm.RPMCALLBACK_INST_OPEN_FILE:
        global fd
        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:
        os.close(fd)
            
    elif what == rpm.RPMCALLBACK_INST_PROGRESS:
        percent(amount, total)
    elif what == rpm.RPMCALLBACK_UNINST_STOP:
        pass

def showVersion(hasGui):
    if hasGui:
        import gui
        gui.showAbout()
    else:        
        print "Red Hat Update Agent v%s" % up2date.version()
        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 gpgWarning1(hasGui):
    errMsg =  _("""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 hasGui:
        import gui
        gui.errorWindow(errMsg)
    else:
        print errMsg

def gpgWarning2(hasGui):
    errMsg = _("""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.

To install the key, run the following as root:

    /usr/bin/gpg --import /usr/share/rhn/RPM-GPG-KEY""")
    if hasGui:
        import gui
        if not gui.installGpgKeyQuestion():
            return 1
        else:
            return 0
    else:
        print errMsg
        return 1
    
def batchRun(onlyList, pkgNames, fullUpdate = 0, fromDaemon = 0):
    if not fromDaemon:
        try:
            availUpdates, skippedUpdates = up2date.getUpdatedPackageList(printit, percent)
        except up2date.RpmError, e:
            print _("There was a fatal RPM error.  The message was:\n") + e.errmsg
            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(availUpdates) + len(skippedUpdates):
            print _("There are no packages available for update.")
            return 1


        if onlyList:
            if len(availUpdates):
                print _("""
                
Name                                    Version        Rel
--------------------------------------------------------------""")

                for pkg in availUpdates:
                    print "%-40s%-15s%-20s" % (pkg[0], pkg[1], pkg[2])
                print
            
            if (len(skippedUpdates)):
                print _("The following Packages were marked to be skipped by your configuration:")
                print _("""
Name                                    Version        Rel  Reason
-------------------------------------------------------------------------------""")
                for pkg,reason in skippedUpdates:
                    print "%-40s%-15s%-5s%s" % (pkg[0], pkg[1], pkg[2], reason)
                print
            
            return 0

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

    if len(pkgNames):
        if not fromDaemon:
            selPkgList = selectPackages(pkgNames, availUpdates + skippedUpdates)
        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.ConflictError, e:
            print e
            return 1
        if len(depPackages):
            print _("The following packages were added to your selection to satify dependencies:")
            print _("""
Name                                    Version        Release
--------------------------------------------------------------""")
            for pkg in depPackages:
                print "%-40s%-15s%-20s" % (pkg[0], pkg[1], pkg[2])
            print
        
        print _("Retrieving selected packages.")
        for pkg in selPkgList:
            try:
                up2date.getPackage(pkg, printit, percent)
            except up2date.CommunicationError, e:
                print e
                sys.exit(1)

            # do signature checking only if GPG installation checks out,
            # and GPG checking is enabled.
            if up2date.cfg.readEntry("useGPG") and not up2date.checkGPGInstallation():
                try:
                    res = up2date.hasBadSignature(pkg)
                except up2date.RpmError, e:
                    print e
                    sys.exit(1)
                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
    configure = 0
    
    if not len(arglist):
        arglist = sys.argv[1:]
    try:
        optlist, args = getopt.getopt(arglist,
                                      'adihlpruvt',
                                      ['allpackages', 'configure',
                                       'download', 'install', 'help',
                                       'list', 'nosig', 'packages',
                                       'register', 'tmpdir=',
                                       '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] == "--configure":
            configure = 1
        if opt[0] == "-d" or opt[0] == "--download":
            up2date.cfg.writeEntry("retrieveOnly", 1)
        if opt[0] == "-i" or opt[0] == "--install":
            up2date.cfg.writeEntry("retrieveOnly", 0)
        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] == "-p" or opt[0] == "--packages":
            print _("Updating package profile...")
            up2date.updatePackageProfile()
            return 0
        if opt[0] == "-r" or opt[0] == "--register":
            anonyRegister = 1
        if opt[0] == "--tmpdir":
            up2date.cfg.writeEntry("storageDir", opt[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 configure:
        if not hasGui:
            print _("You must be running X in order to graphically configure Update Agent.")
            if fromDaemon:
                return 1
            else:
                sys.exit(1)
        else:
            import configdlg
            configdlg.main()
            if fromDaemon:
                return 0
            else:
                sys.exit(0)

    if up2date.getSystemId() == None and not anonyRegister:
        if not hasGui or (onlyList or len(pkgNames) or fullUpdate):
            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.""")
            sys.exit(1)
    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)

        print _("Successfully completed anonymous registration.  You do not need to use this\noption in the future.")

    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 1:
        gpgWarning1(hasGui)
        if fromDaemon:
            return 1
        else:
            sys.exit(1)

    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 2:
        if gpgWarning2(hasGui):
            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()
