source: server/lib/gutenbach/server/printer.py @ 97f20dd

no-cups
Last change on this file since 97f20dd was 97f20dd, checked in by Jessica B. Hamrick <jhamrick@…>, 12 years ago

Add documentation for printer and job attributes

  • Property mode set to 100644
File size: 84.6 KB
Line 
1from . import sync
2from .errors import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
3from .job import GutenbachJob
4from gutenbach.ipp import PrinterStates as States
5import gutenbach.ipp as ipp
6import logging
7import math
8import sys
9import tempfile
10import threading
11import time
12import traceback
13
14# initialize logger
15logger = logging.getLogger(__name__)
16
17class 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
Note: See TracBrowser for help on using the repository browser.