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

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

Better threading model

  • Property mode set to 100644
File size: 31.8 KB
Line 
1import gutenbach.ipp as ipp
2import logging
3import traceback
4import sys
5
6# initialize logger
7logger = logging.getLogger(__name__)
8
9def handler_for(operation):
10    """A decorator method to mark a function with the operation id
11    that it handles.  This value will be stored in
12    'func.ipp_operation'.
13
14    """
15   
16    def f(func):
17        func.ipp_operation = operation
18        return func
19    return f
20
21def make_empty_response(request):
22    # Operation attributes -- typically the same for any request
23    attribute_group = ipp.AttributeGroup(
24        ipp.AttributeTags.OPERATION,
25        [ipp.AttributesCharset('utf-8'),
26         ipp.AttributesNaturalLanguage('en-us')])
27   
28    # Set up the default response -- handlers will override these
29    # values if they need to
30    response_kwargs = {}
31    response_kwargs['version']          = request.version
32    response_kwargs['operation_id']     = ipp.StatusCodes.OK
33    response_kwargs['request_id']       = request.request_id
34    response_kwargs['attribute_groups'] = [attribute_group]
35    response = ipp.Request(**response_kwargs)
36   
37    return response
38
39def verify_attribute(attr, cls, length=1):
40    vals = [val.value for val in attr.values]
41    if attr != cls(*vals):
42        raise ipp.errors.ClientErrorBadRequest(str(attr))
43    if length is not None and len(vals) != length:
44        raise ipp.errors.ClientErrorBadRequest(str(attr))
45    return vals
46
47class GutenbachRequestHandler(object):
48
49    def __init__(self, gutenbach_printer):
50        self.printer = gutenbach_printer
51
52    def generic_handle(self, request):
53        # check the IPP version number
54        if request.version != (1, 1):
55            raise ipp.errors.ServerErrorVersionNotSupported(str(request.version))
56
57        # make sure the operation attribute group has the correct tag
58        operation = request.attribute_groups[0]
59        if operation.tag != ipp.AttributeTags.OPERATION:
60            raise ipp.errors.ClientErrorBadRequest(
61                "Attribute group does not have OPERATION tag: 0x%x" % operation.tag)
62
63        # check charset
64        charset_attr = operation.attributes[0]
65        charset = verify_attribute(charset_attr, ipp.AttributesCharset)[0]
66        if charset != 'utf-8':
67            raise ipp.errors.ClientErrorAttributes(str(charset_attr))
68
69        # check for attributes-natural-language
70        natlang_attr = operation.attributes[1]
71        natlang = verify_attribute(natlang_attr, ipp.AttributesNaturalLanguage)[0]
72        if natlang != 'en-us':
73            raise ipp.errors.ClientErrorAttributes(str(natlang_attr))
74   
75    def handle(self, request):
76        # look up the handler
77        handler = None
78        handler_name = None
79        for d in dir(self):
80            if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id:
81                handler_name = d
82                break
83
84        # we couldn't find a handler, so default to unknown operation
85        if handler_name is None:
86            handler_name = "unknown_operation"
87
88        # actually get the handler
89        handler = getattr(self, handler_name)
90        logger.info("request is '%s'" % handler_name)
91
92        # try to handle the request
93        try:
94            self.generic_handle(request)
95            response = make_empty_response(request)
96            handler(request, response)
97
98        # Handle any errors that occur.  If an exception occurs that
99        # is an IPP error, then we can get the error code from the
100        # exception itself.
101        except ipp.errors.IPPException:
102            exctype, excval, exctb = sys.exc_info()
103            logger.error("%s: %s" % (exctype.__name__, excval.message))
104            response = make_empty_response(request)
105            excval.update_response(response)
106
107        # If it wasn't an IPP error, then it's our fault, so mark it
108        # as an internal server error
109        except Exception:
110            logger.error(traceback.format_exc())
111            response = make_empty_response(request)
112            response.operation_id = ipp.StatusCodes.INTERNAL_ERROR
113
114        return response
115
116    def unknown_operation(self, request, response):
117        logger.warning("unknown operation 0x%x" % request.operation_id)
118        response = make_empty_response(request)
119        response.operation_id = ipp.StatusCodes.OPERATION_NOT_SUPPORTED
120        return response
121       
122    ##### Printer Commands
123
124    @handler_for(ipp.OperationCodes.PRINT_JOB)
125    def print_job(self, request, response):
126        """RFC 2911: 3.2.1 Print-Job Operation
127
128        This REQUIRED operation allows a client to submit a print job
129        with only one document and supply the document data (rather
130        than just a reference to the data). See Section 15 for the
131        suggested steps for processing create operations and their
132        Operation and Job Template attributes.
133
134        Request
135        -------
136        Group 1: Operation Attributes
137            REQUIRED 'attributes-charset'
138            REQUIRED 'attributes-natural-language'
139            REQUIRED 'printer-uri' (uri)
140            OPTIONAL 'requesting-user-name' (name(MAX))
141            OPTIONAL 'job-name' (name(MAX))
142            OPTIONAL 'ipp-attribute-fidelity' (boolean)
143            OPTIONAL 'document-name' (name(MAX))
144            OPTIONAL 'compression' (type3 keyword)
145            OPTIONAL 'document-format' (mimeMediaType)
146            OPTIONAL 'document-natural-language' (naturalLanguage)
147            OPTIONAL 'job-k-octets' (integer(0:MAX))
148            OPTIONAL 'job-impressions' (integer(0:MAX))
149            OPTIONAL 'job-media-sheets' (integer(0:MAX))
150        Group 2: Job Template Attributes
151        Group 3: Document Content
152
153        Response
154        --------
155        Group 1: Operation Attributes
156            OPTIONAL 'status-message' (text(255))
157            OPTIONAL 'detailed-status-message' (text(MAX))
158            REQUIRED 'attributes-charset'
159            REQUIRED 'attributes-natural-language'
160        Group 2: Unsupported Attributes
161        Group 3: Job Object Attributes
162            REQUIRED 'job-uri' (uri)
163            REQUIRED 'job-id' (integer(1:MAX))
164            REQUIRED 'job-state' (type1 enum)
165            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
166            OPTIONAL 'job-state-message' (text(MAX))
167            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
168
169        """
170       
171        raise ipp.errors.ServerErrorOperationNotSupported
172
173    @handler_for(ipp.OperationCodes.VALIDATE_JOB)
174    def validate_job(self, request, response):
175
176        raise ipp.errors.ServerErrorOperationNotSupported
177
178    @handler_for(ipp.OperationCodes.GET_JOBS)
179    def get_jobs(self, request, response):
180        """3.2.6 Get-Jobs Operation
181       
182        This REQUIRED operation allows a client to retrieve the list
183        of Job objects belonging to the target Printer object. The
184        client may also supply a list of Job attribute names and/or
185        attribute group names. A group of Job object attributes will
186        be returned for each Job object that is returned.
187
188        This operation is similar to the Get-Job-Attributes operation,
189        except that this Get-Jobs operation returns attributes from
190        possibly more than one object.
191
192        Request
193        -------
194        Group 1: Operation Attributes
195            REQUIRED 'attributes-charset'
196            REQUIRED 'attributes-natural-language'
197            REQUIRED 'printer-uri' (uri)
198            OPTIONAL 'requesting-user-name' (name(MAX))
199            OPTIONAL 'limit' (integer(1:MAX))
200            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
201            OPTIONAL 'which-jobs' (type2 keyword)
202            OPTIONAL 'my-jobs' (boolean)
203
204        Response
205        --------
206        Group 1: Operation Attributes
207            OPTIONAL 'status-message' (text(255))
208            OPTIONAL 'detailed-status-message' (text(MAX))
209            REQUIRED 'attributes-charset'
210            REQUIRED 'attributes-natural-language'
211        Group 2: Unsupported Attributes
212        Groups 3 to N: Job Object Attributes
213
214        """
215
216        operation = request.attribute_groups[0]
217
218        # initialize operation attribute variables
219        printer_name = None
220        user = None
221        limit = None
222        attributes = None
223        which_jobs = None
224        my_jobs = None
225
226        # requested printer uri
227        if 'printer-uri' not in operation:
228            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
229        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
230        if printer_uri not in self.printer.uris:
231            raise ipp.errors.ClientErrorAttributes(
232                str(operation['printer-uri']), operation['printer-uri'])
233
234        # optional attributes
235        if 'limit' in operation:
236            limit = verify_attribute(
237                operation['limit'], ipp.Limit)[0]
238           
239        if 'requested-attributes' in operation:
240            attributes = verify_attribute(
241                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
242           
243        if 'which-jobs' in operation:
244            which_jobs = verify_attribute(
245                operation['which-jobs'], ipp.WhichJobs)[0]
246           
247        if 'my-jobs' in operation:
248            my_jobs = verify_attribute(
249                operation['my-jobs'], ipp.MyJobs)[0]
250
251        if 'requesting-user-name' in operation:
252            user = verify_attribute(
253                operation['requesting-user-name'], ipp.RequestingUserName)[0]
254            # ignore if we're not filtering jobs by user
255            if not my_jobs:
256                user = None
257           
258        # get the job attributes and add them to the response
259        jobs = self.printer.get_jobs(
260            which_jobs=which_jobs,
261            requesting_user_name=user)
262        for job in jobs:
263            attrs = job.get_job_attributes(requested_attributes=attributes)
264            response.attribute_groups.append(ipp.AttributeGroup(
265                ipp.AttributeTags.JOB, attrs))
266
267    @handler_for(ipp.OperationCodes.PRINT_URI)
268    def print_uri(self, request, response):
269        raise ipp.errors.ServerErrorOperationNotSupported
270
271    @handler_for(ipp.OperationCodes.CREATE_JOB)
272    def create_job(self, request, response):
273        """RFC 2911: 3.2.4 Create-Job Operation
274
275        This OPTIONAL operation is similar to the Print-Job operation
276        (section 3.2.1) except that in the Create-Job request, a
277        client does not supply document data or any reference to
278        document data. Also, the client does not supply any of the
279        'document-name', 'document- format', 'compression', or
280        'document-natural-language' operation attributes. This
281        operation is followed by one or more Send-Document or Send-URI
282        operations. In each of those operation requests, the client
283        OPTIONALLY supplies the 'document-name', 'document-format',
284        and 'document-natural-language' attributes for each document
285        in the multi-document Job object.
286
287        Request
288        -------
289        Group 1: Operation Attributes
290            REQUIRED 'attributes-charset'
291            REQUIRED 'attributes-natural-language'
292            REQUIRED 'printer-uri' (uri)
293            OPTIONAL 'requesting-user-name' (name(MAX))
294            OPTIONAL 'job-name' (name(MAX))
295            OPTIONAL 'ipp-attribute-fidelity' (boolean)
296            OPTIONAL 'job-k-octets' (integer(0:MAX))
297            OPTIONAL 'job-impressions' (integer(0:MAX))
298            OPTIONAL 'job-media-sheets' (integer(0:MAX))
299        Group 2: Job Template Attributes
300
301        Response
302        --------
303        Group 1: Operation Attributes
304            OPTIONAL 'status-message' (text(255))
305            OPTIONAL 'detailed-status-message' (text(MAX))
306            REQUIRED 'attributes-charset'
307            REQUIRED 'attributes-natural-language'
308        Group 2: Unsupported Attributes
309        Group 3: Job Object Attributes
310            REQUIRED 'job-uri' (uri)
311            REQUIRED 'job-id' (integer(1:MAX))
312            REQUIRED 'job-state' (type1 enum)
313            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
314            OPTIONAL 'job-state-message' (text(MAX))
315            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
316       
317        """
318
319        operation = request.attribute_groups[0]
320
321        printer_uri = None
322        requesting_user_name = None
323        job_name = None
324        ipp_attribute_fidelity=None
325        job_k_octets = None
326
327        # requested printer uri
328        if 'printer-uri' not in operation:
329            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
330        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
331        if printer_uri not in self.printer.uris:
332            raise ipp.errors.ClientErrorAttributes(
333                str(operation['printer-uri']), operation['printer-uri'])
334
335        if 'requesting-user-name' in operation:
336            user_name = verify_attribute(
337                operation['requesting-user-name'], ipp.RequestingUserName)[0]
338
339        if 'job-name' in operation:
340            job_name = verify_attribute(
341                operation['job-name'], ipp.JobName)[0]
342
343        if 'job-k-octets' in operation:
344            job_k_octets = verify_attribute(
345                operation['job-k-octets'], ipp.JobKOctets)[0]
346
347        if 'ipp-attribute-fidelity' in operation:
348            pass # don't care
349        if 'job-impressions' in operation:
350            pass # don't care
351        if 'job-media-sheets' in operation:
352            pass # don't care
353
354        # get attributes from the printer and add to response
355        job = self.printer.create_job(
356            requesting_user_name=requesting_user_name,
357            job_name=job_name,
358            job_k_octets=job_k_octets)
359        response.attribute_groups.append(ipp.AttributeGroup(
360            ipp.AttributeTags.JOB, job.get_job_attributes()))
361   
362    @handler_for(ipp.OperationCodes.PAUSE_PRINTER)
363    def pause_printer(self, request, response):
364        raise ipp.errors.ServerErrorOperationNotSupported
365
366    @handler_for(ipp.OperationCodes.RESUME_PRINTER)
367    def resume_printer(self, request, response):
368        raise ipp.errors.ServerErrorOperationNotSupported
369
370    @handler_for(ipp.OperationCodes.GET_PRINTER_ATTRIBUTES)
371    def get_printer_attributes(self, request, response):
372        """RFC 2911: 3.2.5 Get-Printer-Attributes Operation
373
374        This REQUIRED operation allows a client to request the values
375        of the attributes of a Printer object.
376       
377        In the request, the client supplies the set of Printer
378        attribute names and/or attribute group names in which the
379        requester is interested. In the response, the Printer object
380        returns a corresponding attribute set with the appropriate
381        attribute values filled in.
382
383        Request
384        -------
385
386        Group 1: Operation Attributes
387            REQUIRED 'attributes-charset'
388            REQUIRED 'attributes-natural-language'
389            REQUIRED 'printer-uri' (uri)
390            OPTIONAL 'requesting-user-name' (name(MAX))
391            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
392            OPTIONAL 'document-format' (mimeMediaType)
393           
394        Response
395        --------
396
397        Group 1: Operation Attributes
398            OPTIONAL 'status-message' (text(255))
399            OPTIONAL 'detailed-status-message' (text(MAX))
400            REQUIRED 'attributes-charset'
401            REQUIRED 'attributes-natural-language'
402        Group 2: Unsupported Attributes
403        Group 3: Printer Object Attributes
404
405        """
406
407        operation = request.attribute_groups[0]
408
409        printer_uri = None
410        requesting_user_name = None
411        requested_attributes = None
412        document_format = None
413
414        # requested printer uri
415        if 'printer-uri' not in operation:
416            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
417        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
418        if printer_uri not in self.printer.uris:
419            raise ipp.errors.ClientErrorAttributes(
420                str(operation['printer-uri']), operation['printer-uri'])
421
422        # optional attributes
423        if 'requesting-user-name' in operation:
424            user_name = verify_attribute(
425                operation['requesting-user-name'], ipp.RequestingUserName)[0]
426           
427        if 'requested-attributes' in operation:
428            requested_attributes = verify_attribute(
429                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
430
431        if 'document-format' in operation:
432            pass # XXX: todo
433
434        # get attributes from the printer and add to response
435        response.attribute_groups.append(ipp.AttributeGroup(
436            ipp.AttributeTags.PRINTER,
437            self.printer.get_printer_attributes(
438                requested_attributes=requested_attributes)))
439
440    @handler_for(ipp.OperationCodes.SET_PRINTER_ATTRIBUTES)
441    def set_printer_attributes(self, request, response):
442        raise ipp.errors.ServerErrorOperationNotSupported
443
444    ##### Job Commands
445
446    @handler_for(ipp.OperationCodes.CANCEL_JOB)
447    def cancel_job(self, request, response):
448        raise ipp.errors.ServerErrorOperationNotSupported
449
450    @handler_for(ipp.OperationCodes.SEND_DOCUMENT)
451    def send_document(self, request, response):
452        """3.3.1 Send-Document Operation
453       
454        This OPTIONAL operation allows a client to create a
455        multi-document Job object that is initially 'empty' (contains
456        no documents). In the Create-Job response, the Printer object
457        returns the Job object's URI (the 'job-uri' attribute) and the
458        Job object's 32-bit identifier (the 'job-id' attribute). For
459        each new document that the client desires to add, the client
460        uses a Send-Document operation. Each Send- Document Request
461        contains the entire stream of document data for one document.
462
463        If the Printer supports this operation but does not support
464        multiple documents per job, the Printer MUST reject subsequent
465        Send-Document operations supplied with data and return the
466        'server-error-multiple- document-jobs-not-supported'. However,
467        the Printer MUST accept the first document with a 'true' or
468        'false' value for the 'last-document' operation attribute (see
469        below), so that clients MAY always submit one document jobs
470        with a 'false' value for 'last-document' in the first
471        Send-Document and a 'true' for 'last-document' in the second
472        Send-Document (with no data).
473       
474        Since the Create-Job and the send operations (Send-Document or
475        Send- URI operations) that follow could occur over an
476        arbitrarily long period of time for a particular job, a client
477        MUST send another send operation within an IPP Printer defined
478        minimum time interval after the receipt of the previous
479        request for the job. If a Printer object supports the
480        Create-Job and Send-Document operations, the Printer object
481        MUST support the 'multiple-operation-time-out' attribute (see
482        section 4.4.31). This attribute indicates the minimum number
483        of seconds the Printer object will wait for the next send
484        operation before taking some recovery action.
485
486        An IPP object MUST recover from an errant client that does not
487        supply a send operation, sometime after the minimum time
488        interval specified by the Printer object's
489        'multiple-operation-time-out' attribute.
490
491        Access Rights: The authenticated user (see section 8.3)
492        performing this operation must either be the job owner (as
493        determined in the Create-Job operation) or an operator or
494        administrator of the Printer object (see Sections 1 and
495        8.5). Otherwise, the IPP object MUST reject the operation and
496        return: 'client-error-forbidden', 'client-
497        error-not-authenticated', or 'client-error-not-authorized' as
498        appropriate.
499
500        Request
501        -------
502
503        Group 1: Operation Attributes
504            REQUIRED 'attributes-charset'
505            REQUIRED 'attributes-natural-language'
506            REQUIRED 'job-id' (uri)
507            OPTIONAL 'printer-uri' (uri)
508            OPTIONAL 'requesting-user-name' (name(MAX))
509            OPTIONAL 'document-name' (name(MAX))
510            OPTIONAL 'compression' (type3 keyword)
511            OPTIONAL 'document-format' (mimeMediaType)
512            OPTIONAL 'document-natural-language' (naturalLanguage)
513            OPTIONAL 'last-document' (boolean)
514        Group 2: Document Content
515           
516        Response
517        --------
518
519        Group 1: Operation Attributes
520            OPTIONAL 'status-message' (text(255))
521            OPTIONAL 'detailed-status-message' (text(MAX))
522            REQUIRED 'attributes-charset'
523            REQUIRED 'attributes-natural-language'
524        Group 2: Unsupported Attributes
525        Group 3: Job Object Attributes
526            REQUIRED 'job-uri' (uri)
527            REQUIRED 'job-id' (integer(1:MAX))
528            REQUIRED 'job-state' (type1 enum)
529            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
530            OPTIONAL 'job-state-message' (text(MAX))
531            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
532
533        """
534       
535        operation = request.attribute_groups[0]
536
537        job_id = None
538        printer_uri = None
539        requesting_user_name = None
540        document_name = None
541        compression = None
542        document_format = None
543        document_natural_language = None
544        last_document = None
545
546        # required attributes
547        if 'job-id' not in operation:
548            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
549        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
550
551        if 'last-document' not in operation:
552            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
553        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
554        if not last_document:
555            raise ipp.errors.ServerErrorMultipleJobsNotSupported
556
557        # optional attributes
558        if 'printer-uri' in operation:
559            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
560            if printer_uri not in self.printer.uris:
561                raise ipp.errors.ClientErrorAttributes(
562                    str(operation['printer-uri']), operation['printer-uri'])
563
564        if 'requesting-user-name' in operation:
565            user_name = verify_attribute(
566                operation['requesting-user-name'], ipp.RequestingUserName)[0]
567
568        if 'document-name' in operation:
569            document_name = verify_attribute(
570                operation['document-name'], ipp.DocumentName)[0]
571
572        if 'compression' in operation:
573            compression = verify_attribute(
574                operation['compression'], ipp.Compression)[0]
575
576        if 'document-format' in operation:
577            document_format = verify_attribute(
578                operation['document-format'], ipp.DocumentFormat)[0]
579
580        if 'document-natural-language' in operation:
581            document_natural_language = verify_attribute(
582                operation['document_natural_language'],
583                ipp.DocumentNaturalLanguage)[0]
584
585        job = self.printer.get_job(job_id)
586        job.send_document(
587            request.data,
588            requesting_user_name=user_name,
589            document_name=document_name,
590            compression=compression,
591            document_format=document_format,
592            document_natural_language=document_natural_language,
593            last_document=last_document)
594
595        response.attribute_groups.append(ipp.AttributeGroup(
596            ipp.AttributeTags.JOB, job.get_job_attributes()))
597
598    @handler_for(ipp.OperationCodes.SEND_URI)
599    def send_uri(self, request, response):
600        raise ipp.errors.ServerErrorOperationNotSupported
601
602    @handler_for(ipp.OperationCodes.GET_JOB_ATTRIBUTES)
603    def get_job_attributes(self, request, response):
604        """3.3.4 Get-Job-Attributes Operation
605
606        This REQUIRED operation allows a client to request the values
607        of attributes of a Job object and it is almost identical to
608        the Get- Printer-Attributes operation (see section 3.2.5). The
609        only differences are that the operation is directed at a Job
610        object rather than a Printer object, there is no
611        'document-format' operation attribute used when querying a Job
612        object, and the returned attribute group is a set of Job
613        object attributes rather than a set of Printer object
614        attributes.
615
616        For Jobs, the possible names of attribute groups are:
617          - 'job-template': the subset of the Job Template attributes
618            that apply to a Job object (the first column of the table
619            in Section 4.2) that the implementation supports for Job
620            objects.
621          - 'job-description': the subset of the Job Description
622            attributes specified in Section 4.3 that the
623            implementation supports for Job objects.
624          - 'all': the special group 'all' that includes all
625            attributes that the implementation supports for Job
626            objects.
627
628        Since a client MAY request specific attributes or named
629        groups, there is a potential that there is some overlap. For
630        example, if a client requests, 'job-name' and
631        'job-description', the client is actually requesting the
632        'job-name' attribute once by naming it explicitly, and once by
633        inclusion in the 'job-description' group. In such cases, the
634        Printer object NEED NOT return the attribute only once in the
635        response even if it is requested multiple times. The client
636        SHOULD NOT request the same attribute in multiple ways.
637
638        It is NOT REQUIRED that a Job object support all attributes
639        belonging to a group (since some attributes are
640        OPTIONAL). However it is REQUIRED that each Job object support
641        all these group names.
642
643        Request
644        -------
645
646        Group 1: Operation Attributes
647            REQUIRED 'attributes-charset'
648            REQUIRED 'attributes-natural-language'
649            REQUIRED 'job-id' (uri)
650            OPTIONAL 'printer-uri' (uri)
651            OPTIONAL 'requesting-user-name' (name(MAX))
652            OPTIONAL 'requested-attributes' (1setOf keyword)
653           
654        Response
655        --------
656
657        Group 1: Operation Attributes
658            OPTIONAL 'status-message' (text(255))
659            OPTIONAL 'detailed-status-message' (text(MAX))
660            REQUIRED 'attributes-charset'
661            REQUIRED 'attributes-natural-language'
662        Group 2: Unsupported Attributes
663        Group 3: Job Object Attributes
664
665        """
666       
667        operation = request.attribute_groups[0]
668
669        job_id = None
670        printer_uri = None
671        requesting_user_name = None
672        requested_attributes = None
673
674        # required attributes
675        if 'job-id' not in operation:
676            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
677        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
678
679        # optional attributes
680        if 'printer-uri' in operation:
681            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
682            if printer_uri not in self.printer.uris:
683                raise ipp.errors.ClientErrorAttributes(
684                    str(operation['printer-uri']), operation['printer-uri'])
685
686        # optional attributes
687        if 'requesting-user-name' in operation:
688            user_name = verify_attribute(
689                operation['requesting-user-name'], ipp.RequestingUserName)[0]
690           
691        if 'requested-attributes' in operation:
692            requested_attributes = verify_attribute(
693                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
694
695        # get the job attributes and add them to the response
696        job = self.printer.get_job(job_id)
697        attrs = job.get_job_attributes(requested_attributes=requested_attributes)
698        response.attribute_groups.append(ipp.AttributeGroup(
699            ipp.AttributeTags.JOB, attrs))
700
701    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
702    def set_job_attributes(self, request, response):
703        raise ipp.errors.ServerErrorOperationNotSupported
704
705    @handler_for(ipp.OperationCodes.RESTART_JOB)
706    def restart_job(self, request, response):
707        raise ipp.errors.ServerErrorOperationNotSupported
708
709    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
710    def promote_job(self, request, response):
711        raise ipp.errors.ServerErrorOperationNotSupported
712
713    ##### CUPS Specific Commands
714
715    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
716    def cups_get_document(self, request, response):
717        raise ipp.errors.ServerErrorOperationNotSupported
718
719    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
720    def cups_get_default(self, request, response):
721        """The CUPS-Get-Default operation (0x4001) returns the default
722        printer URI and attributes.
723
724        Request
725        -------
726
727        Group 1: Operation Attributes
728            REQUIRED 'attributes-charset'
729            REQUIRED 'attributes-natural-language'
730            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
731
732        Response
733        --------
734
735        Group 1: Operation Attributes
736            OPTIONAL 'status-message' (text(255))
737            REQUIRED 'attributes-charset'
738            REQUIRED 'attributes-natural-language'
739        Group 2: Printer Object Attributes
740
741        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
742
743        """
744
745        operation = request.attribute_groups[0]
746        requested_attributes = None
747       
748        if 'requested-attributes' in operation:
749            requested_attributes = verify_attribute(
750                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
751
752        # get attributes from the printer and add to response
753        attrs = self.printer.get_printer_attributes(
754            requested_attributes=requested_attributes)
755        response.attribute_groups.append(ipp.AttributeGroup(
756            ipp.AttributeTags.PRINTER, attrs))
757
758    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
759    def cups_get_printers(self, request, response):
760        """The CUPS-Get-Printers operation (0x4002) returns the
761        printer attributes for every printer known to the system. This
762        may include printers that are not served directly by the
763        server.
764
765        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
766           
767        """
768
769        # get attributes from the printer and add to response
770        response.attribute_groups.append(ipp.AttributeGroup(
771            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
772
773    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
774    def cups_get_classes(self, request, response):
775        """The CUPS-Get-Classes operation (0x4005) returns the printer
776        attributes for every printer class known to the system. This
777        may include printer classes that are not served directly by
778        the server.
779
780        Request
781        -------
782
783        Group 1: Operation Attributes
784            REQUIRED 'attributes-charset'
785            REQUIRED 'attributes-natural-language'
786            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
787            OPTIONAL 'limit' (integer (1:MAX))
788            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
789            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
790            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
791            OPTIONAL 'requested-attributes' (1setOf keyword)
792            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
793            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
794
795        Response
796        --------
797
798        Group 1: Operation Attributes
799            OPTIONAL 'status-message' (text(255))
800            REQUIRED 'attributes-charset'
801            REQUIRED 'attributes-natural-language'
802        Group 2: Printer Class Object Attributes
803
804        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
805
806        """
807
808        raise ipp.errors.ServerErrorOperationNotSupported
809
Note: See TracBrowser for help on using the repository browser.