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

import sys, os
import rpm
import string
from types import StringType
sys.path.append("/usr/share/rhn/")
from up2date_client import config
from up2date_client.translate import _, N_, cat
from up2date_client import up2date

try:
    import clap
except ImportError:
    from up2date_client import clap

try:
    from rhn import rpclib
except ImportError:
    rpclib = __import__("xmlrpclib")
    
try:
    from up2date_client import lilocfg
except ImportError:
    lilocfg = None

# we want to be somewhat less verbose if were not on a tty...
global isatty
isatty = None

optionsTable = [
        [None,  "configure",    None, 
            _("configure Update Agent options")],
        ['d',   "download",     None,
            _("download packages only, even if configuration says to install")],
        [None,  "src",          None,
            _("Download src packages as well as binary rpms")],
        [None,  "nosrc",        None,
            _("Do not download src packages (overrides config setting if set)")],
        [None,  "dbpath",       StringType,
            _("Specify a path where an alternate rpm db is found"), "<dir>"],
        ['f',   "force",        None,
            _("Force package installation, overriding package and file skip list")],
        ['i',   "install",      None,
            _("install packages, even if configuration says to download only")],
        [None, "justdb",        None,
            _("Only add packages to database, do not install to the filesystem")],
        ['l',   "list",         None,
            _("list packages available for retrieval")],
        ['h',   "help",         None,
            _("this help screen")],
        ['?',   "usage",        None,
            _("Briefly describe the options")],
        ['k',   "packagedir",   StringType,
            _("colon separated path of directories to look in for packages")],
        [None,  "nosig",        None,
            _("do not use GPG to check package signatures")],
        [None,  "nox",          None,
            _("do not attempt to use X")],
        ['p',   "packages",     None,
            _("update packages associated with this System Profile on Red Hat Network")],
        [None,  "showall",      None,
            _("List all packages available for download")],
        [None,  "solvedeps",    StringType,
            _("finds, downloads, and installs the packages needed to solve the list of deps"), "<deps>"],
        [None,  "tmpdir",       StringType,
            _("where to store temporary files / RPMs"), "<directory>"],
        ['u',   "update",       None,
            _("update system with all relevant packages")],
        [None,  "version",      None,
            _("show program version information")],
        ['v',   "verbose",      [],
            _("Show additional output")],
        [None,  "show-channels",None,
            _("Show the channel names along with the package name where approriate")],
        [None,  "whatprovides", StringType,
            _("shows the packages which solve the comma separated list of deps"), "<deps>"],
        [None, "channel", StringType,
            _("Specify which channels to use by channel label")],
	[None, "gpg-flags", None,
	       _("Show the flags that gpg will be invoked with (the keyring, etc)")],
        [None, "nodownload",    None,
            _("Do not download packages at all. For testing purposes")]
    ]            
    
def printHelp(poptContext, stream = sys.stdout):
    poptContext.printHelp(stream)
    stream.write("\n" + _("""\
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.""") + "\n")

def printUsage(poptContext, stream = sys.stdout):
    poptContext.printUsage(stream)

def sanityChecks(fromDaemon, hasGui,argObj):

    if up2date.getSystemId() == None:
        # FIXME
        # fire up rhn_register at this point...
        registeredWarning(hasGui)
        if not fromDaemon:
            sys.exit(1)
        return 1
    
    #  see if /var/spool/up2date exists
    if up2date.checkForStorageDir():
        storageDirWarning(hasGui)
        if not fromDaemon:
            sys.exit(1)
        return 1

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

    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 2:
        if gpgWarning2(hasGui):
            if not fromDaemon:
                sys.exit(1)
        return 1

    if not up2date.hasSSL():
        if sslWarning(hasGui):
            if not fromDaemon:
                sys.exit(1)
        return 1

def showInfoMessages(fromDaemon,hasGui, argObj):
    if argObj.getLong("help"):
        printHelp(argObj.ctx)
        if not fromDaemon:
            sys.exit(0)
        return 1

    if argObj.getLong("usage"):
        printUsage(argObj.ctx)
        if not fromDaemon:
            sys.exit(0)
        return 1

    if argObj.getLong("version"):
	showVersion(hasGui)
        if not fromDaemon:
            sys.exit(0)
        return 1

def checkForRhnNetworkConfigs(hasGui):
    """This function checks to see if we've setup any network options for
    rhn_register. If so, and were havent configured those the same for up2date,
    clone the ones from rhn_register"""
    cfg = config.Up2dateConfig()
    cfg.load()
    
    # read the rhn_register too
    regCfg = config.RegisterPeekConfig()
    regCfg.load()
    
    # read the config, if NetworkSetup isnt set, the user is running
    # it the first time. After they go though the config, set enableProxy 
    if cfg.hasEntry('networkSetup'):
        return

    # the info isnt in rhn_register config either, skip
    if not regCfg.hasEntry('networkSetup'):
        return

    # look and see if the config is non-default
    # if so, skip it. We dont want to clobber modified configs
    if cfg.readEntry("enableProxy") == 1:
        return 
    if cfg.readEntry("httpProxy") != "":
        return
    if cfg.readEntry("proxyUser") != "":
        return
    if cfg.readEntry("proxyPassword") != "":
        return
    if cfg.readEntry("enableProxyAuth") == 1:
        return

    # looks like were clear to migrate the settings
    print "Migrating network settings from rhn_register"
    up2date.log.log_me("Migrating network settings from rhn_register")
    cfg.writeEntry("enableProxy", regCfg.readEntry("enableProxy"))
    cfg.writeEntry("httpProxy", regCfg.readEntry("httpProxy"))
    cfg.writeEntry("proxyUser", regCfg.readEntry("proxyUser"))
    cfg.writeEntry("proxyPassword", regCfg.readEntry("proxyPassword"))
    cfg.writeEntry("enableProxyAuth", regCfg.readEntry("enableProxyAuth"))
    cfg.writeEntry("networkSetup", 1)
    cfg.save()
    up2date.reloadConfig()
        
def main(arglist = sys.argv, fromDaemon = 0):
    hasGui = 0
    nox = 0
    nogui = 0
    configure = 0

    # see what we got called as 
    if os.path.basename(sys.argv[0]) == "up2date-nox":
	nox = 1

    if os.path.basename(sys.argv[0]) == "up2date-config":
        configure = 1
        
    # clap on
    argObj = clap.Clap(optionsTable, "[OPTIONS] <packages>", aliasing = 1)
    try:
        argObj.process(arglist)
    except clap.ClapError, e:
        # clap off
        ReportError("Error parsing command line arguments: %s" % 
            string.join(map(str, e.args)))
        if fromDaemon:
            return 1
        sys.exit(1)
        # the clapper
    
    pkgNames = argObj.args

    nox = nox or argObj.getLong("nox")
    
    # detect if were running on a tty or not
    global isatty
    if sys.stdout.isatty():
	    isatty = 1
    else:
	    isatty = None

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


    showInfoMessages(fromDaemon, hasGui, argObj)
    
    # set a default warning message callback, the gui version
    # should reset this
    up2date.setWarningCallback(printit)

    if argObj.getLong("packages"):
        up2date.updateLoginInfo()
        print _("Updating package profile...")
        up2date.updatePackageProfile()
        return 0

    tempchannels = []
    depslist = []
    verbosecount = 0
    depslist = argObj.getLong("whatprovides")
    if depslist:
        up2date.cfg.writeEntry("depslist", depslist)
    else:
        depslist = []
    for i in argObj.getLong("verbose"):
	verbosecount = verbosecount + 1
	if verbosecount >= 2:
	    try:
	    	rpm.setVerbosity(rpm.RPMLOG_DEBUG) 
	    except AttributeError:
		print "extra verbosity not supported in this version of rpm"
    if argObj.getLong("download"):
        up2date.cfg.writeEntry("retrieveOnly", 1)
    if argObj.getLong("nodownload"):
        up2date.cfg.writeEntry("noDownload", 1)
    if argObj.getLong("install"):
        up2date.cfg.writeEntry("retrieveOnly", 0)
    if argObj.getLong("force"):
        up2date.cfg.writeEntry("forceInstall", 1)
    if argObj.getLong("verbose"):
        up2date.cfg.writeEntry("verbose", 1)
    if argObj.getLong("src"):
        up2date.cfg.writeEntry("retrieveSource", 1)
    if argObj.getLong("nosrc"):
        up2date.cfg.writeEntry("retrieveSource", 0)
    if argObj.getLong("channel"):
	tempchannels.append(argObj.getLong("channel"))
    tmpdir = argObj.getLong("tmpdir")
    if tmpdir:
        up2date.cfg.writeEntry("storageDir", tmpdir)
    packagedir = argObj.getLong("packagedir")
    if packagedir is not None:
        packagedir = os.path.abspath(os.path.expanduser(packagedir))
        up2date.cfg.writeEntry("packageDir", packagedir)
    if argObj.getLong("nosig"):
        up2date.cfg.writeEntry("useGPG", 0)
    if argObj.getLong("dbpath") is not None:
        up2date.cfg.writeEntry("dbpath", argObj.getLong("dbpath"))
    if argObj.getLong("justdb"):
        up2date.cfg.writeEntry("justdb", 1)
    if argObj.getLong("show-channels"):
        up2date.cfg.writeEntry("showChannels", 1)
    if argObj.getLong("gpg-flags"):
	print up2date.getGPGflags()
	return 0

    if len(tempchannels):
       up2date.updateLoginInfo()
       up2date.updateChannels(tempchannels)
	
    if argObj.getLong("showall"):
        # FIXME: hack, should set a variable here and then run it later
        up2date.updateLoginInfo()
        printList(up2date.getAvailablePackageList())
        return 0

    fullUpdate = argObj.getLong("update")
    configure = configure or argObj.getLong("configure")

    # show any useage/help/info/version info
    ret = showInfoMessages(fromDaemon, hasGui, argObj)
    if ret:
        return ret

    # see if were running as root, before running configure
    if os.geteuid() != 0:
        rootWarning(hasGui)
        if not fromDaemon:
            sys.exit(1)

    # see if rhn_register has configured a proxy, if so, use it's values
    checkForRhnNetworkConfigs(hasGui)
    
    if configure:
        if hasGui:
            try:
                from up2date_client import configdlg
            except:
                hasGui = 0
            if hasGui:
                configdlg.main()
                if fromDaemon:
                    return 0
                else:
                    sys.exit(0)
        if not hasGui:
            from up2date_client import text_config
            text_config.main()
            if fromDaemon:
                return 1
            else:
                sys.exit(1)

    # run all the sanity checks (gpg key, paths, etc)
    ret = sanityChecks(fromDaemon, hasGui, argObj)
    if ret:
        return ret

            
    # initilized the package sources
    try:
        up2date.updateLoginInfo()
    except rpclib.Fault, f:
        faultError(hasGui,f.faultString)
        sys.exit(1)
        
    except up2date.CommunicationError, e: 
        print e 
        sys.exit(1) 

    if len(depslist):
       return whatprovides(depslist)

    if argObj.getLong("solvedeps"):
        # maybe this will handle comboa cases of packages and deps specified?? NEEDSTEST
        extraPackages = whatprovides(argObj.getLong("solvedeps"), 1)
        if extraPackages:
            pkgNames = pkgNames + extraPackages
        else:
            print _("No packages were found that satisfy those dependencies.")
            sys.exit(1)

    if up2date.cfg.readEntry("showChannels"):
        channels = up2date.loginInfo['X-RHN-Auth-Channels']
        for channel in channels:
            print "%s" % channel[0]
        # nothing else that uses showChannel follows, so we can exit here
        sys.exit(0) 


    if argObj.getLong("list") or len(pkgNames) or fullUpdate:
        if fromDaemon:
            return batchRun(argObj.getLong("list"), pkgNames, fullUpdate, fromDaemon)
        try:
            sys.exit(batchRun(argObj.getLong("list"), pkgNames, fullUpdate))
        except up2date.GPGVerificationError, e:
            # might as well be annoying and loud about it
            print
            print "************ GPG VERIFICATION ERROR ****************"
            print e
            print "****************************************************"
            print
            sys.exit(1)
        except up2date.FileNotFoundError, e:
            print  e.errmsg
            sys.exit(1)
        except up2date.GPGVerificationUnsignedPackageError,e:
            print e, "Aborting..."
            sys.exit(1)
    else:
        if hasGui:
	    try:
            	from up2date_client import gui
                nogui = 0
	    except RuntimeError:
                # if we fail to initialize the interface, fall back to tui
                nogui = 1
                
 	    if not nogui:
                up2date.setWarningCallback(gui.errorWindow)
            	gui.main()	
            	sys.exit(0)
                
        print _("No interactive mode available")
        print _("Please specify either -l, -u, --nox, or package names as command line arguments.")
        argObj.ctx.printHelp()
        if fromDaemon:
            return 1
        else:
            sys.exit(1)
        
def ReportError(*args):
    sys.stderr.write("%s\n" % string.join(map(str, args)))
    

def selectPackages(pkgNames, availablePackages):
    selectedPackages = []

    pkgList = availablePackages
    pkgNames = map(lambda a: [a, '0','0',''], pkgNames)

    # filter the packages that are already installed, or
    # that we have newer versions of
    pkgList = up2date.removeInstalledPackagesFromList(pkgList,
                                                      pkgNames,
                                                      msgCallback = None,
                                                      progressCallback = None)

    # since we want to make sure that the pkgNames specified on the
    # commandline make it here, we look though the available package
    # list and find all that match. We grab all the various arches
    # because the archscore stuff will straighten that out later
    selectedPackages = pkgList
    if len(pkgNames):
	for pkg in pkgNames:
            list = []
            # we can have more than one "kernel" for example...
            list = filter(lambda a,b=pkg: a[0]==b[0], pkgList)
            for i in list:
                selectedPackages.append(i)

    pkgList = selectedPackages
    if len(pkgList) > 0:
        total = len(pkgList)
        h_list = up2date.getHeaders(pkgList,
                                    msgCallback = None,
                                    progressCallback = None,
                                    refreshCallback = None)

    pkgSkipList = []
    
    if len(pkgList) > 0:
        pkgSkipList = pkgSkipList + up2date.removeSkipFilesPackagesFromList(pkgList, msgCallback=None,
                                                                    progressCallback=None)
    
    # uniqueify
    d = {}
    for pkg in pkgList:
        d[tuple(pkg)] = pkg
    pkgList = d.values()
    
    pkgList.sort()
    return (pkgList, pkgSkipList)


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

lastPercent = 0
def percent(amount, total, speed = 0, sec = 0):
    global lastPercent
    hashesTotal = 40
    
    if total:
        hashesNeeded = int(hashesTotal * (float(amount) / total))
    else:
        hashesNeeded = hashesTotal
    
    # dont print if were not running on a tty
    if isatty and (hashesNeeded > lastPercent or amount == total):
        for i in range(hashesNeeded):
            sys.stdout.write('#')

        sys.stdout.write('\r')
        
        if amount == total:
            print
            
    if amount == total:
        lastPercent = 0
    else:
        lastPercent = hashesNeeded


def printPkg(name, shortName = None):
    if shortName:
        print "%-27.27s " % (shortName + ":"),
    else:
        print "%-27.27s " % (name + ":"),

def printRetrieveHash(amount, total, speed = 0, secs = 0):
    hashesTotal = 26
    
    if total:
        percent = int(100 * (float(amount) / total))
        hashesNeeded = int(hashesTotal * (float(amount) / total))
    else:
        percent = 100
        hashesNeeded = hashesTotal

    if isatty:
        for i in range(hashesNeeded):
            sys.stdout.write('#')

        for i in range(hashesNeeded, hashesTotal):
            sys.stdout.write(' ')

    if isatty:
        if amount == total:
            print "%-25s" % " Done."
        else:
            print "%4d k/sec, %02d:%02d:%02d rem." % \
                  (speed / 1024, secs / (60*60), (secs % 3600) / 60,
                   secs % 60),
            for i in range(hashesTotal + 25):
                sys.stdout.write("\b")
    elif amount == total:
        print "Retrieved."
                


hashesPrinted = 0
progressCurrent = 0
progressTotal = 0
packagesTotal = 0
# ported from C code in RPM 2/28/01 -- PGB
def printRpmHash(amount, total):
    hashesTotal = 44

    if total:
        percent = int(100 * (float(amount) / total))
    else:
        percent = 100

    global lastPercent
    if percent <= lastPercent:
        return

    lastPercent = percent
    
    global hashesPrinted
    if (hashesPrinted != hashesTotal):
        if total:
            hashesNeeded = int(hashesTotal * (float(amount) / total))
        else:
            hashesNeeded = hashesTotal
            
        if isatty:
            for i in range(hashesNeeded):
                sys.stdout.write('#')
        
            for i in range(hashesNeeded,hashesTotal):
                sys.stdout.write(' ')
                
            print "(%3d%%)" % percent,
            for i in range(hashesTotal + 6):
                sys.stdout.write("\b")

        hashesPrinted = hashesNeeded
        if hashesPrinted == hashesTotal:
            if isatty:
                global progressCurrent, progressTotal
                progressCurrent = progressCurrent + 1
                for i in range(1,hashesTotal):
                    sys.stdout.write("#")
                if progressTotal:
                    print " [%3d%%]" % int(100 * (float(progressCurrent) /
                                                  progressTotal))
                else:
                    print " [%3d%%]" % 100

    sys.stdout.flush()

fd = 0
def rpmCallback(what, amount, total, hdr, path):
    global fd
    global hashesPrinted
    global lastPercent
    global progressCurrrent
    global progressTotal, progressCurrent, hashesPrinted, lastPercent
    global progressTotal, progressCurrent, packagesTotal
    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_CLOSE_FILE:
        os.close(fd)
        fd = 0
        
    elif what == rpm.RPMCALLBACK_INST_START:
#        global hashesPrinted, lastPercent
        hashesPrinted = 0
        lastPercent = 0
        fileName = "%s/%s-%s-%s.%s.rpm" % (path,
                                           hdr['name'],
                                           hdr['version'],
                                           hdr['release'],
                                           hdr['arch'])
        if isatty:
#            global progressCurrent
            print "%4d:%-23.23s" % (progressCurrent + 1, hdr['name']),
            sys.stdout.flush()
        else:
            printit(_("Installing %s") % fileName)
            
    elif (what == rpm.RPMCALLBACK_TRANS_PROGRESS or
          what == rpm.RPMCALLBACK_INST_PROGRESS):
        printRpmHash(amount, total)

    elif what == rpm.RPMCALLBACK_TRANS_START:

        hashesPrinted = 0
        lastPercent = 0
        progressTotal = 1
        progressCurrent = 0
        print "%-28s" % _("Preparing..."),
        sys.stdout.flush()

    elif what == rpm.RPMCALLBACK_TRANS_STOP:
        printRpmHash(1, 1)

        progressTotal = packagesTotal
        progressCurrent = 0
        
    elif (what == rpm.RPMCALLBACK_UNINST_PROGRESS or
          what == rpm.RPMCALLBACK_UNINST_START or
          what == rpm.RPMCALLBACK_UNINST_STOP):
        pass

    if (rpm.__dict__.has_key("RPMCALLBACK_UNPACK_ERROR")):
        if ((what == rpm.RPMCALLBACK_UNPACK_ERROR) or
                    (what == rpm.RPMCALLBACK_CPIO_ERROR)):
            pkg = "%s-%s-%s" % (hdr[rpm.RPMTAG_NAME],
                                hdr[rpm.RPMTAG_VERSION],
                                hdr[rpm.RPMTAG_RELEASE])

            if what == rpm.RPMCALLBACK_UNPACK_ERROR:
                raise up2date.RpmInstallError, ("There was a rpm unpack error  installing the package: %s" % pkg, pkg)
            elif what == rpm.RPMCALLBACK_CPIO_ERROR:
                raise up2date.RpmInstallError, ("There was a cpio error installing the package: %s" % pkg, pkg)


def showVersion(hasGui):
    if hasGui:
        try:
            from up2date_client import gui
            gui.showAbout()
        except:
            print _("Unable to open gui. Try specifying \"--nox\" as an option")
    else:
        print "Red Hat Update Agent v%s" % up2date.version()
        print "Copyright (c) 1999-2001 Red Hat, Inc."
        print _("Licensed under terms of the GPL.")

def rootWarning(hasGui):
    errMsg =  _("You must run the Update Agent as root.")
    if hasGui:
        try:
            from up2date_client import gui
            gui.rootWarning()
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
    else:
        print errMsg

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:
        try:
            from up2date_client import gui
            gui.errorWindow(errMsg)
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
    else:
        print errMsg

def storageDirWarning(hasGui):
    errMsg = _("""The storage directory %s could not be found, or was not
accessable.""") % up2date.cfg.readEntry("storageDir")
    
    if hasGui:
        try:
            from up2date_client import gui
            gui.errorWindow(errMsg)
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
    else:
        print errMsg

def gpgWarning2(hasGui):
    def cb(arg):
        return arg

    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 %s --import /usr/share/rhn/RPM-GPG-KEY""" % (up2date.getGPGflags() ))

    if hasGui:
        try:
            from up2date_client import gui 
	    import gnome, gnome.ui
            dlg = gnome.ui.GnomeQuestionDialog(errMsg+_("\n\nInstall key?"),
                                               cb)
            if not dlg.run_and_close():
                if up2date.addGPGKey():
                    dlg = gnome.ui.GnomeErrorDialog(_("Some sort of error occurred adding the Red Hat\nGPG key to your keyring."))
                    dlg.run_and_close()
                    return 1
                else:
                    return 0
                
            return 1
        except (ImportError, RuntimeError) :
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
            print
            return 1
    else:
        print errMsg
        print
        return 1

def sslWarning(hasGui):
    def cb(arg):
        return arg

    errMsg = _("""Your system libraries do not support SSL (secure) connections.  Any data
that you send or receive from redhat.com will be transmitted in the clear.

""")

    if hasGui:
        try:
            from up2date_client import gui
	    import  gnome, gnome.ui
            dlg = gnome.ui.GnomeQuestionDialog(errMsg + _("Continue anyway?"),
                                               cb)
            if dlg.run_and_close():
                return 1
            else:
                return 0
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
            return 1
    else:
        print errMsg
        return 1

def registeredWarning(hasGui):
    if hasGui:
        try:
            from up2date_client import gui
            gui.errorWindow(_("""You are not registered with Red Hat Network.  To use Update Agent,
You must be registered.

To register, run \"rhn_register\"."""))
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print _("""You are not registered with Red Hat Network.  To use Update Agent,
You must be registered.

To register, run \"rhn_register\".""")
            return 1
    else:
        print _("""You are not registered with Red Hat Network.  To use Update Agent,
You must be registered.

To register, run \"rhn_register\".""")
    return 1

def faultError(hasGui,msg):
    errMsg = msg
    
    if hasGui:
        try:
            from up2date_client import gui
            gui.errorWindow(errMsg)
        except:
            print _("Unable to open gui. Try `up2date --nox`")
            print errMsg
    else:
        print errMsg



def whatprovides(depslist,returnlist=None):
    tmp_list = string.split(depslist, ",")
    deps_list=[]
    # a little text munging to make this commandline friendlier
    for i in tmp_list:
        i=string.strip(i)
        deps_list.append(i)
    try:
        provides = up2date.solveDep(deps_list)
    except up2date.DependencyError:
        print "Unable to solve deps for  %s" %         deps_list       
        return None
    if returnlist:
        pkgnames = []
        for provide in provides:
            pkgnames.append(provide[0])
        return pkgnames
    pkgDict = up2date.avail_package_dict()
    for provide in provides:
        pkgString = "%s-%s-%s" % (provide[0], provide[1],provide[2])
        if up2date.cfg.readEntry("showChannels"):
            # print the channel name
            channelName = pkgDict["%s-%s-%s.%s" % (provide[0], provide[1],provide[2], provide[4])][6]
            print "%-40s%-30s" % (pkgString, channelName)
        else:
            print "%s" % pkgString


def printList(packageList):
    packageList.sort()
    pkgDict = up2date.avail_package_dict()
    for pkg in packageList:
        pkgString = "%s-%s-%s" % (pkg[0], pkg[1],pkg[2])
        if up2date.cfg.readEntry("showChannels"):
            channelName = pkgDict["%s-%s-%s.%s" % (pkg[0], pkg[1],pkg[2], pkg[4])][6]
            print "%-40s%-30s" % (pkgString, channelName)
        else:
            print "%s" % (pkgString)


# these functions are kind of ugly but...
def printVerboseList(availUpdates):
    print _("""
Name                                    Version        Rel     
----------------------------------------------------------""")
    for pkg in availUpdates:
        print "%-40s%-15s%-20s" % (pkg[0], pkg[1], pkg[2])
        if up2date.cfg.readEntry("verbose"):
            advisories = up2date.getAdvisoryInfo(pkg)
            if advisories:
                for a in advisories:
                    print "[%s] %s" % (a['advisory'],
                                       a['topic'])
            else:
                print "No advisory information available"
    print

def printSkippedPackages(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)
        if up2date.cfg.readEntry("verbose"):
            advisories = up2date.getAdvisoryInfo(pkg)
            if advisories:
                for a in advisories:
                    print "[%s] %s" % (a['advisory'],
                                       a['topic'])
            else:
                print "No advisory information available"
    print
    
def runInteractive(pkgNames, availUpdates, fromDaemon, onlyList, printCallback, percentCallback):
    
    try:
        avUpdates, skippedUpdates = up2date.getUpdatedPackageList(pkgNames, None, fromDaemon,
                                                                  msgCallback = printCallback,
                                                                  progressCallback = percentCallback)
    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

    # list references are weird
    availUpdates[:] = avUpdates
    pkgNames[:] = map(lambda a: a[0], availUpdates)

    if not len(availUpdates) + len(skippedUpdates) and not len(pkgNames):
        print _("There are no packages available for update.")
        return None

    if onlyList:
        if len(availUpdates):
            printVerboseList(availUpdates)

    if (len(skippedUpdates)):
        printSkippedPackages(skippedUpdates)

    if onlyList:
        return 1


def runFromDaemon(pkgNames, actionPkgs, availUpdates,skippedPackages, fromDaemon):
    remotePackages = up2date.getAvailablePackageList()
    # since we have some package with bogus e-v-r's (0-0-0:""), do
    # some minor gymnastics to catch those and use the full
    # n-v-r-e from the list of available packages instead

    updList = []
    errorlist = []

    # FIXME: this could probabaly use some optimization
    # should probably iterate over remotePackageNames once
    # using a counter, and use that to index into remotePackages
    for pkg in actionPkgs:
        # you just knew we were gonna work a filter in here at some point
        # we needed to find all the matching packages, not just the first
        # so that the archscore stuff can sort out the best arch later
        for tmppkg in filter(lambda a,b=pkg: a[0]==b[0], remotePackages):
             updList.append(tmppkg)

    if len(errorlist):
        return -1

    # these will raise exceptions on errors, that get caught in actions/package
    avUpdates, skippedPackagesTmp = up2date.getUpdatedPackageList(updList, updList, fromDaemon,
                                                                   None, None )
    # list reference goo
    availUpdates[:] = avUpdates
    pkgNames[:] = map(lambda a: a[0], availUpdates)

    skippedPackages[:] = skippedPackagesTmp
    return None
    
def batchRun(onlyList, pkgNames, fullUpdate = 0, fromDaemon = 0,actionPkgs=None):
    try:
        up2date.maybeUpdateVersion()
    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

    # quiet mode for rhn_check
    if fromDaemon:
        printCallback = None
        percentCallback = None
    else:
        printCallback = printit
        percentCallback = percent
        
    availUpdates = []
    skippedPackagesPkgSkipList = []
    if not fromDaemon:
        ret = runInteractive(pkgNames, availUpdates,
                             fromDaemon, onlyList,
                             printCallback, percentCallback)
        if ret:
            return ret
    # running from the daemon
    else:
        # runFromDaemon will raise exceptions
        ret = runFromDaemon(pkgNames, actionPkgs, availUpdates, skippedPackagesPkgSkipList, fromDaemon)
        if ret:
            return ret

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

    selPkgList, skippedPackagesFileConfigSkipList  = selectPackages(pkgNames, availUpdates)
    
    if not selPkgList:
        print _("None of the packages you requested were found, or they are already updated.")
	# if we got no updates, but packages we skipped, raise an exception indicating that
	if fromDaemon:
	    if len(skippedPackagesPkgSkipList):
	        raise up2date.SkipListError("The following packages were on your skip list and could not be installed: %s" % skippedPackagesPkgSkipList, skippedPackagesPkgSkipList)
	    if len(skippedPackagesFileConfigSkipList):
		raise up2date.FileConfigSkipListError("The following packages were on your skip list and could not be installed: %s" % skippedPackagesFileConfigSkipList, skippedPackagesFileConfigSkipList)
	return 1
    totalSize = 0

    for pkg in selPkgList:
        if not up2date.isPackageCached(pkg):
            totalSize = totalSize + int(pkg[5])

    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

    # if were running fromDaemon, we want these exceptions to get raised not caught.
    # need to make everything use exceptions.
    if fromDaemon:
        (depPackages, conflicts) = up2date.dryRun(selPkgList, printCallback, percentCallback)
    else:
        try:
            (depPackages, conflicts) = up2date.dryRun(selPkgList, printCallback, percentCallback)
        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.RpmInstallError, e:
            print _("There was a fatal RPM install error. The message was:\n") + e.errmsg
            return 1
        except up2date.DependencyError, e:
            print _("There was a package dependency problem.  The message was:\n") + e.errmsg
            return 1
        except up2date.UnsolvedDependencyError, e:
            print _("There was a package dependency problem.  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 satisfy dependencies:")
        print _("""
Name                                    Version        Release
--------------------------------------------------------------""")
        for pkg in depPackages:
            print "%-40s%-15s%-20s" % (pkg[0], pkg[1], pkg[2])
        print

    # damn translations, let's not break them.
    if not fromDaemon:
        print _("Retrieving selected packages."), "\b.."

        # do signature checking only if GPG installation checks out,
        # and GPG checking is enabled.
        
    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 1:
        errMsg =  _("""GPG does not appear to be installed.  Without it, you will be unable toverify that packages Update Agent downloads are securely signed by a
trusted source.

Your Update Agent options specify that you want to use GPG.  Aborting.""")
        print errMsg
        raise up2date.GPGInstallationError(errMsg)

    if up2date.cfg.readEntry("useGPG") and up2date.checkGPGInstallation() == 2:
        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 %s --import /usr/share/rhn/RPM-GPG-KEY""" % up2date.getGPGflags())
        print errMsg
        raise up2date.GPGKeyringError(errMsg)


    if up2date.cfg.readEntry("noDownload"):
        sys.exit(0)
        
    for pkg in selPkgList:
        try:
            up2date.getPackage(pkg, printPkg, printRetrieveHash)
        except up2date.CommunicationError, e:
            print e
            sys.exit(1)

        if up2date.cfg.readEntry("useGPG"):
            try:
                res = up2date.hasBadSignature(pkg)
            except up2date.RpmError, e:
                print e
                sys.exit(1)
            if res == 1:
                raise up2date.GPGVerificationUnsignedPackageError("%s" % up2date.pkgToString(pkg))
            if res == 2:
                raise up2date.GPGVerificationError("%s" % up2date.pkgToString(pkg))

    if up2date.cfg.readEntry("retrieveOnly"):
        return 0
    
    kernelsToInstall = []
    global packagesTotal
    packagesTotal = len(selPkgList)
    if fromDaemon:
        kernelsToInstall = up2date.installPackages(selPkgList, rpmCallback)
    else:
        try:
            kernelsToInstall = up2date.installPackages(selPkgList, rpmCallback)
        except up2date.CommunicationError, e:
            print e
            return 1
        except up2date.RpmError, e:
            print e
            return 1
        except up2date.RpmInstallError, e:
            print e
            return 1

    if kernelsToInstall and lilocfg:
        try:
            up2date.installBootLoader(kernelsToInstall)
        except (lilocfg.LiloConfError, lilocfg.LiloInstallError),  info:
            print "%s" % info
            return 1
   
    if up2date.cfg.readEntry("keepAfterInstall"):
        return 0
    
    # Try to remove the downloaded packages.
    for pkg in selPkgList:
        try:
            up2date.removePackage(pkg)
        except up2date.FileError, e:
            print e

    # we're fine up to this point...
    return 0
            
        
if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print "\nAborted."
        sys.exit(1)
    except OSError, e:
        print _("An unexpected OS error occurred: %s") % e
        sys.exit(1)
    except IOError, e:
        print _("There was some sort of I/O error: %s") % e
        sys.exit(1)
