1 | from . import sync |
---|
2 | from .errors import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException |
---|
3 | from .job import GutenbachJob |
---|
4 | from gutenbach.ipp import PrinterStates as States |
---|
5 | import gutenbach.ipp as ipp |
---|
6 | import logging |
---|
7 | import math |
---|
8 | import sys |
---|
9 | import tempfile |
---|
10 | import threading |
---|
11 | import time |
---|
12 | import traceback |
---|
13 | |
---|
14 | # initialize logger |
---|
15 | logger = logging.getLogger(__name__) |
---|
16 | |
---|
17 | class GutenbachPrinter(threading.Thread): |
---|
18 | |
---|
19 | # for IPP |
---|
20 | printer_attributes = [ |
---|
21 | "printer-uri-supported", |
---|
22 | "uri-authentication-supported", |
---|
23 | "uri-security-supported", |
---|
24 | "printer-name", |
---|
25 | "printer-state", |
---|
26 | "printer-state-reasons", |
---|
27 | "ipp-versions-supported", |
---|
28 | "operations-supported", |
---|
29 | "charset-configured", |
---|
30 | "charset-supported", |
---|
31 | "natural-language-configured", |
---|
32 | "generated-natural-language-supported", |
---|
33 | "document-format-default", |
---|
34 | "document-format-supported", |
---|
35 | "printer-is-accepting-jobs", |
---|
36 | "queued-job-count", |
---|
37 | "pdl-override-supported", |
---|
38 | "printer-up-time", |
---|
39 | "compression-supported", |
---|
40 | "multiple-operation-time-out", |
---|
41 | "multiple-document-jobs-supported", |
---|
42 | ] |
---|
43 | |
---|
44 | job_attributes = [ |
---|
45 | "job-id", |
---|
46 | "job-name", |
---|
47 | "job-originating-user-name", |
---|
48 | "job-k-octets", |
---|
49 | "job-state", |
---|
50 | "job-printer-uri" |
---|
51 | ] |
---|
52 | |
---|
53 | operations = [ |
---|
54 | "print-job", |
---|
55 | "validate-job", |
---|
56 | "get-jobs", |
---|
57 | "print-uri", |
---|
58 | "create-job", |
---|
59 | "pause-printer", |
---|
60 | "resume-printer", |
---|
61 | "get-printer-attributes", |
---|
62 | "set-printer-attributes", |
---|
63 | "cancel-job", |
---|
64 | "send-document", |
---|
65 | "send-uri", |
---|
66 | "get-job-attributes", |
---|
67 | "set-job-attributes", |
---|
68 | "restart-job", |
---|
69 | "promote-job" |
---|
70 | ] |
---|
71 | |
---|
72 | def __init__(self, name, config, *args, **kwargs): |
---|
73 | |
---|
74 | super(GutenbachPrinter, self).__init__(*args, **kwargs) |
---|
75 | |
---|
76 | self.name = name |
---|
77 | self.config = config |
---|
78 | self.time_created = int(time.time()) |
---|
79 | |
---|
80 | self.finished_jobs = [] |
---|
81 | self.pending_jobs = [] |
---|
82 | self.current_job = None |
---|
83 | self.jobs = {} |
---|
84 | |
---|
85 | self.lock = threading.RLock() |
---|
86 | self._running = False |
---|
87 | self.paused = False |
---|
88 | |
---|
89 | # CUPS ignores jobs with id 0, so we have to start at 1 |
---|
90 | self._next_job_id = 1 |
---|
91 | |
---|
92 | @sync |
---|
93 | def __repr__(self): |
---|
94 | return str(self) |
---|
95 | |
---|
96 | @sync |
---|
97 | def __str__(self): |
---|
98 | return "<Printer '%s'>" % self.name |
---|
99 | |
---|
100 | def run(self): |
---|
101 | self._running = True |
---|
102 | while self._running: |
---|
103 | with self.lock: |
---|
104 | try: |
---|
105 | if self.current_job is None: |
---|
106 | self.start_job() |
---|
107 | elif self.current_job.is_done: |
---|
108 | self.complete_job() |
---|
109 | except: |
---|
110 | self._running = False |
---|
111 | logger.fatal(traceback.format_exc()) |
---|
112 | break |
---|
113 | time.sleep(0.1) |
---|
114 | |
---|
115 | def stop(self): |
---|
116 | with self.lock: |
---|
117 | for job in self.jobs.keys(): |
---|
118 | try: |
---|
119 | self.jobs[job].abort() |
---|
120 | del self.jobs[job] |
---|
121 | except InvalidJobStateException: |
---|
122 | pass |
---|
123 | |
---|
124 | self._running = False |
---|
125 | if self.ident is not None and self.isAlive(): |
---|
126 | self.join() |
---|
127 | |
---|
128 | ###################################################################### |
---|
129 | ### Properties ### |
---|
130 | ###################################################################### |
---|
131 | |
---|
132 | @property |
---|
133 | def name(self): |
---|
134 | return self._name |
---|
135 | @name.setter |
---|
136 | def name(self, val): |
---|
137 | try: |
---|
138 | self._name = str(val) |
---|
139 | except: |
---|
140 | self._name = "gutenbach-printer" |
---|
141 | |
---|
142 | @property |
---|
143 | def config(self): |
---|
144 | return self._config |
---|
145 | @config.setter |
---|
146 | def config(self, val): |
---|
147 | try: |
---|
148 | _config = dict(val).copy() |
---|
149 | except: |
---|
150 | raise ValueError, "not a dictionary" |
---|
151 | if 'ipp-versions' not in _config: |
---|
152 | raise ValueError, "missing ipp-versions" |
---|
153 | self._config = _config |
---|
154 | |
---|
155 | @property |
---|
156 | def uris(self): |
---|
157 | uris = ["ipp://localhost:8000/printers/" + self.name, |
---|
158 | "ipp://localhost/printers/" + self.name] |
---|
159 | return uris |
---|
160 | |
---|
161 | @property |
---|
162 | def uri(self): |
---|
163 | return self.uris[0] |
---|
164 | |
---|
165 | @property |
---|
166 | @sync |
---|
167 | def state(self): |
---|
168 | if self.is_running and not self.paused: |
---|
169 | if len(self.active_jobs) > 0: |
---|
170 | state = States.PROCESSING |
---|
171 | else: |
---|
172 | state = States.IDLE |
---|
173 | else: |
---|
174 | state = States.STOPPED |
---|
175 | |
---|
176 | return state |
---|
177 | |
---|
178 | @property |
---|
179 | @sync |
---|
180 | def active_jobs(self): |
---|
181 | jobs = self.pending_jobs[:] |
---|
182 | if self.current_job is not None: |
---|
183 | jobs.insert(0, self.current_job.id) |
---|
184 | return jobs |
---|
185 | |
---|
186 | @property |
---|
187 | def is_running(self): |
---|
188 | running = self.ident is not None and self.isAlive() and self._running |
---|
189 | return running |
---|
190 | |
---|
191 | ###################################################################### |
---|
192 | ### Methods ### |
---|
193 | ###################################################################### |
---|
194 | |
---|
195 | @sync |
---|
196 | def assert_running(self): |
---|
197 | if not self.is_running: |
---|
198 | raise RuntimeError, "%s not started" % str(self) |
---|
199 | |
---|
200 | @sync |
---|
201 | def start_job(self): |
---|
202 | self.assert_running() |
---|
203 | if not self.paused and self.current_job is None: |
---|
204 | try: |
---|
205 | job_id = self.pending_jobs.pop(0) |
---|
206 | self.current_job = self.get_job(job_id) |
---|
207 | self.current_job.play() |
---|
208 | except IndexError: |
---|
209 | self.current_job = None |
---|
210 | |
---|
211 | @sync |
---|
212 | def complete_job(self): |
---|
213 | self.assert_running() |
---|
214 | if not self.paused and self.current_job is not None: |
---|
215 | try: |
---|
216 | if not self.current_job.is_done: |
---|
217 | self.current_job.stop() |
---|
218 | finally: |
---|
219 | self.finished_jobs.append(self.current_job.id) |
---|
220 | self.current_job = None |
---|
221 | |
---|
222 | @sync |
---|
223 | def get_job(self, job_id): |
---|
224 | self.assert_running() |
---|
225 | if job_id not in self.jobs: |
---|
226 | raise InvalidJobException(job_id) |
---|
227 | return self.jobs[job_id] |
---|
228 | |
---|
229 | ###################################################################### |
---|
230 | ### IPP Attributes ### |
---|
231 | ###################################################################### |
---|
232 | |
---|
233 | @property |
---|
234 | def printer_uri_supported(self): |
---|
235 | """RFC 2911: 4.4.1 printer-uri-supported (1setOf uri) |
---|
236 | |
---|
237 | This REQUIRED Printer attribute contains at least one URI for |
---|
238 | the Printer object. It OPTIONALLY contains more than one URI |
---|
239 | for the Printer object. An administrator determines a Printer |
---|
240 | object's URI(s) and configures this attribute to contain those |
---|
241 | URIs by some means outside the scope of this IPP/1.1 document. |
---|
242 | The precise format of this URI is implementation dependent and |
---|
243 | depends on the protocol. See the next two sections for a |
---|
244 | description of the 'uri-security-supported' and |
---|
245 | 'uri-authentication-supported' attributes, both of which are |
---|
246 | the REQUIRED companion attributes to this 'printer-uri- |
---|
247 | supported' attribute. See section 2.4 on Printer object |
---|
248 | identity and section 8.2 on security and URIs for more |
---|
249 | information. |
---|
250 | |
---|
251 | """ |
---|
252 | self.assert_running() |
---|
253 | return ipp.PrinterUriSupported(self.uri) |
---|
254 | @printer_uri_supported.setter |
---|
255 | def printer_uri_supported(self, val): |
---|
256 | self.assert_running() |
---|
257 | raise ipp.errors.AttributesNotSettable("printer-uri-supported") |
---|
258 | |
---|
259 | @property |
---|
260 | def uri_authentication_supported(self): |
---|
261 | """RFC 2911: 4.4.2 uri-authentication-supported (1setOf type2 |
---|
262 | keyword) |
---|
263 | |
---|
264 | This REQUIRED Printer attribute MUST have the same cardinality |
---|
265 | (contain the same number of values) as the |
---|
266 | 'printer-uri-supported' attribute. This attribute identifies |
---|
267 | the Client Authentication mechanism associated with each URI |
---|
268 | listed in the 'printer-uri- supported' attribute. The Printer |
---|
269 | object uses the specified mechanism to identify the |
---|
270 | authenticated user (see section 8.3). The 'i th' value in |
---|
271 | 'uri-authentication-supported' corresponds to the 'i th' value |
---|
272 | in 'printer-uri-supported' and it describes the authentication |
---|
273 | mechanisms used by the Printer when accessed via that URI. |
---|
274 | See [RFC2910] for more details on Client Authentication. |
---|
275 | |
---|
276 | The following standard keyword values are defined: |
---|
277 | |
---|
278 | 'none': There is no authentication mechanism associated with |
---|
279 | the URI. The Printer object assumes that the |
---|
280 | authenticated user is 'anonymous'. |
---|
281 | |
---|
282 | 'requesting-user-name': When a client performs an operation |
---|
283 | whose target is the associated URI, the Printer object |
---|
284 | assumes that the authenticated user is specified by the |
---|
285 | 'requesting-user- name' Operation attribute (see section |
---|
286 | 8.3). If the 'requesting-user-name' attribute is absent in |
---|
287 | a request, the Printer object assumes that the |
---|
288 | authenticated user is 'anonymous'. |
---|
289 | |
---|
290 | 'basic': When a client performs an operation whose target is |
---|
291 | the associated URI, the Printer object challenges the |
---|
292 | client with HTTP basic authentication [RFC2617]. The |
---|
293 | Printer object assumes that the authenticated user is the |
---|
294 | name received via the basic authentication mechanism. |
---|
295 | |
---|
296 | 'digest': When a client performs an operation whose target is |
---|
297 | the associated URI, the Printer object challenges the |
---|
298 | client with HTTP digest authentication [RFC2617]. The |
---|
299 | Printer object assumes that the authenticated user is the |
---|
300 | name received via the digest authentication mechanism. |
---|
301 | |
---|
302 | 'certificate': When a client performs an operation whose |
---|
303 | target is the associated URI, the Printer object expects |
---|
304 | the client to provide a certificate. The Printer object |
---|
305 | assumes that the authenticated user is the textual name |
---|
306 | contained within the certificate. |
---|
307 | |
---|
308 | """ |
---|
309 | self.assert_running() |
---|
310 | return ipp.UriAuthenticationSupported("none") |
---|
311 | @uri_authentication_supported.setter |
---|
312 | def uri_authentication_supported(self, val): |
---|
313 | self.assert_running() |
---|
314 | raise ipp.errors.AttributesNotSettable("uri-authentication-supported") |
---|
315 | |
---|
316 | @property |
---|
317 | def uri_security_supported(self): |
---|
318 | """RFC 2911: 4.4.3 uri-security-supported (1setOf type2 |
---|
319 | keyword) |
---|
320 | |
---|
321 | This REQUIRED Printer attribute MUST have the same cardinality |
---|
322 | (contain the same number of values) as the |
---|
323 | 'printer-uri-supported' attribute. This attribute identifies |
---|
324 | the security mechanisms used for each URI listed in the |
---|
325 | 'printer-uri-supported' attribute. The 'i th' value in |
---|
326 | 'uri-security-supported' corresponds to the 'i th' value in |
---|
327 | 'printer-uri-supported' and it describes the security |
---|
328 | mechanisms used for accessing the Printer object via that |
---|
329 | URI. See [RFC2910] for more details on security mechanisms. |
---|
330 | |
---|
331 | The following standard keyword values are defined: |
---|
332 | |
---|
333 | 'none': There are no secure communication channel |
---|
334 | protocols in use for the given URI. |
---|
335 | |
---|
336 | 'ssl3': SSL3 [SSL] is the secure communications channel |
---|
337 | protocol in use for the given URI. |
---|
338 | |
---|
339 | 'tls': TLS [RFC2246] is the secure communications channel |
---|
340 | protocol in use for the given URI. |
---|
341 | |
---|
342 | This attribute is orthogonal to the definition of a Client |
---|
343 | Authentication mechanism. Specifically, 'none' does not |
---|
344 | exclude Client Authentication. See section 4.4.2. |
---|
345 | |
---|
346 | Consider the following example. For a single Printer object, |
---|
347 | an administrator configures the 'printer-uri-supported', 'uri- |
---|
348 | authentication-supported' and 'uri-security-supported' |
---|
349 | attributes as follows: |
---|
350 | |
---|
351 | 'printer-uri-supported': 'xxx://acme.com/open-use-printer', |
---|
352 | 'xxx://acme.com/restricted-use-printer', |
---|
353 | 'xxx://acme.com/private-printer' |
---|
354 | 'uri-authentication-supported': 'none', 'digest', 'basic' |
---|
355 | 'uri-security-supported': 'none', 'none', 'tls' |
---|
356 | |
---|
357 | Note: 'xxx' is not a valid scheme. See the IPP/1.1 'Transport |
---|
358 | and Encoding' document [RFC2910] for the actual URI schemes to |
---|
359 | be used in object target attributes. |
---|
360 | |
---|
361 | In this case, one Printer object has three URIs. |
---|
362 | |
---|
363 | - For the first URI, 'xxx://acme.com/open-use-printer', the |
---|
364 | value 'none' in 'uri-security-supported' indicates that |
---|
365 | there is no secure channel protocol configured to run under |
---|
366 | HTTP. The value of 'none' in 'uri-authentication-supported' |
---|
367 | indicates that all users are 'anonymous'. There will be no |
---|
368 | challenge and the Printer will ignore |
---|
369 | 'requesting-user-name'. |
---|
370 | |
---|
371 | - For the second URI, 'xxx://acme.com/restricted-use-printer', |
---|
372 | the value 'none' in 'uri-security-supported' indicates that |
---|
373 | there is no secure channel protocol configured to run under |
---|
374 | HTTP. The value of 'digest' in |
---|
375 | 'uri-authentication-supported' indicates that the Printer |
---|
376 | will issue a challenge and that the Printer will use the |
---|
377 | name supplied by the digest mechanism to determine the |
---|
378 | authenticated user (see section 8.3). |
---|
379 | |
---|
380 | - For the third URI, 'xxx://acme.com/private-printer', the |
---|
381 | value 'tls' in 'uri-security-supported' indicates that TLS |
---|
382 | is being used to secure the channel. The client SHOULD be |
---|
383 | prepared to use TLS framing to negotiate an acceptable |
---|
384 | ciphersuite to use while communicating with the Printer |
---|
385 | object. In this case, the name implies the use of a secure |
---|
386 | communications channel, but the fact is made explicit by the |
---|
387 | presence of the 'tls' value in 'uri-security-supported'. The |
---|
388 | client does not need to resort to understanding which |
---|
389 | security it must use by following naming conventions or by |
---|
390 | parsing the URI to determine which security mechanisms are |
---|
391 | implied. The value of 'basic' in 'uri- |
---|
392 | authentication-supported' indicates that the Printer will |
---|
393 | issue a challenge and that the Printer will use the name |
---|
394 | supplied by the digest mechanism to determine the |
---|
395 | authenticated user (see section 8.3). Because this challenge |
---|
396 | occurs in a tls session, the channel is secure. |
---|
397 | |
---|
398 | It is expected that many IPP Printer objects will be |
---|
399 | configured to support only one channel (either configured to |
---|
400 | use TLS access or not) and only one authentication |
---|
401 | mechanism. Such Printer objects only have one URI listed in |
---|
402 | the 'printer-uri-supported' attribute. No matter the |
---|
403 | configuration of the Printer object (whether it has only one |
---|
404 | URI or more than one URI), a client MUST supply only one URI |
---|
405 | in the target 'printer-uri' operation attribute. |
---|
406 | |
---|
407 | """ |
---|
408 | |
---|
409 | self.assert_running() |
---|
410 | return ipp.UriSecuritySupported("none") |
---|
411 | @uri_security_supported.setter |
---|
412 | def uri_security_supported(self, val): |
---|
413 | self.assert_running() |
---|
414 | raise ipp.errors.AttributesNotSettable("uri-security-supported") |
---|
415 | |
---|
416 | @property |
---|
417 | def printer_name(self): |
---|
418 | """RFC 2911: 4.4.4 printer-name (name(127)) |
---|
419 | |
---|
420 | This REQUIRED Printer attribute contains the name of the |
---|
421 | Printer object. It is a name that is more end-user friendly |
---|
422 | than a URI. An administrator determines a printer's name and |
---|
423 | sets this attribute to that name. This name may be the last |
---|
424 | part of the printer's URI or it may be unrelated. In |
---|
425 | non-US-English locales, a name may contain characters that are |
---|
426 | not allowed in a URI. |
---|
427 | |
---|
428 | """ |
---|
429 | self.assert_running() |
---|
430 | return ipp.PrinterName(self.name) |
---|
431 | @printer_name.setter |
---|
432 | def printer_name(self, val): |
---|
433 | self.assert_running() |
---|
434 | raise ipp.errors.AttributesNotSettable("printer-name") |
---|
435 | |
---|
436 | @property |
---|
437 | def printer_location(self): |
---|
438 | """RFC 2911: 4.4.5 printer-location (text(127)) |
---|
439 | |
---|
440 | This Printer attribute identifies the location of the |
---|
441 | device. This could include things like: 'in Room 123A, second |
---|
442 | floor of building XYZ'. |
---|
443 | |
---|
444 | """ |
---|
445 | raise AttributeError # XXX |
---|
446 | |
---|
447 | @property |
---|
448 | def printer_info(self): |
---|
449 | """RFC 2911: 4.4.6 printer-info (text(127)) |
---|
450 | |
---|
451 | This Printer attribute identifies the descriptive information |
---|
452 | about this Printer object. This could include things like: |
---|
453 | 'This printer can be used for printing color transparencies |
---|
454 | for HR presentations', or 'Out of courtesy for others, please |
---|
455 | print only small (1-5 page) jobs at this printer', or even |
---|
456 | 'This printer is going away on July 1, 1997, please find a new |
---|
457 | printer'. |
---|
458 | |
---|
459 | """ |
---|
460 | raise AttributeError # XXX |
---|
461 | |
---|
462 | @property |
---|
463 | def printer_more_info(self): |
---|
464 | """RFC 2911: 4.4.7 printer-more-info (uri) |
---|
465 | |
---|
466 | This Printer attribute contains a URI used to obtain more |
---|
467 | information about this specific Printer object. For example, |
---|
468 | this could be an HTTP type URI referencing an HTML page |
---|
469 | accessible to a Web Browser. The information obtained from |
---|
470 | this URI is intended for end user consumption. Features |
---|
471 | outside the scope of IPP can be accessed from this URI. The |
---|
472 | information is intended to be specific to this printer |
---|
473 | instance and site specific services (e.g. job pricing, |
---|
474 | services offered, end user assistance). The device |
---|
475 | manufacturer may initially populate this attribute. |
---|
476 | |
---|
477 | """ |
---|
478 | raise AttributeError # XXX |
---|
479 | |
---|
480 | @property |
---|
481 | def printer_state(self): |
---|
482 | """RFC 2911: 4.4.11 printer-state (type1 enum) |
---|
483 | |
---|
484 | This REQUIRED Printer attribute identifies the current state |
---|
485 | of the device. The 'printer-state reasons' attribute augments |
---|
486 | the 'printer-state' attribute to give more detailed |
---|
487 | information about the Printer in the given printer state. |
---|
488 | |
---|
489 | A Printer object need only update this attribute before |
---|
490 | responding to an operation which requests the attribute; the |
---|
491 | Printer object NEED NOT update this attribute continually, |
---|
492 | since asynchronous event notification is not part of |
---|
493 | IPP/1.1. A Printer NEED NOT implement all values if they are |
---|
494 | not applicable to a given implementation. The following |
---|
495 | standard enum values are defined: |
---|
496 | |
---|
497 | Value Symbolic Name and Description |
---|
498 | --------------------------------------------------------------- |
---|
499 | '3' 'idle': Indicates that new jobs can start processing |
---|
500 | without waiting. |
---|
501 | '4' 'processing': Indicates that jobs are processing; new |
---|
502 | jobs will wait before processing. |
---|
503 | '5' 'stopped': Indicates that no jobs can be processed and |
---|
504 | intervention is required. |
---|
505 | |
---|
506 | Values of 'printer-state-reasons', such as 'spool-area-full' |
---|
507 | and 'stopped-partly', MAY be used to provide further |
---|
508 | information. |
---|
509 | |
---|
510 | """ |
---|
511 | self.assert_running() |
---|
512 | return ipp.PrinterState(self.state) |
---|
513 | @printer_state.setter |
---|
514 | def printer_state(self, val): |
---|
515 | self.assert_running() |
---|
516 | raise ipp.errors.AttributesNotSettable("printer-state") |
---|
517 | |
---|
518 | @property |
---|
519 | def printer_state_reasons(self): |
---|
520 | """RFC 2911: 4.4.12 printer-state-reasons (1setOf type2 |
---|
521 | keyword) |
---|
522 | |
---|
523 | This REQUIRED Printer attribute supplies additional detail |
---|
524 | about the device's state. Some of the these value definitions |
---|
525 | indicate conformance requirements; the rest are OPTIONAL. |
---|
526 | Each keyword value MAY have a suffix to indicate its level of |
---|
527 | severity. The three levels are: report (least severe), |
---|
528 | warning, and error (most severe). |
---|
529 | |
---|
530 | - '-report': This suffix indicates that the reason is a |
---|
531 | 'report'. An implementation may choose to omit some or all |
---|
532 | reports. Some reports specify finer granularity about the |
---|
533 | printer state; others serve as a precursor to a warning. A |
---|
534 | report MUST contain nothing that could affect the printed |
---|
535 | output. |
---|
536 | |
---|
537 | - '-warning': This suffix indicates that the reason is a |
---|
538 | 'warning'. An implementation may choose to omit some or all |
---|
539 | warnings. Warnings serve as a precursor to an error. A |
---|
540 | warning MUST contain nothing that prevents a job from |
---|
541 | completing, though in some cases the output may be of lower |
---|
542 | quality. |
---|
543 | |
---|
544 | - '-error': This suffix indicates that the reason is an |
---|
545 | 'error'. An implementation MUST include all errors. If this |
---|
546 | attribute contains one or more errors, printer MUST be in |
---|
547 | the stopped state. |
---|
548 | |
---|
549 | If the implementation does not add any one of the three |
---|
550 | suffixes, all parties MUST assume that the reason is an |
---|
551 | 'error'. |
---|
552 | |
---|
553 | If a Printer object controls more than one output device, each |
---|
554 | value of this attribute MAY apply to one or more of the output |
---|
555 | devices. An error on one output device that does not stop the |
---|
556 | Printer object as a whole MAY appear as a warning in the |
---|
557 | Printer's 'printer-state-reasons attribute'. If the |
---|
558 | 'printer-state' for such a Printer has a value of 'stopped', |
---|
559 | then there MUST be an error reason among the values in the |
---|
560 | 'printer-state-reasons' attribute. |
---|
561 | |
---|
562 | """ |
---|
563 | self.assert_running() |
---|
564 | return ipp.PrinterStateReasons("none") |
---|
565 | @printer_state_reasons.setter |
---|
566 | def printer_state_reasons(self, val): |
---|
567 | self.assert_running() |
---|
568 | raise ipp.errors.AttributesNotSettable("printer-state-reasons") |
---|
569 | |
---|
570 | @property |
---|
571 | def ipp_versions_supported(self): |
---|
572 | """RFC 2911: 4.4.14 ipp-versions-supported (1setOf type2 |
---|
573 | keyword) |
---|
574 | |
---|
575 | This REQUIRED attribute identifies the IPP protocol version(s) |
---|
576 | that this Printer supports, including major and minor |
---|
577 | versions, i.e., the version numbers for which this Printer |
---|
578 | implementation meets the conformance requirements. For version |
---|
579 | number validation, the Printer matches the (two-octet binary) |
---|
580 | 'version-number' parameter supplied by the client in each |
---|
581 | request (see sections 3.1.1 and 3.1.8) with the (US-ASCII) |
---|
582 | keyword values of this attribute. |
---|
583 | |
---|
584 | The following standard keyword values are defined: |
---|
585 | '1.0': Meets the conformance requirement of IPP version 1.0 |
---|
586 | as specified in RFC 2566 [RFC2566] and RFC 2565 |
---|
587 | [RFC2565] including any extensions registered |
---|
588 | according to Section 6 and any extension defined in |
---|
589 | this version or any future version of the IPP 'Model |
---|
590 | and Semantics' document or the IPP 'Encoding and |
---|
591 | Transport' document following the rules, if any, when |
---|
592 | the 'version-number' parameter is '1.0'. |
---|
593 | '1.1': Meets the conformance requirement of IPP version 1.1 |
---|
594 | as specified in this document and [RFC2910] including |
---|
595 | any extensions registered according to Section 6 and |
---|
596 | any extension defined in any future versions of the |
---|
597 | IPP 'Model and Semantics' document or the IPP |
---|
598 | Encoding and Transport document following the rules, |
---|
599 | if any, when the 'version-number' parameter is '1.1'. |
---|
600 | |
---|
601 | """ |
---|
602 | self.assert_running() |
---|
603 | return ipp.IppVersionsSupported(*self.config['ipp-versions']) |
---|
604 | @ipp_versions_supported.setter |
---|
605 | def ipp_versions_supported(self, val): |
---|
606 | self.assert_running() |
---|
607 | raise ipp.errors.AttributesNotSettable("ipp-versions-supported") |
---|
608 | |
---|
609 | # XXX: We should query ourself for the supported operations |
---|
610 | @property |
---|
611 | def operations_supported(self): |
---|
612 | """RFC 2911: 4.4.15 operations-supported (1setOf type2 enum) |
---|
613 | |
---|
614 | This REQUIRED Printer attribute specifies the set of supported |
---|
615 | operations for this Printer object and contained Job objects. |
---|
616 | This attribute is encoded as any other enum attribute syntax |
---|
617 | according to [RFC2910] as 32-bits. However, all 32-bit enum |
---|
618 | values for this attribute MUST NOT exceed 0x00008FFF, since |
---|
619 | these same values are also passed in two octets in the |
---|
620 | 'operation-id' parameter (see section 3.1.1) in each Protocol |
---|
621 | request with the two high order octets omitted in order to |
---|
622 | indicate the operation being performed [RFC2910]. |
---|
623 | |
---|
624 | """ |
---|
625 | self.assert_running() |
---|
626 | return ipp.OperationsSupported(ipp.OperationCodes.GET_JOBS) |
---|
627 | @operations_supported.setter |
---|
628 | def operations_supported(self, val): |
---|
629 | self.assert_running() |
---|
630 | raise ipp.errors.AttributesNotSettable("operations-supported") |
---|
631 | |
---|
632 | @property |
---|
633 | def multiple_document_jobs_supported(self): |
---|
634 | """RFC 2911: 4.4.16 multiple-document-jobs-supported (boolean) |
---|
635 | |
---|
636 | This Printer attribute indicates whether or not the Printer |
---|
637 | supports more than one document per job, i.e., more than one |
---|
638 | Send-Document or Send-Data operation with document data. If |
---|
639 | the Printer supports the Create-Job and Send-Document |
---|
640 | operations (see section 3.2.4 and 3.3.1), it MUST support this |
---|
641 | attribute. |
---|
642 | |
---|
643 | """ |
---|
644 | self.assert_running() |
---|
645 | return ipp.MultipleDocumentJobsSupported(False) |
---|
646 | @multiple_document_jobs_supported.setter |
---|
647 | def multiple_document_jobs_supported(self, val): |
---|
648 | self.assert_running() |
---|
649 | raise ipp.errors.AttributesNotSettable("multiple-document-jobs-supported") |
---|
650 | |
---|
651 | @property |
---|
652 | def charset_configured(self): |
---|
653 | """RFC 2911: 4.4.17 charset-configured (charset) |
---|
654 | |
---|
655 | This REQUIRED Printer attribute identifies the charset that |
---|
656 | the Printer object has been configured to represent 'text' and |
---|
657 | 'name' Printer attributes that are set by the operator, system |
---|
658 | administrator, or manufacturer, i.e., for 'printer-name' |
---|
659 | (name), 'printer-location' (text), 'printer-info' (text), and |
---|
660 | 'printer-make- and-model' (text). Therefore, the value of the |
---|
661 | Printer object's 'charset-configured' attribute MUST also be |
---|
662 | among the values of the Printer object's 'charset-supported' |
---|
663 | attribute. |
---|
664 | |
---|
665 | """ |
---|
666 | self.assert_running() |
---|
667 | return ipp.CharsetConfigured("utf-8") # XXX |
---|
668 | @charset_configured.setter |
---|
669 | def charset_configured(self, val): |
---|
670 | self.assert_running() |
---|
671 | raise ipp.errors.AttributesNotSettable("charset-configured") |
---|
672 | |
---|
673 | @property |
---|
674 | def charset_supported(self): |
---|
675 | """RFC 2911: 4.4.18 charset-supported (1setOf charset) |
---|
676 | |
---|
677 | This REQUIRED Printer attribute identifies the set of charsets |
---|
678 | that the Printer and contained Job objects support in |
---|
679 | attributes with attribute syntax 'text' and 'name'. At least |
---|
680 | the value 'utf-8' MUST be present, since IPP objects MUST |
---|
681 | support the UTF-8 [RFC2279] charset. If a Printer object |
---|
682 | supports a charset, it means that for all attributes of syntax |
---|
683 | 'text' and 'name' the IPP object MUST (1) accept the charset |
---|
684 | in requests and return the charset in responses as needed. |
---|
685 | |
---|
686 | If more charsets than UTF-8 are supported, the IPP object MUST |
---|
687 | perform charset conversion between the charsets as described |
---|
688 | in Section 3.1.4.2. |
---|
689 | |
---|
690 | """ |
---|
691 | self.assert_running() |
---|
692 | return ipp.CharsetSupported("utf-8") # XXX |
---|
693 | @charset_supported.setter |
---|
694 | def charset_supported(self, val): |
---|
695 | self.assert_running() |
---|
696 | raise ipp.errors.AttributesNotSettable("charset-supported") |
---|
697 | |
---|
698 | @property |
---|
699 | def natural_language_configured(self): |
---|
700 | """RFC 2911: 4.4.19 natural-language-configured (naturalLanguage) |
---|
701 | |
---|
702 | This REQUIRED Printer attribute identifies the natural |
---|
703 | language that the Printer object has been configured to |
---|
704 | represent 'text' and 'name' Printer attributes that are set by |
---|
705 | the operator, system administrator, or manufacturer, i.e., for |
---|
706 | 'printer-name' (name), 'printer-location' (text), |
---|
707 | 'printer-info' (text), and 'printer-make- and-model' |
---|
708 | (text). When returning these Printer attributes, the Printer |
---|
709 | object MAY return them in the configured natural language |
---|
710 | specified by this attribute, instead of the natural language |
---|
711 | requested by the client in the 'attributes-natural-language' |
---|
712 | operation attribute. See Section 3.1.4.1 for the specification |
---|
713 | of the OPTIONAL multiple natural language support. Therefore, |
---|
714 | the value of the Printer object's |
---|
715 | 'natural-language-configured' attribute MUST also be among the |
---|
716 | values of the Printer object's 'generated-natural- |
---|
717 | language-supported' attribute. |
---|
718 | |
---|
719 | """ |
---|
720 | self.assert_running() |
---|
721 | return ipp.NaturalLanguageConfigured("en-us") |
---|
722 | @natural_language_configured.setter |
---|
723 | def natural_language_configured(self, val): |
---|
724 | self.assert_running() |
---|
725 | raise ipp.errors.AttributesNotSettable("natural-language-configured") |
---|
726 | |
---|
727 | @property |
---|
728 | def generated_natural_language_supported(self): |
---|
729 | """RFC 2911: 4.4.20 generated-natural-language-supported |
---|
730 | (1setOf naturalLanguage) |
---|
731 | |
---|
732 | This REQUIRED Printer attribute identifies the natural |
---|
733 | language(s) that the Printer object and contained Job objects |
---|
734 | support in attributes with attribute syntax 'text' and |
---|
735 | 'name'. The natural language(s) supported depends on |
---|
736 | implementation and/or configuration. Unlike charsets, IPP |
---|
737 | objects MUST accept requests with any natural language or any |
---|
738 | Natural Language Override whether the natural language is |
---|
739 | supported or not. |
---|
740 | |
---|
741 | If a Printer object supports a natural language, it means that |
---|
742 | for any of the attributes for which the Printer or Job object |
---|
743 | generates messages, i.e., for the 'job-state-message' and |
---|
744 | 'printer-state- message' attributes and Operation Messages |
---|
745 | (see Section 3.1.5) in operation responses, the Printer and |
---|
746 | Job objects MUST be able to generate messages in any of the |
---|
747 | Printer's supported natural languages. See section 3.1.4 for |
---|
748 | the definition of 'text' and 'name' attributes in operation |
---|
749 | requests and responses. |
---|
750 | |
---|
751 | Note: A Printer object that supports multiple natural |
---|
752 | languages, often has separate catalogs of messages, one for |
---|
753 | each natural language supported. |
---|
754 | |
---|
755 | """ |
---|
756 | self.assert_running() |
---|
757 | return ipp.GeneratedNaturalLanguageSupported("en-us") |
---|
758 | @generated_natural_language_supported.setter |
---|
759 | def generated_natural_language_supported(self, val): |
---|
760 | self.assert_running() |
---|
761 | raise ipp.errors.AttributesNotSettable("generated-natural-language-supported") |
---|
762 | |
---|
763 | @property |
---|
764 | def document_format_default(self): |
---|
765 | """RFC 2911: 4.4.21 document-format-default (mimeMediaType) |
---|
766 | |
---|
767 | This REQUIRED Printer attribute identifies the document format |
---|
768 | that the Printer object has been configured to assume if the |
---|
769 | client does not supply a 'document-format' operation attribute |
---|
770 | in any of the operation requests that supply document |
---|
771 | data. The standard values for this attribute are Internet |
---|
772 | Media types (sometimes called MIME types). For further details |
---|
773 | see the description of the 'mimeMediaType' attribute syntax in |
---|
774 | Section 4.1.9. |
---|
775 | |
---|
776 | """ |
---|
777 | self.assert_running() |
---|
778 | return ipp.DocumentFormatDefault("application/octet-stream") |
---|
779 | @document_format_default.setter |
---|
780 | def document_format_default(self, val): |
---|
781 | self.assert_running() |
---|
782 | raise ipp.errors.AttributesNotSettable("document-format-default") |
---|
783 | |
---|
784 | @property |
---|
785 | def document_format_supported(self): |
---|
786 | """RFC 2911: 4.4.22 document-format-supported (1setOf mimeMediaType) |
---|
787 | |
---|
788 | This REQUIRED Printer attribute identifies the set of document |
---|
789 | formats that the Printer object and contained Job objects can |
---|
790 | support. For further details see the description of the |
---|
791 | 'mimeMediaType' attribute syntax in Section 4.1.9. |
---|
792 | |
---|
793 | """ |
---|
794 | self.assert_running() |
---|
795 | return ipp.DocumentFormatSupported("application/octet-stream", "audio/mp3") |
---|
796 | @document_format_supported.setter |
---|
797 | def document_format_supported(self, val): |
---|
798 | self.assert_running() |
---|
799 | raise ipp.errors.AttributesNotSettable("document-format-supported") |
---|
800 | |
---|
801 | @property |
---|
802 | def printer_is_accepting_jobs(self): |
---|
803 | """RFC 2911: 4.4.23 printer-is-accepting-jobs (boolean) |
---|
804 | |
---|
805 | This REQUIRED Printer attribute indicates whether the printer |
---|
806 | is currently able to accept jobs, i.e., is accepting |
---|
807 | Print-Job, Print- URI, and Create-Job requests. If the value |
---|
808 | is 'true', the printer is accepting jobs. If the value is |
---|
809 | 'false', the Printer object is currently rejecting any jobs |
---|
810 | submitted to it. In this case, the Printer object returns the |
---|
811 | 'server-error-not-accepting-jobs' status code. |
---|
812 | |
---|
813 | This value is independent of the 'printer-state' and |
---|
814 | 'printer-state- reasons' attributes because its value does not |
---|
815 | affect the current job; rather it affects future jobs. This |
---|
816 | attribute, when 'false', causes the Printer to reject jobs |
---|
817 | even when the 'printer-state' is 'idle' or, when 'true', |
---|
818 | causes the Printer object to accepts jobs even when the |
---|
819 | 'printer-state' is 'stopped'. |
---|
820 | |
---|
821 | """ |
---|
822 | self.assert_running() |
---|
823 | return ipp.PrinterIsAcceptingJobs(True) # XXX |
---|
824 | @printer_is_accepting_jobs.setter |
---|
825 | def printer_is_accepting_jobs(self, val): |
---|
826 | self.assert_running() |
---|
827 | raise ipp.errors.AttributesNotSettable("printer-is-accepting-jobs") |
---|
828 | |
---|
829 | @property |
---|
830 | def queued_job_count(self): |
---|
831 | """RFC 2911: 4.4.24 queued-job-count (integer(0:MAX)) |
---|
832 | |
---|
833 | This REQUIRED Printer attribute contains a count of the number |
---|
834 | of jobs that are either 'pending', 'processing', |
---|
835 | 'pending-held', or 'processing-stopped' and is set by the |
---|
836 | Printer object. |
---|
837 | |
---|
838 | """ |
---|
839 | self.assert_running() |
---|
840 | return ipp.QueuedJobCount(len(self.active_jobs)) |
---|
841 | @queued_job_count.setter |
---|
842 | def queued_job_count(self, val): |
---|
843 | self.assert_running() |
---|
844 | raise ipp.errors.AttributesNotSettable("queued-job-count") |
---|
845 | |
---|
846 | @property |
---|
847 | def pdl_override_supported(self): |
---|
848 | """RFC 2911: 4.4.28 pdl-override-supported (type2 keyword) |
---|
849 | |
---|
850 | This REQUIRED Printer attribute expresses the ability for a |
---|
851 | particular Printer implementation to either attempt to |
---|
852 | override document data instructions with IPP attributes or |
---|
853 | not. |
---|
854 | |
---|
855 | This attribute takes on the following keyword values: |
---|
856 | |
---|
857 | - 'attempted': This value indicates that the Printer object |
---|
858 | attempts to make the IPP attribute values take precedence |
---|
859 | over embedded instructions in the document data, however |
---|
860 | there is no guarantee. |
---|
861 | |
---|
862 | - 'not-attempted': This value indicates that the Printer |
---|
863 | object makes no attempt to make the IPP attribute values |
---|
864 | take precedence over embedded instructions in the document |
---|
865 | data. |
---|
866 | |
---|
867 | Section 15 contains a full description of how this attribute |
---|
868 | interacts with and affects other IPP attributes, especially |
---|
869 | the 'ipp-attribute-fidelity' attribute. |
---|
870 | |
---|
871 | """ |
---|
872 | self.assert_running() |
---|
873 | return ipp.PdlOverrideSupported("not-attempted") |
---|
874 | @pdl_override_supported.setter |
---|
875 | def pdl_override_supported(self, val): |
---|
876 | self.assert_running() |
---|
877 | raise ipp.errors.AttributesNotSettable("pdl-override-supported") |
---|
878 | |
---|
879 | @property |
---|
880 | def printer_up_time(self): |
---|
881 | """RFC 2911: 4.4.29 printer-up-time (integer(1:MAX)) |
---|
882 | |
---|
883 | This REQUIRED Printer attribute indicates the amount of time |
---|
884 | (in seconds) that this Printer instance has been up and |
---|
885 | running. The value is a monotonically increasing value |
---|
886 | starting from 1 when the Printer object is started-up |
---|
887 | (initialized, booted, etc.). This value is used to populate |
---|
888 | the Event Time Job Description Job attributes |
---|
889 | 'time-at-creation', 'time-at-processing', and |
---|
890 | 'time-at-completed' (see section 4.3.14). |
---|
891 | |
---|
892 | If the Printer object goes down at some value 'n', and comes |
---|
893 | back up, the implementation MAY: |
---|
894 | |
---|
895 | 1. Know how long it has been down, and resume at some |
---|
896 | value greater than 'n', or |
---|
897 | 2. Restart from 1. |
---|
898 | |
---|
899 | In other words, if the device or devices that the Printer |
---|
900 | object is representing are restarted or power cycled, the |
---|
901 | Printer object MAY continue counting this value or MAY reset |
---|
902 | this value to 1 depending on implementation. However, if the |
---|
903 | Printer object software ceases running, and restarts without |
---|
904 | knowing the last value for 'printer- up-time', the |
---|
905 | implementation MUST reset this value to 1. If this value is |
---|
906 | reset and the Printer has persistent jobs, the Printer MUST |
---|
907 | reset the 'time-at-xxx(integer) Event Time Job Description |
---|
908 | attributes according to Section 4.3.14. An implementation MAY |
---|
909 | use both implementation alternatives, depending on warm versus |
---|
910 | cold start, respectively. |
---|
911 | |
---|
912 | """ |
---|
913 | self.assert_running() |
---|
914 | return ipp.PrinterUpTime(int(time.time()) - self.time_created) |
---|
915 | @printer_up_time.setter |
---|
916 | def printer_up_time(self, val): |
---|
917 | self.assert_running() |
---|
918 | raise ipp.errors.AttributesNotSettable("printer-up-time") |
---|
919 | |
---|
920 | @property |
---|
921 | def printer_current_time(self): |
---|
922 | """RFC 2911: 4.4.30 printer-current-time (dateTime) |
---|
923 | |
---|
924 | This Printer attribute indicates the current date and |
---|
925 | time. This value is used to populate the Event Time Job |
---|
926 | Description attributes: 'date-time-at-creation', |
---|
927 | 'date-time-at-processing', and 'date-time- at-completed' (see |
---|
928 | Section 4.3.14). |
---|
929 | |
---|
930 | The date and time is obtained on a 'best efforts basis' and |
---|
931 | does not have to be that precise in order to work in |
---|
932 | practice. A Printer implementation sets the value of this |
---|
933 | attribute by obtaining the date and time via some |
---|
934 | implementation-dependent means, such as getting the value from |
---|
935 | a network time server, initialization at time of manufacture, |
---|
936 | or setting by an administrator. See [IPP-IIG] for examples. If |
---|
937 | an implementation supports this attribute and the |
---|
938 | implementation knows that it has not yet been set, then the |
---|
939 | implementation MUST return the value of this attribute using |
---|
940 | the out-of-band 'no-value' meaning not configured. See the |
---|
941 | beginning of section 4.1. |
---|
942 | |
---|
943 | The time zone of this attribute NEED NOT be the time zone used |
---|
944 | by people located near the Printer object or device. The |
---|
945 | client MUST NOT expect that the time zone of any received |
---|
946 | 'dateTime' value to be in the time zone of the client or in |
---|
947 | the time zone of the people located near the printer. |
---|
948 | |
---|
949 | The client SHOULD display any dateTime attributes to the user |
---|
950 | in client local time by converting the 'dateTime' value |
---|
951 | returned by the server to the time zone of the client, rather |
---|
952 | than using the time zone returned by the Printer in attributes |
---|
953 | that use the 'dateTime' attribute syntax. |
---|
954 | |
---|
955 | """ |
---|
956 | raise AttributeError # XXX |
---|
957 | |
---|
958 | @property |
---|
959 | def multiple_operation_time_out(self): |
---|
960 | """RFC 2911: 4.4.31 multiple-operation-time-out |
---|
961 | (integer(1:MAX)) |
---|
962 | |
---|
963 | This Printer attributes identifies the minimum time (in |
---|
964 | seconds) that the Printer object waits for additional |
---|
965 | Send-Document or Send-URI operations to follow a still-open |
---|
966 | Job object before taking any recovery actions, such as the |
---|
967 | ones indicated in section 3.3.1. If the Printer object |
---|
968 | supports the Create-Job and Send-Document operations (see |
---|
969 | section 3.2.4 and 3.3.1), it MUST support this attribute. |
---|
970 | |
---|
971 | It is RECOMMENDED that vendors supply a value for this |
---|
972 | attribute that is between 60 and 240 seconds. An |
---|
973 | implementation MAY allow a system administrator to set this |
---|
974 | attribute (by means outside this IPP/1.1 document). If so, the |
---|
975 | system administrator MAY be able to set values outside this |
---|
976 | range. |
---|
977 | |
---|
978 | """ |
---|
979 | self.assert_running() |
---|
980 | return ipp.MultipleOperationTimeOut(240) |
---|
981 | @multiple_operation_time_out.setter |
---|
982 | def multiple_operation_time_out(self, val): |
---|
983 | self.assert_running() |
---|
984 | raise ipp.errors.AttributesNotSettable("multiple-operation-time-out") |
---|
985 | |
---|
986 | @property |
---|
987 | def compression_supported(self): |
---|
988 | """RFC 2911: 4.4.32 compression-supported (1setOf type3 |
---|
989 | keyword) |
---|
990 | |
---|
991 | This REQUIRED Printer attribute identifies the set of |
---|
992 | supported compression algorithms for document |
---|
993 | data. Compression only applies to the document data; |
---|
994 | compression does not apply to the encoding of the IPP |
---|
995 | operation itself. The supported values are used to validate |
---|
996 | the client supplied 'compression' operation attributes in |
---|
997 | Print-Job, Send-Document, and Send-URI requests. |
---|
998 | |
---|
999 | Standard keyword values are : |
---|
1000 | 'none': no compression is used. |
---|
1001 | 'deflate': ZIP public domain inflate/deflate) compression |
---|
1002 | technology in RFC 1951 [RFC1951] |
---|
1003 | 'gzip' GNU zip compression technology described in RFC |
---|
1004 | 1952 [RFC1952]. |
---|
1005 | 'compress': UNIX compression technology in RFC 1977 [RFC1977] |
---|
1006 | |
---|
1007 | """ |
---|
1008 | self.assert_running() |
---|
1009 | return ipp.CompressionSupported("none") |
---|
1010 | @compression_supported.setter |
---|
1011 | def compression_supported(self, val): |
---|
1012 | self.assert_running() |
---|
1013 | raise ipp.errors.AttributesNotSettable("compression-supported") |
---|
1014 | |
---|
1015 | ###################################################################### |
---|
1016 | ### Job IPP Attributes ### |
---|
1017 | ###################################################################### |
---|
1018 | |
---|
1019 | def job_uri(self, job_id): |
---|
1020 | """RFC 2911: 4.3.1 job-uri (uri) |
---|
1021 | |
---|
1022 | This REQUIRED attribute contains the URI for the job. The |
---|
1023 | Printer object, on receipt of a new job, generates a URI which |
---|
1024 | identifies the new Job. The Printer object returns the value |
---|
1025 | of the 'job-uri' attribute as part of the response to a create |
---|
1026 | request. The precise format of a Job URI is implementation |
---|
1027 | dependent. If the Printer object supports more than one URI |
---|
1028 | and there is some relationship between the newly formed Job |
---|
1029 | URI and the Printer object's URI, the Printer object uses the |
---|
1030 | Printer URI supplied by the client in the create request. For |
---|
1031 | example, if the create request comes in over a secure channel, |
---|
1032 | the new Job URI MUST use the same secure channel. This can be |
---|
1033 | guaranteed because the Printer object is responsible for |
---|
1034 | generating the Job URI and the Printer object is aware of its |
---|
1035 | security configuration and policy as well as the Printer URI |
---|
1036 | used in the create request. |
---|
1037 | |
---|
1038 | For a description of this attribute and its relationship to |
---|
1039 | 'job-id' and 'job-printer-uri' attribute, see the discussion |
---|
1040 | in section 2.4 on 'Object Identity'. |
---|
1041 | |
---|
1042 | """ |
---|
1043 | raise AttributeError # XXX |
---|
1044 | |
---|
1045 | def job_id(self, job_id): |
---|
1046 | """RFC 2911: 4.3.2 job-id (integer(1:MAX)) |
---|
1047 | |
---|
1048 | This REQUIRED attribute contains the ID of the job. The |
---|
1049 | Printer, on receipt of a new job, generates an ID which |
---|
1050 | identifies the new Job on that Printer. The Printer returns |
---|
1051 | the value of the 'job-id' attribute as part of the response to |
---|
1052 | a create request. The 0 value is not included to allow for |
---|
1053 | compatibility with SNMP index values which also cannot be 0. |
---|
1054 | |
---|
1055 | For a description of this attribute and its relationship to |
---|
1056 | 'job-uri' and 'job-printer-uri' attribute, see the discussion |
---|
1057 | in section 2.4 on 'Object Identity'. |
---|
1058 | |
---|
1059 | """ |
---|
1060 | self.assert_running() |
---|
1061 | job = self.get_job(job_id) |
---|
1062 | return ipp.JobId(job.id) |
---|
1063 | |
---|
1064 | def job_printer_uri(self, job_id): |
---|
1065 | """RFC 2911: 4.3.3 job-printer-uri (uri) |
---|
1066 | |
---|
1067 | This REQUIRED attribute identifies the Printer object that |
---|
1068 | created this Job object. When a Printer object creates a Job |
---|
1069 | object, it populates this attribute with the Printer object |
---|
1070 | URI that was used in the create request. This attribute |
---|
1071 | permits a client to identify the Printer object that created |
---|
1072 | this Job object when only the Job object's URI is available to |
---|
1073 | the client. The client queries the creating Printer object to |
---|
1074 | determine which languages, charsets, operations, are supported |
---|
1075 | for this Job. |
---|
1076 | |
---|
1077 | For a description of this attribute and its relationship to |
---|
1078 | 'job-uri' and 'job-id' attribute, see the discussion in |
---|
1079 | section 2.4 on 'Object Identity'. |
---|
1080 | |
---|
1081 | """ |
---|
1082 | self.assert_running() |
---|
1083 | job = self.get_job(job_id) |
---|
1084 | return ipp.JobPrinterUri(self.uri) |
---|
1085 | |
---|
1086 | def job_name(self, job_id): |
---|
1087 | """RFC 2911: 4.3.5 job-name (name(MAX)) |
---|
1088 | |
---|
1089 | This REQUIRED attribute is the name of the job. It is a name |
---|
1090 | that is more user friendly than the 'job-uri' attribute |
---|
1091 | value. It does not need to be unique between Jobs. The Job's |
---|
1092 | 'job-name' attribute is set to the value supplied by the |
---|
1093 | client in the 'job-name' operation attribute in the create |
---|
1094 | request (see Section 3.2.1.1). |
---|
1095 | |
---|
1096 | If, however, the 'job-name' operation attribute is not |
---|
1097 | supplied by the client in the create request, the Printer |
---|
1098 | object, on creation of the Job, MUST generate a name. The |
---|
1099 | printer SHOULD generate the value of the Job's 'job-name' |
---|
1100 | attribute from the first of the following sources that |
---|
1101 | produces a value: 1) the 'document-name' operation attribute |
---|
1102 | of the first (or only) document, 2) the 'document-URI' |
---|
1103 | attribute of the first (or only) document, or 3) any other |
---|
1104 | piece of Job specific and/or Document Content information. |
---|
1105 | |
---|
1106 | """ |
---|
1107 | self.assert_running() |
---|
1108 | job = self.get_job(job_id) |
---|
1109 | return ipp.JobName(job.name) |
---|
1110 | |
---|
1111 | def job_originating_user_name(self, job_id): |
---|
1112 | """RFC 2911: 4.3.6 job-originating-user-name (name(MAX)) |
---|
1113 | |
---|
1114 | This REQUIRED attribute contains the name of the end user that |
---|
1115 | submitted the print job. The Printer object sets this |
---|
1116 | attribute to the most authenticated printable name that it can |
---|
1117 | obtain from the authentication service over which the IPP |
---|
1118 | operation was received. Only if such is not available, does |
---|
1119 | the Printer object use the value supplied by the client in the |
---|
1120 | 'requesting-user-name' operation attribute of the create |
---|
1121 | operation (see Sections 4.4.2, 4.4.3, and 8). |
---|
1122 | |
---|
1123 | Note: The Printer object needs to keep an internal originating |
---|
1124 | user id of some form, typically as a credential of a |
---|
1125 | principal, with the Job object. Since such an internal |
---|
1126 | attribute is implementation- dependent and not of interest to |
---|
1127 | clients, it is not specified as a Job Description |
---|
1128 | attribute. This originating user id is used for authorization |
---|
1129 | checks (if any) on all subsequent operations. |
---|
1130 | |
---|
1131 | """ |
---|
1132 | self.assert_running() |
---|
1133 | job = self.get_job(job_id) |
---|
1134 | return ipp.JobOriginatingUserName(job.creator) |
---|
1135 | |
---|
1136 | def job_state(self, job_id): |
---|
1137 | """RFC 2911: 4.3.7 job-state (type1 enum) |
---|
1138 | |
---|
1139 | This REQUIRED attribute identifies the current state of the |
---|
1140 | job. Even though the IPP protocol defines seven values for |
---|
1141 | job states (plus the out-of-band 'unknown' value - see Section |
---|
1142 | 4.1), implementations only need to support those states which |
---|
1143 | are appropriate for the particular implementation. In other |
---|
1144 | words, a Printer supports only those job states implemented by |
---|
1145 | the output device and available to the Printer object |
---|
1146 | implementation. Standard enum values are: |
---|
1147 | |
---|
1148 | '3' 'pending': The job is a candidate to start processing, |
---|
1149 | but is not yet processing. |
---|
1150 | |
---|
1151 | '4' 'pending-held': The job is not a candidate for |
---|
1152 | processing for any number of reasons but will return |
---|
1153 | to the 'pending' state as soon as the reasons are no |
---|
1154 | longer present. The job's 'job-state-reason' attribute |
---|
1155 | MUST indicate why the job is no longer a candidate for |
---|
1156 | processing. |
---|
1157 | |
---|
1158 | '5' 'processing': One or more of: |
---|
1159 | |
---|
1160 | 1. the job is using, or is attempting to use, one or |
---|
1161 | more purely software processes that are analyzing, |
---|
1162 | creating, or interpreting a PDL, etc., |
---|
1163 | 2. the job is using, or is attempting to use, one or |
---|
1164 | more hardware devices that are interpreting a PDL, |
---|
1165 | making marks on a medium, and/or performing |
---|
1166 | finishing, such as stapling, etc., |
---|
1167 | 3. the Printer object has made the job ready for |
---|
1168 | printing, but the output device is not yet printing |
---|
1169 | it, either because the job hasn't reached the |
---|
1170 | output device or because the job is queued in the |
---|
1171 | output device or some other spooler, awaiting the |
---|
1172 | output device to print it. |
---|
1173 | |
---|
1174 | When the job is in the 'processing' state, the entire |
---|
1175 | job state includes the detailed status represented in |
---|
1176 | the Printer object's 'printer-state', 'printer-state- |
---|
1177 | reasons', and 'printer-state-message' attributes. |
---|
1178 | |
---|
1179 | Implementations MAY, though they NEED NOT, include |
---|
1180 | additional values in the job's 'job-state-reasons' |
---|
1181 | attribute to indicate the progress of the job, such as |
---|
1182 | adding the 'job-printing' value to indicate when the |
---|
1183 | output device is actually making marks on paper and/or |
---|
1184 | the 'processing-to-stop-point' value to indicate that |
---|
1185 | the IPP object is in the process of canceling or |
---|
1186 | aborting the job. Most implementations won't bother |
---|
1187 | with this nuance. |
---|
1188 | |
---|
1189 | '6' 'processing-stopped': The job has stopped while |
---|
1190 | processing for any number of reasons and will return |
---|
1191 | to the 'processing' state as soon as the reasons are |
---|
1192 | no longer present. |
---|
1193 | |
---|
1194 | The job's 'job-state-reason' attribute MAY indicate |
---|
1195 | why the job has stopped processing. For example, if |
---|
1196 | the output device is stopped, the 'printer-stopped' |
---|
1197 | value MAY be included in the job's 'job-state-reasons' |
---|
1198 | attribute. |
---|
1199 | |
---|
1200 | Note: When an output device is stopped, the device |
---|
1201 | usually indicates its condition in human readable form |
---|
1202 | locally at the device. A client can obtain more |
---|
1203 | complete device status remotely by querying the |
---|
1204 | Printer object's 'printer-state', |
---|
1205 | 'printer-state-reasons' and 'printer- state-message' |
---|
1206 | attributes. |
---|
1207 | |
---|
1208 | '7' 'canceled': The job has been canceled by a Cancel-Job |
---|
1209 | operation and the Printer object has completed |
---|
1210 | canceling the job and all job status attributes have |
---|
1211 | reached their final values for the job. While the |
---|
1212 | Printer object is canceling the job, the job remains |
---|
1213 | in its current state, but the job's |
---|
1214 | 'job-state-reasons' attribute SHOULD contain the |
---|
1215 | 'processing-to-stop-point' value and one of the |
---|
1216 | 'canceled-by-user', 'canceled-by-operator', or |
---|
1217 | 'canceled-at-device' value. When the job moves to the |
---|
1218 | 'canceled' state, the 'processing-to-stop-point' |
---|
1219 | value, if present, MUST be removed, but the |
---|
1220 | 'canceled-by-xxx', if present, MUST remain. |
---|
1221 | |
---|
1222 | '8' 'aborted': The job has been aborted by the system, |
---|
1223 | usually while the job was in the 'processing' or |
---|
1224 | 'processing- stopped' state and the Printer has |
---|
1225 | completed aborting the job and all job status |
---|
1226 | attributes have reached their final values for the |
---|
1227 | job. While the Printer object is aborting the job, the |
---|
1228 | job remains in its current state, but the job's |
---|
1229 | 'job-state-reasons' attribute SHOULD contain the |
---|
1230 | 'processing-to-stop-point' and 'aborted-by- system' |
---|
1231 | values. When the job moves to the 'aborted' state, the |
---|
1232 | 'processing-to-stop-point' value, if present, MUST be |
---|
1233 | removed, but the 'aborted-by-system' value, if |
---|
1234 | present, MUST remain. |
---|
1235 | |
---|
1236 | '9' 'completed': The job has completed successfully or |
---|
1237 | with warnings or errors after processing and all of |
---|
1238 | the job media sheets have been successfully stacked in |
---|
1239 | the appropriate output bin(s) and all job status |
---|
1240 | attributes have reached their final values for the |
---|
1241 | job. The job's 'job-state-reasons' attribute SHOULD |
---|
1242 | contain one of: 'completed-successfully', |
---|
1243 | 'completed-with-warnings', or 'completed-with-errors' |
---|
1244 | values. |
---|
1245 | |
---|
1246 | The final value for this attribute MUST be one of: |
---|
1247 | 'completed', 'canceled', or 'aborted' before the Printer |
---|
1248 | removes the job altogether. The length of time that jobs |
---|
1249 | remain in the 'canceled', 'aborted', and 'completed' states |
---|
1250 | depends on implementation. See section 4.3.7.2. |
---|
1251 | |
---|
1252 | The following figure shows the normal job state transitions. |
---|
1253 | |
---|
1254 | +----> canceled |
---|
1255 | / |
---|
1256 | +----> pending --------> processing ---------+------> completed |
---|
1257 | | ^ ^ \ |
---|
1258 | --->+ | | +----> aborted |
---|
1259 | | v v / |
---|
1260 | +----> pending-held processing-stopped ---+ |
---|
1261 | |
---|
1262 | Normally a job progresses from left to right. Other state |
---|
1263 | transitions are unlikely, but are not forbidden. Not shown are |
---|
1264 | the transitions to the 'canceled' state from the 'pending', |
---|
1265 | 'pending- held', and 'processing-stopped' states. |
---|
1266 | |
---|
1267 | Jobs reach one of the three terminal states: 'completed', |
---|
1268 | 'canceled', or 'aborted', after the jobs have completed all |
---|
1269 | activity, including stacking output media, after the jobs have |
---|
1270 | completed all activity, and all job status attributes have |
---|
1271 | reached their final values for the job. |
---|
1272 | |
---|
1273 | """ |
---|
1274 | self.assert_running() |
---|
1275 | job = self.get_job(job_id) |
---|
1276 | return ipp.JobState(job.state) |
---|
1277 | |
---|
1278 | def job_k_octets(self, job_id): |
---|
1279 | """RFC 2911: 4.3.17.1 job-k-octets (integer(0:MAX)) |
---|
1280 | |
---|
1281 | This attribute specifies the total size of the document(s) in |
---|
1282 | K octets, i.e., in units of 1024 octets requested to be |
---|
1283 | processed in the job. The value MUST be rounded up, so that a |
---|
1284 | job between 1 and 1024 octets MUST be indicated as being 1, |
---|
1285 | 1025 to 2048 MUST be 2, etc. |
---|
1286 | |
---|
1287 | This value MUST NOT include the multiplicative factors |
---|
1288 | contributed by the number of copies specified by the 'copies' |
---|
1289 | attribute, independent of whether the device can process |
---|
1290 | multiple copies without making multiple passes over the job or |
---|
1291 | document data and independent of whether the output is |
---|
1292 | collated or not. Thus the value is independent of the |
---|
1293 | implementation and indicates the size of the document(s) |
---|
1294 | measured in K octets independent of the number of copies. |
---|
1295 | |
---|
1296 | This value MUST also not include the multiplicative factor due |
---|
1297 | to a copies instruction embedded in the document data. If the |
---|
1298 | document data actually includes replications of the document |
---|
1299 | data, this value will include such replication. In other |
---|
1300 | words, this value is always the size of the source document |
---|
1301 | data, rather than a measure of the hardcopy output to be |
---|
1302 | produced. |
---|
1303 | |
---|
1304 | """ |
---|
1305 | self.assert_running() |
---|
1306 | job = self.get_job(job_id) |
---|
1307 | return ipp.JobKOctets(int(math.ceil(job.size / 1024.))) |
---|
1308 | |
---|
1309 | def job_k_octets_completed(self, job_id): |
---|
1310 | """RFC 2911: 4.3.18.1 job-k-octets-processed (integer(0:MAX)) |
---|
1311 | |
---|
1312 | This attribute specifies the total number of octets processed |
---|
1313 | in K octets, i.e., in units of 1024 octets so far. The value |
---|
1314 | MUST be rounded up, so that a job between 1 and 1024 octets |
---|
1315 | inclusive MUST be indicated as being 1, 1025 to 2048 inclusive |
---|
1316 | MUST be 2, etc. For implementations where multiple copies are |
---|
1317 | produced by the interpreter with only a single pass over the |
---|
1318 | data, the final value MUST be equal to the value of the |
---|
1319 | 'job-k-octets' attribute. For implementations where multiple |
---|
1320 | copies are produced by the interpreter by processing the data |
---|
1321 | for each copy, the final value MUST be a multiple of the value |
---|
1322 | of the 'job-k-octets' attribute. |
---|
1323 | |
---|
1324 | """ |
---|
1325 | raise AttributeError # XXX |
---|
1326 | |
---|
1327 | def attributes_charset(self, job_id): |
---|
1328 | """RFC 2911: 4.3.19 attributes-charset (charset) |
---|
1329 | |
---|
1330 | This REQUIRED attribute is populated using the value in the |
---|
1331 | client supplied 'attributes-charset' attribute in the create |
---|
1332 | request. It identifies the charset (coded character set and |
---|
1333 | encoding method) used by any Job attributes with attribute |
---|
1334 | syntax 'text' and 'name' that were supplied by the client in |
---|
1335 | the create request. See Section 3.1.4 for a complete |
---|
1336 | description of the 'attributes-charset' operation attribute. |
---|
1337 | |
---|
1338 | This attribute does not indicate the charset in which the |
---|
1339 | 'text' and 'name' values are stored internally in the Job |
---|
1340 | object. The internal charset is implementation-defined. The |
---|
1341 | IPP object MUST convert from whatever the internal charset is |
---|
1342 | to that being requested in an operation as specified in |
---|
1343 | Section 3.1.4. |
---|
1344 | |
---|
1345 | """ |
---|
1346 | raise AttributeError # XXX |
---|
1347 | |
---|
1348 | def attributes_natural_language(self, job_id): |
---|
1349 | """RFC 2911: 4.3.20 attributes-natural-language |
---|
1350 | (naturalLanguage) |
---|
1351 | |
---|
1352 | This REQUIRED attribute is populated using the value in the |
---|
1353 | client supplied 'attributes-natural-language' attribute in the |
---|
1354 | create request. It identifies the natural language used for |
---|
1355 | any Job attributes with attribute syntax 'text' and 'name' |
---|
1356 | that were supplied by the client in the create request. See |
---|
1357 | Section 3.1.4 for a complete description of the |
---|
1358 | 'attributes-natural-language' operation attribute. See |
---|
1359 | Sections 4.1.1.2 and 4.1.2.2 for how a Natural Language |
---|
1360 | Override may be supplied explicitly for each 'text' and 'name' |
---|
1361 | attribute value that differs from the value identified by the |
---|
1362 | 'attributes-natural-language' attribute. |
---|
1363 | |
---|
1364 | """ |
---|
1365 | raise AttributeError # XXX |
---|
1366 | |
---|
1367 | ###################################################################### |
---|
1368 | ### IPP Operations ### |
---|
1369 | ###################################################################### |
---|
1370 | |
---|
1371 | @sync |
---|
1372 | def print_job(self, document, document_name=None, document_format=None, |
---|
1373 | document_natural_language=None, requesting_user_name=None, |
---|
1374 | compression=None, job_name=None, job_k_octets=None): |
---|
1375 | """RFC 2911: 3.2.1 Print-Job Operation |
---|
1376 | |
---|
1377 | This REQUIRED operation allows a client to submit a print job |
---|
1378 | with only one document and supply the document data (rather |
---|
1379 | than just a reference to the data). See Section 15 for the |
---|
1380 | suggested steps for processing create operations and their |
---|
1381 | Operation and Job Template attributes. |
---|
1382 | |
---|
1383 | Parameters |
---|
1384 | ---------- |
---|
1385 | document (file) |
---|
1386 | an open file handler to the document |
---|
1387 | document_name (string) |
---|
1388 | the name of the document |
---|
1389 | document_format (string) |
---|
1390 | the encoding/format of the document |
---|
1391 | document_natural_language (string) |
---|
1392 | if the document is a text file, what language it is in |
---|
1393 | requesting_user_name (string) |
---|
1394 | the user name of the job owner |
---|
1395 | compression (string) |
---|
1396 | the form of compression used on the file |
---|
1397 | job_name (string) |
---|
1398 | the name that the job should be called |
---|
1399 | job_k_octets (int) |
---|
1400 | the size of the job in bytes |
---|
1401 | |
---|
1402 | """ |
---|
1403 | |
---|
1404 | self.assert_running() |
---|
1405 | |
---|
1406 | # create the job |
---|
1407 | job_id = self.create_job( |
---|
1408 | requesting_user_name=requesting_user_name, |
---|
1409 | job_name=job_name, |
---|
1410 | job_k_octets=job_k_octets) |
---|
1411 | |
---|
1412 | # send the document |
---|
1413 | self.send_document( |
---|
1414 | job_id, |
---|
1415 | document, |
---|
1416 | document_name=document_name, |
---|
1417 | document_format=document_format, |
---|
1418 | document_natural_language=document_natural_language, |
---|
1419 | requesting_user_name=requesting_user_name, |
---|
1420 | compression=compression, |
---|
1421 | last_document=False) |
---|
1422 | |
---|
1423 | return job_id |
---|
1424 | |
---|
1425 | @sync |
---|
1426 | def validate_job(self, document_name=None, document_format=None, |
---|
1427 | document_natural_language=None, requesting_user_name=None, |
---|
1428 | compression=None, job_name=None, job_k_octets=None): |
---|
1429 | """RFC 2911: 3.2.3 Validate-Job Operation |
---|
1430 | |
---|
1431 | This REQUIRED operation is similar to the Print-Job operation |
---|
1432 | (section 3.2.1) except that a client supplies no document data |
---|
1433 | and the Printer allocates no resources (i.e., it does not |
---|
1434 | create a new Job object). This operation is used only to |
---|
1435 | verify capabilities of a printer object against whatever |
---|
1436 | attributes are supplied by the client in the Validate-Job |
---|
1437 | request. By using the Validate-Job operation a client can |
---|
1438 | validate that an identical Print-Job operation (with the |
---|
1439 | document data) would be accepted. The Validate-Job operation |
---|
1440 | also performs the same security negotiation as the Print-Job |
---|
1441 | operation (see section 8), so that a client can check that the |
---|
1442 | client and Printer object security requirements can be met |
---|
1443 | before performing a Print-Job operation. |
---|
1444 | |
---|
1445 | The Validate-Job operation does not accept a 'document-uri' |
---|
1446 | attribute in order to allow a client to check that the same |
---|
1447 | Print-URI operation will be accepted, since the client doesn't |
---|
1448 | send the data with the Print-URI operation. The client SHOULD |
---|
1449 | just issue the Print-URI request. |
---|
1450 | |
---|
1451 | Parameters |
---|
1452 | ---------- |
---|
1453 | document (file) |
---|
1454 | an open file handler to the document |
---|
1455 | document_name (string) |
---|
1456 | the name of the document |
---|
1457 | document_format (string) |
---|
1458 | the encoding/format of the document |
---|
1459 | document_natural_language (string) |
---|
1460 | if the document is a text file, what language it is in |
---|
1461 | requesting_user_name (string) |
---|
1462 | the user name of the job owner |
---|
1463 | compression (string) |
---|
1464 | the form of compression used on the file |
---|
1465 | job_name (string) |
---|
1466 | the name that the job should be called |
---|
1467 | job_k_octets (int) |
---|
1468 | the size of the job in bytes |
---|
1469 | |
---|
1470 | """ |
---|
1471 | |
---|
1472 | self.assert_running() |
---|
1473 | |
---|
1474 | job_id = self._next_job_id |
---|
1475 | job = GutenbachJob( |
---|
1476 | job_id, |
---|
1477 | creator=requesting_user_name, |
---|
1478 | name=job_name) |
---|
1479 | job.spool(tempfile.TemporaryFile()) |
---|
1480 | job.abort() |
---|
1481 | del job |
---|
1482 | |
---|
1483 | @sync |
---|
1484 | def get_jobs(self, requesting_user_name=None, which_jobs=None, |
---|
1485 | requested_attributes=None): |
---|
1486 | """RFC 2911: 3.2.6 Get-Jobs Operation |
---|
1487 | |
---|
1488 | This REQUIRED operation allows a client to retrieve the list |
---|
1489 | of Job objects belonging to the target Printer object. The |
---|
1490 | client may also supply a list of Job attribute names and/or |
---|
1491 | attribute group names. A group of Job object attributes will |
---|
1492 | be returned for each Job object that is returned. |
---|
1493 | |
---|
1494 | This operation is similar to the Get-Job-Attributes operation, |
---|
1495 | except that this Get-Jobs operation returns attributes from |
---|
1496 | possibly more than one object. |
---|
1497 | |
---|
1498 | Parameters |
---|
1499 | ---------- |
---|
1500 | requesting_user_name (string) |
---|
1501 | the user name of the job owner, used as a filter |
---|
1502 | which_jobs (string) |
---|
1503 | a filter for the types of jobs to return: |
---|
1504 | * 'completed' -- only jobs that have finished |
---|
1505 | * 'not-completed' -- processing or pending jobs |
---|
1506 | this defaults to 'not-completed' |
---|
1507 | requested_attributes (list) |
---|
1508 | the job attributes to return |
---|
1509 | |
---|
1510 | """ |
---|
1511 | |
---|
1512 | self.assert_running() |
---|
1513 | |
---|
1514 | # Filter by the which-jobs attribute |
---|
1515 | if which_jobs is None: |
---|
1516 | which_jobs = "not-completed" |
---|
1517 | |
---|
1518 | if which_jobs == "completed": |
---|
1519 | jobs = [self.jobs[job_id] for job_id in self.finished_jobs] |
---|
1520 | elif which_jobs == "not-completed": |
---|
1521 | jobs = [self.jobs[job_id] for job_id in self.active_jobs] |
---|
1522 | else: |
---|
1523 | raise ipp.errors.ClientErrorAttributes( |
---|
1524 | which_jobs, ipp.WhichJobs(which_jobs)) |
---|
1525 | |
---|
1526 | # Filter by username |
---|
1527 | if requesting_user_name is None: |
---|
1528 | user_jobs = jobs |
---|
1529 | else: |
---|
1530 | user_jobs = [job for job in jobs if job.creator == requesting_user_name] |
---|
1531 | |
---|
1532 | # Get the attributes of each job |
---|
1533 | job_attrs = [self.get_job_attributes( |
---|
1534 | job.id, requested_attributes=requested_attributes) for job in user_jobs] |
---|
1535 | |
---|
1536 | return job_attrs |
---|
1537 | |
---|
1538 | @sync |
---|
1539 | def print_uri(self): |
---|
1540 | """RFC 2911: 3.2.2 Print-URI Operation |
---|
1541 | |
---|
1542 | This OPTIONAL operation is identical to the Print-Job |
---|
1543 | operation (section 3.2.1) except that a client supplies a URI |
---|
1544 | reference to the document data using the 'document-uri' (uri) |
---|
1545 | operation attribute (in Group 1) rather than including the |
---|
1546 | document data itself. Before returning the response, the |
---|
1547 | Printer MUST validate that the Printer supports the retrieval |
---|
1548 | method (e.g., http, ftp, etc.) implied by the URI, and MUST |
---|
1549 | check for valid URI syntax. If the client-supplied URI scheme |
---|
1550 | is not supported, i.e. the value is not in the Printer |
---|
1551 | object's 'referenced-uri-scheme-supported' attribute, the |
---|
1552 | Printer object MUST reject the request and return the |
---|
1553 | 'client-error-uri- scheme-not-supported' status code. |
---|
1554 | |
---|
1555 | If the Printer object supports this operation, it MUST support |
---|
1556 | the 'reference-uri-schemes-supported' Printer attribute (see |
---|
1557 | section 4.4.27). |
---|
1558 | |
---|
1559 | It is up to the IPP object to interpret the URI and |
---|
1560 | subsequently 'pull' the document from the source referenced by |
---|
1561 | the URI string. |
---|
1562 | |
---|
1563 | """ |
---|
1564 | |
---|
1565 | self.assert_running() |
---|
1566 | # XXX: todo |
---|
1567 | |
---|
1568 | @sync |
---|
1569 | def create_job(self, requesting_user_name=None, |
---|
1570 | job_name=None, job_k_octets=None): |
---|
1571 | """RFC 2911: 3.2.4 Create-Job Operation |
---|
1572 | |
---|
1573 | This OPTIONAL operation is similar to the Print-Job operation |
---|
1574 | (section 3.2.1) except that in the Create-Job request, a |
---|
1575 | client does not supply document data or any reference to |
---|
1576 | document data. Also, the client does not supply any of the |
---|
1577 | 'document-name', 'document- format', 'compression', or |
---|
1578 | 'document-natural-language' operation attributes. This |
---|
1579 | operation is followed by one or more Send-Document or Send-URI |
---|
1580 | operations. In each of those operation requests, the client |
---|
1581 | OPTIONALLY supplies the 'document-name', 'document-format', |
---|
1582 | and 'document-natural-language' attributes for each document |
---|
1583 | in the multi-document Job object. |
---|
1584 | |
---|
1585 | Parameters |
---|
1586 | ---------- |
---|
1587 | requesting_user_name (string) |
---|
1588 | the user name of the job owner |
---|
1589 | job_name (string) |
---|
1590 | the name that the job should be called |
---|
1591 | job_k_octets (int) |
---|
1592 | the size of the job in bytes |
---|
1593 | |
---|
1594 | """ |
---|
1595 | |
---|
1596 | self.assert_running() |
---|
1597 | |
---|
1598 | job_id = self._next_job_id |
---|
1599 | self._next_job_id += 1 |
---|
1600 | |
---|
1601 | job = GutenbachJob( |
---|
1602 | job_id, |
---|
1603 | creator=requesting_user_name, |
---|
1604 | name=job_name) |
---|
1605 | |
---|
1606 | self.jobs[job_id] = job |
---|
1607 | return job_id |
---|
1608 | |
---|
1609 | @sync |
---|
1610 | def pause_printer(self): |
---|
1611 | """RFC 2911: 3.2.7 Pause-Printer Operation |
---|
1612 | |
---|
1613 | This OPTIONAL operation allows a client to stop the Printer |
---|
1614 | object from scheduling jobs on all its devices. Depending on |
---|
1615 | implementation, the Pause-Printer operation MAY also stop the |
---|
1616 | Printer from processing the current job or jobs. Any job that |
---|
1617 | is currently being printed is either stopped as soon as the |
---|
1618 | implementation permits or is completed, depending on |
---|
1619 | implementation. The Printer object MUST still accept create |
---|
1620 | operations to create new jobs, but MUST prevent any jobs from |
---|
1621 | entering the 'processing' state. |
---|
1622 | |
---|
1623 | If the Pause-Printer operation is supported, then the |
---|
1624 | Resume-Printer operation MUST be supported, and vice-versa. |
---|
1625 | |
---|
1626 | The IPP Printer MUST accept the request in any state and |
---|
1627 | transition the Printer to the indicated new 'printer-state' |
---|
1628 | before returning as follows: |
---|
1629 | |
---|
1630 | Current New Reasons Reponse |
---|
1631 | -------------------------------------------------------------- |
---|
1632 | 'idle' 'stopped' 'paused' 'successful-ok' |
---|
1633 | 'processing' 'processing' 'moving-to-paused' 'successful-ok' |
---|
1634 | 'processing' 'stopped' 'paused' 'successful-ok' |
---|
1635 | 'stopped' 'stopped' 'paused' 'successful-ok' |
---|
1636 | |
---|
1637 | """ |
---|
1638 | |
---|
1639 | self.assert_running() |
---|
1640 | if not self.paused: |
---|
1641 | if self.current_job is not None and self.current_job.is_playing: |
---|
1642 | self.current_job.pause() |
---|
1643 | self.paused = True |
---|
1644 | logger.info("%s paused", str(self)) |
---|
1645 | |
---|
1646 | @sync |
---|
1647 | def resume_printer(self): |
---|
1648 | """RFC 2911: 3.2.8 Resume-Printer Operation |
---|
1649 | |
---|
1650 | This operation allows a client to resume the Printer object |
---|
1651 | scheduling jobs on all its devices. The Printer object MUST |
---|
1652 | remove the 'paused' and 'moving-to-paused' values from the |
---|
1653 | Printer object's 'printer-state-reasons' attribute, if |
---|
1654 | present. If there are no other reasons to keep a device |
---|
1655 | paused (such as media-jam), the IPP Printer is free to |
---|
1656 | transition itself to the 'processing' or 'idle' states, |
---|
1657 | depending on whether there are jobs to be processed or not, |
---|
1658 | respectively, and the device(s) resume processing jobs. |
---|
1659 | |
---|
1660 | If the Pause-Printer operation is supported, then the |
---|
1661 | Resume-Printer operation MUST be supported, and vice-versa. |
---|
1662 | |
---|
1663 | The IPP Printer removes the 'printer-stopped' value from any |
---|
1664 | job's 'job-state-reasons' attributes contained in that |
---|
1665 | Printer. |
---|
1666 | |
---|
1667 | The IPP Printer MUST accept the request in any state, |
---|
1668 | transition the Printer object to the indicated new state as |
---|
1669 | follows: |
---|
1670 | |
---|
1671 | Current New Response |
---|
1672 | --------------------------------------------- |
---|
1673 | 'idle' 'idle' 'successful-ok' |
---|
1674 | 'processing' 'processing' 'successful-ok' |
---|
1675 | 'stopped' 'processing' 'successful-ok' |
---|
1676 | 'stopped' 'idle' 'successful-ok' |
---|
1677 | |
---|
1678 | """ |
---|
1679 | |
---|
1680 | self.assert_running() |
---|
1681 | if self.paused: |
---|
1682 | if self.current_job is not None: |
---|
1683 | self.current_job.resume() |
---|
1684 | self.paused = False |
---|
1685 | logger.info("%s unpaused", str(self)) |
---|
1686 | |
---|
1687 | @sync |
---|
1688 | def get_printer_attributes(self, requested_attributes=None): |
---|
1689 | """RFC 2911: 3.2.5 Get-Printer-Attributes Operation |
---|
1690 | |
---|
1691 | This REQUIRED operation allows a client to request the values |
---|
1692 | of the attributes of a Printer object. |
---|
1693 | |
---|
1694 | In the request, the client supplies the set of Printer |
---|
1695 | attribute names and/or attribute group names in which the |
---|
1696 | requester is interested. In the response, the Printer object |
---|
1697 | returns a corresponding attribute set with the appropriate |
---|
1698 | attribute values filled in. |
---|
1699 | |
---|
1700 | Parameters |
---|
1701 | ---------- |
---|
1702 | requested_attributes (list) |
---|
1703 | the attributes to return |
---|
1704 | |
---|
1705 | """ |
---|
1706 | |
---|
1707 | self.assert_running() |
---|
1708 | if requested_attributes is None: |
---|
1709 | requested = self.printer_attributes |
---|
1710 | else: |
---|
1711 | requested = [a for a in self.printer_attributes \ |
---|
1712 | if a in requested_attributes] |
---|
1713 | |
---|
1714 | _attributes = [attr.replace("-", "_") for attr in requested] |
---|
1715 | attributes = [getattr(self, attr) for attr in _attributes] |
---|
1716 | return attributes |
---|
1717 | |
---|
1718 | @sync |
---|
1719 | def set_printer_attributes(self, attributes): |
---|
1720 | self.assert_running() |
---|
1721 | for attr in attributes: |
---|
1722 | try: |
---|
1723 | setattr(self, attr, attributes[attr]) |
---|
1724 | except AttributeError: |
---|
1725 | raise ipp.errors.ClientErrorAttributes |
---|
1726 | |
---|
1727 | @sync |
---|
1728 | def cancel_job(self, job_id, requesting_user_name=None): |
---|
1729 | """RFC 2911: 3.3.3 Cancel-Job Operation |
---|
1730 | |
---|
1731 | This REQUIRED operation allows a client to cancel a Print Job |
---|
1732 | from the time the job is created up to the time it is |
---|
1733 | completed, canceled, or aborted. Since a Job might already be |
---|
1734 | printing by the time a Cancel-Job is received, some media |
---|
1735 | sheet pages might be printed before the job is actually |
---|
1736 | terminated. |
---|
1737 | |
---|
1738 | The IPP object MUST accept or reject the request based on the |
---|
1739 | job's current state and transition the job to the indicated |
---|
1740 | new state as follows: |
---|
1741 | |
---|
1742 | Current State New State Response |
---|
1743 | ----------------------------------------------------------------- |
---|
1744 | pending canceled successful-ok |
---|
1745 | pending-held canceled successful-ok |
---|
1746 | processing canceled successful-ok |
---|
1747 | processing processing successful-ok See Rule 1 |
---|
1748 | processing processing client-error-not-possible See Rule 2 |
---|
1749 | processing-stopped canceled successful-ok |
---|
1750 | processing-stopped processing-stopped successful-ok See Rule 1 |
---|
1751 | processing-stopped processing-stopped client-error-not-possible See Rule 2 |
---|
1752 | completed completed client-error-not-possible |
---|
1753 | canceled canceled client-error-not-possible |
---|
1754 | aborted aborted client-error-not-possible |
---|
1755 | |
---|
1756 | Rule 1: If the implementation requires some measurable time to |
---|
1757 | cancel the job in the 'processing' or 'processing-stopped' job |
---|
1758 | states, the IPP object MUST add the 'processing-to-stop-point' |
---|
1759 | value to the job's 'job-state-reasons' attribute and then |
---|
1760 | transition the job to the 'canceled' state when the processing |
---|
1761 | ceases (see section 4.3.8). |
---|
1762 | |
---|
1763 | Rule 2: If the Job object already has the |
---|
1764 | 'processing-to-stop-point' value in its 'job-state-reasons' |
---|
1765 | attribute, then the Printer object MUST reject a Cancel-Job |
---|
1766 | operation. |
---|
1767 | |
---|
1768 | Parameters |
---|
1769 | ---------- |
---|
1770 | job_id (integer) |
---|
1771 | the id of the job to cancel |
---|
1772 | requesting_user_name (string) |
---|
1773 | the name of the job's owner |
---|
1774 | |
---|
1775 | """ |
---|
1776 | |
---|
1777 | self.assert_running() |
---|
1778 | job = self.get_job(job_id) |
---|
1779 | try: |
---|
1780 | job.cancel() |
---|
1781 | except InvalidJobStateException: |
---|
1782 | # XXX |
---|
1783 | raise |
---|
1784 | |
---|
1785 | @sync |
---|
1786 | def send_document(self, job_id, document, document_name=None, |
---|
1787 | document_format=None, document_natural_language=None, |
---|
1788 | requesting_user_name=None, compression=None, |
---|
1789 | last_document=None): |
---|
1790 | """RFC 2911: 3.3.1 Send-Document Operation |
---|
1791 | |
---|
1792 | This OPTIONAL operation allows a client to create a |
---|
1793 | multi-document Job object that is initially 'empty' (contains |
---|
1794 | no documents). In the Create-Job response, the Printer object |
---|
1795 | returns the Job object's URI (the 'job-uri' attribute) and the |
---|
1796 | Job object's 32-bit identifier (the 'job-id' attribute). For |
---|
1797 | each new document that the client desires to add, the client |
---|
1798 | uses a Send-Document operation. Each Send- Document Request |
---|
1799 | contains the entire stream of document data for one document. |
---|
1800 | |
---|
1801 | If the Printer supports this operation but does not support |
---|
1802 | multiple documents per job, the Printer MUST reject subsequent |
---|
1803 | Send-Document operations supplied with data and return the |
---|
1804 | 'server-error-multiple- document-jobs-not-supported'. However, |
---|
1805 | the Printer MUST accept the first document with a 'true' or |
---|
1806 | 'false' value for the 'last-document' operation attribute (see |
---|
1807 | below), so that clients MAY always submit one document jobs |
---|
1808 | with a 'false' value for 'last-document' in the first |
---|
1809 | Send-Document and a 'true' for 'last-document' in the second |
---|
1810 | Send-Document (with no data). |
---|
1811 | |
---|
1812 | Since the Create-Job and the send operations (Send-Document or |
---|
1813 | Send- URI operations) that follow could occur over an |
---|
1814 | arbitrarily long period of time for a particular job, a client |
---|
1815 | MUST send another send operation within an IPP Printer defined |
---|
1816 | minimum time interval after the receipt of the previous |
---|
1817 | request for the job. If a Printer object supports the |
---|
1818 | Create-Job and Send-Document operations, the Printer object |
---|
1819 | MUST support the 'multiple-operation-time-out' attribute (see |
---|
1820 | section 4.4.31). This attribute indicates the minimum number |
---|
1821 | of seconds the Printer object will wait for the next send |
---|
1822 | operation before taking some recovery action. |
---|
1823 | |
---|
1824 | An IPP object MUST recover from an errant client that does not |
---|
1825 | supply a send operation, sometime after the minimum time |
---|
1826 | interval specified by the Printer object's |
---|
1827 | 'multiple-operation-time-out' attribute. |
---|
1828 | |
---|
1829 | Parameters |
---|
1830 | ---------- |
---|
1831 | job_id (integer) |
---|
1832 | the id of the job to send the document |
---|
1833 | document (file) |
---|
1834 | an open file handler to the document |
---|
1835 | document_name (string) |
---|
1836 | the name of the document |
---|
1837 | document_format (string) |
---|
1838 | the encoding/format of the document |
---|
1839 | document_natural_language (string) |
---|
1840 | if the document is a text file, what language it is in |
---|
1841 | requesting_user_name (string) |
---|
1842 | the user name of the job owner |
---|
1843 | compression (string) |
---|
1844 | the form of compression used on the file |
---|
1845 | last_document (boolean) |
---|
1846 | whether or not this is the last document in this job |
---|
1847 | |
---|
1848 | """ |
---|
1849 | |
---|
1850 | self.assert_running() |
---|
1851 | job = self.get_job(job_id) |
---|
1852 | job.spool(document) |
---|
1853 | if 'dryrun' in self.config and self.config['dryrun']: |
---|
1854 | job.player._dryrun = True |
---|
1855 | self.pending_jobs.append(job_id) |
---|
1856 | |
---|
1857 | @sync |
---|
1858 | def send_uri(self, job_id, document_uri, document_name=None, |
---|
1859 | document_format=None, document_natural_language=None, |
---|
1860 | requesting_user_name=None, compression=None, |
---|
1861 | last_document=None): |
---|
1862 | """RFC 2911: 3.2.2 Send URI |
---|
1863 | |
---|
1864 | This OPTIONAL operation is identical to the Send-Document |
---|
1865 | operation (see section 3.3.1) except that a client MUST supply |
---|
1866 | a URI reference ('document-uri' operation attribute) rather |
---|
1867 | than the document data itself. If a Printer object supports |
---|
1868 | this operation, clients can use both Send-URI or Send-Document |
---|
1869 | operations to add new documents to an existing multi-document |
---|
1870 | Job object. However, if a client needs to indicate that the |
---|
1871 | previous Send-URI or Send-Document was the last document, the |
---|
1872 | client MUST use the Send-Document operation with no document |
---|
1873 | data and the 'last-document' flag set to 'true' (rather than |
---|
1874 | using a Send-URI operation with no 'document-uri' operation |
---|
1875 | attribute). |
---|
1876 | |
---|
1877 | If a Printer object supports this operation, it MUST also |
---|
1878 | support the Print-URI operation (see section 3.2.2). |
---|
1879 | |
---|
1880 | The Printer object MUST validate the syntax and URI scheme of |
---|
1881 | the supplied URI before returning a response, just as in the |
---|
1882 | Print-URI operation. The IPP Printer MAY validate the |
---|
1883 | accessibility of the document as part of the operation or |
---|
1884 | subsequently (see section 3.2.2). |
---|
1885 | |
---|
1886 | Parameters |
---|
1887 | ---------- |
---|
1888 | job_id (integer) |
---|
1889 | the id of the job to send the uri |
---|
1890 | document_uri (string) |
---|
1891 | the uri of the document |
---|
1892 | document_name (string) |
---|
1893 | the name of the document |
---|
1894 | document_format (string) |
---|
1895 | the encoding/format of the document |
---|
1896 | document_natural_language (string) |
---|
1897 | if the document is a text file, what language it is in |
---|
1898 | requesting_user_name (string) |
---|
1899 | the user name of the job owner |
---|
1900 | compression (string) |
---|
1901 | the form of compression used on the file |
---|
1902 | last_document (boolean) |
---|
1903 | whether or not this is the last document in this job |
---|
1904 | |
---|
1905 | """ |
---|
1906 | |
---|
1907 | self.assert_running() |
---|
1908 | job = self.get_job(job_id) |
---|
1909 | # XXX: need to validate URI |
---|
1910 | # XXX: need to deal with the URI stream? |
---|
1911 | |
---|
1912 | #job.spool_uri(document_uri) |
---|
1913 | #if 'dryrun' in self.config and self.config['dryrun']: |
---|
1914 | # job.player._dryrun = True |
---|
1915 | #self.pending_jobs.append(job_id) |
---|
1916 | |
---|
1917 | @sync |
---|
1918 | def get_job_attributes(self, job_id, requested_attributes=None): |
---|
1919 | """RFC 2911: 3.3.4 Get-Job-Attributes Operation |
---|
1920 | |
---|
1921 | This REQUIRED operation allows a client to request the values |
---|
1922 | of attributes of a Job object and it is almost identical to |
---|
1923 | the Get- Printer-Attributes operation (see section 3.2.5). The |
---|
1924 | only differences are that the operation is directed at a Job |
---|
1925 | object rather than a Printer object, there is no |
---|
1926 | 'document-format' operation attribute used when querying a Job |
---|
1927 | object, and the returned attribute group is a set of Job |
---|
1928 | object attributes rather than a set of Printer object |
---|
1929 | attributes. |
---|
1930 | |
---|
1931 | For Jobs, the possible names of attribute groups are: |
---|
1932 | - 'job-template': the subset of the Job Template attributes |
---|
1933 | that apply to a Job object (the first column of the table |
---|
1934 | in Section 4.2) that the implementation supports for Job |
---|
1935 | objects. |
---|
1936 | - 'job-description': the subset of the Job Description |
---|
1937 | attributes specified in Section 4.3 that the |
---|
1938 | implementation supports for Job objects. |
---|
1939 | - 'all': the special group 'all' that includes all |
---|
1940 | attributes that the implementation supports for Job |
---|
1941 | objects. |
---|
1942 | |
---|
1943 | Since a client MAY request specific attributes or named |
---|
1944 | groups, there is a potential that there is some overlap. For |
---|
1945 | example, if a client requests, 'job-name' and |
---|
1946 | 'job-description', the client is actually requesting the |
---|
1947 | 'job-name' attribute once by naming it explicitly, and once by |
---|
1948 | inclusion in the 'job-description' group. In such cases, the |
---|
1949 | Printer object NEED NOT return the attribute only once in the |
---|
1950 | response even if it is requested multiple times. The client |
---|
1951 | SHOULD NOT request the same attribute in multiple ways. |
---|
1952 | |
---|
1953 | It is NOT REQUIRED that a Job object support all attributes |
---|
1954 | belonging to a group (since some attributes are |
---|
1955 | OPTIONAL). However it is REQUIRED that each Job object support |
---|
1956 | all these group names. |
---|
1957 | |
---|
1958 | Parameters |
---|
1959 | ---------- |
---|
1960 | job_id (integer) |
---|
1961 | the id of the job to send the uri |
---|
1962 | requested_attributes (list) |
---|
1963 | the attributes to return |
---|
1964 | |
---|
1965 | """ |
---|
1966 | |
---|
1967 | self.assert_running() |
---|
1968 | if requested_attributes is None: |
---|
1969 | requested = self.job_attributes |
---|
1970 | else: |
---|
1971 | requested = [a for a in self.job_attributes \ |
---|
1972 | if a in requested_attributes] |
---|
1973 | |
---|
1974 | _attributes = [attr.replace("-", "_") for attr in requested] |
---|
1975 | attributes = [getattr(self, attr)(job_id) for attr in _attributes] |
---|
1976 | return attributes |
---|
1977 | |
---|
1978 | @sync |
---|
1979 | def set_job_attributes(self, job_id, attributes): |
---|
1980 | self.assert_running() |
---|
1981 | job = self.get_job(job_id) |
---|
1982 | for attr in attributes: |
---|
1983 | if attr in ("job-id", "job-k-octets", "job-state", "job-printer-uri"): |
---|
1984 | raise ipp.errors.ClientErrorAttributesNotSettable(attr) |
---|
1985 | elif attr == "job-name": |
---|
1986 | job.name = attributes[attr] |
---|
1987 | elif attr == "job-originating-user-name": |
---|
1988 | job.creator = attributes[attr] # XXX: do we want this? |
---|
1989 | |
---|
1990 | @sync |
---|
1991 | def restart_job(self, job_id, requesting_user_name=None): |
---|
1992 | self.assert_running() |
---|
1993 | job = self.get_job(job_id) |
---|
1994 | try: |
---|
1995 | job.restart() |
---|
1996 | except InvalidJobStateException: |
---|
1997 | # XXX |
---|
1998 | raise ipp.errors.ClientErrorNotPossible |
---|
1999 | |
---|
2000 | self.finished_jobs.remove(job_id) |
---|
2001 | self.pending_jobs.append(job_id) |
---|
2002 | |
---|
2003 | @sync |
---|
2004 | def promote_job(self, job_id, requesting_user_name=None): |
---|
2005 | # According to RFC 3998, we need to put the job at the front |
---|
2006 | # of the queue (so that when the currently playing job |
---|
2007 | # completes, this one will go next |
---|
2008 | |
---|
2009 | self.assert_running() |
---|
2010 | job = self.get_job(job_id) |
---|
2011 | job.priority = 1 # XXX we need to actually do something |
---|
2012 | # correct here |
---|