source: server/lib/gutenbach/server/requests.py @ 079ef11

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

wrote print_job/print_uri

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