#!/usr/bin/python # cups2lprng.py - Cups Backend to transfer print jobs to LPRng # Copyright 2003, 2006 NC State University # Written by Jack Neely # # 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. # Cups Exit Codes: # 1 and 4: Place queue in error state and halt future printing # # 0 (CUPS_BACKEND_OK) # 1 (CUPS_BACKEND_FAILED) # 2 (CUPS_BACKEND_AUTH_REQUIRED) # 3 (CUPS_BACKEND_HOLD) # 4 (CUPS_BACKEND_STOP) # 5 (CUPS_BACKEND_CANCEL) import os import sys import string import pwd import tempfile import shutil version = "0.0.2" lpr = "/usr/local/bin/lpr" pcap = "/afs/bp/contrib/lprng/etc/printcap" def getRealmPrinter(): file = "/etc/rc.conf.d/HostPrinter" if not os.access(file, os.R_OK): return None printer = open(file).read().strip() if printer == "lp": return None if printer == "": return None # Return exactly what's in the HostPrinter file # if this is a realm printer there should be no @print.ncsu.edu # but allow for that case to print to weird printers. # Although having @ in this file will break CLI printing with Cups return printer def parseURI(uri): "Parse the URI and return printer[@server]" tuple = string.split(uri, "/") server = tuple[2] printer = tuple[3] if printer == "ncsurealmdefault": return getRealmPrinter() else: #return "%s@%s" % (printer, server) return printer 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 setupTokens(): # God is this evil, grab the user's tokens os.system("/usr/bin/aklog bp.ncsu.edu") os.system("/usr/bin/aklog eos.ncsu.edu") os.system("/usr/bin/aklog unity.ncsu.edu") def setUpAuth(auth): """Set up LPRng authentication. Mainly a catch for kerberos as we no longer know 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 ncsu \"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 try: if file != "-": os.environ["TMPDIR"] = "/tmp" tmpfile = tempfile.mktemp() shutil.copyfile(file, tmpfile) os.chown(tmpfile, userInfo[2], userInfo[3]) os.chmod(tmpfile, 0600) file = tmpfile except Exception, e: sys.stderr.write("An IO error occured in the NCSU Cups driver.\n") sys.stderr.write("Error: %s\n" % str(e)) sys.exit(5) # the local printcap Cups creates is for compatibility and interfears # with running LPRng commands. We nuke it. if os.access("/etc/printcap", os.F_OK): os.unlink("/etc/printcap") # Okay, Bye Bye root!! os.setgid(userInfo[3]) os.setuid(userInfo[2]) # Setup Kerberos and AFS Authentication setUpAuth("kerberos") setupTokens() os.environ['USER'] = userInfo[0] os.environ['PATH'] = "/usr/local/bin:/usr/bin:/bin" # 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(lpr, os.X_OK): sys.stderr.write("ERROR: %s was not found.\n" % lpr) sys.exit(1) printer = parseURI(URI) if printer == None: sys.stderr.write("Error: No printer in /etc/rc.conf.d/HostPrinter") sys.exit(5) cmd = "%s -P%s -J '%s' -K %s" % (lpr, printer, jobname, copies) # Work with options...this can be extended as much as needed #opt = parseOptions(options_string) # Ignore this for now, the printcap file should provide what we want #if opt.has_key("auth") and opt["auth"] != "no": # # 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 try: pipe = os.popen(cmd, "w") line = fd.readline() while line != "": pipe.write(line) line = fd.readline() pipe.close() except Exception, e: sys.stderr.write("Error writing to pipe to lpr command.") sys.exit(5) if file != "-": try: fd.close() os.unlink(file) except Exception, e: pass if __name__ == "__main__": main()