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 | |
---|
11 | import cups |
---|
12 | import hesiod |
---|
13 | import getopt |
---|
14 | import os |
---|
15 | import shlex |
---|
16 | import socket |
---|
17 | import sys |
---|
18 | from subprocess import call, PIPE |
---|
19 | |
---|
20 | from debathena import printing |
---|
21 | |
---|
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): |
---|
28 | """A wrapper with somewhat graceful error handling.""" |
---|
29 | try: |
---|
30 | h = hesiod.Lookup(hes_name, hes_type) |
---|
31 | if len(h.results) > 0: |
---|
32 | return h.results |
---|
33 | except IOError: |
---|
34 | return [] |
---|
35 | |
---|
36 | def lpropt_transform(args): |
---|
37 | if 'LPROPT' in os.environ: |
---|
38 | return shlex.split(os.environ['LPROPT']) + args |
---|
39 | return args |
---|
40 | |
---|
41 | def zephyr_transform(options): |
---|
42 | zephyr = True |
---|
43 | return_options = [] |
---|
44 | for o, a in options: |
---|
45 | if o == '-N': |
---|
46 | zephyr = False |
---|
47 | else: |
---|
48 | return_options.append((o, a)) |
---|
49 | |
---|
50 | if zephyr and os.environ.get('ATHENA_USER'): |
---|
51 | return_options.append(('-m', 'zephyr%' + os.environ['ATHENA_USER'])) |
---|
52 | |
---|
53 | return return_options |
---|
54 | |
---|
55 | opts = { |
---|
56 | 'cups': { |
---|
57 | 'lp': ('EU:cd:h:mn:o:q:st:H:P:i:', None, '-d'), |
---|
58 | 'lpq': ('EU:h:P:al', None, '-P'), |
---|
59 | 'lpr': ('EH:U:P:#:hlmo:pqrC:J:T:', |
---|
60 | (lpropt_transform, zephyr_transform), '-P'), |
---|
61 | 'lprm': ('EU:h:P:', None, '-P'), |
---|
62 | }, |
---|
63 | 'lprng': { |
---|
64 | 'lp': ('ckmprswBGYd:D:f:n:q:t:', None, '-d'), |
---|
65 | 'lpq': ('aAlLVcvP:st:D:', None, '-P'), |
---|
66 | 'lpr': ('ABblC:D:F:Ghi:kJ:K:#:m:NP:rR:sT:U:Vw:X:YZ:z1:2:3:4:', |
---|
67 | (lpropt_transform, zephyr_transform), '-P'), |
---|
68 | 'lprm': ('aAD:P:VU:', None, '-P'), |
---|
69 | } |
---|
70 | } |
---|
71 | |
---|
72 | def error(code, message): |
---|
73 | """Exit out with an error |
---|
74 | """ |
---|
75 | sys.stderr.write(message) |
---|
76 | sys.exit(code) |
---|
77 | |
---|
78 | def execCups(command, queue, args): |
---|
79 | """Pass the command and arguments on to the CUPS versions of the command |
---|
80 | """ |
---|
81 | new_command = '/usr/bin/cups-%s' % command |
---|
82 | os.execv(new_command, [command] + args) |
---|
83 | |
---|
84 | def execLprng(command, queue, args): |
---|
85 | """Pass the command and arguments on to the LPRng versions of the command |
---|
86 | """ |
---|
87 | new_command = '/usr/bin/mit-%s' % command |
---|
88 | os.execv(new_command, [command] + args) |
---|
89 | |
---|
90 | def getPrintQueue(command, args): |
---|
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 |
---|
93 | that this script was called as. Also, return whether the command line |
---|
94 | options imply a particular version of the command. |
---|
95 | """ |
---|
96 | |
---|
97 | preference = 'cups' |
---|
98 | for version in ['cups', 'lprng']: |
---|
99 | try: |
---|
100 | # Get the set of options that correspond to the command that this |
---|
101 | # script was invoked as |
---|
102 | (cmd_opts, transformers, destopt) = opts[version][command] |
---|
103 | if transformers: |
---|
104 | (transform_args, transform_opts) = transformers |
---|
105 | else: |
---|
106 | transform_args = transform_opts = lambda x: x |
---|
107 | except KeyError: |
---|
108 | error(1, """ |
---|
109 | Error: this script was called as %s, when it must be called as |
---|
110 | one of lpr, lpq, lprm, or lp |
---|
111 | |
---|
112 | """ % command) |
---|
113 | |
---|
114 | # Attempt to parse it with the current version of this command |
---|
115 | targs = transform_args(args) |
---|
116 | try: |
---|
117 | options, realargs = getopt.gnu_getopt(targs, cmd_opts) |
---|
118 | except getopt.GetoptError: |
---|
119 | # That's the wrong version, so try the next one. |
---|
120 | continue |
---|
121 | |
---|
122 | options = transform_opts(options) |
---|
123 | ttargs = [o + a for o, a in options] + realargs |
---|
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) |
---|
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 | |
---|
136 | # Either we failed to getopt, or nobody told us what printer to use, |
---|
137 | # so let's use the default printer |
---|
138 | default = os.getenv('PRINTER') |
---|
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: |
---|
145 | (key, value) = result.split(None, 1) |
---|
146 | if key == 'lpr': |
---|
147 | default = value |
---|
148 | |
---|
149 | if default: |
---|
150 | return (preference, default, args) |
---|
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 |
---|
194 | else: |
---|
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 |
---|
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)) |
---|
204 | |
---|
205 | # Determine if the arguments prefer one version of this command, |
---|
206 | # For instance, "lpr -Zduplex" wants mit-lpr. |
---|
207 | preference, queue, args = getPrintQueue(command, sys.argv) |
---|
208 | |
---|
209 | system, server, queue = printing.find_queue(queue) |
---|
210 | |
---|
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 |
---|
216 | execCups(command, queue, args) |
---|
217 | else: |
---|
218 | execLprng(command, queue, args) |
---|