#!/usr/bin/python

#    cups2lprng.py - Cups Backend to transfer print jobs to LPRng
#    Copyright 2003 NC State University
#    Written by Jack Neely <jjneely@pams.ncsu.edu>
#
#    SDG
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    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
import sys
import string
import pwd
import tempfile
import shutil

version = "0.0.1"

def parseURI(uri):
    "Parse the URI and return printer[@server]"

    tuple = string.split(uri, "/")
    server = tuple[2]
    printer = tuple[3]

    # Construct return value
    return "%s@%s" % (printer, server)


def parseOptions(options):
    """Returns a dict of options passed to this backend.  The string passed
       in is of the format name=value separated by spaces.  This will be
       transformed into a dict where name is the key that the value is
       referenced by."""

    ret = {}
    list = string.split(options)
    for pair in list:
        (name, value) = string.split(pair, "=")
        ret[name] = value

    return ret


def fileCompare(file1, file2):
    "Compare files by last modification time."

    time1 = os.stat(file1).st_mtime
    time2 = os.stat(file2).st_mtime

    if time1 == time2:
        return 0
    if time1 < time2:
        return -1
    return 1


def setUpAuth(auth):
    """Set up LPRng authentication.  Mainly a catch for kerberos as we
       no longer no for sure where the credential cache file is, so
       let's find it."""
    
    os.environ["AUTH"] = auth
    if auth == "kerberos":
        # Find the credential cache (Kerberos 5)
        basefile = "/tmp/krb5cc_%s" % os.getuid()
        if os.access(basefile, os.R_OK):
            os.environ["KRB5CCNAME"] = "FILE:%s" % basefile
            return
        
        # This is kind of a hack...find the krb file that looks right
        ls = os.listdir("/tmp")
        caches = []
        for file in ls:
            abs = os.path.join("/tmp", file)
            if abs[0:len(basefile)] == basefile:
                caches.append(abs)

        # Sort by last modification time
        if len(caches) == 0:
            sys.stderr.write("WARNING: Can't find kerberos cache file.\n")
            return
        caches.sort(fileCompare)
        caches.reverse()
        os.environ["KRB5CCNAME"] = "FILE:%s" % caches[0]

    
def main():

    global version
    
    # Report device discovery
    if len(sys.argv) <= 1:
        print "network LPRng \"Unknown\" \"Cups to LPRng Gateway %s\""% version
        sys.exit(0)
    
    # Read in data from Cups
    if len(sys.argv) == 7:
        file = sys.argv[6]
    elif len(sys.argv) == 6:
        file = "-"
    else:
        sys.stderr.write("ERROR: Not enough cammand arguments.\n")
        sys.exit(1)
    
    user = sys.argv[2]
    copies = sys.argv[4]
    jobname = sys.argv[3]
    options_string = sys.argv[5]
    URI = os.environ["DEVICE_URI"]

    # Get user's information
    userInfo = pwd.getpwnam(user)

    # Arg...only lp or root can read the spool file
    if file != "-":
        # Pile of shit Cups sets TMPDIR to a place only root can get to
        os.environ["TMPDIR"] = "/tmp"
        tmpfile = tempfile.mktemp()
        shutil.copyfile(file, tmpfile)
        os.chown(tmpfile, userInfo[2], userInfo[3])
        os.chmod(tmpfile, 0600)
        file = tmpfile

    # Okay, Bye Bye root!!
    os.setgid(userInfo[3])
    os.setuid(userInfo[2])

    # We should now be the user that submitted this job and hopefully
    # have access to very cool things...like kerberos tickets

    if not os.access("/usr/bin/lpr.LPRng", os.X_OK):
        sys.stderr.write("ERROR: /usr/bin/lpr.LPRng was not found.\n")
        sys.exit(1)

    cmd = "/usr/bin/lpr.LPRng -P%s -J '%s' -K %s" % (parseURI(URI),
          jobname, copies)

    # Work with options...this can be extended as much as needed
    opt = parseOptions(options_string)
    if opt.has_key("auth"):
        # Set up LPRng authentication...like kerberos
        setUpAuth(opt["auth"])
        cmd = "%s -A" % cmd

    if file == "-":
        fd = sys.stdin
    else:
        fd = open(file)

    # Write file or stdin to the lpr command.
    # Attempting to not read in all data at once to save memory
    pipe = os.popen(cmd, "w")
    line = fd.readline()
    while line != "":
        pipe.write(line)
        line = fd.readline()
        
    pipe.close()
    if file != "-":
        fd.close()
        os.unlink(file)
    

if __name__ == "__main__":
    main()

