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

no-cups
Last change on this file since d994f15 was d994f15, checked in by Quentin Smith <quentin@…>, 12 years ago

Hack that answers get-jobs requests incorrectly sent to / (by CUPS)

  • Property mode set to 100644
File size: 36.6 KB
Line 
1from . import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
2import gutenbach.ipp as ipp
3import logging
4import traceback
5import sys
6
7# initialize logger
8logger = logging.getLogger(__name__)
9
10def handler_for(operation):
11    """A decorator method to mark a function with the operation id
12    that it handles.  This value will be stored in
13    'func.ipp_operation'.
14
15    """
16   
17    def f(func):
18        func.ipp_operation = operation
19        return func
20    return f
21
22def make_empty_response(request):
23    # Operation attributes -- typically the same for any request
24    attribute_group = ipp.AttributeGroup(
25        ipp.AttributeTags.OPERATION,
26        [ipp.AttributesCharset('utf-8'),
27         ipp.AttributesNaturalLanguage('en-us')])
28   
29    # Set up the default response -- handlers will override these
30    # values if they need to
31    response_kwargs = {}
32    response_kwargs['version']          = request.version
33    response_kwargs['operation_id']     = ipp.StatusCodes.OK
34    response_kwargs['request_id']       = request.request_id
35    response_kwargs['attribute_groups'] = [attribute_group]
36    response = ipp.Request(**response_kwargs)
37   
38    return response
39
40def verify_attribute(attr, cls, length=1):
41    vals = [val.value for val in attr.values]
42    if attr != cls(*vals):
43        raise ipp.errors.ClientErrorBadRequest(str(attr))
44    if length is not None and len(vals) != length:
45        raise ipp.errors.ClientErrorBadRequest(str(attr))
46    return vals
47
48class GutenbachRequestHandler(object):
49
50    def __init__(self, gutenbach_printer):
51        self.printer = gutenbach_printer
52
53    def generic_handle(self, request):
54        # check the IPP version number
55        if request.version != (1, 1):
56            raise ipp.errors.ServerErrorVersionNotSupported(str(request.version))
57
58        # make sure the operation attribute group has the correct tag
59        operation = request.attribute_groups[0]
60        if operation.tag != ipp.AttributeTags.OPERATION:
61            raise ipp.errors.ClientErrorBadRequest(
62                "Attribute group does not have OPERATION tag: 0x%x" % operation.tag)
63
64        # check charset
65        charset_attr = operation.attributes[0]
66        charset = verify_attribute(charset_attr, ipp.AttributesCharset)[0]
67        if charset != 'utf-8':
68            raise ipp.errors.ClientErrorAttributes(str(charset_attr))
69
70        # check for attributes-natural-language
71        natlang_attr = operation.attributes[1]
72        natlang = verify_attribute(natlang_attr, ipp.AttributesNaturalLanguage)[0]
73        if natlang != 'en-us':
74            raise ipp.errors.ClientErrorAttributes(str(natlang_attr))
75   
76    def handle(self, request):
77        # look up the handler
78        handler = None
79        handler_name = None
80        for d in dir(self):
81            if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id:
82                handler_name = d
83                break
84
85        # we couldn't find a handler, so default to unknown operation
86        if handler_name is None:
87            handler_name = "unknown_operation"
88
89        # actually get the handler
90        handler = getattr(self, handler_name)
91        logger.info("request is '%s'" % handler_name)
92
93        # try to handle the request
94        try:
95            self.generic_handle(request)
96            response = make_empty_response(request)
97            handler(request, response)
98
99        # Handle any errors that occur.  If an exception occurs that
100        # is an IPP error, then we can get the error code from the
101        # exception itself.
102        except ipp.errors.IPPException:
103            exctype, excval, exctb = sys.exc_info()
104            logger.error("%s: %s" % (exctype.__name__, excval.message))
105            response = make_empty_response(request)
106            excval.update_response(response)
107
108        # If it wasn't an IPP error, then it's our fault, so mark it
109        # as an internal server error
110        except Exception:
111            logger.error(traceback.format_exc())
112            response = make_empty_response(request)
113            response.operation_id = ipp.StatusCodes.INTERNAL_ERROR
114
115        return response
116
117    def unknown_operation(self, request, response):
118        logger.warning("unknown operation 0x%x" % request.operation_id)
119        response = make_empty_response(request)
120        response.operation_id = ipp.StatusCodes.OPERATION_NOT_SUPPORTED
121        return response
122       
123    ##### Printer Commands
124
125    @handler_for(ipp.OperationCodes.PRINT_JOB)
126    def print_job(self, request, response):
127        """RFC 2911: 3.2.1 Print-Job Operation
128
129        This REQUIRED operation allows a client to submit a print job
130        with only one document and supply the document data (rather
131        than just a reference to the data). See Section 15 for the
132        suggested steps for processing create operations and their
133        Operation and Job Template attributes.
134
135        Request
136        -------
137        Group 1: Operation Attributes
138            REQUIRED 'attributes-charset'
139            REQUIRED 'attributes-natural-language'
140            REQUIRED 'printer-uri' (uri)
141            OPTIONAL 'requesting-user-name' (name(MAX))
142            OPTIONAL 'job-name' (name(MAX))
143            OPTIONAL 'ipp-attribute-fidelity' (boolean)
144            OPTIONAL 'document-name' (name(MAX))
145            OPTIONAL 'compression' (type3 keyword)
146            OPTIONAL 'document-format' (mimeMediaType)
147            OPTIONAL 'document-natural-language' (naturalLanguage)
148            OPTIONAL 'job-k-octets' (integer(0:MAX))
149            OPTIONAL 'job-impressions' (integer(0:MAX))
150            OPTIONAL 'job-media-sheets' (integer(0:MAX))
151        Group 2: Job Template Attributes
152        Group 3: Document Content
153
154        Response
155        --------
156        Group 1: Operation Attributes
157            OPTIONAL 'status-message' (text(255))
158            OPTIONAL 'detailed-status-message' (text(MAX))
159            REQUIRED 'attributes-charset'
160            REQUIRED 'attributes-natural-language'
161        Group 2: Unsupported Attributes
162        Group 3: Job Object Attributes
163            REQUIRED 'job-uri' (uri)
164            REQUIRED 'job-id' (integer(1:MAX))
165            REQUIRED 'job-state' (type1 enum)
166            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
167            OPTIONAL 'job-state-message' (text(MAX))
168            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
169
170        """
171       
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 and printer_uri != "ipp://localhost/":
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        job_attrs = self.printer.get_jobs(
261            which_jobs=which_jobs,
262            requesting_user_name=user,
263            requested_attributes=attributes)
264        for attrs in job_attrs:
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_id = self.printer.create_job(
357            requesting_user_name=requesting_user_name,
358            job_name=job_name,
359            job_k_octets=job_k_octets)
360        attrs = self.printer.get_job_attributes(job_id)
361        response.attribute_groups.append(ipp.AttributeGroup(
362            ipp.AttributeTags.JOB, attrs))
363   
364    @handler_for(ipp.OperationCodes.PAUSE_PRINTER)
365    def pause_printer(self, request, response):
366        raise ipp.errors.ServerErrorOperationNotSupported
367
368    @handler_for(ipp.OperationCodes.RESUME_PRINTER)
369    def resume_printer(self, request, response):
370        raise ipp.errors.ServerErrorOperationNotSupported
371
372    @handler_for(ipp.OperationCodes.GET_PRINTER_ATTRIBUTES)
373    def get_printer_attributes(self, request, response):
374        """RFC 2911: 3.2.5 Get-Printer-Attributes Operation
375
376        This REQUIRED operation allows a client to request the values
377        of the attributes of a Printer object.
378       
379        In the request, the client supplies the set of Printer
380        attribute names and/or attribute group names in which the
381        requester is interested. In the response, the Printer object
382        returns a corresponding attribute set with the appropriate
383        attribute values filled in.
384
385        Request
386        -------
387
388        Group 1: Operation Attributes
389            REQUIRED 'attributes-charset'
390            REQUIRED 'attributes-natural-language'
391            REQUIRED 'printer-uri' (uri)
392            OPTIONAL 'requesting-user-name' (name(MAX))
393            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
394            OPTIONAL 'document-format' (mimeMediaType)
395           
396        Response
397        --------
398
399        Group 1: Operation Attributes
400            OPTIONAL 'status-message' (text(255))
401            OPTIONAL 'detailed-status-message' (text(MAX))
402            REQUIRED 'attributes-charset'
403            REQUIRED 'attributes-natural-language'
404        Group 2: Unsupported Attributes
405        Group 3: Printer Object Attributes
406
407        """
408
409        operation = request.attribute_groups[0]
410
411        printer_uri = None
412        requesting_user_name = None
413        requested_attributes = None
414        document_format = None
415
416        # requested printer uri
417        if 'printer-uri' not in operation:
418            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
419        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
420        if printer_uri not in self.printer.uris:
421            raise ipp.errors.ClientErrorAttributes(
422                str(operation['printer-uri']), operation['printer-uri'])
423
424        # optional attributes
425        if 'requesting-user-name' in operation:
426            user_name = verify_attribute(
427                operation['requesting-user-name'], ipp.RequestingUserName)[0]
428           
429        if 'requested-attributes' in operation:
430            requested_attributes = verify_attribute(
431                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
432
433        if 'document-format' in operation:
434            pass # XXX: todo
435
436        # get attributes from the printer and add to response
437        response.attribute_groups.append(ipp.AttributeGroup(
438            ipp.AttributeTags.PRINTER,
439            self.printer.get_printer_attributes(
440                requested_attributes=requested_attributes)))
441
442    @handler_for(ipp.OperationCodes.SET_PRINTER_ATTRIBUTES)
443    def set_printer_attributes(self, request, response):
444        raise ipp.errors.ServerErrorOperationNotSupported
445
446    ##### Job Commands
447
448    @handler_for(ipp.OperationCodes.CANCEL_JOB)
449    def cancel_job(self, request, response):
450        """3.3.3 Cancel-Job Operation
451
452        This REQUIRED operation allows a client to cancel a Print Job from
453        the time the job is created up to the time it is completed, canceled,
454        or aborted. Since a Job might already be printing by the time a
455        Cancel-Job is received, some media sheet pages might be printed
456        before the job is actually terminated.
457
458        The IPP object MUST accept or reject the request based on the job's
459        current state and transition the job to the indicated new state as
460        follows:
461
462        Current State       New State           Response
463        -----------------------------------------------------------------
464        pending             canceled            successful-ok
465        pending-held        canceled            successful-ok
466        processing          canceled            successful-ok
467        processing          processing          successful-ok               See Rule 1
468        processing          processing          client-error-not-possible   See Rule 2
469        processing-stopped  canceled            successful-ok
470        processing-stopped  processing-stopped  successful-ok               See Rule 1
471        processing-stopped  processing-stopped  client-error-not-possible   See Rule 2
472        completed           completed           client-error-not-possible
473        canceled            canceled            client-error-not-possible
474        aborted             aborted             client-error-not-possible
475
476        Rule 1: If the implementation requires some measurable time to
477        cancel the job in the 'processing' or 'processing-stopped' job
478        states, the IPP object MUST add the 'processing-to-stop-point'
479        value to the job's 'job-state-reasons' attribute and then
480        transition the job to the 'canceled' state when the processing
481        ceases (see section 4.3.8).
482
483        Rule 2: If the Job object already has the
484        'processing-to-stop-point' value in its 'job-state-reasons'
485        attribute, then the Printer object MUST reject a Cancel-Job
486        operation.
487
488        Access Rights: The authenticated user (see section 8.3)
489        performing this operation must either be the job owner or an
490        operator or administrator of the Printer object (see Sections
491        1 and 8.5).  Otherwise, the IPP object MUST reject the
492        operation and return: 'client-error-forbidden',
493        'client-error-not-authenticated', or
494        'client-error-not-authorized' as appropriate.
495
496        Request
497        -------
498
499        Group 1: Operation Attributes
500            REQUIRED 'attributes-charset'
501            REQUIRED 'attributes-natural-language'
502            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
503              -or-   'job-uri' (uri)
504            OPTIONAL 'requesting-user-name' (name(MAX))
505            OPTIONAL 'message' (text(127))
506           
507        Response
508        --------
509
510        Group 1: Operation Attributes
511            OPTIONAL 'status-message' (text(255))
512            OPTIONAL 'detailed-status-message' (text(MAX))
513            REQUIRED 'attributes-charset'
514            REQUIRED 'attributes-natural-language'
515        Group 2: Unsupported Attributes
516
517        """
518
519        operation = request.attribute_groups[0]
520
521        job_id = None
522        printer_uri = None
523        requesting_user_name = None
524        message = None
525
526        # required attributes
527        if 'job-id' in operation and 'printer-uri' in operation:
528            job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
529            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
530            if printer_uri not in self.printer.uris:
531                raise ipp.errors.ClientErrorAttributes(
532                    str(operation['printer-uri']), operation['printer-uri'])
533
534        elif 'job-uri' in operation:
535            job_uri = verify_attribute(operation['job-uri'], ipp.JobUri)[0]
536            job_id = int(job_uri.split("/")[-1])
537
538        if 'requesting-user-name' in operation:
539            requesting_user_name = verify_attribute(
540                operation['requesting-user-name'], ipp.RequestingUserName)[0]
541
542        try:
543            self.printer.cancel_job(job_id, requesting_user_name=requesting_user_name)
544        except InvalidJobException:
545            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
546
547    @handler_for(ipp.OperationCodes.SEND_DOCUMENT)
548    def send_document(self, request, response):
549        """3.3.1 Send-Document Operation
550       
551        This OPTIONAL operation allows a client to create a
552        multi-document Job object that is initially 'empty' (contains
553        no documents). In the Create-Job response, the Printer object
554        returns the Job object's URI (the 'job-uri' attribute) and the
555        Job object's 32-bit identifier (the 'job-id' attribute). For
556        each new document that the client desires to add, the client
557        uses a Send-Document operation. Each Send- Document Request
558        contains the entire stream of document data for one document.
559
560        If the Printer supports this operation but does not support
561        multiple documents per job, the Printer MUST reject subsequent
562        Send-Document operations supplied with data and return the
563        'server-error-multiple- document-jobs-not-supported'. However,
564        the Printer MUST accept the first document with a 'true' or
565        'false' value for the 'last-document' operation attribute (see
566        below), so that clients MAY always submit one document jobs
567        with a 'false' value for 'last-document' in the first
568        Send-Document and a 'true' for 'last-document' in the second
569        Send-Document (with no data).
570       
571        Since the Create-Job and the send operations (Send-Document or
572        Send- URI operations) that follow could occur over an
573        arbitrarily long period of time for a particular job, a client
574        MUST send another send operation within an IPP Printer defined
575        minimum time interval after the receipt of the previous
576        request for the job. If a Printer object supports the
577        Create-Job and Send-Document operations, the Printer object
578        MUST support the 'multiple-operation-time-out' attribute (see
579        section 4.4.31). This attribute indicates the minimum number
580        of seconds the Printer object will wait for the next send
581        operation before taking some recovery action.
582
583        An IPP object MUST recover from an errant client that does not
584        supply a send operation, sometime after the minimum time
585        interval specified by the Printer object's
586        'multiple-operation-time-out' attribute.
587
588        Access Rights: The authenticated user (see section 8.3)
589        performing this operation must either be the job owner (as
590        determined in the Create-Job operation) or an operator or
591        administrator of the Printer object (see Sections 1 and
592        8.5). Otherwise, the IPP object MUST reject the operation and
593        return: 'client-error-forbidden', 'client-
594        error-not-authenticated', or 'client-error-not-authorized' as
595        appropriate.
596
597        Request
598        -------
599
600        Group 1: Operation Attributes
601            REQUIRED 'attributes-charset'
602            REQUIRED 'attributes-natural-language'
603            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
604              -or-   'job-uri' (uri)
605            OPTIONAL 'requesting-user-name' (name(MAX))
606            OPTIONAL 'document-name' (name(MAX))
607            OPTIONAL 'compression' (type3 keyword)
608            OPTIONAL 'document-format' (mimeMediaType)
609            OPTIONAL 'document-natural-language' (naturalLanguage)
610            OPTIONAL 'last-document' (boolean)
611        Group 2: Document Content
612           
613        Response
614        --------
615
616        Group 1: Operation Attributes
617            OPTIONAL 'status-message' (text(255))
618            OPTIONAL 'detailed-status-message' (text(MAX))
619            REQUIRED 'attributes-charset'
620            REQUIRED 'attributes-natural-language'
621        Group 2: Unsupported Attributes
622        Group 3: Job Object Attributes
623            REQUIRED 'job-uri' (uri)
624            REQUIRED 'job-id' (integer(1:MAX))
625            REQUIRED 'job-state' (type1 enum)
626            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
627            OPTIONAL 'job-state-message' (text(MAX))
628            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
629
630        """
631       
632        operation = request.attribute_groups[0]
633
634        job_id = None
635        printer_uri = None
636        requesting_user_name = None
637        document_name = None
638        compression = None
639        document_format = None
640        document_natural_language = None
641        last_document = None
642
643        # required attributes
644        if 'job-id' not in operation:
645            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
646        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
647
648        if 'last-document' not in operation:
649            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
650        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
651        if not last_document:
652            raise ipp.errors.ServerErrorMultipleJobsNotSupported
653
654        # optional attributes
655        if 'printer-uri' in operation:
656            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
657            if printer_uri not in self.printer.uris:
658                raise ipp.errors.ClientErrorAttributes(
659                    str(operation['printer-uri']), operation['printer-uri'])
660
661        if 'requesting-user-name' in operation:
662            user_name = verify_attribute(
663                operation['requesting-user-name'], ipp.RequestingUserName)[0]
664
665        if 'document-name' in operation:
666            document_name = verify_attribute(
667                operation['document-name'], ipp.DocumentName)[0]
668
669        if 'compression' in operation:
670            compression = verify_attribute(
671                operation['compression'], ipp.Compression)[0]
672
673        if 'document-format' in operation:
674            document_format = verify_attribute(
675                operation['document-format'], ipp.DocumentFormat)[0]
676
677        if 'document-natural-language' in operation:
678            document_natural_language = verify_attribute(
679                operation['document_natural_language'],
680                ipp.DocumentNaturalLanguage)[0]
681
682        try:
683            self.printer.send_document(
684                job_id,
685                request.data,
686                document_name=document_name,
687                document_format=document_format,
688                document_natural_language=document_natural_language,
689                requesting_user_name=user_name,
690                compression=compression,
691                last_document=last_document)
692            attrs = self.printer.get_job_attributes(job_id)
693        except InvalidJobException:
694            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
695
696        response.attribute_groups.append(ipp.AttributeGroup(
697            ipp.AttributeTags.JOB, attrs))
698
699    @handler_for(ipp.OperationCodes.SEND_URI)
700    def send_uri(self, request, response):
701        raise ipp.errors.ServerErrorOperationNotSupported
702
703    @handler_for(ipp.OperationCodes.GET_JOB_ATTRIBUTES)
704    def get_job_attributes(self, request, response):
705        """3.3.4 Get-Job-Attributes Operation
706
707        This REQUIRED operation allows a client to request the values
708        of attributes of a Job object and it is almost identical to
709        the Get- Printer-Attributes operation (see section 3.2.5). The
710        only differences are that the operation is directed at a Job
711        object rather than a Printer object, there is no
712        'document-format' operation attribute used when querying a Job
713        object, and the returned attribute group is a set of Job
714        object attributes rather than a set of Printer object
715        attributes.
716
717        For Jobs, the possible names of attribute groups are:
718          - 'job-template': the subset of the Job Template attributes
719            that apply to a Job object (the first column of the table
720            in Section 4.2) that the implementation supports for Job
721            objects.
722          - 'job-description': the subset of the Job Description
723            attributes specified in Section 4.3 that the
724            implementation supports for Job objects.
725          - 'all': the special group 'all' that includes all
726            attributes that the implementation supports for Job
727            objects.
728
729        Since a client MAY request specific attributes or named
730        groups, there is a potential that there is some overlap. For
731        example, if a client requests, 'job-name' and
732        'job-description', the client is actually requesting the
733        'job-name' attribute once by naming it explicitly, and once by
734        inclusion in the 'job-description' group. In such cases, the
735        Printer object NEED NOT return the attribute only once in the
736        response even if it is requested multiple times. The client
737        SHOULD NOT request the same attribute in multiple ways.
738
739        It is NOT REQUIRED that a Job object support all attributes
740        belonging to a group (since some attributes are
741        OPTIONAL). However it is REQUIRED that each Job object support
742        all these group names.
743
744        Request
745        -------
746
747        Group 1: Operation Attributes
748            REQUIRED 'attributes-charset'
749            REQUIRED 'attributes-natural-language'
750            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
751              -or-   'job-uri' (uri)
752            OPTIONAL 'requesting-user-name' (name(MAX))
753            OPTIONAL 'requested-attributes' (1setOf keyword)
754           
755        Response
756        --------
757
758        Group 1: Operation Attributes
759            OPTIONAL 'status-message' (text(255))
760            OPTIONAL 'detailed-status-message' (text(MAX))
761            REQUIRED 'attributes-charset'
762            REQUIRED 'attributes-natural-language'
763        Group 2: Unsupported Attributes
764        Group 3: Job Object Attributes
765
766        """
767       
768        operation = request.attribute_groups[0]
769
770        job_id = None
771        printer_uri = None
772        requesting_user_name = None
773        requested_attributes = None
774
775        # required attributes
776        if 'job-id' not in operation:
777            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
778        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
779
780        # optional attributes
781        if 'printer-uri' in operation:
782            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
783            if printer_uri not in self.printer.uris:
784                raise ipp.errors.ClientErrorAttributes(
785                    str(operation['printer-uri']), operation['printer-uri'])
786
787        # optional attributes
788        if 'requesting-user-name' in operation:
789            user_name = verify_attribute(
790                operation['requesting-user-name'], ipp.RequestingUserName)[0]
791           
792        if 'requested-attributes' in operation:
793            requested_attributes = verify_attribute(
794                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
795
796        # get the job attributes and add them to the response
797        try:
798            attrs = self.printer.get_job_attributes(
799                job_id,
800                requested_attributes=requested_attributes)
801        except InvalidJobException:
802            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
803
804        response.attribute_groups.append(ipp.AttributeGroup(
805            ipp.AttributeTags.JOB, attrs))
806
807    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
808    def set_job_attributes(self, request, response):
809        raise ipp.errors.ServerErrorOperationNotSupported
810
811    @handler_for(ipp.OperationCodes.RESTART_JOB)
812    def restart_job(self, request, response):
813        raise ipp.errors.ServerErrorOperationNotSupported
814
815    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
816    def promote_job(self, request, response):
817        raise ipp.errors.ServerErrorOperationNotSupported
818
819    ##### CUPS Specific Commands
820
821    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
822    def cups_get_document(self, request, response):
823        raise ipp.errors.ServerErrorOperationNotSupported
824
825    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
826    def cups_get_default(self, request, response):
827        """The CUPS-Get-Default operation (0x4001) returns the default
828        printer URI and attributes.
829
830        Request
831        -------
832
833        Group 1: Operation Attributes
834            REQUIRED 'attributes-charset'
835            REQUIRED 'attributes-natural-language'
836            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
837
838        Response
839        --------
840
841        Group 1: Operation Attributes
842            OPTIONAL 'status-message' (text(255))
843            REQUIRED 'attributes-charset'
844            REQUIRED 'attributes-natural-language'
845        Group 2: Printer Object Attributes
846
847        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
848
849        """
850
851        operation = request.attribute_groups[0]
852        requested_attributes = None
853       
854        if 'requested-attributes' in operation:
855            requested_attributes = verify_attribute(
856                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
857
858        # get attributes from the printer and add to response
859        attrs = self.printer.get_printer_attributes(
860            requested_attributes=requested_attributes)
861        response.attribute_groups.append(ipp.AttributeGroup(
862            ipp.AttributeTags.PRINTER, attrs))
863
864    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
865    def cups_get_printers(self, request, response):
866        """The CUPS-Get-Printers operation (0x4002) returns the
867        printer attributes for every printer known to the system. This
868        may include printers that are not served directly by the
869        server.
870
871        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
872           
873        """
874
875        # get attributes from the printer and add to response
876        response.attribute_groups.append(ipp.AttributeGroup(
877            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
878
879    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
880    def cups_get_classes(self, request, response):
881        """The CUPS-Get-Classes operation (0x4005) returns the printer
882        attributes for every printer class known to the system. This
883        may include printer classes that are not served directly by
884        the server.
885
886        Request
887        -------
888
889        Group 1: Operation Attributes
890            REQUIRED 'attributes-charset'
891            REQUIRED 'attributes-natural-language'
892            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
893            OPTIONAL 'limit' (integer (1:MAX))
894            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
895            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
896            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
897            OPTIONAL 'requested-attributes' (1setOf keyword)
898            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
899            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
900
901        Response
902        --------
903
904        Group 1: Operation Attributes
905            OPTIONAL 'status-message' (text(255))
906            REQUIRED 'attributes-charset'
907            REQUIRED 'attributes-natural-language'
908        Group 2: Printer Class Object Attributes
909
910        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
911
912        """
913
914        raise ipp.errors.ServerErrorOperationNotSupported
915
Note: See TracBrowser for help on using the repository browser.