source: server/lib/gutenbach/server/requests.py @ 3cb6c7b

no-cups
Last change on this file since 3cb6c7b was 3cb6c7b, checked in by Daniel Cooper <danny@…>, 12 years ago

added more request handlers

  • Property mode set to 100644
File size: 60.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        operation = request.attribute_groups[0]
173        document = request.data       
174        user_name = None
175        job_name = None
176        job_k_octets = None
177        document_format = None
178        document_natural_language = None
179        compression = None
180        last_document = None
181        # requested printer uri
182        if 'printer-uri' not in operation:
183            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
184        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
185        if printer_uri not in self.printer.uris:
186            raise ipp.errors.ClientErrorAttributes(
187                str(operation['printer-uri']), operation['printer-uri'])
188
189        if 'requesting-user-name' in operation:
190            user_name = verify_attribute(
191                operation['requesting-user-name'], ipp.RequestingUserName)[0]
192
193        if 'job-name' in operation:
194            job_name = verify_attribute(
195                operation['job-name'], ipp.JobName)[0]
196
197        if 'job-k-octets' in operation:
198            job_k_octets = verify_attribute(
199                operation['job-k-octets'], ipp.JobKOctets)[0]
200
201        if 'ipp-attribute-fidelity' in operation:
202            pass # don't care
203        if 'job-impressions' in operation:
204            pass # don't care
205        if 'job-media-sheets' in operation:
206            pass # don't care
207
208        # get attributes from the printer and add to response
209        try:
210            job_id = self.printer.print_job(document,
211                    document_name               = document_name,
212                    document_format             = document_format,
213                    document_natural_language   = document_natural_language,
214                    requesting_user_name        = user_name,
215                    compression                 = compression,
216                    job_name                    = job_name,
217                    job_k_octets                = job_k_octets)
218        except InvalidJobException:
219            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
220
221
222        attrs = self.printer.get_job_attributes(job_id)
223     
224        #Actually append the attributes we pulled
225        response.attribute_groups.append(ipp.AttributeGroup(
226            ipp.AttributeTags.JOB, attrs))
227
228       
229    @handler_for(ipp.OperationCodes.VALIDATE_JOB)
230    def validate_job(self, request, response):
231        """3.2.3 Validate-Job Operation
232
233        This REQUIRED operation is similar to the Print-Job operation
234        (section 3.2.1) except that a client supplies no document data and
235        the Printer allocates no resources (i.e., it does not create a new
236        Job object).  This operation is used only to verify capabilities of a
237        printer object against whatever attributes are supplied by the client
238        in the Validate-Job request.  By using the Validate-Job operation a
239        client can validate that an identical Print-Job operation (with the
240        document data) would be accepted. The Validate-Job operation also
241        performs the same security negotiation as the Print-Job operation
242        (see section 8), so that a client can check that the client and
243        Printer object security requirements can be met before performing a
244        Print-Job operation.
245
246        The Validate-Job operation does not accept a 'document-uri' attribute
247        in order to allow a client to check that the same Print-URI operation
248        will be accepted, since the client doesn't send the data with the
249        Print-URI operation.  The client SHOULD just issue the Print-URI
250        request.
251
252        The Printer object returns the same status codes, Operation
253        Attributes (Group 1) and Unsupported Attributes (Group 2) as the
254        Print-Job operation.  However, no Job Object Attributes (Group 3) are
255        returned, since no Job object is created.
256        """
257        operation = request.attribute_groups[0]
258        user_name = None
259        job_name = None
260        job_k_octets = None
261        # requested printer uri
262        if 'printer-uri' not in operation:
263            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
264        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
265        if printer_uri not in self.printer.uris:
266            raise ipp.errors.ClientErrorAttributes(
267                str(operation['printer-uri']), operation['printer-uri'])
268
269        if 'requesting-user-name' in operation:
270            user_name = verify_attribute(
271                operation['requesting-user-name'], ipp.RequestingUserName)[0]
272
273        if 'job-name' in operation:
274            job_name = verify_attribute(
275                operation['job-name'], ipp.JobName)[0]
276
277        if 'job-k-octets' in operation:
278            job_k_octets = verify_attribute(
279                operation['job-k-octets'], ipp.JobKOctets)[0]
280       
281        self.printer.verify_job(requesting_user_name=user_name,
282            job_name=job_name,
283            job_k_octets = job_k_octets) 
284
285        #raise ipp.errors.ServerErrorOperationNotSupported
286
287    @handler_for(ipp.OperationCodes.GET_JOBS)
288    def get_jobs(self, request, response):
289        """3.2.6 Get-Jobs Operation
290       
291        This REQUIRED operation allows a client to retrieve the list
292        of Job objects belonging to the target Printer object. The
293        client may also supply a list of Job attribute names and/or
294        attribute group names. A group of Job object attributes will
295        be returned for each Job object that is returned.
296
297        This operation is similar to the Get-Job-Attributes operation,
298        except that this Get-Jobs operation returns attributes from
299        possibly more than one object.
300
301        Request
302        -------
303        Group 1: Operation Attributes
304            REQUIRED 'attributes-charset'
305            REQUIRED 'attributes-natural-language'
306            REQUIRED 'printer-uri' (uri)
307            OPTIONAL 'requesting-user-name' (name(MAX))
308            OPTIONAL 'limit' (integer(1:MAX))
309            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
310            OPTIONAL 'which-jobs' (type2 keyword)
311            OPTIONAL 'my-jobs' (boolean)
312
313        Response
314        --------
315        Group 1: Operation Attributes
316            OPTIONAL 'status-message' (text(255))
317            OPTIONAL 'detailed-status-message' (text(MAX))
318            REQUIRED 'attributes-charset'
319            REQUIRED 'attributes-natural-language'
320        Group 2: Unsupported Attributes
321        Groups 3 to N: Job Object Attributes
322
323        """
324
325        operation = request.attribute_groups[0]
326
327        # initialize operation attribute variables
328        printer_name = None
329        user = None
330        limit = None
331        attributes = None
332        which_jobs = None
333        my_jobs = None
334
335        # requested printer uri
336        if 'printer-uri' not in operation:
337            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
338        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
339        if printer_uri not in self.printer.uris and printer_uri != "ipp://localhost/":
340            raise ipp.errors.ClientErrorAttributes(
341                str(operation['printer-uri']), operation['printer-uri'])
342
343        # optional attributes
344        if 'limit' in operation:
345            limit = verify_attribute(
346                operation['limit'], ipp.Limit)[0]
347           
348        if 'requested-attributes' in operation:
349            attributes = verify_attribute(
350                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
351           
352        if 'which-jobs' in operation:
353            which_jobs = verify_attribute(
354                operation['which-jobs'], ipp.WhichJobs)[0]
355           
356        if 'my-jobs' in operation:
357            my_jobs = verify_attribute(
358                operation['my-jobs'], ipp.MyJobs)[0]
359
360        if 'requesting-user-name' in operation:
361            user = verify_attribute(
362                operation['requesting-user-name'], ipp.RequestingUserName)[0]
363            # ignore if we're not filtering jobs by user
364            if not my_jobs:
365                user = None
366           
367        # get the job attributes and add them to the response
368        job_attrs = self.printer.get_jobs(
369            which_jobs=which_jobs,
370            requesting_user_name=user,
371            requested_attributes=attributes)
372        for attrs in job_attrs:
373            response.attribute_groups.append(ipp.AttributeGroup(
374                ipp.AttributeTags.JOB, attrs))
375
376    @handler_for(ipp.OperationCodes.PRINT_URI)
377    def print_uri(self, request, response):
378        """3.2.2 Print-URI Operation
379
380        This OPTIONAL operation is identical to the Print-Job operation
381        (section 3.2.1) except that a client supplies a URI reference to the
382        document data using the 'document-uri' (uri) operation attribute (in
383        Group 1) rather than including the document data itself.  Before
384        returning the response, the Printer MUST validate that the Printer
385        supports the retrieval method (e.g., http, ftp, etc.) implied by the
386        URI, and MUST check for valid URI syntax.  If the client-supplied URI
387        scheme is not supported, i.e. the value is not in the Printer
388        object's 'referenced-uri-scheme-supported' attribute, the Printer
389        object MUST reject the request and return the 'client-error-uri-
390        scheme-not-supported' status code.
391
392        The IPP Printer MAY validate the accessibility of the document as
393        part of the operation or subsequently.  If the Printer determines an
394        accessibility problem before returning an operation response, it
395        rejects the request and returns the 'client-error-document-access-
396        error' status code.  The Printer MAY also return a specific document
397        access error code using the 'document-access-error' operation
398        attribute (see section 3.1.6.4).
399
400        If the Printer determines this document accessibility problem after
401        accepting the request and returning an operation response with one
402        of the successful status codes, the Printer adds the
403        'document-access- error' value to the job's 'job-state-reasons'
404        attribute and MAY populate the job's 'job-document-access-errors'
405        Job Description attribute (see section 4.3.11).  See The
406        Implementer's Guide [IPP- IIG] for suggested additional checks.
407                                                                             
408        If the Printer object supports this operation, it MUST support the
409        'reference-uri-schemes-supported' Printer attribute (see section 4.4.27).
410
411        It is up to the IPP object to interpret the URI and subsequently
412        'pull' the document from the source referenced by the URI string."""
413        operation = request.attribute_groups[0]
414        document = request.data       
415        user_name = None
416        job_name = None
417        job_k_octets = None
418        document_format = None
419        document_natural_language = None
420        compression = None
421        last_document = None
422
423
424        # requested printer uri
425        if 'printer-uri' not in operation:
426            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
427        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
428        if printer_uri not in self.printer.uris:
429            raise ipp.errors.ClientErrorAttributes(
430                str(operation['printer-uri']), operation['printer-uri'])
431
432        if 'requesting-user-name' in operation:
433            user_name = verify_attribute(
434                operation['requesting-user-name'], ipp.RequestingUserName)[0]
435
436        if 'job-name' in operation:
437            job_name = verify_attribute(
438                operation['job-name'], ipp.JobName)[0]
439
440        if 'job-k-octets' in operation:
441            job_k_octets = verify_attribute(
442                operation['job-k-octets'], ipp.JobKOctets)[0]
443
444        if 'ipp-attribute-fidelity' in operation:
445            pass # don't care
446        if 'job-impressions' in operation:
447            pass # don't care
448        if 'job-media-sheets' in operation:
449            pass # don't care
450
451        # get attributes from the printer and add to response
452        try:
453            job_id = self.printer.print_uri(document,
454                    document_name               = document_name,
455                    document_format             = document_format,
456                    document_natural_language   = document_natural_language,
457                    requesting_user_name        = user_name,
458                    compression                 = compression,
459                    job_name                    = job_name,
460                    job_k_octets                = job_k_octets)
461        except InvalidJobException:
462            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
463
464
465        attrs = self.printer.get_job_attributes(job_id)
466     
467        #Actually append the attributes we pulled
468        response.attribute_groups.append(ipp.AttributeGroup(
469            ipp.AttributeTags.JOB, attrs))
470
471 
472       #raise ipp.errors.ServerErrorOperationNotSupported
473
474    @handler_for(ipp.OperationCodes.CREATE_JOB)
475    def create_job(self, request, response):
476        """RFC 2911: 3.2.4 Create-Job Operation
477
478        This OPTIONAL operation is similar to the Print-Job operation
479        (section 3.2.1) except that in the Create-Job request, a
480        client does not supply document data or any reference to
481        document data. Also, the client does not supply any of the
482        'document-name', 'document- format', 'compression', or
483        'document-natural-language' operation attributes. This
484        operation is followed by one or more Send-Document or Send-URI
485        operations. In each of those operation requests, the client
486        OPTIONALLY supplies the 'document-name', 'document-format',
487        and 'document-natural-language' attributes for each document
488        in the multi-document Job object.
489
490        Request
491        -------
492        Group 1: Operation Attributes
493            REQUIRED 'attributes-charset'
494            REQUIRED 'attributes-natural-language'
495            REQUIRED 'printer-uri' (uri)
496            OPTIONAL 'requesting-user-name' (name(MAX))
497            OPTIONAL 'job-name' (name(MAX))
498            OPTIONAL 'ipp-attribute-fidelity' (boolean)
499            OPTIONAL 'job-k-octets' (integer(0:MAX))
500            OPTIONAL 'job-impressions' (integer(0:MAX))
501            OPTIONAL 'job-media-sheets' (integer(0:MAX))
502        Group 2: Job Template Attributes
503
504        Response
505        --------
506        Group 1: Operation Attributes
507            OPTIONAL 'status-message' (text(255))
508            OPTIONAL 'detailed-status-message' (text(MAX))
509            REQUIRED 'attributes-charset'
510            REQUIRED 'attributes-natural-language'
511        Group 2: Unsupported Attributes
512        Group 3: Job Object Attributes
513            REQUIRED 'job-uri' (uri)
514            REQUIRED 'job-id' (integer(1:MAX))
515            REQUIRED 'job-state' (type1 enum)
516            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
517            OPTIONAL 'job-state-message' (text(MAX))
518            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
519       
520        """
521
522        operation = request.attribute_groups[0]
523
524        printer_uri = None
525        requesting_user_name = None
526        job_name = None
527        ipp_attribute_fidelity=None
528        job_k_octets = None
529
530        # requested printer uri
531        if 'printer-uri' not in operation:
532            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
533        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
534        if printer_uri not in self.printer.uris:
535            raise ipp.errors.ClientErrorAttributes(
536                str(operation['printer-uri']), operation['printer-uri'])
537
538        if 'requesting-user-name' in operation:
539            user_name = verify_attribute(
540                operation['requesting-user-name'], ipp.RequestingUserName)[0]
541
542        if 'job-name' in operation:
543            job_name = verify_attribute(
544                operation['job-name'], ipp.JobName)[0]
545
546        if 'job-k-octets' in operation:
547            job_k_octets = verify_attribute(
548                operation['job-k-octets'], ipp.JobKOctets)[0]
549
550        if 'ipp-attribute-fidelity' in operation:
551            pass # don't care
552        if 'job-impressions' in operation:
553            pass # don't care
554        if 'job-media-sheets' in operation:
555            pass # don't care
556
557        # get attributes from the printer and add to response
558        job_id = self.printer.create_job(
559            requesting_user_name=requesting_user_name,
560            job_name=job_name,
561            job_k_octets=job_k_octets)
562        attrs = self.printer.get_job_attributes(job_id)
563        response.attribute_groups.append(ipp.AttributeGroup(
564            ipp.AttributeTags.JOB, attrs))
565   
566    @handler_for(ipp.OperationCodes.PAUSE_PRINTER)
567    def pause_printer(self, request, response):
568        """
569            3.2.7 Pause-Printer Operation
570
571            This OPTIONAL operation allows a client to stop the Printer object
572            from scheduling jobs on all its devices.  Depending on
573            implementation, the Pause-Printer operation MAY also stop the Printer
574            from processing the current job or jobs.  Any job that is currently
575            being printed is either stopped as soon as the implementation permits
576            or is completed, depending on implementation.  The Printer object
577            MUST still accept create operations to create new jobs, but MUST
578            prevent any jobs from entering the 'processing' state.
579
580            If the Pause-Printer operation is supported, then the Resume-Printer
581            operation MUST be supported, and vice-versa.
582
583            The IPP Printer stops the current job(s) on its device(s) that were
584            in the 'processing' or 'processing-stopped' states as soon as the
585            implementation permits.  If the implementation will take appreciable
586            time to stop, the IPP Printer adds the 'moving-to-paused' value to
587            the Printer object's 'printer-state-reasons' attribute (see section
588            4.4.12).  When the device(s) have all stopped, the IPP Printer
589            transitions the Printer object to the 'stopped' state, removes the
590            'moving-to-paused' value, if present, and adds the 'paused' value to
591            the Printer object's 'printer-state-reasons' attribute.
592
593            When the current job(s) complete that were in the 'processing' state,
594            the IPP Printer transitions them to the 'completed' state.  When the
595            current job(s) stop in mid processing that were in the 'processing'
596            state, the IPP Printer transitions them to the 'processing-stopped'
597            state and adds the 'printer-stopped' value to the job's 'job-state-
598            reasons' attribute.
599
600            For any jobs that are 'pending' or 'pending-held', the 'printer-
601            stopped' value of the jobs' 'job-state-reasons' attribute also
602            applies.  However, the IPP Printer NEED NOT update those jobs' 'job-
603            state-reasons' attributes and only need return the 'printer-stopped'
604            value when those jobs are queried (so-called 'lazy evaluation').
605
606            Whether the Pause-Printer operation affects jobs that were submitted
607            to the device from other sources than the IPP Printer object in the
608            same way that the Pause-Printer operation affects jobs that were
609            submitted to the IPP Printer object using IPP, depends on
610            implementation, i.e., on whether the IPP protocol is being used as a
611            universal management protocol or just to manage IPP jobs,
612            respectively.
613
614            The IPP Printer MUST accept the request in any state and transition
615            the Printer to the indicated new 'printer-state' before returning as
616            follows:
617
618            Current        New      'printer   IPP Printer's response status
619            'printer-    'printer-   -state-          code and action:
620            state'       state'    reasons'
621
622            'idle'       'stopped'    'paused'  'successful-ok'
623            'processing' 'processing' 'moving-  OPTION 1: 'successful-ok';
624                                                      to-       Later, when all output has
625                                                      paused'   stopped, the 'printer-state'
626                                                                            becomes 'stopped', and the
627                                                                            'paused' value replaces the
628                                                                            'moving-to-paused' value in the
629                                                                            'printer-state-reasons'
630                                                                            attribute
631            'processing' 'stopped'    'paused'  OPTION 2: 'successful-ok';
632                                                                            all device output stopped
633                                                                            immediately
634            'stopped'    'stopped'    'paused'  'successful-ok'
635
636            Access Rights: The authenticated user (see section 8.3) performing
637            this operation must be an operator or administrator of the Printer
638            object (see Sections 1 and 8.5).   Otherwise, the IPP Printer MUST
639            reject the operation and return:  'client-error-forbidden', 'client-
640            error-not-authenticated', or 'client-error-not-authorized' as
641            appropriate.
642
643            3.2.7.1 Pause-Printer Request
644
645            The following groups of attributes are part of the Pause-Printer
646            Request:
647
648            Group 1: Operation Attributes
649
650            Natural Language and Character Set:
651            The 'attributes-charset' and 'attributes-natural-language'
652            attributes as described in section 3.1.4.1.
653
654            Target:
655            The 'printer-uri' (uri) operation attribute which is the target
656            for this operation as described in section 3.1.5.
657
658            Requesting User Name:
659            The 'requesting-user-name' (name(MAX)) attribute SHOULD be
660            supplied by the client as described in section 8.3.
661
662            3.2.7.2 Pause-Printer Response
663
664            The following groups of attributes are part of the Pause-Printer
665            Response:
666
667            Group 1: Operation Attributes
668
669            Status Message:
670            In addition to the REQUIRED status code returned in every
671            response, the response OPTIONALLY includes a 'status-message'
672            (text(255)) and/or a 'detailed-status-message' (text(MAX))
673            operation attribute as described in sections 13 and  3.1.6.
674
675            Natural Language and Character Set:
676            The 'attributes-charset' and 'attributes-natural-language'
677            attributes as described in section 3.1.4.2.
678
679            Group 2: Unsupported Attributes
680
681            See section 3.1.7 for details on returning Unsupported Attributes.
682
683   
684    """
685    operation = request.attribute_groups[0]
686    printer_uri = None
687    user_name   = None
688    if 'printer-uri' not in operation:
689            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
690        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
691        if printer_uri not in self.printer.uris:
692            raise ipp.errors.ClientErrorAttributes(
693                str(operation['printer-uri']), operation['printer-uri'])
694
695    if 'requesting-user-name' in operation:
696        user_name = verify_attribute(
697            operation['requesting-user-name'], ipp.RequestingUserName)[0]
698   self.printer.pause_printer()
699
700
701    #    raise ipp.errors.ServerErrorOperationNotSupported
702
703    @handler_for(ipp.OperationCodes.RESUME_PRINTER)
704    def resume_printer(self, request, response):
705        """
706        3.2.8 Resume-Printer Operation
707
708        This operation allows a client to resume the Printer object
709        scheduling jobs on all its devices.  The Printer object MUST remove
710        the 'paused' and 'moving-to-paused' values from the Printer object's
711        'printer-state-reasons' attribute, if present.  If there are no other
712        reasons to keep a device paused (such as media-jam), the IPP Printer
713        is free to transition itself to the 'processing' or 'idle' states,
714        depending on whether there are jobs to be processed or not,
715        respectively, and the device(s) resume processing jobs.
716
717        If the Pause-Printer operation is supported, then the Resume-Printer
718        operation MUST be supported, and vice-versa.
719
720        The IPP Printer removes the 'printer-stopped' value from any job's
721        'job-state-reasons' attributes contained in that Printer.
722
723        The IPP Printer MUST accept the request in any state, transition the
724        Printer object to the indicated new state as follows:
725
726
727        Current    New 'printer-  IPP Printer's response status code and
728        'printer-      state'                     action:
729        state'
730
731        'idle'       'idle'         'successful-ok'
732        'processing' 'processing'   'successful-ok'
733
734        'stopped'    'processing'   'successful-ok';
735                                                   when there are jobs to be processed
736        'stopped'    'idle'         'successful-ok';
737                                                   when there are no jobs to be processed.
738
739        Access Rights: The authenticated user (see section 8.3) performing
740        this operation must be an operator or administrator of the Printer
741        object (see Sections 1 and 8.5).  Otherwise, the IPP Printer MUST
742        reject the operation and return:  'client-error-forbidden', 'client-
743        error-not-authenticated', or 'client-error-not-authorized' as
744        appropriate.
745
746        The Resume-Printer Request and Resume-Printer Response have the same
747        attribute groups and attributes as the Pause-Printer operation (see
748        sections 3.2.7.1 and 3.2.7.2).                 
749        """
750    operation = request.attribute_groups[0]
751    printer_uri = None
752    user_name   = None
753    if 'printer-uri' not in operation:
754            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
755        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
756        if printer_uri not in self.printer.uris:
757            raise ipp.errors.ClientErrorAttributes(
758                str(operation['printer-uri']), operation['printer-uri'])
759
760    if 'requesting-user-name' in operation:
761        user_name = verify_attribute(
762            operation['requesting-user-name'], ipp.RequestingUserName)[0]
763   self.printer.resume_printer()
764
765
766    @handler_for(ipp.OperationCodes.GET_PRINTER_ATTRIBUTES)
767    def get_printer_attributes(self, request, response):
768        """RFC 2911: 3.2.5 Get-Printer-Attributes Operation
769
770        This REQUIRED operation allows a client to request the values
771        of the attributes of a Printer object.
772       
773        In the request, the client supplies the set of Printer
774        attribute names and/or attribute group names in which the
775        requester is interested. In the response, the Printer object
776        returns a corresponding attribute set with the appropriate
777        attribute values filled in.
778
779        Request
780        -------
781
782        Group 1: Operation Attributes
783            REQUIRED 'attributes-charset'
784            REQUIRED 'attributes-natural-language'
785            REQUIRED 'printer-uri' (uri)
786            OPTIONAL 'requesting-user-name' (name(MAX))
787            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
788            OPTIONAL 'document-format' (mimeMediaType)
789           
790        Response
791        --------
792
793        Group 1: Operation Attributes
794            OPTIONAL 'status-message' (text(255))
795            OPTIONAL 'detailed-status-message' (text(MAX))
796            REQUIRED 'attributes-charset'
797            REQUIRED 'attributes-natural-language'
798        Group 2: Unsupported Attributes
799        Group 3: Printer Object Attributes
800
801        """
802
803        operation = request.attribute_groups[0]
804
805        printer_uri = None
806        requesting_user_name = None
807        requested_attributes = None
808        document_format = None
809
810        # requested printer uri
811        if 'printer-uri' not in operation:
812            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
813        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
814        if printer_uri not in self.printer.uris:
815            raise ipp.errors.ClientErrorAttributes(
816                str(operation['printer-uri']), operation['printer-uri'])
817
818        # optional attributes
819        if 'requesting-user-name' in operation:
820            user_name = verify_attribute(
821                operation['requesting-user-name'], ipp.RequestingUserName)[0]
822           
823        if 'requested-attributes' in operation:
824            requested_attributes = verify_attribute(
825                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
826
827        if 'document-format' in operation:
828            pass # XXX: todo
829
830        # get attributes from the printer and add to response
831        response.attribute_groups.append(ipp.AttributeGroup(
832            ipp.AttributeTags.PRINTER,
833            self.printer.get_printer_attributes(
834                requested_attributes=requested_attributes)))
835
836    @handler_for(ipp.OperationCodes.SET_PRINTER_ATTRIBUTES)
837    def set_printer_attributes(self, request, response):
838
839        raise ipp.errors.ServerErrorOperationNotSupported
840
841    ##### Job Commands
842
843    @handler_for(ipp.OperationCodes.CANCEL_JOB)
844    def cancel_job(self, request, response):
845        """3.3.3 Cancel-Job Operation
846
847        This REQUIRED operation allows a client to cancel a Print Job from
848        the time the job is created up to the time it is completed, canceled,
849        or aborted. Since a Job might already be printing by the time a
850        Cancel-Job is received, some media sheet pages might be printed
851        before the job is actually terminated.
852
853        The IPP object MUST accept or reject the request based on the job's
854        current state and transition the job to the indicated new state as
855        follows:
856
857        Current State       New State           Response
858        -----------------------------------------------------------------
859        pending             canceled            successful-ok
860        pending-held        canceled            successful-ok
861        processing          canceled            successful-ok
862        processing          processing          successful-ok               See Rule 1
863        processing          processing          client-error-not-possible   See Rule 2
864        processing-stopped  canceled            successful-ok
865        processing-stopped  processing-stopped  successful-ok               See Rule 1
866        processing-stopped  processing-stopped  client-error-not-possible   See Rule 2
867        completed           completed           client-error-not-possible
868        canceled            canceled            client-error-not-possible
869        aborted             aborted             client-error-not-possible
870
871        Rule 1: If the implementation requires some measurable time to
872        cancel the job in the 'processing' or 'processing-stopped' job
873        states, the IPP object MUST add the 'processing-to-stop-point'
874        value to the job's 'job-state-reasons' attribute and then
875        transition the job to the 'canceled' state when the processing
876        ceases (see section 4.3.8).
877
878        Rule 2: If the Job object already has the
879        'processing-to-stop-point' value in its 'job-state-reasons'
880        attribute, then the Printer object MUST reject a Cancel-Job
881        operation.
882
883        Access Rights: The authenticated user (see section 8.3)
884        performing this operation must either be the job owner or an
885        operator or administrator of the Printer object (see Sections
886        1 and 8.5).  Otherwise, the IPP object MUST reject the
887        operation and return: 'client-error-forbidden',
888        'client-error-not-authenticated', or
889        'client-error-not-authorized' as appropriate.
890
891        Request
892        -------
893
894        Group 1: Operation Attributes
895            REQUIRED 'attributes-charset'
896            REQUIRED 'attributes-natural-language'
897            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
898              -or-   'job-uri' (uri)
899            OPTIONAL 'requesting-user-name' (name(MAX))
900            OPTIONAL 'message' (text(127))
901           
902        Response
903        --------
904
905        Group 1: Operation Attributes
906            OPTIONAL 'status-message' (text(255))
907            OPTIONAL 'detailed-status-message' (text(MAX))
908            REQUIRED 'attributes-charset'
909            REQUIRED 'attributes-natural-language'
910        Group 2: Unsupported Attributes
911
912        """
913
914        operation = request.attribute_groups[0]
915
916        job_id = None
917        printer_uri = None
918        requesting_user_name = None
919        message = None
920
921        # required attributes
922        if 'job-id' in operation and 'printer-uri' in operation:
923            job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
924            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
925            if printer_uri not in self.printer.uris:
926                raise ipp.errors.ClientErrorAttributes(
927                    str(operation['printer-uri']), operation['printer-uri'])
928
929        elif 'job-uri' in operation:
930            job_uri = verify_attribute(operation['job-uri'], ipp.JobUri)[0]
931            job_id = int(job_uri.split("/")[-1])
932
933        if 'requesting-user-name' in operation:
934            requesting_user_name = verify_attribute(
935                operation['requesting-user-name'], ipp.RequestingUserName)[0]
936
937        try:
938            self.printer.cancel_job(job_id, requesting_user_name=requesting_user_name)
939        except InvalidJobException:
940            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
941
942    @handler_for(ipp.OperationCodes.SEND_DOCUMENT)
943    def send_document(self, request, response):
944        """3.3.1 Send-Document Operation
945       
946        This OPTIONAL operation allows a client to create a
947        multi-document Job object that is initially 'empty' (contains
948        no documents). In the Create-Job response, the Printer object
949        returns the Job object's URI (the 'job-uri' attribute) and the
950        Job object's 32-bit identifier (the 'job-id' attribute). For
951        each new document that the client desires to add, the client
952        uses a Send-Document operation. Each Send- Document Request
953        contains the entire stream of document data for one document.
954
955        If the Printer supports this operation but does not support
956        multiple documents per job, the Printer MUST reject subsequent
957        Send-Document operations supplied with data and return the
958        'server-error-multiple- document-jobs-not-supported'. However,
959        the Printer MUST accept the first document with a 'true' or
960        'false' value for the 'last-document' operation attribute (see
961        below), so that clients MAY always submit one document jobs
962        with a 'false' value for 'last-document' in the first
963        Send-Document and a 'true' for 'last-document' in the second
964        Send-Document (with no data).
965       
966        Since the Create-Job and the send operations (Send-Document or
967        Send- URI operations) that follow could occur over an
968        arbitrarily long period of time for a particular job, a client
969        MUST send another send operation within an IPP Printer defined
970        minimum time interval after the receipt of the previous
971        request for the job. If a Printer object supports the
972        Create-Job and Send-Document operations, the Printer object
973        MUST support the 'multiple-operation-time-out' attribute (see
974        section 4.4.31). This attribute indicates the minimum number
975        of seconds the Printer object will wait for the next send
976        operation before taking some recovery action.
977
978        An IPP object MUST recover from an errant client that does not
979        supply a send operation, sometime after the minimum time
980        interval specified by the Printer object's
981        'multiple-operation-time-out' attribute.
982
983        Access Rights: The authenticated user (see section 8.3)
984        performing this operation must either be the job owner (as
985        determined in the Create-Job operation) or an operator or
986        administrator of the Printer object (see Sections 1 and
987        8.5). Otherwise, the IPP object MUST reject the operation and
988        return: 'client-error-forbidden', 'client-
989        error-not-authenticated', or 'client-error-not-authorized' as
990        appropriate.
991
992        Request
993        -------
994
995        Group 1: Operation Attributes
996            REQUIRED 'attributes-charset'
997            REQUIRED 'attributes-natural-language'
998            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
999              -or-   'job-uri' (uri)
1000            OPTIONAL 'requesting-user-name' (name(MAX))
1001            OPTIONAL 'document-name' (name(MAX))
1002            OPTIONAL 'compression' (type3 keyword)
1003            OPTIONAL 'document-format' (mimeMediaType)
1004            OPTIONAL 'document-natural-language' (naturalLanguage)
1005            OPTIONAL 'last-document' (boolean)
1006        Group 2: Document Content
1007           
1008        Response
1009        --------
1010
1011        Group 1: Operation Attributes
1012            OPTIONAL 'status-message' (text(255))
1013            OPTIONAL 'detailed-status-message' (text(MAX))
1014            REQUIRED 'attributes-charset'
1015            REQUIRED 'attributes-natural-language'
1016        Group 2: Unsupported Attributes
1017        Group 3: Job Object Attributes
1018            REQUIRED 'job-uri' (uri)
1019            REQUIRED 'job-id' (integer(1:MAX))
1020            REQUIRED 'job-state' (type1 enum)
1021            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
1022            OPTIONAL 'job-state-message' (text(MAX))
1023            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
1024
1025        """
1026       
1027        operation = request.attribute_groups[0]
1028
1029        job_id = None
1030        printer_uri = None
1031        requesting_user_name = None
1032        document_name = None
1033        compression = None
1034        document_format = None
1035        document_natural_language = None
1036        last_document = None
1037
1038        # required attributes
1039        if 'job-id' not in operation:
1040            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
1041        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
1042
1043        if 'last-document' not in operation:
1044            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
1045        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
1046        if not last_document:
1047            raise ipp.errors.ServerErrorMultipleJobsNotSupported
1048
1049        # optional attributes
1050        if 'printer-uri' in operation:
1051            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
1052            if printer_uri not in self.printer.uris:
1053                raise ipp.errors.ClientErrorAttributes(
1054                    str(operation['printer-uri']), operation['printer-uri'])
1055
1056        if 'requesting-user-name' in operation:
1057            user_name = verify_attribute(
1058                operation['requesting-user-name'], ipp.RequestingUserName)[0]
1059
1060        if 'document-name' in operation:
1061            document_name = verify_attribute(
1062                operation['document-name'], ipp.DocumentName)[0]
1063
1064        if 'compression' in operation:
1065            compression = verify_attribute(
1066                operation['compression'], ipp.Compression)[0]
1067
1068        if 'document-format' in operation:
1069            document_format = verify_attribute(
1070                operation['document-format'], ipp.DocumentFormat)[0]
1071
1072        if 'document-natural-language' in operation:
1073            document_natural_language = verify_attribute(
1074                operation['document_natural_language'],
1075                ipp.DocumentNaturalLanguage)[0]
1076
1077        try:
1078            self.printer.send_document(
1079                job_id,
1080                request.data,
1081                document_name=document_name,
1082                document_format=document_format,
1083                document_natural_language=document_natural_language,
1084                requesting_user_name=user_name,
1085                compression=compression,
1086                last_document=last_document)
1087            attrs = self.printer.get_job_attributes(job_id)
1088        except InvalidJobException:
1089            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
1090
1091        response.attribute_groups.append(ipp.AttributeGroup(
1092            ipp.AttributeTags.JOB, attrs))
1093
1094    @handler_for(ipp.OperationCodes.SEND_URI)
1095    def send_uri(self, request, response):
1096
1097        """3.2.2 Send URI
1098
1099        This OPTIONAL operation is identical to the Send-Document
1100        operation (see section 3.3.1) except that a client MUST supply
1101        a URI reference ('document-uri' operation attribute) rather
1102        than the document data itself.  If a Printer object supports
1103        this operation, clients can use both Send-URI or Send-Document
1104        operations to add new documents to an existing multi-document
1105        Job object.  However, if a client needs to indicate that the
1106        previous Send-URI or Send-Document was the last document, the
1107        client MUST use the Send-Document operation with no document
1108        data and the 'last-document' flag set to 'true' (rather than
1109        using a Send-URI operation with no 'document-uri' operation
1110        attribute).
1111
1112        If a Printer object supports this operation, it MUST also
1113        support the Print-URI operation (see section 3.2.2).
1114
1115        The Printer object MUST validate the syntax and URI scheme of
1116        the supplied URI before returning a response, just as in the
1117        Print-URI operation.  The IPP Printer MAY validate the
1118        accessibility of the document as part of the operation or
1119        subsequently (see section 3.2.2).
1120
1121        Request
1122        -------
1123
1124        Group 1: Operation Attributes
1125            REQUIRED 'attributes-charset'
1126            REQUIRED 'attributes-natural-language'
1127            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
1128            REQUIRED 'document-uri' (uri)
1129            OPTIONAL 'job-uri' (uri)
1130            OPTIONAL 'requesting-user-name' (name(MAX))
1131            OPTIONAL 'document-name' (name(MAX))
1132            OPTIONAL 'compression' (type3 keyword)
1133            OPTIONAL 'document-format' (mimeMediaType)
1134            OPTIONAL 'document-natural-language' (naturalLanguage)
1135           
1136        Response
1137        --------
1138
1139        Group 1: Operation Attributes
1140            OPTIONAL 'status-message' (text(255))
1141            OPTIONAL 'detailed-status-message' (text(MAX))
1142            REQUIRED 'attributes-charset'
1143            REQUIRED 'attributes-natural-language'
1144        Group 2: Unsupported Attributes
1145        Group 3: Job Object Attributes
1146            REQUIRED 'job-uri' (uri)
1147            REQUIRED 'job-id' (integer(1:MAX))
1148            REQUIRED 'job-state' (type1 enum)
1149            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
1150            OPTIONAL 'job-state-message' (text(MAX))
1151            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
1152
1153        """
1154       
1155        operation = request.attribute_groups[0]
1156
1157        job_id = None
1158        printer_uri = None
1159        requesting_user_name = None
1160        document_name = None
1161        compression = None
1162        document_format = None
1163        document_natural_language = None
1164        last_document = None
1165
1166        # required attributes
1167        if 'job-id' not in operation:
1168            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
1169        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
1170
1171        if 'last-document' not in operation:
1172            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
1173        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
1174
1175        if 'document-uri' not in operation:
1176            raise ipp.errors.ClientErrorBadRequest("Missing 'document-uri' attribute")
1177        document_uri = verify_attribute(operation['document-uri'], ipp.DocumentUri)[0]
1178        if not last_document:
1179            raise ipp.errors.ServerErrorMultipleJobsNotSupported
1180
1181        # optional attributes
1182        if 'printer-uri' in operation:
1183            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
1184            if printer_uri not in self.printer.uris:
1185                raise ipp.errors.ClientErrorAttributes(
1186                    str(operation['printer-uri']), operation['printer-uri'])
1187
1188        if 'requesting-user-name' in operation:
1189            user_name = verify_attribute(
1190                operation['requesting-user-name'], ipp.RequestingUserName)[0]
1191
1192        if 'document-name' in operation:
1193            document_name = verify_attribute(
1194                operation['document-name'], ipp.DocumentName)[0]
1195
1196        if 'compression' in operation:
1197            compression = verify_attribute(
1198                operation['compression'], ipp.Compression)[0]
1199
1200        if 'document-format' in operation:
1201            document_format = verify_attribute(
1202                operation['document-format'], ipp.DocumentFormat)[0]
1203
1204        if 'document-natural-language' in operation:
1205            document_natural_language = verify_attribute(
1206                operation['document_natural_language'],
1207                ipp.DocumentNaturalLanguage)[0]
1208
1209        try:
1210            self.printer.send_uri(
1211                job_id,
1212                document_uri,
1213                document_name=document_name,
1214                document_format=document_format,
1215                document_natural_language=document_natural_language,
1216                requesting_user_name=user_name,
1217                compression=compression,
1218                last_document=last_document)
1219            attrs = self.printer.get_job_attributes(job_id)
1220        except InvalidJobException:
1221            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
1222
1223        response.attribute_groups.append(ipp.AttributeGroup(
1224            ipp.AttributeTags.JOB, attrs))
1225
1226    @handler_for(ipp.OperationCodes.GET_JOB_ATTRIBUTES)
1227    def get_job_attributes(self, request, response):
1228        """3.3.4 Get-Job-Attributes Operation
1229
1230        This REQUIRED operation allows a client to request the values
1231        of attributes of a Job object and it is almost identical to
1232        the Get- Printer-Attributes operation (see section 3.2.5). The
1233        only differences are that the operation is directed at a Job
1234        object rather than a Printer object, there is no
1235        'document-format' operation attribute used when querying a Job
1236        object, and the returned attribute group is a set of Job
1237        object attributes rather than a set of Printer object
1238        attributes.
1239
1240        For Jobs, the possible names of attribute groups are:
1241          - 'job-template': the subset of the Job Template attributes
1242            that apply to a Job object (the first column of the table
1243            in Section 4.2) that the implementation supports for Job
1244            objects.
1245          - 'job-description': the subset of the Job Description
1246            attributes specified in Section 4.3 that the
1247            implementation supports for Job objects.
1248          - 'all': the special group 'all' that includes all
1249            attributes that the implementation supports for Job
1250            objects.
1251
1252        Since a client MAY request specific attributes or named
1253        groups, there is a potential that there is some overlap. For
1254        example, if a client requests, 'job-name' and
1255        'job-description', the client is actually requesting the
1256        'job-name' attribute once by naming it explicitly, and once by
1257        inclusion in the 'job-description' group. In such cases, the
1258        Printer object NEED NOT return the attribute only once in the
1259        response even if it is requested multiple times. The client
1260        SHOULD NOT request the same attribute in multiple ways.
1261
1262        It is NOT REQUIRED that a Job object support all attributes
1263        belonging to a group (since some attributes are
1264        OPTIONAL). However it is REQUIRED that each Job object support
1265        all these group names.
1266
1267        Request
1268        -------
1269
1270        Group 1: Operation Attributes
1271            REQUIRED 'attributes-charset'
1272            REQUIRED 'attributes-natural-language'
1273            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
1274              -or-   'job-uri' (uri)
1275            OPTIONAL 'requesting-user-name' (name(MAX))
1276            OPTIONAL 'requested-attributes' (1setOf keyword)
1277           
1278        Response
1279        --------
1280
1281        Group 1: Operation Attributes
1282            OPTIONAL 'status-message' (text(255))
1283            OPTIONAL 'detailed-status-message' (text(MAX))
1284            REQUIRED 'attributes-charset'
1285            REQUIRED 'attributes-natural-language'
1286        Group 2: Unsupported Attributes
1287        Group 3: Job Object Attributes
1288
1289        """
1290       
1291        operation = request.attribute_groups[0]
1292
1293        job_id = None
1294        printer_uri = None
1295        requesting_user_name = None
1296        requested_attributes = None
1297
1298        # required attributes
1299        if 'job-id' not in operation:
1300            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
1301        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
1302
1303        # optional attributes
1304        if 'printer-uri' in operation:
1305            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
1306            if printer_uri not in self.printer.uris:
1307                raise ipp.errors.ClientErrorAttributes(
1308                    str(operation['printer-uri']), operation['printer-uri'])
1309
1310        # optional attributes
1311        if 'requesting-user-name' in operation:
1312            user_name = verify_attribute(
1313                operation['requesting-user-name'], ipp.RequestingUserName)[0]
1314           
1315        if 'requested-attributes' in operation:
1316            requested_attributes = verify_attribute(
1317                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
1318
1319        # get the job attributes and add them to the response
1320        try:
1321            attrs = self.printer.get_job_attributes(
1322                job_id,
1323                requested_attributes=requested_attributes)
1324        except InvalidJobException:
1325            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
1326
1327        response.attribute_groups.append(ipp.AttributeGroup(
1328            ipp.AttributeTags.JOB, attrs))
1329
1330    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
1331    def set_job_attributes(self, request, response):
1332       
1333        raise ipp.errors.ServerErrorOperationNotSupported
1334
1335    @handler_for(ipp.OperationCodes.RESTART_JOB)
1336    def restart_job(self, request, response):
1337        raise ipp.errors.ServerErrorOperationNotSupported
1338
1339    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
1340    def promote_job(self, request, response):
1341        raise ipp.errors.ServerErrorOperationNotSupported
1342
1343    ##### CUPS Specific Commands
1344
1345    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
1346    def cups_get_document(self, request, response):
1347        raise ipp.errors.ServerErrorOperationNotSupported
1348
1349    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
1350    def cups_get_default(self, request, response):
1351        """The CUPS-Get-Default operation (0x4001) returns the default
1352        printer URI and attributes.
1353
1354        Request
1355        -------
1356
1357        Group 1: Operation Attributes
1358            REQUIRED 'attributes-charset'
1359            REQUIRED 'attributes-natural-language'
1360            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
1361
1362        Response
1363        --------
1364
1365        Group 1: Operation Attributes
1366            OPTIONAL 'status-message' (text(255))
1367            REQUIRED 'attributes-charset'
1368            REQUIRED 'attributes-natural-language'
1369        Group 2: Printer Object Attributes
1370
1371        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
1372
1373        """
1374
1375        operation = request.attribute_groups[0]
1376        requested_attributes = None
1377       
1378        if 'requested-attributes' in operation:
1379            requested_attributes = verify_attribute(
1380                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
1381
1382        # get attributes from the printer and add to response
1383        attrs = self.printer.get_printer_attributes(
1384            requested_attributes=requested_attributes)
1385        response.attribute_groups.append(ipp.AttributeGroup(
1386            ipp.AttributeTags.PRINTER, attrs))
1387
1388    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
1389    def cups_get_printers(self, request, response):
1390        """The CUPS-Get-Printers operation (0x4002) returns the
1391        printer attributes for every printer known to the system. This
1392        may include printers that are not served directly by the
1393        server.
1394
1395        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
1396           
1397        """
1398
1399        # get attributes from the printer and add to response
1400        response.attribute_groups.append(ipp.AttributeGroup(
1401            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
1402
1403    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
1404    def cups_get_classes(self, request, response):
1405        """The CUPS-Get-Classes operation (0x4005) returns the printer
1406        attributes for every printer class known to the system. This
1407        may include printer classes that are not served directly by
1408        the server.
1409
1410        Request
1411        -------
1412
1413        Group 1: Operation Attributes
1414            REQUIRED 'attributes-charset'
1415            REQUIRED 'attributes-natural-language'
1416            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
1417            OPTIONAL 'limit' (integer (1:MAX))
1418            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
1419            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
1420            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
1421            OPTIONAL 'requested-attributes' (1setOf keyword)
1422            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
1423            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
1424
1425        Response
1426        --------
1427
1428        Group 1: Operation Attributes
1429            OPTIONAL 'status-message' (text(255))
1430            REQUIRED 'attributes-charset'
1431            REQUIRED 'attributes-natural-language'
1432        Group 2: Printer Class Object Attributes
1433
1434        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
1435
1436        """
1437       
1438        raise ipp.errors.ServerErrorOperationNotSupported
1439
Note: See TracBrowser for help on using the repository browser.