30 | | import select |
31 | | import signal |
32 | | import time |
| 30 | import tempfile |
| 31 | |
| 32 | class CupsBackend : |
| 33 | """Base class for tools with no database access.""" |
| 34 | def __init__(self) : |
| 35 | """Initializes the CUPS backend wrapper.""" |
| 36 | self.debug = 1 |
| 37 | |
| 38 | def fakePrint(self) : |
| 39 | """Fakes to print the job.""" |
| 40 | if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) : |
| 41 | # Either the job's datas are on our stdin, or in a file |
| 42 | if len(sys.argv) == 7 : |
| 43 | self.InputFile = sys.argv[6] |
| 44 | else : |
| 45 | self.InputFile = None |
| 46 | |
| 47 | # check that the DEVICE_URI environment variable's value is |
| 48 | # prefixed with "cupsoftee:" otherwise don't touch it. |
| 49 | # If this is the case, we have to remove the prefix from |
| 50 | # the environment before launching the real backend in cupspykota |
| 51 | device_uri = os.environ.get("DEVICE_URI", "") |
| 52 | if device_uri.startswith("cupspykota:") : |
| 53 | fulldevice_uri = device_uri[:] |
| 54 | device_uri = fulldevice_uri[len("cupspykota:"):] |
| 55 | if device_uri.startswith("//") : # lpd (at least) |
| 56 | device_uri = device_uri[2:] |
| 57 | os.environ["DEVICE_URI"] = device_uri # TODO : side effect ! |
| 58 | # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp |
| 59 | try : |
| 60 | (backend, destination) = device_uri.split(":", 1) |
| 61 | except ValueError : |
| 62 | raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri |
| 63 | while destination.startswith("/") : |
| 64 | destination = destination[1:] |
| 65 | checkauth = destination.split("@", 1) |
| 66 | if len(checkauth) == 2 : |
| 67 | destination = checkauth[1] |
| 68 | printerhostname = destination.split("/")[0].split(":")[0] |
| 69 | return ("CUPS", \ |
| 70 | printerhostname, \ |
| 71 | os.environ.get("PRINTER"), \ |
| 72 | sys.argv[2].strip(), \ |
| 73 | sys.argv[1].strip(), \ |
| 74 | inputfile, \ |
| 75 | int(sys.argv[4].strip()), \ |
| 76 | sys.argv[3], \ |
| 77 | sys.argv[5], \ |
| 78 | backend) |
| 79 | |
| 80 | def discoverOtherBackends(self) : |
| 81 | """Discovers the other CUPS backends. |
| 82 | |
| 83 | Executes each existing backend in turn in device enumeration mode. |
| 84 | Returns the list of available backends. |
| 85 | """ |
| 86 | self.logDebug("Entering device enumeration loop.") |
| 87 | available = [] |
| 88 | tmpdir = tempfile.gettempdir() |
| 89 | lockfilename = os.path.join(tmpdir, "cupsoftee..LCK") |
| 90 | if os.path.exists(lockfilename) : |
| 91 | self.logInfo("Lock file present.") |
| 92 | locked = 1 |
| 93 | lockfile = open(lockfilename, "r") |
| 94 | pid = int(lockfile.read()) |
| 95 | lockfile.close() |
| 96 | try : |
| 97 | # see if the pid contained in the lock file is still running |
| 98 | os.kill(pid, 0) |
| 99 | except OSError, e : |
| 100 | if e.errno != errno.EPERM : |
| 101 | # process doesn't exist anymore |
| 102 | self.logInfo("Removing stalled lock.") |
| 103 | os.remove(lockfilename) |
| 104 | locked = 0 |
| 105 | if locked : |
| 106 | self.logInfo("No device enumeration.") |
| 107 | |
| 108 | if not os.path.exists(lockfilename) : |
| 109 | lockfile = open(lockfilename, "w") |
| 110 | lockfile.write("%i" % os.getpid()) |
| 111 | lockfile.close() |
| 112 | (directory, myname) = os.path.split(sys.argv[0]) |
| 113 | for backend in [ os.path.join(directory, b) \ |
| 114 | for b in os.listdir(directory) |
| 115 | if os.path.isfile(os.path.join(directory, b))\ |
| 116 | and (b != myname)] : |
| 117 | answer = os.popen(backend, "r") |
| 118 | try : |
| 119 | devices = [line.strip() for line in answer.readlines()] |
| 120 | except : |
| 121 | devices = [] |
| 122 | status = answer.close() |
| 123 | if status is None : |
| 124 | for d in devices : |
| 125 | # each line is of the form : |
| 126 | # 'xxxx xxxx "xxxx xxx" "xxxx xxx"' |
| 127 | # so we have to decompose it carefully |
| 128 | fdevice = cStringIO.StringIO(d) |
| 129 | tokenizer = shlex.shlex(fdevice) |
| 130 | tokenizer.wordchars = tokenizer.wordchars + \ |
| 131 | r".:,?!~/\_$*-+={}[]()#" |
| 132 | arguments = [] |
| 133 | while 1 : |
| 134 | token = tokenizer.get_token() |
| 135 | if token : |
| 136 | arguments.append(token) |
| 137 | else : |
| 138 | break |
| 139 | fdevice.close() |
| 140 | try : |
| 141 | (devicetype, device, name, fullname) = arguments |
| 142 | except ValueError : |
| 143 | pass # ignore this 'bizarre' device |
| 144 | else : |
| 145 | if name.startswith('"') and name.endswith('"') : |
| 146 | name = name[1:-1] |
| 147 | if fullname.startswith('"') and fullname.endswith('"') : |
| 148 | fullname = fullname[1:-1] |
| 149 | available.append('%s cupsoftee:%s "CupsOfTee+%s" "CupsOfTee managed %s"' \ |
| 150 | % (devicetype, device, name, fullname)) |
| 151 | os.remove(lockfilename) |
| 152 | self.logDebug("Exiting device enumeration loop.") |
| 153 | return available |
| 154 | |
| 155 | def logDebug(self, message) : |
| 156 | """Logs something to debug output if debug is enabled.""" |
| 157 | if self.debug : |
| 158 | sys.stderr.write("DEBUG: %s\n" % message) |
| 159 | sys.stderr.flush() |
| 160 | |
| 161 | def logInfo(self, message, level="info") : |
| 162 | """Logs a message to CUPS' error_log file.""" |
| 163 | sys.stderr.write("%s: %s\n" % (level.upper(), message)) |
| 164 | sys.stderr.flush() |
| 165 | |
38 | | # we will execute each existing backend in device enumeration mode |
39 | | # and generate their PyKota accounting counterpart |
40 | | (directory, myname) = os.path.split(sys.argv[0]) |
41 | | for backend in [os.path.join(directory, b) for b in os.listdir(directory) if os.path.isfile(os.path.join(directory, b)) and (b != myname)] : |
42 | | answer = os.popen(backend, "r") |
43 | | try : |
44 | | devices = [line.strip() for line in answer.readlines()] |
45 | | except : |
46 | | devices = [] |
47 | | status = answer.close() |
48 | | if status is None : |
49 | | for d in devices : |
50 | | # each line is of the form : 'xxxx xxxx "xxxx xxx" "xxxx xxx"' |
51 | | # so we have to decompose it carefully |
52 | | fdevice = cStringIO.StringIO("%s" % d) |
53 | | tokenizer = shlex.shlex(fdevice) |
54 | | tokenizer.wordchars = tokenizer.wordchars + r".:,?!~/\_$*-+={}[]()#" |
55 | | arguments = [] |
56 | | while 1 : |
57 | | token = tokenizer.get_token() |
58 | | if token : |
59 | | arguments.append(token) |
60 | | else : |
61 | | break |
62 | | fdevice.close() |
63 | | try : |
64 | | (devicetype, device, name, fullname) = arguments |
65 | | except ValueError : |
66 | | pass # ignore this 'bizarre' device |
67 | | else : |
68 | | if name.startswith('"') and name.endswith('"') : |
69 | | name = name[1:-1] |
70 | | if fullname.startswith('"') and fullname.endswith('"') : |
71 | | fullname = fullname[1:-1] |
72 | | print '%s cupspykota:%s "PyKota+%s" "PyKota managed %s"' % (devicetype, device, name, fullname) |
73 | | retcode = 0 |
| 171 | print "\n".join(wrapper.discoverOtherBackends()) |
| 172 | sys.exit(0) |