source: server/lib/gutenbach/server/requests.py @ 34a4e5d

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

Cancelling jobs work

  • Property mode set to 100644
File size: 36.5 KB
Line 
1from . import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
2import gutenbach.ipp as ipp
3import logging
4import traceback
5import sys
6
7# initialize logger
8logger = logging.getLogger(__name__)
9
10def handler_for(operation):
11    """A decorator method to mark a function with the operation id
12    that it handles.  This value will be stored in
13    'func.ipp_operation'.
14
15    """
16   
17    def f(func):
18        func.ipp_operation = operation
19        return func
20    return f
21
22def make_empty_response(request):
23    # Operation attributes -- typically the same for any request
24    attribute_group = ipp.AttributeGroup(
25        ipp.AttributeTags.OPERATION,
26        [ipp.AttributesCharset('utf-8'),
27         ipp.AttributesNaturalLanguage('en-us')])
28   
29    # Set up the default response -- handlers will override these
30    # values if they need to
31    response_kwargs = {}
32    response_kwargs['version']          = request.version
33    response_kwargs['operation_id']     = ipp.StatusCodes.OK
34    response_kwargs['request_id']       = request.request_id
35    response_kwargs['attribute_groups'] = [attribute_group]
36    response = ipp.Request(**response_kwargs)
37   
38    return response
39
40def verify_attribute(attr, cls, length=1):
41    vals = [val.value for val in attr.values]
42    if attr != cls(*vals):
43        raise ipp.errors.ClientErrorBadRequest(str(attr))
44    if length is not None and len(vals) != length:
45        raise ipp.errors.ClientErrorBadRequest(str(attr))
46    return vals
47
48class GutenbachRequestHandler(object):
49
50    def __init__(self, gutenbach_printer):
51        self.printer = gutenbach_printer
52
53    def generic_handle(self, request):
54        # check the IPP version number
55        if request.version != (1, 1):
56            raise ipp.errors.ServerErrorVersionNotSupported(str(request.version))
57
58        # make sure the operation attribute group has the correct tag
59        operation = request.attribute_groups[0]
60        if operation.tag != ipp.AttributeTags.OPERATION:
61            raise ipp.errors.ClientErrorBadRequest(
62                "Attribute group does not have OPERATION tag: 0x%x" % operation.tag)
63
64        # check charset
65        charset_attr = operation.attributes[0]
66        charset = verify_attribute(charset_attr, ipp.AttributesCharset)[0]
67        if charset != 'utf-8':
68            raise ipp.errors.ClientErrorAttributes(str(charset_attr))
69
70        # check for attributes-natural-language
71        natlang_attr = operation.attributes[1]
72        natlang = verify_attribute(natlang_attr, ipp.AttributesNaturalLanguage)[0]
73        if natlang != 'en-us':
74            raise ipp.errors.ClientErrorAttributes(str(natlang_attr))
75   
76    def handle(self, request):
77        # look up the handler
78        handler = None
79        handler_name = None
80        for d in dir(self):
81            if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id:
82                handler_name = d
83                break
84
85        # we couldn't find a handler, so default to unknown operation
86        if handler_name is None:
87            handler_name = "unknown_operation"
88
89        # actually get the handler
90        handler = getattr(self, handler_name)
91        logger.info("request is '%s'" % handler_name)
92
93        # try to handle the request
94        try:
95            self.generic_handle(request)
96            response = make_empty_response(request)
97            handler(request, response)
98
99        # Handle any errors that occur.  If an exception occurs that
100        # is an IPP error, then we can get the error code from the
101        # exception itself.
102        except ipp.errors.IPPException:
103            exctype, excval, exctb = sys.exc_info()
104            logger.error("%s: %s" % (exctype.__name__, excval.message))
105            response = make_empty_response(request)
106            excval.update_response(response)
107
108        # If it wasn't an IPP error, then it's our fault, so mark it
109        # as an internal server error
110        except Exception:
111            logger.error(traceback.format_exc())
112            response = make_empty_response(request)
113            response.operation_id = ipp.StatusCodes.INTERNAL_ERROR
114
115        return response
116
117    def unknown_operation(self, request, response):
118        logger.warning("unknown operation 0x%x" % request.operation_id)
119        response = make_empty_response(request)
120        response.operation_id = ipp.StatusCodes.OPERATION_NOT_SUPPORTED
121        return response
122       
123    ##### Printer Commands
124
125    @handler_for(ipp.OperationCodes.PRINT_JOB)
126    def print_job(self, request, response):
127        """RFC 2911: 3.2.1 Print-Job Operation
128
129        This REQUIRED operation allows a client to submit a print job
130        with only one document and supply the document data (rather
131        than just a reference to the data). See Section 15 for the
132        suggested steps for processing create operations and their
133        Operation and Job Template attributes.
134
135        Request
136        -------
137        Group 1: Operation Attributes
138            REQUIRED 'attributes-charset'
139            REQUIRED 'attributes-natural-language'
140            REQUIRED 'printer-uri' (uri)
141            OPTIONAL 'requesting-user-name' (name(MAX))
142            OPTIONAL 'job-name' (name(MAX))
143            OPTIONAL 'ipp-attribute-fidelity' (boolean)
144            OPTIONAL 'document-name' (name(MAX))
145            OPTIONAL 'compression' (type3 keyword)
146            OPTIONAL 'document-format' (mimeMediaType)
147            OPTIONAL 'document-natural-language' (naturalLanguage)
148            OPTIONAL 'job-k-octets' (integer(0:MAX))
149            OPTIONAL 'job-impressions' (integer(0:MAX))
150            OPTIONAL 'job-media-sheets' (integer(0:MAX))
151        Group 2: Job Template Attributes
152        Group 3: Document Content
153
154        Response
155        --------
156        Group 1: Operation Attributes
157            OPTIONAL 'status-message' (text(255))
158            OPTIONAL 'detailed-status-message' (text(MAX))
159            REQUIRED 'attributes-charset'
160            REQUIRED 'attributes-natural-language'
161        Group 2: Unsupported Attributes
162        Group 3: Job Object Attributes
163            REQUIRED 'job-uri' (uri)
164            REQUIRED 'job-id' (integer(1:MAX))
165            REQUIRED 'job-state' (type1 enum)
166            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
167            OPTIONAL 'job-state-message' (text(MAX))
168            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
169
170        """
171       
172        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        """3.3.3 Cancel-Job Operation
450
451        This REQUIRED operation allows a client to cancel a Print Job from
452        the time the job is created up to the time it is completed, canceled,
453        or aborted. Since a Job might already be printing by the time a
454        Cancel-Job is received, some media sheet pages might be printed
455        before the job is actually terminated.
456
457        The IPP object MUST accept or reject the request based on the job's
458        current state and transition the job to the indicated new state as
459        follows:
460
461        Current State       New State           Response
462        -----------------------------------------------------------------
463        pending             canceled            successful-ok
464        pending-held        canceled            successful-ok
465        processing          canceled            successful-ok
466        processing          processing          successful-ok               See Rule 1
467        processing          processing          client-error-not-possible   See Rule 2
468        processing-stopped  canceled            successful-ok
469        processing-stopped  processing-stopped  successful-ok               See Rule 1
470        processing-stopped  processing-stopped  client-error-not-possible   See Rule 2
471        completed           completed           client-error-not-possible
472        canceled            canceled            client-error-not-possible
473        aborted             aborted             client-error-not-possible
474
475        Rule 1: If the implementation requires some measurable time to
476        cancel the job in the 'processing' or 'processing-stopped' job
477        states, the IPP object MUST add the 'processing-to-stop-point'
478        value to the job's 'job-state-reasons' attribute and then
479        transition the job to the 'canceled' state when the processing
480        ceases (see section 4.3.8).
481
482        Rule 2: If the Job object already has the
483        'processing-to-stop-point' value in its 'job-state-reasons'
484        attribute, then the Printer object MUST reject a Cancel-Job
485        operation.
486
487        Access Rights: The authenticated user (see section 8.3)
488        performing this operation must either be the job owner or an
489        operator or administrator of the Printer object (see Sections
490        1 and 8.5).  Otherwise, the IPP object MUST reject the
491        operation and return: 'client-error-forbidden',
492        'client-error-not-authenticated', or
493        'client-error-not-authorized' as appropriate.
494
495        Request
496        -------
497
498        Group 1: Operation Attributes
499            REQUIRED 'attributes-charset'
500            REQUIRED 'attributes-natural-language'
501            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
502              -or-   'job-uri' (uri)
503            OPTIONAL 'requesting-user-name' (name(MAX))
504            OPTIONAL 'message' (text(127))
505           
506        Response
507        --------
508
509        Group 1: Operation Attributes
510            OPTIONAL 'status-message' (text(255))
511            OPTIONAL 'detailed-status-message' (text(MAX))
512            REQUIRED 'attributes-charset'
513            REQUIRED 'attributes-natural-language'
514        Group 2: Unsupported Attributes
515
516        """
517
518        operation = request.attribute_groups[0]
519
520        job_id = None
521        printer_uri = None
522        requesting_user_name = None
523        message = None
524
525        # required attributes
526        if 'job-id' in operation and 'printer-uri' in operation:
527            job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
528            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
529            if printer_uri not in self.printer.uris:
530                raise ipp.errors.ClientErrorAttributes(
531                    str(operation['printer-uri']), operation['printer-uri'])
532
533        elif 'job-uri' in operation:
534            job_uri = verify_attribute(operation['job-uri'], ipp.JobUri)[0]
535            job_id = int(job_uri.split("/")[-1])
536
537        if 'requesting-user-name' in operation:
538            user_name = verify_attribute(
539                operation['requesting-user-name'], ipp.RequestingUserName)[0]
540
541        try:
542            job = self.printer.get_job(job_id)
543            job.cancel()
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            job = self.printer.get_job(job_id)
684            job.send_document(
685                request.data,
686                requesting_user_name=user_name,
687                document_name=document_name,
688                compression=compression,
689                document_format=document_format,
690                document_natural_language=document_natural_language,
691                last_document=last_document)
692            attrs = job.get_job_attributes()
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            job = self.printer.get_job(job_id)
799            attrs = job.get_job_attributes(requested_attributes=requested_attributes)
800        except InvalidJobException:
801            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
802
803        response.attribute_groups.append(ipp.AttributeGroup(
804            ipp.AttributeTags.JOB, attrs))
805
806    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
807    def set_job_attributes(self, request, response):
808        raise ipp.errors.ServerErrorOperationNotSupported
809
810    @handler_for(ipp.OperationCodes.RESTART_JOB)
811    def restart_job(self, request, response):
812        raise ipp.errors.ServerErrorOperationNotSupported
813
814    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
815    def promote_job(self, request, response):
816        raise ipp.errors.ServerErrorOperationNotSupported
817
818    ##### CUPS Specific Commands
819
820    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
821    def cups_get_document(self, request, response):
822        raise ipp.errors.ServerErrorOperationNotSupported
823
824    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
825    def cups_get_default(self, request, response):
826        """The CUPS-Get-Default operation (0x4001) returns the default
827        printer URI and attributes.
828
829        Request
830        -------
831
832        Group 1: Operation Attributes
833            REQUIRED 'attributes-charset'
834            REQUIRED 'attributes-natural-language'
835            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
836
837        Response
838        --------
839
840        Group 1: Operation Attributes
841            OPTIONAL 'status-message' (text(255))
842            REQUIRED 'attributes-charset'
843            REQUIRED 'attributes-natural-language'
844        Group 2: Printer Object Attributes
845
846        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
847
848        """
849
850        operation = request.attribute_groups[0]
851        requested_attributes = None
852       
853        if 'requested-attributes' in operation:
854            requested_attributes = verify_attribute(
855                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
856
857        # get attributes from the printer and add to response
858        attrs = self.printer.get_printer_attributes(
859            requested_attributes=requested_attributes)
860        response.attribute_groups.append(ipp.AttributeGroup(
861            ipp.AttributeTags.PRINTER, attrs))
862
863    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
864    def cups_get_printers(self, request, response):
865        """The CUPS-Get-Printers operation (0x4002) returns the
866        printer attributes for every printer known to the system. This
867        may include printers that are not served directly by the
868        server.
869
870        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
871           
872        """
873
874        # get attributes from the printer and add to response
875        response.attribute_groups.append(ipp.AttributeGroup(
876            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
877
878    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
879    def cups_get_classes(self, request, response):
880        """The CUPS-Get-Classes operation (0x4005) returns the printer
881        attributes for every printer class known to the system. This
882        may include printer classes that are not served directly by
883        the server.
884
885        Request
886        -------
887
888        Group 1: Operation Attributes
889            REQUIRED 'attributes-charset'
890            REQUIRED 'attributes-natural-language'
891            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
892            OPTIONAL 'limit' (integer (1:MAX))
893            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
894            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
895            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
896            OPTIONAL 'requested-attributes' (1setOf keyword)
897            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
898            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
899
900        Response
901        --------
902
903        Group 1: Operation Attributes
904            OPTIONAL 'status-message' (text(255))
905            REQUIRED 'attributes-charset'
906            REQUIRED 'attributes-natural-language'
907        Group 2: Printer Class Object Attributes
908
909        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
910
911        """
912
913        raise ipp.errors.ServerErrorOperationNotSupported
914
Note: See TracBrowser for help on using the repository browser.