[23055] | 1 | #!/usr/bin/python |
---|
| 2 | |
---|
| 3 | # lpr.debathena |
---|
| 4 | # |
---|
| 5 | # also lp.debathena, lpq.debathena, and lprm.debathena |
---|
| 6 | # |
---|
| 7 | # Wrapper script that intelligently determines whether a command was |
---|
| 8 | # intended for should go to CUPS or LPRng and sends it off in the |
---|
| 9 | # right direction |
---|
| 10 | |
---|
[24201] | 11 | import cups |
---|
[23157] | 12 | import hesiod |
---|
[23055] | 13 | import getopt |
---|
| 14 | import os |
---|
[23950] | 15 | import shlex |
---|
[24201] | 16 | import socket |
---|
[23055] | 17 | import sys |
---|
| 18 | from subprocess import call, PIPE |
---|
| 19 | |
---|
[24515] | 20 | from debathena import printing |
---|
| 21 | |
---|
[24201] | 22 | try: |
---|
| 23 | cupsd = cups.Connection() |
---|
| 24 | except RuntimeError: # Is this actually safer than "except:"? |
---|
| 25 | cupsd = None |
---|
| 26 | |
---|
| 27 | def hesiod_lookup(hes_name, hes_type): |
---|
[24209] | 28 | """A wrapper with somewhat graceful error handling.""" |
---|
[24201] | 29 | try: |
---|
| 30 | h = hesiod.Lookup(hes_name, hes_type) |
---|
| 31 | if len(h.results) > 0: |
---|
| 32 | return h.results |
---|
[24265] | 33 | except IOError: |
---|
| 34 | return [] |
---|
[24201] | 35 | |
---|
[23950] | 36 | def lpropt_transform(args): |
---|
| 37 | if 'LPROPT' in os.environ: |
---|
[24001] | 38 | return shlex.split(os.environ['LPROPT']) + args |
---|
[23950] | 39 | return args |
---|
| 40 | |
---|
[23945] | 41 | def zephyr_transform(options): |
---|
[23954] | 42 | zephyr = True |
---|
| 43 | return_options = [] |
---|
| 44 | for o, a in options: |
---|
[24216] | 45 | if o == '-N': |
---|
| 46 | zephyr = False |
---|
[23954] | 47 | else: |
---|
| 48 | return_options.append((o, a)) |
---|
[23945] | 49 | |
---|
[23954] | 50 | if zephyr and os.environ.get('ATHENA_USER'): |
---|
| 51 | return_options.append(('-m', 'zephyr%' + os.environ['ATHENA_USER'])) |
---|
[23945] | 52 | |
---|
[23954] | 53 | return return_options |
---|
| 54 | |
---|
[23055] | 55 | opts = { |
---|
[23847] | 56 | 'cups': { |
---|
[24201] | 57 | 'lp': ('EU:cd:h:mn:o:q:st:H:P:i:', None, '-d'), |
---|
| 58 | 'lpq': ('EU:h:P:al', None, '-P'), |
---|
[24374] | 59 | 'lpr': ('EH:U:P:#:hlmo:pqrC:J:T:', |
---|
| 60 | (lpropt_transform, zephyr_transform), '-P'), |
---|
[24201] | 61 | 'lprm': ('EU:h:P:', None, '-P'), |
---|
[23847] | 62 | }, |
---|
| 63 | 'lprng': { |
---|
[24201] | 64 | 'lp': ('ckmprswBGYd:D:f:n:q:t:', None, '-d'), |
---|
| 65 | 'lpq': ('aAlLVcvP:st:D:', None, '-P'), |
---|
[24216] | 66 | 'lpr': ('ABblC:D:F:Ghi:kJ:K:#:m:NP:rR:sT:U:Vw:X:YZ:z1:2:3:4:', |
---|
[24201] | 67 | (lpropt_transform, zephyr_transform), '-P'), |
---|
| 68 | 'lprm': ('aAD:P:VU:', None, '-P'), |
---|
[23847] | 69 | } |
---|
[23055] | 70 | } |
---|
| 71 | |
---|
| 72 | def error(code, message): |
---|
| 73 | """Exit out with an error |
---|
| 74 | """ |
---|
| 75 | sys.stderr.write(message) |
---|
| 76 | sys.exit(code) |
---|
| 77 | |
---|
[24201] | 78 | def execCups(command, queue, args): |
---|
| 79 | """Pass the command and arguments on to the CUPS versions of the command |
---|
[23055] | 80 | """ |
---|
[24201] | 81 | new_command = '/usr/bin/cups-%s' % command |
---|
| 82 | os.execv(new_command, [command] + args) |
---|
[23055] | 83 | |
---|
[24201] | 84 | def execLprng(command, queue, args): |
---|
| 85 | """Pass the command and arguments on to the LPRng versions of the command |
---|
[23055] | 86 | """ |
---|
[24201] | 87 | new_command = '/usr/bin/mit-%s' % command |
---|
| 88 | os.execv(new_command, [command] + args) |
---|
[23055] | 89 | |
---|
[24201] | 90 | def getPrintQueue(command, args): |
---|
[23055] | 91 | """Given argv, extract the printer name, using knowledge of the possible |
---|
| 92 | command line options for both the CUPS and LPRng versions of the command |
---|
[23847] | 93 | that this script was called as. Also, return whether the command line |
---|
| 94 | options imply a particular version of the command. |
---|
[23055] | 95 | """ |
---|
[24201] | 96 | |
---|
| 97 | preference = 'cups' |
---|
| 98 | for version in ['cups', 'lprng']: |
---|
[23847] | 99 | try: |
---|
| 100 | # Get the set of options that correspond to the command that this |
---|
| 101 | # script was invoked as |
---|
[24201] | 102 | (cmd_opts, transformers, destopt) = opts[version][command] |
---|
[23950] | 103 | if transformers: |
---|
| 104 | (transform_args, transform_opts) = transformers |
---|
| 105 | else: |
---|
[24001] | 106 | transform_args = transform_opts = lambda x: x |
---|
[23847] | 107 | except KeyError: |
---|
| 108 | error(1, """ |
---|
| 109 | Error: this script was called as %s, when it must be called as |
---|
[24202] | 110 | one of lpr, lpq, lprm, or lp |
---|
[23847] | 111 | |
---|
| 112 | """ % command) |
---|
| 113 | |
---|
| 114 | # Attempt to parse it with the current version of this command |
---|
[24001] | 115 | targs = transform_args(args) |
---|
[23847] | 116 | try: |
---|
[24001] | 117 | options, realargs = getopt.gnu_getopt(targs, cmd_opts) |
---|
[23847] | 118 | except getopt.GetoptError: |
---|
| 119 | # That's the wrong version, so try the next one. |
---|
| 120 | continue |
---|
| 121 | |
---|
[24001] | 122 | options = transform_opts(options) |
---|
| 123 | ttargs = [o + a for o, a in options] + realargs |
---|
[24201] | 124 | if destopt: |
---|
| 125 | for o, a in options: |
---|
| 126 | if o == destopt: |
---|
| 127 | return (version, a, ttargs) |
---|
| 128 | else: |
---|
| 129 | if len(realargs) > 0: |
---|
| 130 | return (version, realargs[0], ttargs) |
---|
[23847] | 131 | # Since we've successfully getopt'd, don't try any other versions, |
---|
| 132 | # but do note that we like this version. |
---|
| 133 | preference = version |
---|
| 134 | break |
---|
| 135 | |
---|
[24201] | 136 | # Either we failed to getopt, or nobody told us what printer to use, |
---|
| 137 | # so let's use the default printer |
---|
[23157] | 138 | default = os.getenv('PRINTER') |
---|
[24201] | 139 | if not default: |
---|
| 140 | if cupsd: |
---|
| 141 | default = cupsd.getDefault() |
---|
| 142 | if not default: |
---|
| 143 | h = hesiod_lookup(os.uname()[1], 'cluster') |
---|
| 144 | for result in h: |
---|
[24227] | 145 | (key, value) = result.split(None, 1) |
---|
[24201] | 146 | if key == 'lpr': |
---|
| 147 | default = value |
---|
| 148 | |
---|
[23157] | 149 | if default: |
---|
[23945] | 150 | return (preference, default, args) |
---|
[24201] | 151 | else: |
---|
| 152 | error(2, """ |
---|
| 153 | No default printer configured. Specify a -P option, or configure a |
---|
| 154 | default printer via e.g. System | Administration | Printing. |
---|
| 155 | |
---|
| 156 | """) |
---|
| 157 | |
---|
| 158 | def translate_lprng_args_to_cups(command, args): |
---|
| 159 | # TODO yell at user if/when we decide that these args are deprecated |
---|
| 160 | |
---|
| 161 | # If getopt fails, something went very wrong -- _we_ generated this |
---|
| 162 | options, realargs = getopt.gnu_getopt(args, opts['lprng'][command][0]) |
---|
| 163 | cupsargs = [] |
---|
| 164 | for (o, a) in options: |
---|
| 165 | if o in ('-a') and command == 'lpq': |
---|
| 166 | cupsargs += [('-a', a)] |
---|
| 167 | elif o in ('-b', '-l') and command == 'lpr': |
---|
| 168 | cupsargs += [('-l', a)] |
---|
| 169 | elif o in ('-h') and command == 'lpr': |
---|
| 170 | cupsargs += [('-h', a)] |
---|
| 171 | elif o in ('-J') and command == 'lpr': |
---|
| 172 | cupsargs += [('-J', a)] |
---|
| 173 | elif o in ('-K', '-#') and command == 'lpr': |
---|
| 174 | cupsargs += [('-#', a)] |
---|
| 175 | elif o in ('-P'): |
---|
| 176 | cupsargs += [('-P', a)] |
---|
| 177 | elif o in ('-T') and command == 'lpr': |
---|
| 178 | cupsargs += [('-T', a)] |
---|
| 179 | elif o in ('-U') and command == 'lpr': |
---|
| 180 | cupsargs += [('-U', a)] |
---|
| 181 | elif o in ('-v') and command == 'lpq': |
---|
| 182 | cupsargs += [('-l', a)] |
---|
| 183 | elif o in ('-Z') and command == 'lpr': |
---|
| 184 | if a == 'simplex': |
---|
| 185 | cupsargs += [('-o', 'sides=one-sided')] |
---|
| 186 | elif a == 'duplex': |
---|
| 187 | cupsargs += [('-o', 'sides=two-sided-long-edge')] |
---|
| 188 | elif a == 'duplexshort': |
---|
| 189 | cupsargs += [('-o', 'sides=two-sided-short-edge')] |
---|
| 190 | # TODO attempt to deal banner=staff |
---|
| 191 | elif o in ('-m') and command == 'lpr': |
---|
| 192 | # TODO figure out if CUPS can do mail/zephyr |
---|
| 193 | pass # Don't warn about this, we probably generated it |
---|
[23157] | 194 | else: |
---|
[24201] | 195 | sys.stderr.write("Warning: option %s%s not converted to CUPS\n" |
---|
| 196 | % (o, a)) |
---|
| 197 | joincupsargs = [o + a for o, a in cupsargs] + realargs |
---|
| 198 | sys.stderr.write("Using cups-%s %s\n" % (command, ' '.join(joincupsargs))) |
---|
| 199 | return joincupsargs |
---|
[23055] | 200 | |
---|
| 201 | if __name__ == '__main__': |
---|
| 202 | # Remove the command name from the arguments when we extract it |
---|
| 203 | command = os.path.basename(sys.argv.pop(0)) |
---|
[23847] | 204 | |
---|
| 205 | # Determine if the arguments prefer one version of this command, |
---|
[24201] | 206 | # For instance, "lpr -Zduplex" wants mit-lpr. |
---|
[24515] | 207 | preference, queue, args = getPrintQueue(command, sys.argv) |
---|
[23847] | 208 | |
---|
[24515] | 209 | system, server, queue = printing.find_queue(queue) |
---|
[24209] | 210 | |
---|
[24515] | 211 | if system == printing.SYSTEM_CUPS: |
---|
| 212 | if preference == 'lprng': |
---|
| 213 | args = translate_lprng_args_to_cups(command, args) |
---|
| 214 | if server: |
---|
| 215 | os.environ['CUPS_SERVER'] = server |
---|
[24201] | 216 | execCups(command, queue, args) |
---|
[24519] | 217 | elif system == printing.SYSTEM_LPRNG: |
---|
| 218 | execLprng(command, queue, args) |
---|
[24515] | 219 | else: |
---|
[24519] | 220 | error(1, """ |
---|
| 221 | Error: the print queue %s uses an unknown printing infrastructure |
---|
| 222 | |
---|
| 223 | """ % queue) |
---|