#!/bin/env python
import dbus, sys, os, time
import traceback
import locale
sys.path.append("/usr/share/system-config-printer")
import cups, cupshelpers, foomatic
from syslog import *

class HalPrinter:
    def __init__(self):
        self.get_properties()
        self.uris = None
        try:
            self.cups_connection = cups.Connection()
        except RuntimeError:
            self.cups_connection = None

    def get_properties(self):
        self.properties = {}
        for key, value in os.environ.iteritems():
            if key.startswith("HAL_PROP_"):
                name = key[9:].lower().replace("_", '.')
                self.properties[name] = value
        self.uid = os.getenv("UDI", "")
        self.read()

    def read(self):
        p = self.properties
        self.make = (p.get("printer.vendor", "") or
                     p.get("usb.vendor", "Unknown"))
        self.model = (p.get("printer.product", "") or
                      p.get("usb.product", "Unknown"))
        self.description = p.get("printer.description", "")
        self.name = self.get_name()
        self.commandsets = p.get('printer.commandset', '').split('\t')

    def get_name(self):
        # XXX check for unallowed chars
        if self.properties.has_key("usb.port_number"):
            name = "%s-%s" % (self.model,
                              self.properties["usb.port_number"])
        else:
            name = self.model
        name = name.replace(" ", "_")
        name = name.replace("/", "_")
        return name.replace("#", "_")

    def get_cups_uris(self, removed=False):
        if self.uris != None:
            return self.uris
        uris=["hal://%s" % self.uid]
        if (not removed and self.properties.has_key("printer.vendor") and
            self.properties.has_key("printer.serial")):
            vendor = self.properties["printer.vendor"].lower ()
            if vendor == "hewlett-packard" or vendor == "hp":
                # Perhaps HPLIP can drive this device.  If so, we
                # should use an 'hp:...' URI for CUPS.
                try:
                    time.sleep (1) # Give HPLIP a chance to reconnect
                    devices = cupshelpers.getDevices (self.cups_connection)
                    for uri in devices.keys ():
                        if not uri.startswith ("hp:"): continue
                        s = uri.find ("?serial=")
                        if s == -1: continue
                        s += 8
                        e = uri[s:].find ("?")
                        if e == -1: e = len (uri)
                        serial = uri[s:e]
                        if serial == self.properties["printer.serial"]:
                           uris.insert (0, uri)
                           break
                except:
                    pass
        self.uris = uris
        return uris

    def get_cups_uri(self):
        return self.get_cups_uris()[0]

    def add(self):
        syslog (LOG_DEBUG, "add")
        printers = cupshelpers.getPrinters(self.cups_connection)

        uris = self.get_cups_uris ()
        syslog (LOG_DEBUG, "URIs: %s" % uris)
        for name, printer in printers.iteritems():
            if printer.is_class: continue
            if (name == self.name or
                printer.device_uri in uris):
                syslog (LOG_DEBUG,
                        "Not adding printer: %s already exists" % name)
                if not printer.enabled:
                    syslog (LOG_INFO,
                            "Printer %s exists but is disabled; "
                            "use 'cupsenable %s' to enable it" % (name, name))
                return

        # really new printer - try autodetection
        
        f = foomatic.Foomatic()
        ppd = f.getPPD(self.make, self.model, self.description,
                       self.commandsets)
        if ppd:
            try:
                info = ppd.findAttr('NickName').value
                b = info.find (' (')
                if b != -1:
                    info = info[:b]
            except:
                info = "%s %s (HAL)" % (self.make, self.model)
            language = locale.getlocale (locale.LC_MESSAGES)
            cupshelpers.setPPDPageSize(ppd, language[0])
            self.cups_connection.addPrinter(
                self.name, device=self.get_cups_uri(),
                ppd=ppd, info=info)
            self.cups_connection.enablePrinter(self.name)
            self.cups_connection.acceptJobs(self.name)
            syslog (LOG_INFO, "Added printer %s" % self.name)
        else:
            # ask eggcups
            syslog (LOG_INFO, "Prompting for driver for printer %s" % self.name)
            bus = dbus.SystemBus()
            obj = bus.get_object("com.redhat.PrintDriverSelection",
                                 "/com/redhat/PrintDriverSelection")
            interface = dbus.Interface(obj, "com.redhat.PrintDriverSelection")
            interface.PromptPrintDriver(self.make, self.model,
                                        self.uid, self.name)

    def remove(self):
        syslog (LOG_DEBUG, "remove: taking no action")
        uris = self.get_cups_uris (removed=True)
        syslog (LOG_DEBUG, "URIs: %s" % uris)

    def configure(self):
        syslog (LOG_DEBUG, "configure")
        make, model = sys.stdin.readlines()
        if make[-1]=="\n": make = make[:-1]
        if model[-1]=="\n": model = model[:-1]

        # get PPD
        f = foomatic.Foomatic()
        printer = f.getMakeModel(make, model)

        if not printer:
            syslog (LOG_ERR,
                    "User-selected make/model \"%s\" \"%s\" not found" %
                    (make, model))
            return
        
        ppd = printer.getPPD()
        if not ppd:
            syslog (LOG_ERR,
                    "Failed to get PPD for %s %s" % (make, model))
            return

        # add printer
        self.cups_connection.addPrinter(
            self.name, device=self.get_cups_uri(),
            ppd=ppd, info="Added by HAL")
        self.cups_connection.enablePrinter(self.name)
        self.cups_connection.acceptJobs(self.name)
        syslog (LOG_INFO,
                "Added printer %s with user-selected make/model" % self.name)

class HalLpAdmin:

    def __init__(self):
        if len(sys.argv)!=2:
            return self.usage()

        if sys.argv[1]=="--add":
            self.addPrinter()
        elif sys.argv[1]=="--remove":
            self.removePrinter()
        elif sys.argv[1]=="--configure":
            self.configurePrinter()
        else:
            return self.usage()

    def usage(self):
        print "Usage: hal_lpadmin (--add|--remove|--configure)"

    def addPrinter(self):
        printer = HalPrinter()
        printer.add()
        
    def removePrinter(self):
        printer = HalPrinter()
        printer.remove()

    def configurePrinter(self):
        printer = HalPrinter()
        printer.configure()

def main():
    openlog ("hal_lpadmin", 0, LOG_DAEMON)
    try:
        h = HalLpAdmin()
    except:
        (type, value, tb) = sys.exc_info ()
        tblast = traceback.extract_tb (tb, limit=None)
        if len (tblast):
            tblast = tblast[:len (tblast) - 1]
        for line in traceback.format_tb (tb):
            syslog (LOG_ERR, line.strip ())
        extxt = traceback.format_exception_only (type, value)
        syslog (LOG_ERR, extxt[0].strip ())

main()
