source: server/lib/gutenbach/server/requests.py @ 6c60b2e

no-cups
Last change on this file since 6c60b2e was 6c60b2e, checked in by Daniel Cooper <danny@…>, 12 years ago

completed print_job function definition in request.py

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