source: server/lib/gutenbach/server/requests.py @ 7daf25f

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

Fix indentation

  • Property mode set to 100644
File size: 55.4 KB
Line 
1from . import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
2import gutenbach.ipp as ipp
3import logging
4import traceback
5import sys
6
7# initialize logger
8logger = logging.getLogger(__name__)
9
10def handler_for(operation):
11    """A decorator method to mark a function with the operation id
12    that it handles.  This value will be stored in
13    'func.ipp_operation'.
14
15    """
16   
17    def f(func):
18        func.ipp_operation = operation
19        return func
20    return f
21
22def make_empty_response(request):
23    # Operation attributes -- typically the same for any request
24    attribute_group = ipp.AttributeGroup(
25        ipp.AttributeTags.OPERATION,
26        [ipp.AttributesCharset('utf-8'),
27         ipp.AttributesNaturalLanguage('en-us')])
28   
29    # Set up the default response -- handlers will override these
30    # values if they need to
31    response_kwargs = {}
32    response_kwargs['version']          = request.version
33    response_kwargs['operation_id']     = ipp.StatusCodes.OK
34    response_kwargs['request_id']       = request.request_id
35    response_kwargs['attribute_groups'] = [attribute_group]
36    response = ipp.Request(**response_kwargs)
37   
38    return response
39
40def verify_attribute(attr, cls, length=1):
41    vals = [val.value for val in attr.values]
42    if attr != cls(*vals):
43        raise ipp.errors.ClientErrorBadRequest(str(attr))
44    if length is not None and len(vals) != length:
45        raise ipp.errors.ClientErrorBadRequest(str(attr))
46    return vals
47
48class GutenbachRequestHandler(object):
49
50    def __init__(self, gutenbach_printer):
51        self.printer = gutenbach_printer
52
53    def generic_handle(self, request):
54        # check the IPP version number
55        if request.version != (1, 1):
56            raise ipp.errors.ServerErrorVersionNotSupported(str(request.version))
57
58        # make sure the operation attribute group has the correct tag
59        operation = request.attribute_groups[0]
60        if operation.tag != ipp.AttributeTags.OPERATION:
61            raise ipp.errors.ClientErrorBadRequest(
62                "Attribute group does not have OPERATION tag: 0x%x" % operation.tag)
63
64        # check charset
65        charset_attr = operation.attributes[0]
66        charset = verify_attribute(charset_attr, ipp.AttributesCharset)[0]
67        if charset != 'utf-8':
68            raise ipp.errors.ClientErrorAttributes(str(charset_attr))
69
70        # check for attributes-natural-language
71        natlang_attr = operation.attributes[1]
72        natlang = verify_attribute(natlang_attr, ipp.AttributesNaturalLanguage)[0]
73        if natlang != 'en-us':
74            raise ipp.errors.ClientErrorAttributes(str(natlang_attr))
75   
76    def handle(self, request):
77        # look up the handler
78        handler = None
79        handler_name = None
80        for d in dir(self):
81            if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id:
82                handler_name = d
83                break
84
85        # we couldn't find a handler, so default to unknown operation
86        if handler_name is None:
87            handler_name = "unknown_operation"
88
89        # actually get the handler
90        handler = getattr(self, handler_name)
91        logger.info("request is '%s'" % handler_name)
92
93        # try to handle the request
94        try:
95            self.generic_handle(request)
96            response = make_empty_response(request)
97            handler(request, response)
98
99        # Handle any errors that occur.  If an exception occurs that
100        # is an IPP error, then we can get the error code from the
101        # exception itself.
102        except ipp.errors.IPPException:
103            exctype, excval, exctb = sys.exc_info()
104            logger.error("%s: %s" % (exctype.__name__, excval.message))
105            response = make_empty_response(request)
106            excval.update_response(response)
107
108        # If it wasn't an IPP error, then it's our fault, so mark it
109        # as an internal server error
110        except Exception:
111            logger.error(traceback.format_exc())
112            response = make_empty_response(request)
113            response.operation_id = ipp.StatusCodes.INTERNAL_ERROR
114
115        return response
116
117    def unknown_operation(self, request, response):
118        logger.warning("unknown operation 0x%x" % request.operation_id)
119        response = make_empty_response(request)
120        response.operation_id = ipp.StatusCodes.OPERATION_NOT_SUPPORTED
121        return response
122       
123    ##### Printer Commands
124
125    @handler_for(ipp.OperationCodes.PRINT_JOB)
126    def print_job(self, request, response):
127        """RFC 2911: 3.2.1 Print-Job Operation
128
129        This REQUIRED operation allows a client to submit a print job
130        with only one document and supply the document data (rather
131        than just a reference to the data). See Section 15 for the
132        suggested steps for processing create operations and their
133        Operation and Job Template attributes.
134
135        Request
136        -------
137        Group 1: Operation Attributes
138            REQUIRED 'attributes-charset'
139            REQUIRED 'attributes-natural-language'
140            REQUIRED 'printer-uri' (uri)
141            OPTIONAL 'requesting-user-name' (name(MAX))
142            OPTIONAL 'job-name' (name(MAX))
143            OPTIONAL 'ipp-attribute-fidelity' (boolean)
144            OPTIONAL 'document-name' (name(MAX))
145            OPTIONAL 'compression' (type3 keyword)
146            OPTIONAL 'document-format' (mimeMediaType)
147            OPTIONAL 'document-natural-language' (naturalLanguage)
148            OPTIONAL 'job-k-octets' (integer(0:MAX))
149            OPTIONAL 'job-impressions' (integer(0:MAX))
150            OPTIONAL 'job-media-sheets' (integer(0:MAX))
151        Group 2: Job Template Attributes
152        Group 3: Document Content
153
154        Response
155        --------
156        Group 1: Operation Attributes
157            OPTIONAL 'status-message' (text(255))
158            OPTIONAL 'detailed-status-message' (text(MAX))
159            REQUIRED 'attributes-charset'
160            REQUIRED 'attributes-natural-language'
161        Group 2: Unsupported Attributes
162        Group 3: Job Object Attributes
163            REQUIRED 'job-uri' (uri)
164            REQUIRED 'job-id' (integer(1:MAX))
165            REQUIRED 'job-state' (type1 enum)
166            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
167            OPTIONAL 'job-state-message' (text(MAX))
168            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
169
170        """
171
172        operation = request.attribute_groups[0]
173
174        # requested printer uri
175        if 'printer-uri' not in operation:
176            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
177        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
178        if printer_uri not in self.printer.uris:
179            raise ipp.errors.ClientErrorAttributes(
180                str(operation['printer-uri']), operation['printer-uri'])
181
182        if 'requesting-user-name' in operation:
183            user_name = verify_attribute(
184                operation['requesting-user-name'], ipp.RequestingUserName)[0]
185
186        if 'job-name' in operation:
187            job_name = verify_attribute(
188                operation['job-name'], ipp.JobName)[0]
189
190        if 'job-k-octets' in operation:
191            job_k_octets = verify_attribute(
192                operation['job-k-octets'], ipp.JobKOctets)[0]
193
194        if 'ipp-attribute-fidelity' in operation:
195            pass # don't care
196        if 'job-impressions' in operation:
197            pass # don't care
198        if 'job-media-sheets' in operation:
199            pass # don't care
200
201        # get attributes from the printer and add to response
202        job_id = self.printer.create_job(
203            requesting_user_name=user_name,
204            job_name=job_name,
205            job_k_octets=job_k_octets)
206        attrs = self.printer.get_job_attributes(job_id)
207        response.attribute_groups.append(ipp.AttributeGroup(
208            ipp.AttributeTags.JOB, attrs))
209            #raise ipp.errors.ServerErrorOperationNotSupported
210        # Get nescessary information for calling send_document
211        # Any field being set to None here just means that we either aren't using or haven't implemented parsing it
212        document = request.data       
213        #XXX
214        document_format = None
215        document_natural_language = None
216        compression = None
217        last_document = None
218
219
220       
221        # Actually put the document in the job
222        try:
223            self.printer.send_document(job_id,document,
224                    document_name = document_name,
225                    document_format = document_format,
226                    document_natural_language = document_natural_language,
227                    requesting_user_name = requesting_user_name,
228                    compression = compression,
229                    last_document = last_document)
230        except InvalidJobException:
231            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
232
233       
234        # Print the job, now that we've filled it in as appropriate
235       
236       #XXX
237       #Should we be handling a possible exception here?
238        self.print_job(document,
239                document_name = document_name,
240                document_format = document_format,
241                document_natural_language = document_natural_language,
242                requesting_user_name = requesting_user_name,
243                compression=compression,
244                job_name = job_name,
245                job_k_octets = job_k_octets)
246
247        attrs = self.printer.get_job_attributes(job_id)
248        response.attribute_groups.append(ipp.AttributeGroup(ipp.AttributeTags.JOB, attrs))
249
250    @handler_for(ipp.OperationCodes.VALIDATE_JOB)
251    def validate_job(self, request, response):
252
253        raise ipp.errors.ServerErrorOperationNotSupported
254
255    @handler_for(ipp.OperationCodes.GET_JOBS)
256    def get_jobs(self, request, response):
257        """3.2.6 Get-Jobs Operation
258       
259        This REQUIRED operation allows a client to retrieve the list
260        of Job objects belonging to the target Printer object. The
261        client may also supply a list of Job attribute names and/or
262        attribute group names. A group of Job object attributes will
263        be returned for each Job object that is returned.
264
265        This operation is similar to the Get-Job-Attributes operation,
266        except that this Get-Jobs operation returns attributes from
267        possibly more than one object.
268
269        Request
270        -------
271        Group 1: Operation Attributes
272            REQUIRED 'attributes-charset'
273            REQUIRED 'attributes-natural-language'
274            REQUIRED 'printer-uri' (uri)
275            OPTIONAL 'requesting-user-name' (name(MAX))
276            OPTIONAL 'limit' (integer(1:MAX))
277            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
278            OPTIONAL 'which-jobs' (type2 keyword)
279            OPTIONAL 'my-jobs' (boolean)
280
281        Response
282        --------
283        Group 1: Operation Attributes
284            OPTIONAL 'status-message' (text(255))
285            OPTIONAL 'detailed-status-message' (text(MAX))
286            REQUIRED 'attributes-charset'
287            REQUIRED 'attributes-natural-language'
288        Group 2: Unsupported Attributes
289        Groups 3 to N: Job Object Attributes
290
291        """
292
293        operation = request.attribute_groups[0]
294
295        # initialize operation attribute variables
296        printer_name = None
297        user = None
298        limit = None
299        attributes = None
300        which_jobs = None
301        my_jobs = None
302
303        # requested printer uri
304        if 'printer-uri' not in operation:
305            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
306        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
307        if printer_uri not in self.printer.uris and printer_uri != "ipp://localhost/":
308            raise ipp.errors.ClientErrorAttributes(
309                str(operation['printer-uri']), operation['printer-uri'])
310
311        # optional attributes
312        if 'limit' in operation:
313            limit = verify_attribute(
314                operation['limit'], ipp.Limit)[0]
315           
316        if 'requested-attributes' in operation:
317            attributes = verify_attribute(
318                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
319           
320        if 'which-jobs' in operation:
321            which_jobs = verify_attribute(
322                operation['which-jobs'], ipp.WhichJobs)[0]
323           
324        if 'my-jobs' in operation:
325            my_jobs = verify_attribute(
326                operation['my-jobs'], ipp.MyJobs)[0]
327
328        if 'requesting-user-name' in operation:
329            user = verify_attribute(
330                operation['requesting-user-name'], ipp.RequestingUserName)[0]
331            # ignore if we're not filtering jobs by user
332            if not my_jobs:
333                user = None
334           
335        # get the job attributes and add them to the response
336        job_attrs = self.printer.get_jobs(
337            which_jobs=which_jobs,
338            requesting_user_name=user,
339            requested_attributes=attributes)
340        for attrs in job_attrs:
341            response.attribute_groups.append(ipp.AttributeGroup(
342                ipp.AttributeTags.JOB, attrs))
343
344    @handler_for(ipp.OperationCodes.PRINT_URI)
345    def print_uri(self, request, response):
346        """3.2.2 Print-URI Operation
347
348        This OPTIONAL operation is identical to the Print-Job operation
349        (section 3.2.1) except that a client supplies a URI reference to the
350        document data using the 'document-uri' (uri) operation attribute (in
351        Group 1) rather than including the document data itself.  Before
352        returning the response, the Printer MUST validate that the Printer
353        supports the retrieval method (e.g., http, ftp, etc.) implied by the
354        URI, and MUST check for valid URI syntax.  If the client-supplied URI
355        scheme is not supported, i.e. the value is not in the Printer
356        object's 'referenced-uri-scheme-supported' attribute, the Printer
357        object MUST reject the request and return the 'client-error-uri-
358        scheme-not-supported' status code.
359
360        The IPP Printer MAY validate the accessibility of the document as
361        part of the operation or subsequently.  If the Printer determines an
362        accessibility problem before returning an operation response, it
363        rejects the request and returns the 'client-error-document-access-
364        error' status code.  The Printer MAY also return a specific document
365        access error code using the 'document-access-error' operation
366        attribute (see section 3.1.6.4).
367
368        If the Printer determines this document accessibility problem after
369        accepting the request and returning an operation response with one
370        of the successful status codes, the Printer adds the
371        'document-access- error' value to the job's 'job-state-reasons'
372        attribute and MAY populate the job's 'job-document-access-errors'
373        Job Description attribute (see section 4.3.11).  See The
374        Implementer's Guide [IPP- IIG] for suggested additional checks.
375                                                                             
376        If the Printer object supports this operation, it MUST support the
377        'reference-uri-schemes-supported' Printer attribute (see section 4.4.27).
378
379        It is up to the IPP object to interpret the URI and subsequently
380        'pull' the document from the source referenced by the URI string."""
381        raise ipp.errors.ServerErrorOperationNotSupported
382
383    @handler_for(ipp.OperationCodes.CREATE_JOB)
384    def create_job(self, request, response):
385        """RFC 2911: 3.2.4 Create-Job Operation
386
387        This OPTIONAL operation is similar to the Print-Job operation
388        (section 3.2.1) except that in the Create-Job request, a
389        client does not supply document data or any reference to
390        document data. Also, the client does not supply any of the
391        'document-name', 'document- format', 'compression', or
392        'document-natural-language' operation attributes. This
393        operation is followed by one or more Send-Document or Send-URI
394        operations. In each of those operation requests, the client
395        OPTIONALLY supplies the 'document-name', 'document-format',
396        and 'document-natural-language' attributes for each document
397        in the multi-document Job object.
398
399        Request
400        -------
401        Group 1: Operation Attributes
402            REQUIRED 'attributes-charset'
403            REQUIRED 'attributes-natural-language'
404            REQUIRED 'printer-uri' (uri)
405            OPTIONAL 'requesting-user-name' (name(MAX))
406            OPTIONAL 'job-name' (name(MAX))
407            OPTIONAL 'ipp-attribute-fidelity' (boolean)
408            OPTIONAL 'job-k-octets' (integer(0:MAX))
409            OPTIONAL 'job-impressions' (integer(0:MAX))
410            OPTIONAL 'job-media-sheets' (integer(0:MAX))
411        Group 2: Job Template Attributes
412
413        Response
414        --------
415        Group 1: Operation Attributes
416            OPTIONAL 'status-message' (text(255))
417            OPTIONAL 'detailed-status-message' (text(MAX))
418            REQUIRED 'attributes-charset'
419            REQUIRED 'attributes-natural-language'
420        Group 2: Unsupported Attributes
421        Group 3: Job Object Attributes
422            REQUIRED 'job-uri' (uri)
423            REQUIRED 'job-id' (integer(1:MAX))
424            REQUIRED 'job-state' (type1 enum)
425            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
426            OPTIONAL 'job-state-message' (text(MAX))
427            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
428       
429        """
430
431        operation = request.attribute_groups[0]
432
433        printer_uri = None
434        requesting_user_name = None
435        job_name = None
436        ipp_attribute_fidelity=None
437        job_k_octets = None
438
439        # requested printer uri
440        if 'printer-uri' not in operation:
441            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
442        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
443        if printer_uri not in self.printer.uris:
444            raise ipp.errors.ClientErrorAttributes(
445                str(operation['printer-uri']), operation['printer-uri'])
446
447        if 'requesting-user-name' in operation:
448            user_name = verify_attribute(
449                operation['requesting-user-name'], ipp.RequestingUserName)[0]
450
451        if 'job-name' in operation:
452            job_name = verify_attribute(
453                operation['job-name'], ipp.JobName)[0]
454
455        if 'job-k-octets' in operation:
456            job_k_octets = verify_attribute(
457                operation['job-k-octets'], ipp.JobKOctets)[0]
458
459        if 'ipp-attribute-fidelity' in operation:
460            pass # don't care
461        if 'job-impressions' in operation:
462            pass # don't care
463        if 'job-media-sheets' in operation:
464            pass # don't care
465
466        # get attributes from the printer and add to response
467        job_id = self.printer.create_job(
468            requesting_user_name=requesting_user_name,
469            job_name=job_name,
470            job_k_octets=job_k_octets)
471        attrs = self.printer.get_job_attributes(job_id)
472        response.attribute_groups.append(ipp.AttributeGroup(
473            ipp.AttributeTags.JOB, attrs))
474   
475    @handler_for(ipp.OperationCodes.PAUSE_PRINTER)
476    def pause_printer(self, request, response):
477        """
478            3.2.7 Pause-Printer Operation
479
480            This OPTIONAL operation allows a client to stop the Printer object
481            from scheduling jobs on all its devices.  Depending on
482            implementation, the Pause-Printer operation MAY also stop the Printer
483            from processing the current job or jobs.  Any job that is currently
484            being printed is either stopped as soon as the implementation permits
485            or is completed, depending on implementation.  The Printer object
486            MUST still accept create operations to create new jobs, but MUST
487            prevent any jobs from entering the 'processing' state.
488
489            If the Pause-Printer operation is supported, then the Resume-Printer
490            operation MUST be supported, and vice-versa.
491
492            The IPP Printer stops the current job(s) on its device(s) that were
493            in the 'processing' or 'processing-stopped' states as soon as the
494            implementation permits.  If the implementation will take appreciable
495            time to stop, the IPP Printer adds the 'moving-to-paused' value to
496            the Printer object's 'printer-state-reasons' attribute (see section
497            4.4.12).  When the device(s) have all stopped, the IPP Printer
498            transitions the Printer object to the 'stopped' state, removes the
499            'moving-to-paused' value, if present, and adds the 'paused' value to
500            the Printer object's 'printer-state-reasons' attribute.
501
502            When the current job(s) complete that were in the 'processing' state,
503            the IPP Printer transitions them to the 'completed' state.  When the
504            current job(s) stop in mid processing that were in the 'processing'
505            state, the IPP Printer transitions them to the 'processing-stopped'
506            state and adds the 'printer-stopped' value to the job's 'job-state-
507            reasons' attribute.
508
509            For any jobs that are 'pending' or 'pending-held', the 'printer-
510            stopped' value of the jobs' 'job-state-reasons' attribute also
511            applies.  However, the IPP Printer NEED NOT update those jobs' 'job-
512            state-reasons' attributes and only need return the 'printer-stopped'
513            value when those jobs are queried (so-called 'lazy evaluation').
514
515            Whether the Pause-Printer operation affects jobs that were submitted
516            to the device from other sources than the IPP Printer object in the
517            same way that the Pause-Printer operation affects jobs that were
518            submitted to the IPP Printer object using IPP, depends on
519            implementation, i.e., on whether the IPP protocol is being used as a
520            universal management protocol or just to manage IPP jobs,
521            respectively.
522
523            The IPP Printer MUST accept the request in any state and transition
524            the Printer to the indicated new 'printer-state' before returning as
525            follows:
526
527            Current        New      'printer   IPP Printer's response status
528            'printer-    'printer-   -state-          code and action:
529            state'       state'    reasons'
530
531            'idle'       'stopped'    'paused'  'successful-ok'
532            'processing' 'processing' 'moving-  OPTION 1: 'successful-ok';
533                                                      to-       Later, when all output has
534                                                      paused'   stopped, the 'printer-state'
535                                                                            becomes 'stopped', and the
536                                                                            'paused' value replaces the
537                                                                            'moving-to-paused' value in the
538                                                                            'printer-state-reasons'
539                                                                            attribute
540            'processing' 'stopped'    'paused'  OPTION 2: 'successful-ok';
541                                                                            all device output stopped
542                                                                            immediately
543            'stopped'    'stopped'    'paused'  'successful-ok'
544
545            Access Rights: The authenticated user (see section 8.3) performing
546            this operation must be an operator or administrator of the Printer
547            object (see Sections 1 and 8.5).   Otherwise, the IPP Printer MUST
548            reject the operation and return:  'client-error-forbidden', 'client-
549            error-not-authenticated', or 'client-error-not-authorized' as
550            appropriate.
551
552            3.2.7.1 Pause-Printer Request
553
554            The following groups of attributes are part of the Pause-Printer
555            Request:
556
557            Group 1: Operation Attributes
558
559            Natural Language and Character Set:
560            The 'attributes-charset' and 'attributes-natural-language'
561            attributes as described in section 3.1.4.1.
562
563            Target:
564            The 'printer-uri' (uri) operation attribute which is the target
565            for this operation as described in section 3.1.5.
566
567            Requesting User Name:
568            The 'requesting-user-name' (name(MAX)) attribute SHOULD be
569            supplied by the client as described in section 8.3.
570
571            3.2.7.2 Pause-Printer Response
572
573            The following groups of attributes are part of the Pause-Printer
574            Response:
575
576            Group 1: Operation Attributes
577
578            Status Message:
579            In addition to the REQUIRED status code returned in every
580            response, the response OPTIONALLY includes a 'status-message'
581            (text(255)) and/or a 'detailed-status-message' (text(MAX))
582            operation attribute as described in sections 13 and  3.1.6.
583
584            Natural Language and Character Set:
585            The 'attributes-charset' and 'attributes-natural-language'
586            attributes as described in section 3.1.4.2.
587
588            Group 2: Unsupported Attributes
589
590            See section 3.1.7 for details on returning Unsupported Attributes.
591
592   
593    """
594        raise ipp.errors.ServerErrorOperationNotSupported
595
596    @handler_for(ipp.OperationCodes.RESUME_PRINTER)
597    def resume_printer(self, request, response):
598        """
599        3.2.8 Resume-Printer Operation
600
601        This operation allows a client to resume the Printer object
602        scheduling jobs on all its devices.  The Printer object MUST remove
603        the 'paused' and 'moving-to-paused' values from the Printer object's
604        'printer-state-reasons' attribute, if present.  If there are no other
605        reasons to keep a device paused (such as media-jam), the IPP Printer
606        is free to transition itself to the 'processing' or 'idle' states,
607        depending on whether there are jobs to be processed or not,
608        respectively, and the device(s) resume processing jobs.
609
610        If the Pause-Printer operation is supported, then the Resume-Printer
611        operation MUST be supported, and vice-versa.
612
613        The IPP Printer removes the 'printer-stopped' value from any job's
614        'job-state-reasons' attributes contained in that Printer.
615
616        The IPP Printer MUST accept the request in any state, transition the
617        Printer object to the indicated new state as follows:
618
619
620        Current    New 'printer-  IPP Printer's response status code and
621        'printer-      state'                     action:
622        state'
623
624        'idle'       'idle'         'successful-ok'
625        'processing' 'processing'   'successful-ok'
626
627        'stopped'    'processing'   'successful-ok';
628                                                   when there are jobs to be processed
629        'stopped'    'idle'         'successful-ok';
630                                                   when there are no jobs to be processed.
631
632        Access Rights: The authenticated user (see section 8.3) performing
633        this operation must be an operator or administrator of the Printer
634        object (see Sections 1 and 8.5).  Otherwise, the IPP Printer MUST
635        reject the operation and return:  'client-error-forbidden', 'client-
636        error-not-authenticated', or 'client-error-not-authorized' as
637        appropriate.
638
639        The Resume-Printer Request and Resume-Printer Response have the same
640        attribute groups and attributes as the Pause-Printer operation (see
641        sections 3.2.7.1 and 3.2.7.2).                 
642        """
643        raise ipp.errors.ServerErrorOperationNotSupported
644
645    @handler_for(ipp.OperationCodes.GET_PRINTER_ATTRIBUTES)
646    def get_printer_attributes(self, request, response):
647        """RFC 2911: 3.2.5 Get-Printer-Attributes Operation
648
649        This REQUIRED operation allows a client to request the values
650        of the attributes of a Printer object.
651       
652        In the request, the client supplies the set of Printer
653        attribute names and/or attribute group names in which the
654        requester is interested. In the response, the Printer object
655        returns a corresponding attribute set with the appropriate
656        attribute values filled in.
657
658        Request
659        -------
660
661        Group 1: Operation Attributes
662            REQUIRED 'attributes-charset'
663            REQUIRED 'attributes-natural-language'
664            REQUIRED 'printer-uri' (uri)
665            OPTIONAL 'requesting-user-name' (name(MAX))
666            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
667            OPTIONAL 'document-format' (mimeMediaType)
668           
669        Response
670        --------
671
672        Group 1: Operation Attributes
673            OPTIONAL 'status-message' (text(255))
674            OPTIONAL 'detailed-status-message' (text(MAX))
675            REQUIRED 'attributes-charset'
676            REQUIRED 'attributes-natural-language'
677        Group 2: Unsupported Attributes
678        Group 3: Printer Object Attributes
679
680        """
681
682        operation = request.attribute_groups[0]
683
684        printer_uri = None
685        requesting_user_name = None
686        requested_attributes = None
687        document_format = None
688
689        # requested printer uri
690        if 'printer-uri' not in operation:
691            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
692        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
693        if printer_uri not in self.printer.uris:
694            raise ipp.errors.ClientErrorAttributes(
695                str(operation['printer-uri']), operation['printer-uri'])
696
697        # optional attributes
698        if 'requesting-user-name' in operation:
699            user_name = verify_attribute(
700                operation['requesting-user-name'], ipp.RequestingUserName)[0]
701           
702        if 'requested-attributes' in operation:
703            requested_attributes = verify_attribute(
704                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
705
706        if 'document-format' in operation:
707            pass # XXX: todo
708
709        # get attributes from the printer and add to response
710        response.attribute_groups.append(ipp.AttributeGroup(
711            ipp.AttributeTags.PRINTER,
712            self.printer.get_printer_attributes(
713                requested_attributes=requested_attributes)))
714
715    @handler_for(ipp.OperationCodes.SET_PRINTER_ATTRIBUTES)
716    def set_printer_attributes(self, request, response):
717
718        raise ipp.errors.ServerErrorOperationNotSupported
719
720    ##### Job Commands
721
722    @handler_for(ipp.OperationCodes.CANCEL_JOB)
723    def cancel_job(self, request, response):
724        """3.3.3 Cancel-Job Operation
725
726        This REQUIRED operation allows a client to cancel a Print Job from
727        the time the job is created up to the time it is completed, canceled,
728        or aborted. Since a Job might already be printing by the time a
729        Cancel-Job is received, some media sheet pages might be printed
730        before the job is actually terminated.
731
732        The IPP object MUST accept or reject the request based on the job's
733        current state and transition the job to the indicated new state as
734        follows:
735
736        Current State       New State           Response
737        -----------------------------------------------------------------
738        pending             canceled            successful-ok
739        pending-held        canceled            successful-ok
740        processing          canceled            successful-ok
741        processing          processing          successful-ok               See Rule 1
742        processing          processing          client-error-not-possible   See Rule 2
743        processing-stopped  canceled            successful-ok
744        processing-stopped  processing-stopped  successful-ok               See Rule 1
745        processing-stopped  processing-stopped  client-error-not-possible   See Rule 2
746        completed           completed           client-error-not-possible
747        canceled            canceled            client-error-not-possible
748        aborted             aborted             client-error-not-possible
749
750        Rule 1: If the implementation requires some measurable time to
751        cancel the job in the 'processing' or 'processing-stopped' job
752        states, the IPP object MUST add the 'processing-to-stop-point'
753        value to the job's 'job-state-reasons' attribute and then
754        transition the job to the 'canceled' state when the processing
755        ceases (see section 4.3.8).
756
757        Rule 2: If the Job object already has the
758        'processing-to-stop-point' value in its 'job-state-reasons'
759        attribute, then the Printer object MUST reject a Cancel-Job
760        operation.
761
762        Access Rights: The authenticated user (see section 8.3)
763        performing this operation must either be the job owner or an
764        operator or administrator of the Printer object (see Sections
765        1 and 8.5).  Otherwise, the IPP object MUST reject the
766        operation and return: 'client-error-forbidden',
767        'client-error-not-authenticated', or
768        'client-error-not-authorized' as appropriate.
769
770        Request
771        -------
772
773        Group 1: Operation Attributes
774            REQUIRED 'attributes-charset'
775            REQUIRED 'attributes-natural-language'
776            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
777              -or-   'job-uri' (uri)
778            OPTIONAL 'requesting-user-name' (name(MAX))
779            OPTIONAL 'message' (text(127))
780           
781        Response
782        --------
783
784        Group 1: Operation Attributes
785            OPTIONAL 'status-message' (text(255))
786            OPTIONAL 'detailed-status-message' (text(MAX))
787            REQUIRED 'attributes-charset'
788            REQUIRED 'attributes-natural-language'
789        Group 2: Unsupported Attributes
790
791        """
792
793        operation = request.attribute_groups[0]
794
795        job_id = None
796        printer_uri = None
797        requesting_user_name = None
798        message = None
799
800        # required attributes
801        if 'job-id' in operation and 'printer-uri' in operation:
802            job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
803            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
804            if printer_uri not in self.printer.uris:
805                raise ipp.errors.ClientErrorAttributes(
806                    str(operation['printer-uri']), operation['printer-uri'])
807
808        elif 'job-uri' in operation:
809            job_uri = verify_attribute(operation['job-uri'], ipp.JobUri)[0]
810            job_id = int(job_uri.split("/")[-1])
811
812        if 'requesting-user-name' in operation:
813            requesting_user_name = verify_attribute(
814                operation['requesting-user-name'], ipp.RequestingUserName)[0]
815
816        try:
817            self.printer.cancel_job(job_id, requesting_user_name=requesting_user_name)
818        except InvalidJobException:
819            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
820
821    @handler_for(ipp.OperationCodes.SEND_DOCUMENT)
822    def send_document(self, request, response):
823        """3.3.1 Send-Document Operation
824       
825        This OPTIONAL operation allows a client to create a
826        multi-document Job object that is initially 'empty' (contains
827        no documents). In the Create-Job response, the Printer object
828        returns the Job object's URI (the 'job-uri' attribute) and the
829        Job object's 32-bit identifier (the 'job-id' attribute). For
830        each new document that the client desires to add, the client
831        uses a Send-Document operation. Each Send- Document Request
832        contains the entire stream of document data for one document.
833
834        If the Printer supports this operation but does not support
835        multiple documents per job, the Printer MUST reject subsequent
836        Send-Document operations supplied with data and return the
837        'server-error-multiple- document-jobs-not-supported'. However,
838        the Printer MUST accept the first document with a 'true' or
839        'false' value for the 'last-document' operation attribute (see
840        below), so that clients MAY always submit one document jobs
841        with a 'false' value for 'last-document' in the first
842        Send-Document and a 'true' for 'last-document' in the second
843        Send-Document (with no data).
844       
845        Since the Create-Job and the send operations (Send-Document or
846        Send- URI operations) that follow could occur over an
847        arbitrarily long period of time for a particular job, a client
848        MUST send another send operation within an IPP Printer defined
849        minimum time interval after the receipt of the previous
850        request for the job. If a Printer object supports the
851        Create-Job and Send-Document operations, the Printer object
852        MUST support the 'multiple-operation-time-out' attribute (see
853        section 4.4.31). This attribute indicates the minimum number
854        of seconds the Printer object will wait for the next send
855        operation before taking some recovery action.
856
857        An IPP object MUST recover from an errant client that does not
858        supply a send operation, sometime after the minimum time
859        interval specified by the Printer object's
860        'multiple-operation-time-out' attribute.
861
862        Access Rights: The authenticated user (see section 8.3)
863        performing this operation must either be the job owner (as
864        determined in the Create-Job operation) or an operator or
865        administrator of the Printer object (see Sections 1 and
866        8.5). Otherwise, the IPP object MUST reject the operation and
867        return: 'client-error-forbidden', 'client-
868        error-not-authenticated', or 'client-error-not-authorized' as
869        appropriate.
870
871        Request
872        -------
873
874        Group 1: Operation Attributes
875            REQUIRED 'attributes-charset'
876            REQUIRED 'attributes-natural-language'
877            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
878              -or-   'job-uri' (uri)
879            OPTIONAL 'requesting-user-name' (name(MAX))
880            OPTIONAL 'document-name' (name(MAX))
881            OPTIONAL 'compression' (type3 keyword)
882            OPTIONAL 'document-format' (mimeMediaType)
883            OPTIONAL 'document-natural-language' (naturalLanguage)
884            OPTIONAL 'last-document' (boolean)
885        Group 2: Document Content
886           
887        Response
888        --------
889
890        Group 1: Operation Attributes
891            OPTIONAL 'status-message' (text(255))
892            OPTIONAL 'detailed-status-message' (text(MAX))
893            REQUIRED 'attributes-charset'
894            REQUIRED 'attributes-natural-language'
895        Group 2: Unsupported Attributes
896        Group 3: Job Object Attributes
897            REQUIRED 'job-uri' (uri)
898            REQUIRED 'job-id' (integer(1:MAX))
899            REQUIRED 'job-state' (type1 enum)
900            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
901            OPTIONAL 'job-state-message' (text(MAX))
902            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
903
904        """
905       
906        operation = request.attribute_groups[0]
907
908        job_id = None
909        printer_uri = None
910        requesting_user_name = None
911        document_name = None
912        compression = None
913        document_format = None
914        document_natural_language = None
915        last_document = None
916
917        # required attributes
918        if 'job-id' not in operation:
919            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
920        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
921
922        if 'last-document' not in operation:
923            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
924        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
925        if not last_document:
926            raise ipp.errors.ServerErrorMultipleJobsNotSupported
927
928        # optional attributes
929        if 'printer-uri' in operation:
930            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
931            if printer_uri not in self.printer.uris:
932                raise ipp.errors.ClientErrorAttributes(
933                    str(operation['printer-uri']), operation['printer-uri'])
934
935        if 'requesting-user-name' in operation:
936            user_name = verify_attribute(
937                operation['requesting-user-name'], ipp.RequestingUserName)[0]
938
939        if 'document-name' in operation:
940            document_name = verify_attribute(
941                operation['document-name'], ipp.DocumentName)[0]
942
943        if 'compression' in operation:
944            compression = verify_attribute(
945                operation['compression'], ipp.Compression)[0]
946
947        if 'document-format' in operation:
948            document_format = verify_attribute(
949                operation['document-format'], ipp.DocumentFormat)[0]
950
951        if 'document-natural-language' in operation:
952            document_natural_language = verify_attribute(
953                operation['document_natural_language'],
954                ipp.DocumentNaturalLanguage)[0]
955
956        try:
957            self.printer.send_document(
958                job_id,
959                request.data,
960                document_name=document_name,
961                document_format=document_format,
962                document_natural_language=document_natural_language,
963                requesting_user_name=user_name,
964                compression=compression,
965                last_document=last_document)
966            attrs = self.printer.get_job_attributes(job_id)
967        except InvalidJobException:
968            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
969
970        response.attribute_groups.append(ipp.AttributeGroup(
971            ipp.AttributeTags.JOB, attrs))
972
973    @handler_for(ipp.OperationCodes.SEND_URI)
974    def send_uri(self, request, response):
975
976        """3.2.2 Send URI
977
978        This OPTIONAL operation is identical to the Send-Document
979        operation (see section 3.3.1) except that a client MUST supply
980        a URI reference ('document-uri' operation attribute) rather
981        than the document data itself.  If a Printer object supports
982        this operation, clients can use both Send-URI or Send-Document
983        operations to add new documents to an existing multi-document
984        Job object.  However, if a client needs to indicate that the
985        previous Send-URI or Send-Document was the last document, the
986        client MUST use the Send-Document operation with no document
987        data and the 'last-document' flag set to 'true' (rather than
988        using a Send-URI operation with no 'document-uri' operation
989        attribute).
990
991        If a Printer object supports this operation, it MUST also
992        support the Print-URI operation (see section 3.2.2).
993
994        The Printer object MUST validate the syntax and URI scheme of
995        the supplied URI before returning a response, just as in the
996        Print-URI operation.  The IPP Printer MAY validate the
997        accessibility of the document as part of the operation or
998        subsequently (see section 3.2.2).
999
1000        Request
1001        -------
1002
1003        Group 1: Operation Attributes
1004            REQUIRED 'attributes-charset'
1005            REQUIRED 'attributes-natural-language'
1006            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
1007            REQUIRED 'document-uri' (uri)
1008            OPTIONAL 'job-uri' (uri)
1009            OPTIONAL 'requesting-user-name' (name(MAX))
1010            OPTIONAL 'document-name' (name(MAX))
1011            OPTIONAL 'compression' (type3 keyword)
1012            OPTIONAL 'document-format' (mimeMediaType)
1013            OPTIONAL 'document-natural-language' (naturalLanguage)
1014           
1015        Response
1016        --------
1017
1018        Group 1: Operation Attributes
1019            OPTIONAL 'status-message' (text(255))
1020            OPTIONAL 'detailed-status-message' (text(MAX))
1021            REQUIRED 'attributes-charset'
1022            REQUIRED 'attributes-natural-language'
1023        Group 2: Unsupported Attributes
1024        Group 3: Job Object Attributes
1025            REQUIRED 'job-uri' (uri)
1026            REQUIRED 'job-id' (integer(1:MAX))
1027            REQUIRED 'job-state' (type1 enum)
1028            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
1029            OPTIONAL 'job-state-message' (text(MAX))
1030            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
1031
1032        """
1033       
1034        operation = request.attribute_groups[0]
1035
1036        job_id = None
1037        printer_uri = None
1038        requesting_user_name = None
1039        document_name = None
1040        compression = None
1041        document_format = None
1042        document_natural_language = None
1043        last_document = None
1044
1045        # required attributes
1046        if 'job-id' not in operation:
1047            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
1048        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
1049
1050        if 'last-document' not in operation:
1051            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
1052        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
1053
1054        if 'document-uri' not in operation:
1055            raise ipp.errors.ClientErrorBadRequest("Missing 'document-uri' attribute")
1056        document_uri = verify_attribute(operation['document-uri'], ipp.DocumentUri)[0]
1057        if not last_document:
1058            raise ipp.errors.ServerErrorMultipleJobsNotSupported
1059
1060        # optional attributes
1061        if 'printer-uri' in operation:
1062            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
1063            if printer_uri not in self.printer.uris:
1064                raise ipp.errors.ClientErrorAttributes(
1065                    str(operation['printer-uri']), operation['printer-uri'])
1066
1067        if 'requesting-user-name' in operation:
1068            user_name = verify_attribute(
1069                operation['requesting-user-name'], ipp.RequestingUserName)[0]
1070
1071        if 'document-name' in operation:
1072            document_name = verify_attribute(
1073                operation['document-name'], ipp.DocumentName)[0]
1074
1075        if 'compression' in operation:
1076            compression = verify_attribute(
1077                operation['compression'], ipp.Compression)[0]
1078
1079        if 'document-format' in operation:
1080            document_format = verify_attribute(
1081                operation['document-format'], ipp.DocumentFormat)[0]
1082
1083        if 'document-natural-language' in operation:
1084            document_natural_language = verify_attribute(
1085                operation['document_natural_language'],
1086                ipp.DocumentNaturalLanguage)[0]
1087
1088        try:
1089            self.printer.send_uri(
1090                job_id,
1091                document_uri,
1092                document_name=document_name,
1093                document_format=document_format,
1094                document_natural_language=document_natural_language,
1095                requesting_user_name=user_name,
1096                compression=compression,
1097                last_document=last_document)
1098            attrs = self.printer.get_job_attributes(job_id)
1099        except InvalidJobException:
1100            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
1101
1102        response.attribute_groups.append(ipp.AttributeGroup(
1103            ipp.AttributeTags.JOB, attrs))
1104
1105    @handler_for(ipp.OperationCodes.GET_JOB_ATTRIBUTES)
1106    def get_job_attributes(self, request, response):
1107        """3.3.4 Get-Job-Attributes Operation
1108
1109        This REQUIRED operation allows a client to request the values
1110        of attributes of a Job object and it is almost identical to
1111        the Get- Printer-Attributes operation (see section 3.2.5). The
1112        only differences are that the operation is directed at a Job
1113        object rather than a Printer object, there is no
1114        'document-format' operation attribute used when querying a Job
1115        object, and the returned attribute group is a set of Job
1116        object attributes rather than a set of Printer object
1117        attributes.
1118
1119        For Jobs, the possible names of attribute groups are:
1120          - 'job-template': the subset of the Job Template attributes
1121            that apply to a Job object (the first column of the table
1122            in Section 4.2) that the implementation supports for Job
1123            objects.
1124          - 'job-description': the subset of the Job Description
1125            attributes specified in Section 4.3 that the
1126            implementation supports for Job objects.
1127          - 'all': the special group 'all' that includes all
1128            attributes that the implementation supports for Job
1129            objects.
1130
1131        Since a client MAY request specific attributes or named
1132        groups, there is a potential that there is some overlap. For
1133        example, if a client requests, 'job-name' and
1134        'job-description', the client is actually requesting the
1135        'job-name' attribute once by naming it explicitly, and once by
1136        inclusion in the 'job-description' group. In such cases, the
1137        Printer object NEED NOT return the attribute only once in the
1138        response even if it is requested multiple times. The client
1139        SHOULD NOT request the same attribute in multiple ways.
1140
1141        It is NOT REQUIRED that a Job object support all attributes
1142        belonging to a group (since some attributes are
1143        OPTIONAL). However it is REQUIRED that each Job object support
1144        all these group names.
1145
1146        Request
1147        -------
1148
1149        Group 1: Operation Attributes
1150            REQUIRED 'attributes-charset'
1151            REQUIRED 'attributes-natural-language'
1152            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
1153              -or-   'job-uri' (uri)
1154            OPTIONAL 'requesting-user-name' (name(MAX))
1155            OPTIONAL 'requested-attributes' (1setOf keyword)
1156           
1157        Response
1158        --------
1159
1160        Group 1: Operation Attributes
1161            OPTIONAL 'status-message' (text(255))
1162            OPTIONAL 'detailed-status-message' (text(MAX))
1163            REQUIRED 'attributes-charset'
1164            REQUIRED 'attributes-natural-language'
1165        Group 2: Unsupported Attributes
1166        Group 3: Job Object Attributes
1167
1168        """
1169       
1170        operation = request.attribute_groups[0]
1171
1172        job_id = None
1173        printer_uri = None
1174        requesting_user_name = None
1175        requested_attributes = None
1176
1177        # required attributes
1178        if 'job-id' not in operation:
1179            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
1180        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
1181
1182        # optional attributes
1183        if 'printer-uri' in operation:
1184            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
1185            if printer_uri not in self.printer.uris:
1186                raise ipp.errors.ClientErrorAttributes(
1187                    str(operation['printer-uri']), operation['printer-uri'])
1188
1189        # optional attributes
1190        if 'requesting-user-name' in operation:
1191            user_name = verify_attribute(
1192                operation['requesting-user-name'], ipp.RequestingUserName)[0]
1193           
1194        if 'requested-attributes' in operation:
1195            requested_attributes = verify_attribute(
1196                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
1197
1198        # get the job attributes and add them to the response
1199        try:
1200            attrs = self.printer.get_job_attributes(
1201                job_id,
1202                requested_attributes=requested_attributes)
1203        except InvalidJobException:
1204            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
1205
1206        response.attribute_groups.append(ipp.AttributeGroup(
1207            ipp.AttributeTags.JOB, attrs))
1208
1209    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
1210    def set_job_attributes(self, request, response):
1211       
1212        raise ipp.errors.ServerErrorOperationNotSupported
1213
1214    @handler_for(ipp.OperationCodes.RESTART_JOB)
1215    def restart_job(self, request, response):
1216        raise ipp.errors.ServerErrorOperationNotSupported
1217
1218    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
1219    def promote_job(self, request, response):
1220        raise ipp.errors.ServerErrorOperationNotSupported
1221
1222    ##### CUPS Specific Commands
1223
1224    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
1225    def cups_get_document(self, request, response):
1226        raise ipp.errors.ServerErrorOperationNotSupported
1227
1228    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
1229    def cups_get_default(self, request, response):
1230        """The CUPS-Get-Default operation (0x4001) returns the default
1231        printer URI and attributes.
1232
1233        Request
1234        -------
1235
1236        Group 1: Operation Attributes
1237            REQUIRED 'attributes-charset'
1238            REQUIRED 'attributes-natural-language'
1239            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
1240
1241        Response
1242        --------
1243
1244        Group 1: Operation Attributes
1245            OPTIONAL 'status-message' (text(255))
1246            REQUIRED 'attributes-charset'
1247            REQUIRED 'attributes-natural-language'
1248        Group 2: Printer Object Attributes
1249
1250        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
1251
1252        """
1253
1254        operation = request.attribute_groups[0]
1255        requested_attributes = None
1256       
1257        if 'requested-attributes' in operation:
1258            requested_attributes = verify_attribute(
1259                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
1260
1261        # get attributes from the printer and add to response
1262        attrs = self.printer.get_printer_attributes(
1263            requested_attributes=requested_attributes)
1264        response.attribute_groups.append(ipp.AttributeGroup(
1265            ipp.AttributeTags.PRINTER, attrs))
1266
1267    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
1268    def cups_get_printers(self, request, response):
1269        """The CUPS-Get-Printers operation (0x4002) returns the
1270        printer attributes for every printer known to the system. This
1271        may include printers that are not served directly by the
1272        server.
1273
1274        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
1275           
1276        """
1277
1278        # get attributes from the printer and add to response
1279        response.attribute_groups.append(ipp.AttributeGroup(
1280            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
1281
1282    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
1283    def cups_get_classes(self, request, response):
1284        """The CUPS-Get-Classes operation (0x4005) returns the printer
1285        attributes for every printer class known to the system. This
1286        may include printer classes that are not served directly by
1287        the server.
1288
1289        Request
1290        -------
1291
1292        Group 1: Operation Attributes
1293            REQUIRED 'attributes-charset'
1294            REQUIRED 'attributes-natural-language'
1295            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
1296            OPTIONAL 'limit' (integer (1:MAX))
1297            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
1298            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
1299            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
1300            OPTIONAL 'requested-attributes' (1setOf keyword)
1301            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
1302            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
1303
1304        Response
1305        --------
1306
1307        Group 1: Operation Attributes
1308            OPTIONAL 'status-message' (text(255))
1309            REQUIRED 'attributes-charset'
1310            REQUIRED 'attributes-natural-language'
1311        Group 2: Printer Class Object Attributes
1312
1313        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
1314
1315        """
1316
1317        raise ipp.errors.ServerErrorOperationNotSupported
1318
Note: See TracBrowser for help on using the repository browser.