source: server/lib/gutenbach/server/requests.py @ 7c8a2f8

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

Fix docstring formatting for send-uri

  • Property mode set to 100644
File size: 41.9 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        """3.2.2 Send URI
702
703        This OPTIONAL operation is identical to the Send-Document
704        operation (see section 3.3.1) except that a client MUST supply
705        a URI reference ('document-uri' operation attribute) rather
706        than the document data itself.  If a Printer object supports
707        this operation, clients can use both Send-URI or Send-Document
708        operations to add new documents to an existing multi-document
709        Job object.  However, if a client needs to indicate that the
710        previous Send-URI or Send-Document was the last document, the
711        client MUST use the Send-Document operation with no document
712        data and the 'last-document' flag set to 'true' (rather than
713        using a Send-URI operation with no 'document-uri' operation
714        attribute).
715
716        If a Printer object supports this operation, it MUST also
717        support the Print-URI operation (see section 3.2.2).
718
719        The Printer object MUST validate the syntax and URI scheme of
720        the supplied URI before returning a response, just as in the
721        Print-URI operation.  The IPP Printer MAY validate the
722        accessibility of the document as part of the operation or
723        subsequently (see section 3.2.2).
724
725        Request
726        -------
727
728        Group 1: Operation Attributes
729            REQUIRED 'attributes-charset'
730            REQUIRED 'attributes-natural-language'
731            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
732            REQUIRED 'document-uri' (uri)
733              -or-   'job-uri' (uri)
734            OPTIONAL 'requesting-user-name' (name(MAX))
735            OPTIONAL 'document-name' (name(MAX))
736            OPTIONAL 'compression' (type3 keyword)
737            OPTIONAL 'document-format' (mimeMediaType)
738            OPTIONAL 'document-natural-language' (naturalLanguage)
739        Group 2: Document Content
740           
741        Response
742        --------
743
744        Group 1: Operation Attributes
745            OPTIONAL 'status-message' (text(255))
746            OPTIONAL 'detailed-status-message' (text(MAX))
747            REQUIRED 'attributes-charset'
748            REQUIRED 'attributes-natural-language'
749        Group 2: Unsupported Attributes
750        Group 3: Job Object Attributes
751            REQUIRED 'job-uri' (uri)
752            REQUIRED 'job-id' (integer(1:MAX))
753            REQUIRED 'job-state' (type1 enum)
754            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
755            OPTIONAL 'job-state-message' (text(MAX))
756            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
757
758        """
759       
760        operation = request.attribute_groups[0]
761
762        job_id = None
763        printer_uri = None
764        requesting_user_name = None
765        document_name = None
766        compression = None
767        document_format = None
768        document_natural_language = None
769        last_document = None
770
771        # required attributes
772        if 'job-id' not in operation:
773            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
774        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
775
776        if 'last-document' not in operation:
777            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
778        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
779
780        if 'document-uri' not in operation:
781            raise ipp.errors.ClientErrorBadRequest("Missing 'document-uri' attribute")
782        document_uri = verify_attribute(operation['document-uri'], ipp.DocumentUri)[0]
783        if not last_document:
784            raise ipp.errors.ServerErrorMultipleJobsNotSupported
785
786        # optional attributes
787        if 'printer-uri' in operation:
788            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
789            if printer_uri not in self.printer.uris:
790                raise ipp.errors.ClientErrorAttributes(
791                    str(operation['printer-uri']), operation['printer-uri'])
792
793        if 'requesting-user-name' in operation:
794            user_name = verify_attribute(
795                operation['requesting-user-name'], ipp.RequestingUserName)[0]
796
797        if 'document-name' in operation:
798            document_name = verify_attribute(
799                operation['document-name'], ipp.DocumentName)[0]
800
801        if 'compression' in operation:
802            compression = verify_attribute(
803                operation['compression'], ipp.Compression)[0]
804
805        if 'document-format' in operation:
806            document_format = verify_attribute(
807                operation['document-format'], ipp.DocumentFormat)[0]
808
809        if 'document-natural-language' in operation:
810            document_natural_language = verify_attribute(
811                operation['document_natural_language'],
812                ipp.DocumentNaturalLanguage)[0]
813
814        try:
815            self.printer.send_uri(
816                job_id,
817                document_uri=document_uri,
818                document_name=document_name,
819                document_format=document_format,
820                document_natural_language=document_natural_language,
821                requesting_user_name=user_name,
822                compression=compression,
823                last_document=last_document)
824            attrs = self.printer.get_job_attributes(job_id)
825        except InvalidJobException:
826            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
827
828        response.attribute_groups.append(ipp.AttributeGroup(
829            ipp.AttributeTags.JOB, attrs))
830
831    @handler_for(ipp.OperationCodes.GET_JOB_ATTRIBUTES)
832    def get_job_attributes(self, request, response):
833        """3.3.4 Get-Job-Attributes Operation
834
835        This REQUIRED operation allows a client to request the values
836        of attributes of a Job object and it is almost identical to
837        the Get- Printer-Attributes operation (see section 3.2.5). The
838        only differences are that the operation is directed at a Job
839        object rather than a Printer object, there is no
840        'document-format' operation attribute used when querying a Job
841        object, and the returned attribute group is a set of Job
842        object attributes rather than a set of Printer object
843        attributes.
844
845        For Jobs, the possible names of attribute groups are:
846          - 'job-template': the subset of the Job Template attributes
847            that apply to a Job object (the first column of the table
848            in Section 4.2) that the implementation supports for Job
849            objects.
850          - 'job-description': the subset of the Job Description
851            attributes specified in Section 4.3 that the
852            implementation supports for Job objects.
853          - 'all': the special group 'all' that includes all
854            attributes that the implementation supports for Job
855            objects.
856
857        Since a client MAY request specific attributes or named
858        groups, there is a potential that there is some overlap. For
859        example, if a client requests, 'job-name' and
860        'job-description', the client is actually requesting the
861        'job-name' attribute once by naming it explicitly, and once by
862        inclusion in the 'job-description' group. In such cases, the
863        Printer object NEED NOT return the attribute only once in the
864        response even if it is requested multiple times. The client
865        SHOULD NOT request the same attribute in multiple ways.
866
867        It is NOT REQUIRED that a Job object support all attributes
868        belonging to a group (since some attributes are
869        OPTIONAL). However it is REQUIRED that each Job object support
870        all these group names.
871
872        Request
873        -------
874
875        Group 1: Operation Attributes
876            REQUIRED 'attributes-charset'
877            REQUIRED 'attributes-natural-language'
878            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
879              -or-   'job-uri' (uri)
880            OPTIONAL 'requesting-user-name' (name(MAX))
881            OPTIONAL 'requested-attributes' (1setOf keyword)
882           
883        Response
884        --------
885
886        Group 1: Operation Attributes
887            OPTIONAL 'status-message' (text(255))
888            OPTIONAL 'detailed-status-message' (text(MAX))
889            REQUIRED 'attributes-charset'
890            REQUIRED 'attributes-natural-language'
891        Group 2: Unsupported Attributes
892        Group 3: Job Object Attributes
893
894        """
895       
896        operation = request.attribute_groups[0]
897
898        job_id = None
899        printer_uri = None
900        requesting_user_name = None
901        requested_attributes = None
902
903        # required attributes
904        if 'job-id' not in operation:
905            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
906        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
907
908        # optional attributes
909        if 'printer-uri' in operation:
910            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
911            if printer_uri not in self.printer.uris:
912                raise ipp.errors.ClientErrorAttributes(
913                    str(operation['printer-uri']), operation['printer-uri'])
914
915        # optional attributes
916        if 'requesting-user-name' in operation:
917            user_name = verify_attribute(
918                operation['requesting-user-name'], ipp.RequestingUserName)[0]
919           
920        if 'requested-attributes' in operation:
921            requested_attributes = verify_attribute(
922                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
923
924        # get the job attributes and add them to the response
925        try:
926            attrs = self.printer.get_job_attributes(
927                job_id,
928                requested_attributes=requested_attributes)
929        except InvalidJobException:
930            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
931
932        response.attribute_groups.append(ipp.AttributeGroup(
933            ipp.AttributeTags.JOB, attrs))
934
935    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
936    def set_job_attributes(self, request, response):
937        raise ipp.errors.ServerErrorOperationNotSupported
938
939    @handler_for(ipp.OperationCodes.RESTART_JOB)
940    def restart_job(self, request, response):
941        raise ipp.errors.ServerErrorOperationNotSupported
942
943    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
944    def promote_job(self, request, response):
945        raise ipp.errors.ServerErrorOperationNotSupported
946
947    ##### CUPS Specific Commands
948
949    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
950    def cups_get_document(self, request, response):
951        raise ipp.errors.ServerErrorOperationNotSupported
952
953    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
954    def cups_get_default(self, request, response):
955        """The CUPS-Get-Default operation (0x4001) returns the default
956        printer URI and attributes.
957
958        Request
959        -------
960
961        Group 1: Operation Attributes
962            REQUIRED 'attributes-charset'
963            REQUIRED 'attributes-natural-language'
964            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
965
966        Response
967        --------
968
969        Group 1: Operation Attributes
970            OPTIONAL 'status-message' (text(255))
971            REQUIRED 'attributes-charset'
972            REQUIRED 'attributes-natural-language'
973        Group 2: Printer Object Attributes
974
975        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
976
977        """
978
979        operation = request.attribute_groups[0]
980        requested_attributes = None
981       
982        if 'requested-attributes' in operation:
983            requested_attributes = verify_attribute(
984                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
985
986        # get attributes from the printer and add to response
987        attrs = self.printer.get_printer_attributes(
988            requested_attributes=requested_attributes)
989        response.attribute_groups.append(ipp.AttributeGroup(
990            ipp.AttributeTags.PRINTER, attrs))
991
992    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
993    def cups_get_printers(self, request, response):
994        """The CUPS-Get-Printers operation (0x4002) returns the
995        printer attributes for every printer known to the system. This
996        may include printers that are not served directly by the
997        server.
998
999        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
1000           
1001        """
1002
1003        # get attributes from the printer and add to response
1004        response.attribute_groups.append(ipp.AttributeGroup(
1005            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
1006
1007    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
1008    def cups_get_classes(self, request, response):
1009        """The CUPS-Get-Classes operation (0x4005) returns the printer
1010        attributes for every printer class known to the system. This
1011        may include printer classes that are not served directly by
1012        the server.
1013
1014        Request
1015        -------
1016
1017        Group 1: Operation Attributes
1018            REQUIRED 'attributes-charset'
1019            REQUIRED 'attributes-natural-language'
1020            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
1021            OPTIONAL 'limit' (integer (1:MAX))
1022            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
1023            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
1024            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
1025            OPTIONAL 'requested-attributes' (1setOf keyword)
1026            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
1027            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
1028
1029        Response
1030        --------
1031
1032        Group 1: Operation Attributes
1033            OPTIONAL 'status-message' (text(255))
1034            REQUIRED 'attributes-charset'
1035            REQUIRED 'attributes-natural-language'
1036        Group 2: Printer Class Object Attributes
1037
1038        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
1039
1040        """
1041
1042        raise ipp.errors.ServerErrorOperationNotSupported
1043
Note: See TracBrowser for help on using the repository browser.