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

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

Clean up printer, job, and requests code

  • Property mode set to 100644
File size: 31.9 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_server):
50        self.gutenbach_server = gutenbach_server
51        self.printer = gutenbach_server.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        raise ipp.errors.ServerErrorOperationNotSupported
173
174    @handler_for(ipp.OperationCodes.VALIDATE_JOB)
175    def validate_job(self, request, response):
176
177        raise ipp.errors.ServerErrorOperationNotSupported
178
179    @handler_for(ipp.OperationCodes.GET_JOBS)
180    def get_jobs(self, request, response):
181        """3.2.6 Get-Jobs Operation
182       
183        This REQUIRED operation allows a client to retrieve the list
184        of Job objects belonging to the target Printer object. The
185        client may also supply a list of Job attribute names and/or
186        attribute group names. A group of Job object attributes will
187        be returned for each Job object that is returned.
188
189        This operation is similar to the Get-Job-Attributes operation,
190        except that this Get-Jobs operation returns attributes from
191        possibly more than one object.
192
193        Request
194        -------
195        Group 1: Operation Attributes
196            REQUIRED 'attributes-charset'
197            REQUIRED 'attributes-natural-language'
198            REQUIRED 'printer-uri' (uri)
199            OPTIONAL 'requesting-user-name' (name(MAX))
200            OPTIONAL 'limit' (integer(1:MAX))
201            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
202            OPTIONAL 'which-jobs' (type2 keyword)
203            OPTIONAL 'my-jobs' (boolean)
204
205        Response
206        --------
207        Group 1: Operation Attributes
208            OPTIONAL 'status-message' (text(255))
209            OPTIONAL 'detailed-status-message' (text(MAX))
210            REQUIRED 'attributes-charset'
211            REQUIRED 'attributes-natural-language'
212        Group 2: Unsupported Attributes
213        Groups 3 to N: Job Object Attributes
214
215        """
216
217        operation = request.attribute_groups[0]
218
219        # initialize operation attribute variables
220        printer_name = None
221        user = None
222        limit = None
223        attributes = None
224        which_jobs = None
225        my_jobs = None
226
227        # requested printer uri
228        if 'printer-uri' not in operation:
229            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
230        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
231        if printer_uri not in self.printer.uris:
232            raise ipp.errors.ClientErrorAttributes(
233                str(operation['printer-uri']), operation['printer-uri'])
234
235        # optional attributes
236        if 'limit' in operation:
237            limit = verify_attribute(
238                operation['limit'], ipp.Limit)[0]
239           
240        if 'requested-attributes' in operation:
241            attributes = verify_attribute(
242                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
243           
244        if 'which-jobs' in operation:
245            which_jobs = verify_attribute(
246                operation['which-jobs'], ipp.WhichJobs)[0]
247           
248        if 'my-jobs' in operation:
249            my_jobs = verify_attribute(
250                operation['my-jobs'], ipp.MyJobs)[0]
251
252        if 'requesting-user-name' in operation:
253            user = verify_attribute(
254                operation['requesting-user-name'], ipp.RequestingUserName)[0]
255            # ignore if we're not filtering jobs by user
256            if not my_jobs:
257                user = None
258           
259        # get the job attributes and add them to the response
260        jobs = self.printer.get_jobs(
261            which_jobs=which_jobs,
262            requesting_user_name=user)
263        for job in jobs:
264            attrs = job.get_job_attributes(requested_attributes=attributes)
265            response.attribute_groups.append(ipp.AttributeGroup(
266                ipp.AttributeTags.JOB, attrs))
267
268    @handler_for(ipp.OperationCodes.PRINT_URI)
269    def print_uri(self, request, response):
270        raise ipp.errors.ServerErrorOperationNotSupported
271
272    @handler_for(ipp.OperationCodes.CREATE_JOB)
273    def create_job(self, request, response):
274        """RFC 2911: 3.2.4 Create-Job Operation
275
276        This OPTIONAL operation is similar to the Print-Job operation
277        (section 3.2.1) except that in the Create-Job request, a
278        client does not supply document data or any reference to
279        document data. Also, the client does not supply any of the
280        'document-name', 'document- format', 'compression', or
281        'document-natural-language' operation attributes. This
282        operation is followed by one or more Send-Document or Send-URI
283        operations. In each of those operation requests, the client
284        OPTIONALLY supplies the 'document-name', 'document-format',
285        and 'document-natural-language' attributes for each document
286        in the multi-document Job object.
287
288        Request
289        -------
290        Group 1: Operation Attributes
291            REQUIRED 'attributes-charset'
292            REQUIRED 'attributes-natural-language'
293            REQUIRED 'printer-uri' (uri)
294            OPTIONAL 'requesting-user-name' (name(MAX))
295            OPTIONAL 'job-name' (name(MAX))
296            OPTIONAL 'ipp-attribute-fidelity' (boolean)
297            OPTIONAL 'job-k-octets' (integer(0:MAX))
298            OPTIONAL 'job-impressions' (integer(0:MAX))
299            OPTIONAL 'job-media-sheets' (integer(0:MAX))
300        Group 2: Job Template Attributes
301
302        Response
303        --------
304        Group 1: Operation Attributes
305            OPTIONAL 'status-message' (text(255))
306            OPTIONAL 'detailed-status-message' (text(MAX))
307            REQUIRED 'attributes-charset'
308            REQUIRED 'attributes-natural-language'
309        Group 2: Unsupported Attributes
310        Group 3: Job Object Attributes
311            REQUIRED 'job-uri' (uri)
312            REQUIRED 'job-id' (integer(1:MAX))
313            REQUIRED 'job-state' (type1 enum)
314            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
315            OPTIONAL 'job-state-message' (text(MAX))
316            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
317       
318        """
319
320        operation = request.attribute_groups[0]
321
322        printer_uri = None
323        requesting_user_name = None
324        job_name = None
325        ipp_attribute_fidelity=None
326        job_k_octets = None
327
328        # requested printer uri
329        if 'printer-uri' not in operation:
330            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
331        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
332        if printer_uri not in self.printer.uris:
333            raise ipp.errors.ClientErrorAttributes(
334                str(operation['printer-uri']), operation['printer-uri'])
335
336        if 'requesting-user-name' in operation:
337            user_name = verify_attribute(
338                operation['requesting-user-name'], ipp.RequestingUserName)[0]
339
340        if 'job-name' in operation:
341            job_name = verify_attribute(
342                operation['job-name'], ipp.JobName)[0]
343
344        if 'job-k-octets' in operation:
345            job_k_octets = verify_attribute(
346                operation['job-k-octets'], ipp.JobKOctets)[0]
347
348        if 'ipp-attribute-fidelity' in operation:
349            pass # don't care
350        if 'job-impressions' in operation:
351            pass # don't care
352        if 'job-media-sheets' in operation:
353            pass # don't care
354
355        # get attributes from the printer and add to response
356        job = self.printer.create_job(
357            requesting_user_name=requesting_user_name,
358            job_name=job_name,
359            job_k_octets=job_k_octets)
360        response.attribute_groups.append(ipp.AttributeGroup(
361            ipp.AttributeTags.JOB, job.get_job_attributes()))
362   
363    @handler_for(ipp.OperationCodes.PAUSE_PRINTER)
364    def pause_printer(self, request, response):
365        raise ipp.errors.ServerErrorOperationNotSupported
366
367    @handler_for(ipp.OperationCodes.RESUME_PRINTER)
368    def resume_printer(self, request, response):
369        raise ipp.errors.ServerErrorOperationNotSupported
370
371    @handler_for(ipp.OperationCodes.GET_PRINTER_ATTRIBUTES)
372    def get_printer_attributes(self, request, response):
373        """RFC 2911: 3.2.5 Get-Printer-Attributes Operation
374
375        This REQUIRED operation allows a client to request the values
376        of the attributes of a Printer object.
377       
378        In the request, the client supplies the set of Printer
379        attribute names and/or attribute group names in which the
380        requester is interested. In the response, the Printer object
381        returns a corresponding attribute set with the appropriate
382        attribute values filled in.
383
384        Request
385        -------
386
387        Group 1: Operation Attributes
388            REQUIRED 'attributes-charset'
389            REQUIRED 'attributes-natural-language'
390            REQUIRED 'printer-uri' (uri)
391            OPTIONAL 'requesting-user-name' (name(MAX))
392            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
393            OPTIONAL 'document-format' (mimeMediaType)
394           
395        Response
396        --------
397
398        Group 1: Operation Attributes
399            OPTIONAL 'status-message' (text(255))
400            OPTIONAL 'detailed-status-message' (text(MAX))
401            REQUIRED 'attributes-charset'
402            REQUIRED 'attributes-natural-language'
403        Group 2: Unsupported Attributes
404        Group 3: Printer Object Attributes
405
406        """
407
408        operation = request.attribute_groups[0]
409
410        printer_uri = None
411        requesting_user_name = None
412        requested_attributes = None
413        document_format = None
414
415        # requested printer uri
416        if 'printer-uri' not in operation:
417            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
418        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
419        if printer_uri not in self.printer.uris:
420            raise ipp.errors.ClientErrorAttributes(
421                str(operation['printer-uri']), operation['printer-uri'])
422
423        # optional attributes
424        if 'requesting-user-name' in operation:
425            user_name = verify_attribute(
426                operation['requesting-user-name'], ipp.RequestingUserName)[0]
427           
428        if 'requested-attributes' in operation:
429            requested_attributes = verify_attribute(
430                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
431
432        if 'document-format' in operation:
433            pass # XXX: todo
434
435        # get attributes from the printer and add to response
436        response.attribute_groups.append(ipp.AttributeGroup(
437            ipp.AttributeTags.PRINTER,
438            self.printer.get_printer_attributes(
439                requested_attributes=requested_attributes)))
440
441    @handler_for(ipp.OperationCodes.SET_PRINTER_ATTRIBUTES)
442    def set_printer_attributes(self, request, response):
443        raise ipp.errors.ServerErrorOperationNotSupported
444
445    ##### Job Commands
446
447    @handler_for(ipp.OperationCodes.CANCEL_JOB)
448    def cancel_job(self, request, response):
449        raise ipp.errors.ServerErrorOperationNotSupported
450
451    @handler_for(ipp.OperationCodes.SEND_DOCUMENT)
452    def send_document(self, request, response):
453        """3.3.1 Send-Document Operation
454       
455        This OPTIONAL operation allows a client to create a
456        multi-document Job object that is initially 'empty' (contains
457        no documents). In the Create-Job response, the Printer object
458        returns the Job object's URI (the 'job-uri' attribute) and the
459        Job object's 32-bit identifier (the 'job-id' attribute). For
460        each new document that the client desires to add, the client
461        uses a Send-Document operation. Each Send- Document Request
462        contains the entire stream of document data for one document.
463
464        If the Printer supports this operation but does not support
465        multiple documents per job, the Printer MUST reject subsequent
466        Send-Document operations supplied with data and return the
467        'server-error-multiple- document-jobs-not-supported'. However,
468        the Printer MUST accept the first document with a 'true' or
469        'false' value for the 'last-document' operation attribute (see
470        below), so that clients MAY always submit one document jobs
471        with a 'false' value for 'last-document' in the first
472        Send-Document and a 'true' for 'last-document' in the second
473        Send-Document (with no data).
474       
475        Since the Create-Job and the send operations (Send-Document or
476        Send- URI operations) that follow could occur over an
477        arbitrarily long period of time for a particular job, a client
478        MUST send another send operation within an IPP Printer defined
479        minimum time interval after the receipt of the previous
480        request for the job. If a Printer object supports the
481        Create-Job and Send-Document operations, the Printer object
482        MUST support the 'multiple-operation-time-out' attribute (see
483        section 4.4.31). This attribute indicates the minimum number
484        of seconds the Printer object will wait for the next send
485        operation before taking some recovery action.
486
487        An IPP object MUST recover from an errant client that does not
488        supply a send operation, sometime after the minimum time
489        interval specified by the Printer object's
490        'multiple-operation-time-out' attribute.
491
492        Access Rights: The authenticated user (see section 8.3)
493        performing this operation must either be the job owner (as
494        determined in the Create-Job operation) or an operator or
495        administrator of the Printer object (see Sections 1 and
496        8.5). Otherwise, the IPP object MUST reject the operation and
497        return: 'client-error-forbidden', 'client-
498        error-not-authenticated', or 'client-error-not-authorized' as
499        appropriate.
500
501        Request
502        -------
503
504        Group 1: Operation Attributes
505            REQUIRED 'attributes-charset'
506            REQUIRED 'attributes-natural-language'
507            REQUIRED 'job-id' (uri)
508            OPTIONAL 'printer-uri' (uri)
509            OPTIONAL 'requesting-user-name' (name(MAX))
510            OPTIONAL 'document-name' (name(MAX))
511            OPTIONAL 'compression' (type3 keyword)
512            OPTIONAL 'document-format' (mimeMediaType)
513            OPTIONAL 'document-natural-language' (naturalLanguage)
514            OPTIONAL 'last-document' (boolean)
515        Group 2: Document Content
516           
517        Response
518        --------
519
520        Group 1: Operation Attributes
521            OPTIONAL 'status-message' (text(255))
522            OPTIONAL 'detailed-status-message' (text(MAX))
523            REQUIRED 'attributes-charset'
524            REQUIRED 'attributes-natural-language'
525        Group 2: Unsupported Attributes
526        Group 3: Job Object Attributes
527            REQUIRED 'job-uri' (uri)
528            REQUIRED 'job-id' (integer(1:MAX))
529            REQUIRED 'job-state' (type1 enum)
530            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
531            OPTIONAL 'job-state-message' (text(MAX))
532            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
533
534        """
535       
536        operation = request.attribute_groups[0]
537
538        job_id = None
539        printer_uri = None
540        requesting_user_name = None
541        document_name = None
542        compression = None
543        document_format = None
544        document_natural_language = None
545        last_document = None
546
547        # required attributes
548        if 'job-id' not in operation:
549            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
550        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
551
552        if 'last-document' not in operation:
553            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
554        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
555        if not last_document:
556            raise ipp.errors.ServerErrorMultipleJobsNotSupported
557
558        # optional attributes
559        if 'printer-uri' in operation:
560            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
561            if printer_uri not in self.printer.uris:
562                raise ipp.errors.ClientErrorAttributes(
563                    str(operation['printer-uri']), operation['printer-uri'])
564
565        if 'requesting-user-name' in operation:
566            user_name = verify_attribute(
567                operation['requesting-user-name'], ipp.RequestingUserName)[0]
568
569        if 'document-name' in operation:
570            document_name = verify_attribute(
571                operation['document-name'], ipp.DocumentName)[0]
572
573        if 'compression' in operation:
574            compression = verify_attribute(
575                operation['compression'], ipp.Compression)[0]
576
577        if 'document-format' in operation:
578            document_format = verify_attribute(
579                operation['document-format'], ipp.DocumentFormat)[0]
580
581        if 'document-natural-language' in operation:
582            document_natural_language = verify_attribute(
583                operation['document_natural_language'],
584                ipp.DocumentNaturalLanguage)[0]
585
586        job = self.printer.get_job(job_id)
587        job.send_document(
588            request.data,
589            requesting_user_name=user_name,
590            document_name=document_name,
591            compression=compression,
592            document_format=document_format,
593            document_natural_language=document_natural_language,
594            last_document=last_document)
595
596        response.attribute_groups.append(ipp.AttributeGroup(
597            ipp.AttributeTags.JOB, job.get_job_attributes()))
598
599    @handler_for(ipp.OperationCodes.SEND_URI)
600    def send_uri(self, request, response):
601        raise ipp.errors.ServerErrorOperationNotSupported
602
603    @handler_for(ipp.OperationCodes.GET_JOB_ATTRIBUTES)
604    def get_job_attributes(self, request, response):
605        """3.3.4 Get-Job-Attributes Operation
606
607        This REQUIRED operation allows a client to request the values
608        of attributes of a Job object and it is almost identical to
609        the Get- Printer-Attributes operation (see section 3.2.5). The
610        only differences are that the operation is directed at a Job
611        object rather than a Printer object, there is no
612        'document-format' operation attribute used when querying a Job
613        object, and the returned attribute group is a set of Job
614        object attributes rather than a set of Printer object
615        attributes.
616
617        For Jobs, the possible names of attribute groups are:
618          - 'job-template': the subset of the Job Template attributes
619            that apply to a Job object (the first column of the table
620            in Section 4.2) that the implementation supports for Job
621            objects.
622          - 'job-description': the subset of the Job Description
623            attributes specified in Section 4.3 that the
624            implementation supports for Job objects.
625          - 'all': the special group 'all' that includes all
626            attributes that the implementation supports for Job
627            objects.
628
629        Since a client MAY request specific attributes or named
630        groups, there is a potential that there is some overlap. For
631        example, if a client requests, 'job-name' and
632        'job-description', the client is actually requesting the
633        'job-name' attribute once by naming it explicitly, and once by
634        inclusion in the 'job-description' group. In such cases, the
635        Printer object NEED NOT return the attribute only once in the
636        response even if it is requested multiple times. The client
637        SHOULD NOT request the same attribute in multiple ways.
638
639        It is NOT REQUIRED that a Job object support all attributes
640        belonging to a group (since some attributes are
641        OPTIONAL). However it is REQUIRED that each Job object support
642        all these group names.
643
644        Request
645        -------
646
647        Group 1: Operation Attributes
648            REQUIRED 'attributes-charset'
649            REQUIRED 'attributes-natural-language'
650            REQUIRED 'job-id' (uri)
651            OPTIONAL 'printer-uri' (uri)
652            OPTIONAL 'requesting-user-name' (name(MAX))
653            OPTIONAL 'requested-attributes' (1setOf keyword)
654           
655        Response
656        --------
657
658        Group 1: Operation Attributes
659            OPTIONAL 'status-message' (text(255))
660            OPTIONAL 'detailed-status-message' (text(MAX))
661            REQUIRED 'attributes-charset'
662            REQUIRED 'attributes-natural-language'
663        Group 2: Unsupported Attributes
664        Group 3: Job Object Attributes
665
666        """
667       
668        operation = request.attribute_groups[0]
669
670        job_id = None
671        printer_uri = None
672        requesting_user_name = None
673        requested_attributes = None
674
675        # required attributes
676        if 'job-id' not in operation:
677            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
678        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
679
680        # optional attributes
681        if 'printer-uri' in operation:
682            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
683            if printer_uri not in self.printer.uris:
684                raise ipp.errors.ClientErrorAttributes(
685                    str(operation['printer-uri']), operation['printer-uri'])
686
687        # optional attributes
688        if 'requesting-user-name' in operation:
689            user_name = verify_attribute(
690                operation['requesting-user-name'], ipp.RequestingUserName)[0]
691           
692        if 'requested-attributes' in operation:
693            requested_attributes = verify_attribute(
694                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
695
696        # get the job attributes and add them to the response
697        job = self.printer.get_job(job_id)
698        attrs = job.get_job_attributes(requested_attributes=requested_attributes)
699        response.attribute_groups.append(ipp.AttributeGroup(
700            ipp.AttributeTags.JOB, attrs))
701
702    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
703    def set_job_attributes(self, request, response):
704        raise ipp.errors.ServerErrorOperationNotSupported
705
706    @handler_for(ipp.OperationCodes.RESTART_JOB)
707    def restart_job(self, request, response):
708        raise ipp.errors.ServerErrorOperationNotSupported
709
710    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
711    def promote_job(self, request, response):
712        raise ipp.errors.ServerErrorOperationNotSupported
713
714    ##### CUPS Specific Commands
715
716    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
717    def cups_get_document(self, request, response):
718        raise ipp.errors.ServerErrorOperationNotSupported
719
720    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
721    def cups_get_default(self, request, response):
722        """The CUPS-Get-Default operation (0x4001) returns the default
723        printer URI and attributes.
724
725        Request
726        -------
727
728        Group 1: Operation Attributes
729            REQUIRED 'attributes-charset'
730            REQUIRED 'attributes-natural-language'
731            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
732
733        Response
734        --------
735
736        Group 1: Operation Attributes
737            OPTIONAL 'status-message' (text(255))
738            REQUIRED 'attributes-charset'
739            REQUIRED 'attributes-natural-language'
740        Group 2: Printer Object Attributes
741
742        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
743
744        """
745
746        operation = request.attribute_groups[0]
747        requested_attributes = None
748       
749        if 'requested-attributes' in operation:
750            requested_attributes = verify_attribute(
751                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
752
753        # get attributes from the printer and add to response
754        attrs = self.printer.get_printer_attributes(
755            requested_attributes=requested_attributes)
756        response.attribute_groups.append(ipp.AttributeGroup(
757            ipp.AttributeTags.PRINTER, attrs))
758
759    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
760    def cups_get_printers(self, request, response):
761        """The CUPS-Get-Printers operation (0x4002) returns the
762        printer attributes for every printer known to the system. This
763        may include printers that are not served directly by the
764        server.
765
766        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
767           
768        """
769
770        # get attributes from the printer and add to response
771        response.attribute_groups.append(ipp.AttributeGroup(
772            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
773
774    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
775    def cups_get_classes(self, request, response):
776        """The CUPS-Get-Classes operation (0x4005) returns the printer
777        attributes for every printer class known to the system. This
778        may include printer classes that are not served directly by
779        the server.
780
781        Request
782        -------
783
784        Group 1: Operation Attributes
785            REQUIRED 'attributes-charset'
786            REQUIRED 'attributes-natural-language'
787            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
788            OPTIONAL 'limit' (integer (1:MAX))
789            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
790            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
791            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
792            OPTIONAL 'requested-attributes' (1setOf keyword)
793            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
794            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
795
796        Response
797        --------
798
799        Group 1: Operation Attributes
800            OPTIONAL 'status-message' (text(255))
801            REQUIRED 'attributes-charset'
802            REQUIRED 'attributes-natural-language'
803        Group 2: Printer Class Object Attributes
804
805        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
806
807        """
808
809        raise ipp.errors.ServerErrorOperationNotSupported
810
Note: See TracBrowser for help on using the repository browser.