source: server/lib/gutenbach/server/printer.py @ 3c0760f

Last change on this file since 3c0760f was 3c0760f, checked in by Steven Allen <steven@…>, 12 years ago

Block instead of looping and sleeping.

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