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

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

Some IPP documentation in printer.py

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