source: server/lib/gutenbach/server/requests.py @ 75928fe

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

Fix whitespace issues in requests.py

  • Property mode set to 100644
File size: 58.3 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        """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        """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        """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        The IPP Printer MAY validate the accessibility of the document
419        as part of the operation or subsequently.  If the Printer
420        determines an accessibility problem before returning an
421        operation response, it rejects the request and returns the
422        'client-error-document-access- error' status code.  The
423        Printer MAY also return a specific document access error code
424        using the 'document-access-error' operation attribute (see
425        section 3.1.6.4).
426
427        If the Printer determines this document accessibility problem
428        after accepting the request and returning an operation
429        response with one of the successful status codes, the Printer
430        adds the 'document-access- error' value to the job's
431        'job-state-reasons' attribute and MAY populate the job's
432        'job-document-access-errors' Job Description attribute (see
433        section 4.3.11).  See The Implementer's Guide [IPP- IIG] for
434        suggested additional checks.
435                                                                             
436        If the Printer object supports this operation, it MUST support
437        the 'reference-uri-schemes-supported' Printer attribute (see
438        section 4.4.27).
439
440        It is up to the IPP object to interpret the URI and
441        subsequently 'pull' the document from the source referenced by
442        the URI string.
443
444        Request
445        -------
446        Group 1: Operation Attributes
447            REQUIRED 'attributes-charset'
448            REQUIRED 'attributes-natural-language'
449            REQUIRED 'printer-uri' (uri)
450            REQUIRED 'document-uri' (uri)
451            OPTIONAL 'requesting-user-name' (name(MAX))
452            OPTIONAL 'job-name' (name(MAX))
453            OPTIONAL 'ipp-attribute-fidelity' (boolean)
454            OPTIONAL 'document-name' (name(MAX))
455            OPTIONAL 'compression' (type3 keyword)
456            OPTIONAL 'document-format' (mimeMediaType)
457            OPTIONAL 'document-natural-language' (naturalLanguage)
458            OPTIONAL 'job-k-octets' (integer(0:MAX))
459            OPTIONAL 'job-impressions' (integer(0:MAX))
460            OPTIONAL 'job-media-sheets' (integer(0:MAX))
461        Group 2: Job Template Attributes
462
463        Response
464        --------
465        Group 1: Operation Attributes
466            OPTIONAL 'status-message' (text(255))
467            OPTIONAL 'detailed-status-message' (text(MAX))
468            REQUIRED 'attributes-charset'
469            REQUIRED 'attributes-natural-language'
470        Group 2: Unsupported Attributes
471        Group 3: Job Object Attributes
472            REQUIRED 'job-uri' (uri)
473            REQUIRED 'job-id' (integer(1:MAX))
474            REQUIRED 'job-state' (type1 enum)
475            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
476            OPTIONAL 'job-state-message' (text(MAX))
477            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
478
479        """
480
481        operation = request.attribute_groups[0]
482        document = request.data       
483        user_name = None
484        job_name = None
485        job_k_octets = None
486        document_format = None
487        document_natural_language = None
488        compression = None
489        last_document = None
490
491        # requested printer uri
492        if 'printer-uri' not in operation:
493            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
494        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
495        if printer_uri not in self.printer.uris:
496            raise ipp.errors.ClientErrorAttributes(
497                str(operation['printer-uri']), operation['printer-uri'])
498
499        if 'requesting-user-name' in operation:
500            user_name = verify_attribute(
501                operation['requesting-user-name'], ipp.RequestingUserName)[0]
502
503        if 'job-name' in operation:
504            job_name = verify_attribute(
505                operation['job-name'], ipp.JobName)[0]
506
507        if 'job-k-octets' in operation:
508            job_k_octets = verify_attribute(
509                operation['job-k-octets'], ipp.JobKOctets)[0]
510
511        if 'ipp-attribute-fidelity' in operation:
512            pass # don't care
513        if 'job-impressions' in operation:
514            pass # don't care
515        if 'job-media-sheets' in operation:
516            pass # don't care
517
518        # get attributes from the printer and add to response
519        try:
520            job_id = self.printer.print_uri(
521                document,
522                document_name=document_name,
523                document_format=document_format,
524                document_natural_language=document_natural_language,
525                requesting_user_name=user_name,
526                compression=compression,
527                job_name=job_name,
528                job_k_octets=job_k_octets)
529
530        except InvalidJobException:
531            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
532
533        attrs = self.printer.get_job_attributes(job_id)
534     
535        # Actually append the attributes we pulled
536        response.attribute_groups.append(ipp.AttributeGroup(
537            ipp.AttributeTags.JOB, attrs))
538
539    @handler_for(ipp.OperationCodes.CREATE_JOB)
540    def create_job(self, request, response):
541        """RFC 2911: 3.2.4 Create-Job Operation
542
543        This OPTIONAL operation is similar to the Print-Job operation
544        (section 3.2.1) except that in the Create-Job request, a
545        client does not supply document data or any reference to
546        document data. Also, the client does not supply any of the
547        'document-name', 'document- format', 'compression', or
548        'document-natural-language' operation attributes. This
549        operation is followed by one or more Send-Document or Send-URI
550        operations. In each of those operation requests, the client
551        OPTIONALLY supplies the 'document-name', 'document-format',
552        and 'document-natural-language' attributes for each document
553        in the multi-document Job object.
554
555        Request
556        -------
557        Group 1: Operation Attributes
558            REQUIRED 'attributes-charset'
559            REQUIRED 'attributes-natural-language'
560            REQUIRED 'printer-uri' (uri)
561            OPTIONAL 'requesting-user-name' (name(MAX))
562            OPTIONAL 'job-name' (name(MAX))
563            OPTIONAL 'ipp-attribute-fidelity' (boolean)
564            OPTIONAL 'job-k-octets' (integer(0:MAX))
565            OPTIONAL 'job-impressions' (integer(0:MAX))
566            OPTIONAL 'job-media-sheets' (integer(0:MAX))
567        Group 2: Job Template Attributes
568
569        Response
570        --------
571        Group 1: Operation Attributes
572            OPTIONAL 'status-message' (text(255))
573            OPTIONAL 'detailed-status-message' (text(MAX))
574            REQUIRED 'attributes-charset'
575            REQUIRED 'attributes-natural-language'
576        Group 2: Unsupported Attributes
577        Group 3: Job Object Attributes
578            REQUIRED 'job-uri' (uri)
579            REQUIRED 'job-id' (integer(1:MAX))
580            REQUIRED 'job-state' (type1 enum)
581            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
582            OPTIONAL 'job-state-message' (text(MAX))
583            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
584       
585        """
586
587        operation = request.attribute_groups[0]
588
589        printer_uri = None
590        requesting_user_name = None
591        job_name = None
592        ipp_attribute_fidelity=None
593        job_k_octets = None
594
595        # requested printer uri
596        if 'printer-uri' not in operation:
597            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
598        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
599        if printer_uri not in self.printer.uris:
600            raise ipp.errors.ClientErrorAttributes(
601                str(operation['printer-uri']), operation['printer-uri'])
602
603        if 'requesting-user-name' in operation:
604            user_name = verify_attribute(
605                operation['requesting-user-name'], ipp.RequestingUserName)[0]
606
607        if 'job-name' in operation:
608            job_name = verify_attribute(
609                operation['job-name'], ipp.JobName)[0]
610
611        if 'job-k-octets' in operation:
612            job_k_octets = verify_attribute(
613                operation['job-k-octets'], ipp.JobKOctets)[0]
614
615        if 'ipp-attribute-fidelity' in operation:
616            pass # don't care
617        if 'job-impressions' in operation:
618            pass # don't care
619        if 'job-media-sheets' in operation:
620            pass # don't care
621
622        # get attributes from the printer and add to response
623        job_id = self.printer.create_job(
624            requesting_user_name=requesting_user_name,
625            job_name=job_name,
626            job_k_octets=job_k_octets)
627        attrs = self.printer.get_job_attributes(job_id)
628        response.attribute_groups.append(ipp.AttributeGroup(
629            ipp.AttributeTags.JOB, attrs))
630   
631    @handler_for(ipp.OperationCodes.PAUSE_PRINTER)
632    def pause_printer(self, request, response):
633        """3.2.7 Pause-Printer Operation
634
635        This OPTIONAL operation allows a client to stop the Printer
636        object from scheduling jobs on all its devices.  Depending on
637        implementation, the Pause-Printer operation MAY also stop the
638        Printer from processing the current job or jobs.  Any job that
639        is currently being printed is either stopped as soon as the
640        implementation permits or is completed, depending on
641        implementation.  The Printer object MUST still accept create
642        operations to create new jobs, but MUST prevent any jobs from
643        entering the 'processing' state.
644
645        If the Pause-Printer operation is supported, then the
646        Resume-Printer operation MUST be supported, and vice-versa.
647
648        The IPP Printer MUST accept the request in any state and
649        transition the Printer to the indicated new 'printer-state'
650        before returning as follows:
651
652        Current       New         Reasons             Reponse
653        --------------------------------------------------------------
654        'idle'       'stopped'    'paused'            'successful-ok'
655        'processing' 'processing' 'moving-to-paused'  'successful-ok'
656        'processing' 'stopped'    'paused'            'successful-ok'
657        'stopped'    'stopped'    'paused'            'successful-ok'
658
659
660        Request
661        -------
662        Group 1: Operation Attributes
663            REQUIRED 'attributes-charset'
664            REQUIRED 'attributes-natural-language'
665            REQUIRED 'printer-uri' (uri)
666            OPTIONAL 'requesting-user-name' (name(MAX))
667
668        Response
669        --------
670        Group 1: Operation Attributes
671            OPTIONAL 'status-message' (text(255))
672            OPTIONAL 'detailed-status-message' (text(MAX))
673            REQUIRED 'attributes-charset'
674            REQUIRED 'attributes-natural-language'
675        Group 2: Unsupported Attributes
676   
677        """
678
679        operation = request.attribute_groups[0]
680        printer_uri = None
681        user_name   = None
682        if 'printer-uri' not in operation:
683            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
684        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
685        if printer_uri not in self.printer.uris:
686            raise ipp.errors.ClientErrorAttributes(
687                str(operation['printer-uri']), operation['printer-uri'])
688
689        if 'requesting-user-name' in operation:
690            user_name = verify_attribute(
691                operation['requesting-user-name'], ipp.RequestingUserName)[0]
692        self.printer.pause_printer()
693
694    @handler_for(ipp.OperationCodes.RESUME_PRINTER)
695    def resume_printer(self, request, response):
696        """3.2.8 Resume-Printer Operation
697
698        This operation allows a client to resume the Printer object
699        scheduling jobs on all its devices.  The Printer object MUST
700        remove the 'paused' and 'moving-to-paused' values from the
701        Printer object's 'printer-state-reasons' attribute, if
702        present.  If there are no other reasons to keep a device
703        paused (such as media-jam), the IPP Printer is free to
704        transition itself to the 'processing' or 'idle' states,
705        depending on whether there are jobs to be processed or not,
706        respectively, and the device(s) resume processing jobs.
707
708        If the Pause-Printer operation is supported, then the
709        Resume-Printer operation MUST be supported, and vice-versa.
710
711        The IPP Printer removes the 'printer-stopped' value from any
712        job's 'job-state-reasons' attributes contained in that
713        Printer.
714
715        The IPP Printer MUST accept the request in any state,
716        transition the Printer object to the indicated new state as
717        follows:
718
719        Current       New           Response
720        ---------------------------------------------
721        'idle'       'idle'         'successful-ok'
722        'processing' 'processing'   'successful-ok'
723        'stopped'    'processing'   'successful-ok'
724        'stopped'    'idle'         'successful-ok'
725
726        Request
727        -------
728        Group 1: Operation Attributes
729            REQUIRED 'attributes-charset'
730            REQUIRED 'attributes-natural-language'
731            REQUIRED 'printer-uri' (uri)
732            OPTIONAL 'requesting-user-name' (name(MAX))
733
734        Response
735        --------
736        Group 1: Operation Attributes
737            OPTIONAL 'status-message' (text(255))
738            OPTIONAL 'detailed-status-message' (text(MAX))
739            REQUIRED 'attributes-charset'
740            REQUIRED 'attributes-natural-language'
741        Group 2: Unsupported Attributes
742
743        """
744       
745        operation = request.attribute_groups[0]
746        printer_uri = None
747        user_name   = None
748        if 'printer-uri' not in operation:
749            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
750        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
751        if printer_uri not in self.printer.uris:
752            raise ipp.errors.ClientErrorAttributes(
753                str(operation['printer-uri']), operation['printer-uri'])
754
755        if 'requesting-user-name' in operation:
756            user_name = verify_attribute(
757                operation['requesting-user-name'], ipp.RequestingUserName)[0]
758
759        self.printer.resume_printer()
760
761    @handler_for(ipp.OperationCodes.GET_PRINTER_ATTRIBUTES)
762    def get_printer_attributes(self, request, response):
763        """RFC 2911: 3.2.5 Get-Printer-Attributes Operation
764
765        This REQUIRED operation allows a client to request the values
766        of the attributes of a Printer object.
767       
768        In the request, the client supplies the set of Printer
769        attribute names and/or attribute group names in which the
770        requester is interested. In the response, the Printer object
771        returns a corresponding attribute set with the appropriate
772        attribute values filled in.
773
774        Request
775        -------
776
777        Group 1: Operation Attributes
778            REQUIRED 'attributes-charset'
779            REQUIRED 'attributes-natural-language'
780            REQUIRED 'printer-uri' (uri)
781            OPTIONAL 'requesting-user-name' (name(MAX))
782            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
783            OPTIONAL 'document-format' (mimeMediaType)
784           
785        Response
786        --------
787
788        Group 1: Operation Attributes
789            OPTIONAL 'status-message' (text(255))
790            OPTIONAL 'detailed-status-message' (text(MAX))
791            REQUIRED 'attributes-charset'
792            REQUIRED 'attributes-natural-language'
793        Group 2: Unsupported Attributes
794        Group 3: Printer Object Attributes
795
796        """
797
798        operation = request.attribute_groups[0]
799
800        printer_uri = None
801        requesting_user_name = None
802        requested_attributes = None
803        document_format = None
804
805        # requested printer uri
806        if 'printer-uri' not in operation:
807            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
808        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
809        if printer_uri not in self.printer.uris:
810            raise ipp.errors.ClientErrorAttributes(
811                str(operation['printer-uri']), operation['printer-uri'])
812
813        # optional attributes
814        if 'requesting-user-name' in operation:
815            user_name = verify_attribute(
816                operation['requesting-user-name'], ipp.RequestingUserName)[0]
817           
818        if 'requested-attributes' in operation:
819            requested_attributes = verify_attribute(
820                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
821
822        if 'document-format' in operation:
823            pass # XXX: todo
824
825        # get attributes from the printer and add to response
826        response.attribute_groups.append(ipp.AttributeGroup(
827            ipp.AttributeTags.PRINTER,
828            self.printer.get_printer_attributes(
829                requested_attributes=requested_attributes)))
830
831    @handler_for(ipp.OperationCodes.SET_PRINTER_ATTRIBUTES)
832    def set_printer_attributes(self, request, response):
833
834        raise ipp.errors.ServerErrorOperationNotSupported
835
836    ##### Job Commands
837
838    @handler_for(ipp.OperationCodes.CANCEL_JOB)
839    def cancel_job(self, request, response):
840        """3.3.3 Cancel-Job Operation
841
842        This REQUIRED operation allows a client to cancel a Print Job
843        from the time the job is created up to the time it is
844        completed, canceled, or aborted. Since a Job might already be
845        printing by the time a Cancel-Job is received, some media
846        sheet pages might be printed before the job is actually
847        terminated.
848
849        The IPP object MUST accept or reject the request based on the
850        job's current state and transition the job to the indicated
851        new state as follows:
852
853        Current State       New State           Response
854        -----------------------------------------------------------------
855        pending             canceled            successful-ok
856        pending-held        canceled            successful-ok
857        processing          canceled            successful-ok
858        processing          processing          successful-ok               See Rule 1
859        processing          processing          client-error-not-possible   See Rule 2
860        processing-stopped  canceled            successful-ok
861        processing-stopped  processing-stopped  successful-ok               See Rule 1
862        processing-stopped  processing-stopped  client-error-not-possible   See Rule 2
863        completed           completed           client-error-not-possible
864        canceled            canceled            client-error-not-possible
865        aborted             aborted             client-error-not-possible
866
867        Rule 1: If the implementation requires some measurable time to
868        cancel the job in the 'processing' or 'processing-stopped' job
869        states, the IPP object MUST add the 'processing-to-stop-point'
870        value to the job's 'job-state-reasons' attribute and then
871        transition the job to the 'canceled' state when the processing
872        ceases (see section 4.3.8).
873
874        Rule 2: If the Job object already has the
875        'processing-to-stop-point' value in its 'job-state-reasons'
876        attribute, then the Printer object MUST reject a Cancel-Job
877        operation.
878
879        Access Rights: The authenticated user (see section 8.3)
880        performing this operation must either be the job owner or an
881        operator or administrator of the Printer object (see Sections
882        1 and 8.5).  Otherwise, the IPP object MUST reject the
883        operation and return: 'client-error-forbidden',
884        'client-error-not-authenticated', or
885        'client-error-not-authorized' as appropriate.
886
887        Request
888        -------
889
890        Group 1: Operation Attributes
891            REQUIRED 'attributes-charset'
892            REQUIRED 'attributes-natural-language'
893            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
894              -or-   'job-uri' (uri)
895            OPTIONAL 'requesting-user-name' (name(MAX))
896            OPTIONAL 'message' (text(127))
897           
898        Response
899        --------
900
901        Group 1: Operation Attributes
902            OPTIONAL 'status-message' (text(255))
903            OPTIONAL 'detailed-status-message' (text(MAX))
904            REQUIRED 'attributes-charset'
905            REQUIRED 'attributes-natural-language'
906        Group 2: Unsupported Attributes
907
908        """
909
910        operation = request.attribute_groups[0]
911
912        job_id = None
913        printer_uri = None
914        requesting_user_name = None
915        message = None
916
917        # required attributes
918        if 'job-id' in operation and 'printer-uri' in operation:
919            job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
920            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
921            if printer_uri not in self.printer.uris:
922                raise ipp.errors.ClientErrorAttributes(
923                    str(operation['printer-uri']), operation['printer-uri'])
924
925        elif 'job-uri' in operation:
926            job_uri = verify_attribute(operation['job-uri'], ipp.JobUri)[0]
927            job_id = int(job_uri.split("/")[-1])
928
929        if 'requesting-user-name' in operation:
930            requesting_user_name = verify_attribute(
931                operation['requesting-user-name'], ipp.RequestingUserName)[0]
932
933        try:
934            self.printer.cancel_job(job_id, requesting_user_name=requesting_user_name)
935        except InvalidJobException:
936            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
937
938    @handler_for(ipp.OperationCodes.SEND_DOCUMENT)
939    def send_document(self, request, response):
940        """3.3.1 Send-Document Operation
941       
942        This OPTIONAL operation allows a client to create a
943        multi-document Job object that is initially 'empty' (contains
944        no documents). In the Create-Job response, the Printer object
945        returns the Job object's URI (the 'job-uri' attribute) and the
946        Job object's 32-bit identifier (the 'job-id' attribute). For
947        each new document that the client desires to add, the client
948        uses a Send-Document operation. Each Send- Document Request
949        contains the entire stream of document data for one document.
950
951        If the Printer supports this operation but does not support
952        multiple documents per job, the Printer MUST reject subsequent
953        Send-Document operations supplied with data and return the
954        'server-error-multiple- document-jobs-not-supported'. However,
955        the Printer MUST accept the first document with a 'true' or
956        'false' value for the 'last-document' operation attribute (see
957        below), so that clients MAY always submit one document jobs
958        with a 'false' value for 'last-document' in the first
959        Send-Document and a 'true' for 'last-document' in the second
960        Send-Document (with no data).
961       
962        Since the Create-Job and the send operations (Send-Document or
963        Send- URI operations) that follow could occur over an
964        arbitrarily long period of time for a particular job, a client
965        MUST send another send operation within an IPP Printer defined
966        minimum time interval after the receipt of the previous
967        request for the job. If a Printer object supports the
968        Create-Job and Send-Document operations, the Printer object
969        MUST support the 'multiple-operation-time-out' attribute (see
970        section 4.4.31). This attribute indicates the minimum number
971        of seconds the Printer object will wait for the next send
972        operation before taking some recovery action.
973
974        An IPP object MUST recover from an errant client that does not
975        supply a send operation, sometime after the minimum time
976        interval specified by the Printer object's
977        'multiple-operation-time-out' attribute.
978
979        Access Rights: The authenticated user (see section 8.3)
980        performing this operation must either be the job owner (as
981        determined in the Create-Job operation) or an operator or
982        administrator of the Printer object (see Sections 1 and
983        8.5). Otherwise, the IPP object MUST reject the operation and
984        return: 'client-error-forbidden', 'client-
985        error-not-authenticated', or 'client-error-not-authorized' as
986        appropriate.
987
988        Request
989        -------
990
991        Group 1: Operation Attributes
992            REQUIRED 'attributes-charset'
993            REQUIRED 'attributes-natural-language'
994            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
995              -or-   'job-uri' (uri)
996            OPTIONAL 'requesting-user-name' (name(MAX))
997            OPTIONAL 'document-name' (name(MAX))
998            OPTIONAL 'compression' (type3 keyword)
999            OPTIONAL 'document-format' (mimeMediaType)
1000            OPTIONAL 'document-natural-language' (naturalLanguage)
1001            OPTIONAL 'last-document' (boolean)
1002        Group 2: Document Content
1003           
1004        Response
1005        --------
1006
1007        Group 1: Operation Attributes
1008            OPTIONAL 'status-message' (text(255))
1009            OPTIONAL 'detailed-status-message' (text(MAX))
1010            REQUIRED 'attributes-charset'
1011            REQUIRED 'attributes-natural-language'
1012        Group 2: Unsupported Attributes
1013        Group 3: Job Object Attributes
1014            REQUIRED 'job-uri' (uri)
1015            REQUIRED 'job-id' (integer(1:MAX))
1016            REQUIRED 'job-state' (type1 enum)
1017            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
1018            OPTIONAL 'job-state-message' (text(MAX))
1019            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
1020
1021        """
1022       
1023        operation = request.attribute_groups[0]
1024
1025        job_id = None
1026        printer_uri = None
1027        requesting_user_name = None
1028        document_name = None
1029        compression = None
1030        document_format = None
1031        document_natural_language = None
1032        last_document = None
1033
1034        # required attributes
1035        if 'job-id' not in operation:
1036            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
1037        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
1038
1039        if 'last-document' not in operation:
1040            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
1041        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
1042        if not last_document:
1043            raise ipp.errors.ServerErrorMultipleJobsNotSupported
1044
1045        # optional attributes
1046        if 'printer-uri' in operation:
1047            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
1048            if printer_uri not in self.printer.uris:
1049                raise ipp.errors.ClientErrorAttributes(
1050                    str(operation['printer-uri']), operation['printer-uri'])
1051
1052        if 'requesting-user-name' in operation:
1053            user_name = verify_attribute(
1054                operation['requesting-user-name'], ipp.RequestingUserName)[0]
1055
1056        if 'document-name' in operation:
1057            document_name = verify_attribute(
1058                operation['document-name'], ipp.DocumentName)[0]
1059
1060        if 'compression' in operation:
1061            compression = verify_attribute(
1062                operation['compression'], ipp.Compression)[0]
1063
1064        if 'document-format' in operation:
1065            document_format = verify_attribute(
1066                operation['document-format'], ipp.DocumentFormat)[0]
1067
1068        if 'document-natural-language' in operation:
1069            document_natural_language = verify_attribute(
1070                operation['document_natural_language'],
1071                ipp.DocumentNaturalLanguage)[0]
1072
1073        try:
1074            self.printer.send_document(
1075                job_id,
1076                request.data,
1077                document_name=document_name,
1078                document_format=document_format,
1079                document_natural_language=document_natural_language,
1080                requesting_user_name=user_name,
1081                compression=compression,
1082                last_document=last_document)
1083            attrs = self.printer.get_job_attributes(job_id)
1084        except InvalidJobException:
1085            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
1086
1087        response.attribute_groups.append(ipp.AttributeGroup(
1088            ipp.AttributeTags.JOB, attrs))
1089
1090    @handler_for(ipp.OperationCodes.SEND_URI)
1091    def send_uri(self, request, response):
1092
1093        """3.2.2 Send URI
1094
1095        This OPTIONAL operation is identical to the Send-Document
1096        operation (see section 3.3.1) except that a client MUST supply
1097        a URI reference ('document-uri' operation attribute) rather
1098        than the document data itself.  If a Printer object supports
1099        this operation, clients can use both Send-URI or Send-Document
1100        operations to add new documents to an existing multi-document
1101        Job object.  However, if a client needs to indicate that the
1102        previous Send-URI or Send-Document was the last document, the
1103        client MUST use the Send-Document operation with no document
1104        data and the 'last-document' flag set to 'true' (rather than
1105        using a Send-URI operation with no 'document-uri' operation
1106        attribute).
1107
1108        If a Printer object supports this operation, it MUST also
1109        support the Print-URI operation (see section 3.2.2).
1110
1111        The Printer object MUST validate the syntax and URI scheme of
1112        the supplied URI before returning a response, just as in the
1113        Print-URI operation.  The IPP Printer MAY validate the
1114        accessibility of the document as part of the operation or
1115        subsequently (see section 3.2.2).
1116
1117        Request
1118        -------
1119
1120        Group 1: Operation Attributes
1121            REQUIRED 'attributes-charset'
1122            REQUIRED 'attributes-natural-language'
1123            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
1124            REQUIRED 'document-uri' (uri)
1125            OPTIONAL 'job-uri' (uri)
1126            OPTIONAL 'requesting-user-name' (name(MAX))
1127            OPTIONAL 'document-name' (name(MAX))
1128            OPTIONAL 'compression' (type3 keyword)
1129            OPTIONAL 'document-format' (mimeMediaType)
1130            OPTIONAL 'document-natural-language' (naturalLanguage)
1131           
1132        Response
1133        --------
1134
1135        Group 1: Operation Attributes
1136            OPTIONAL 'status-message' (text(255))
1137            OPTIONAL 'detailed-status-message' (text(MAX))
1138            REQUIRED 'attributes-charset'
1139            REQUIRED 'attributes-natural-language'
1140        Group 2: Unsupported Attributes
1141        Group 3: Job Object Attributes
1142            REQUIRED 'job-uri' (uri)
1143            REQUIRED 'job-id' (integer(1:MAX))
1144            REQUIRED 'job-state' (type1 enum)
1145            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
1146            OPTIONAL 'job-state-message' (text(MAX))
1147            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
1148
1149        """
1150       
1151        operation = request.attribute_groups[0]
1152
1153        job_id = None
1154        printer_uri = None
1155        requesting_user_name = None
1156        document_name = None
1157        compression = None
1158        document_format = None
1159        document_natural_language = None
1160        last_document = None
1161
1162        # required attributes
1163        if 'job-id' not in operation:
1164            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
1165        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
1166
1167        if 'last-document' not in operation:
1168            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
1169        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
1170
1171        if 'document-uri' not in operation:
1172            raise ipp.errors.ClientErrorBadRequest("Missing 'document-uri' attribute")
1173        document_uri = verify_attribute(operation['document-uri'], ipp.DocumentUri)[0]
1174        if not last_document:
1175            raise ipp.errors.ServerErrorMultipleJobsNotSupported
1176
1177        # optional attributes
1178        if 'printer-uri' in operation:
1179            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
1180            if printer_uri not in self.printer.uris:
1181                raise ipp.errors.ClientErrorAttributes(
1182                    str(operation['printer-uri']), operation['printer-uri'])
1183
1184        if 'requesting-user-name' in operation:
1185            user_name = verify_attribute(
1186                operation['requesting-user-name'], ipp.RequestingUserName)[0]
1187
1188        if 'document-name' in operation:
1189            document_name = verify_attribute(
1190                operation['document-name'], ipp.DocumentName)[0]
1191
1192        if 'compression' in operation:
1193            compression = verify_attribute(
1194                operation['compression'], ipp.Compression)[0]
1195
1196        if 'document-format' in operation:
1197            document_format = verify_attribute(
1198                operation['document-format'], ipp.DocumentFormat)[0]
1199
1200        if 'document-natural-language' in operation:
1201            document_natural_language = verify_attribute(
1202                operation['document_natural_language'],
1203                ipp.DocumentNaturalLanguage)[0]
1204
1205        try:
1206            self.printer.send_uri(
1207                job_id,
1208                document_uri,
1209                document_name=document_name,
1210                document_format=document_format,
1211                document_natural_language=document_natural_language,
1212                requesting_user_name=user_name,
1213                compression=compression,
1214                last_document=last_document)
1215            attrs = self.printer.get_job_attributes(job_id)
1216        except InvalidJobException:
1217            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
1218
1219        response.attribute_groups.append(ipp.AttributeGroup(
1220            ipp.AttributeTags.JOB, attrs))
1221
1222    @handler_for(ipp.OperationCodes.GET_JOB_ATTRIBUTES)
1223    def get_job_attributes(self, request, response):
1224        """3.3.4 Get-Job-Attributes Operation
1225
1226        This REQUIRED operation allows a client to request the values
1227        of attributes of a Job object and it is almost identical to
1228        the Get- Printer-Attributes operation (see section 3.2.5). The
1229        only differences are that the operation is directed at a Job
1230        object rather than a Printer object, there is no
1231        'document-format' operation attribute used when querying a Job
1232        object, and the returned attribute group is a set of Job
1233        object attributes rather than a set of Printer object
1234        attributes.
1235
1236        For Jobs, the possible names of attribute groups are:
1237          - 'job-template': the subset of the Job Template attributes
1238            that apply to a Job object (the first column of the table
1239            in Section 4.2) that the implementation supports for Job
1240            objects.
1241          - 'job-description': the subset of the Job Description
1242            attributes specified in Section 4.3 that the
1243            implementation supports for Job objects.
1244          - 'all': the special group 'all' that includes all
1245            attributes that the implementation supports for Job
1246            objects.
1247
1248        Since a client MAY request specific attributes or named
1249        groups, there is a potential that there is some overlap. For
1250        example, if a client requests, 'job-name' and
1251        'job-description', the client is actually requesting the
1252        'job-name' attribute once by naming it explicitly, and once by
1253        inclusion in the 'job-description' group. In such cases, the
1254        Printer object NEED NOT return the attribute only once in the
1255        response even if it is requested multiple times. The client
1256        SHOULD NOT request the same attribute in multiple ways.
1257
1258        It is NOT REQUIRED that a Job object support all attributes
1259        belonging to a group (since some attributes are
1260        OPTIONAL). However it is REQUIRED that each Job object support
1261        all these group names.
1262
1263        Request
1264        -------
1265
1266        Group 1: Operation Attributes
1267            REQUIRED 'attributes-charset'
1268            REQUIRED 'attributes-natural-language'
1269            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
1270              -or-   'job-uri' (uri)
1271            OPTIONAL 'requesting-user-name' (name(MAX))
1272            OPTIONAL 'requested-attributes' (1setOf keyword)
1273           
1274        Response
1275        --------
1276
1277        Group 1: Operation Attributes
1278            OPTIONAL 'status-message' (text(255))
1279            OPTIONAL 'detailed-status-message' (text(MAX))
1280            REQUIRED 'attributes-charset'
1281            REQUIRED 'attributes-natural-language'
1282        Group 2: Unsupported Attributes
1283        Group 3: Job Object Attributes
1284
1285        """
1286       
1287        operation = request.attribute_groups[0]
1288
1289        job_id = None
1290        printer_uri = None
1291        requesting_user_name = None
1292        requested_attributes = None
1293
1294        # required attributes
1295        if 'job-id' not in operation:
1296            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
1297        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
1298
1299        # optional attributes
1300        if 'printer-uri' in operation:
1301            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
1302            if printer_uri not in self.printer.uris:
1303                raise ipp.errors.ClientErrorAttributes(
1304                    str(operation['printer-uri']), operation['printer-uri'])
1305
1306        # optional attributes
1307        if 'requesting-user-name' in operation:
1308            user_name = verify_attribute(
1309                operation['requesting-user-name'], ipp.RequestingUserName)[0]
1310           
1311        if 'requested-attributes' in operation:
1312            requested_attributes = verify_attribute(
1313                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
1314
1315        # get the job attributes and add them to the response
1316        try:
1317            attrs = self.printer.get_job_attributes(
1318                job_id,
1319                requested_attributes=requested_attributes)
1320        except InvalidJobException:
1321            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
1322
1323        response.attribute_groups.append(ipp.AttributeGroup(
1324            ipp.AttributeTags.JOB, attrs))
1325
1326    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
1327    def set_job_attributes(self, request, response):
1328       
1329        raise ipp.errors.ServerErrorOperationNotSupported
1330
1331    @handler_for(ipp.OperationCodes.RESTART_JOB)
1332    def restart_job(self, request, response):
1333        raise ipp.errors.ServerErrorOperationNotSupported
1334
1335    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
1336    def promote_job(self, request, response):
1337        raise ipp.errors.ServerErrorOperationNotSupported
1338
1339    ##### CUPS Specific Commands
1340
1341    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
1342    def cups_get_document(self, request, response):
1343        raise ipp.errors.ServerErrorOperationNotSupported
1344
1345    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
1346    def cups_get_default(self, request, response):
1347        """The CUPS-Get-Default operation (0x4001) returns the default
1348        printer URI and attributes.
1349
1350        Request
1351        -------
1352
1353        Group 1: Operation Attributes
1354            REQUIRED 'attributes-charset'
1355            REQUIRED 'attributes-natural-language'
1356            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
1357
1358        Response
1359        --------
1360
1361        Group 1: Operation Attributes
1362            OPTIONAL 'status-message' (text(255))
1363            REQUIRED 'attributes-charset'
1364            REQUIRED 'attributes-natural-language'
1365        Group 2: Printer Object Attributes
1366
1367        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
1368
1369        """
1370
1371        operation = request.attribute_groups[0]
1372        requested_attributes = None
1373       
1374        if 'requested-attributes' in operation:
1375            requested_attributes = verify_attribute(
1376                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
1377
1378        # get attributes from the printer and add to response
1379        attrs = self.printer.get_printer_attributes(
1380            requested_attributes=requested_attributes)
1381        response.attribute_groups.append(ipp.AttributeGroup(
1382            ipp.AttributeTags.PRINTER, attrs))
1383
1384    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
1385    def cups_get_printers(self, request, response):
1386        """The CUPS-Get-Printers operation (0x4002) returns the
1387        printer attributes for every printer known to the system. This
1388        may include printers that are not served directly by the
1389        server.
1390
1391        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
1392           
1393        """
1394
1395        # get attributes from the printer and add to response
1396        response.attribute_groups.append(ipp.AttributeGroup(
1397            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
1398
1399    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
1400    def cups_get_classes(self, request, response):
1401        """The CUPS-Get-Classes operation (0x4005) returns the printer
1402        attributes for every printer class known to the system. This
1403        may include printer classes that are not served directly by
1404        the server.
1405
1406        Request
1407        -------
1408
1409        Group 1: Operation Attributes
1410            REQUIRED 'attributes-charset'
1411            REQUIRED 'attributes-natural-language'
1412            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
1413            OPTIONAL 'limit' (integer (1:MAX))
1414            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
1415            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
1416            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
1417            OPTIONAL 'requested-attributes' (1setOf keyword)
1418            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
1419            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
1420
1421        Response
1422        --------
1423
1424        Group 1: Operation Attributes
1425            OPTIONAL 'status-message' (text(255))
1426            REQUIRED 'attributes-charset'
1427            REQUIRED 'attributes-natural-language'
1428        Group 2: Printer Class Object Attributes
1429
1430        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
1431
1432        """
1433       
1434        raise ipp.errors.ServerErrorOperationNotSupported
1435
Note: See TracBrowser for help on using the repository browser.