source: server/lib/gutenbach/server/requests.py @ f70792f

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

Merge branch 'no-cups' of github.com:jhamrick/gutenbach into no-cups

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