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

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

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

  • Property mode set to 100644
File size: 36.6 KB
RevLine 
[34a4e5d]1from . import InvalidJobException, InvalidPrinterStateException, InvalidJobStateException
[b2e077a]2import gutenbach.ipp as ipp
[287d6ec]3import logging
[5e44432]4import traceback
[aded2d1]5import sys
[287d6ec]6
[d04a689]7# initialize logger
8logger = logging.getLogger(__name__)
[287d6ec]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
[1037115]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
[b01b6d1]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
[b2e077a]48class GutenbachRequestHandler(object):
[287d6ec]49
[eee389a]50    def __init__(self, gutenbach_printer):
51        self.printer = gutenbach_printer
[1037115]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]
[b01b6d1]66        charset = verify_attribute(charset_attr, ipp.AttributesCharset)[0]
67        if charset != 'utf-8':
[1037115]68            raise ipp.errors.ClientErrorAttributes(str(charset_attr))
69
70        # check for attributes-natural-language
71        natlang_attr = operation.attributes[1]
[b01b6d1]72        natlang = verify_attribute(natlang_attr, ipp.AttributesNaturalLanguage)[0]
73        if natlang != 'en-us':
[1037115]74            raise ipp.errors.ClientErrorAttributes(str(natlang_attr))
[287d6ec]75   
[ef8df33]76    def handle(self, request):
[b2e077a]77        # look up the handler
78        handler = None
[9eeab06]79        handler_name = None
[287d6ec]80        for d in dir(self):
81            if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id:
[9eeab06]82                handler_name = d
[b2e077a]83                break
[1037115]84
[b2e077a]85        # we couldn't find a handler, so default to unknown operation
[9eeab06]86        if handler_name is None:
87            handler_name = "unknown_operation"
[1037115]88
89        # actually get the handler
[9eeab06]90        handler = getattr(self, handler_name)
[7c143c9]91        logger.info("request is '%s'" % handler_name)
[aef164a]92
[1037115]93        # try to handle the request
[aef164a]94        try:
[1037115]95            self.generic_handle(request)
96            response = make_empty_response(request)
97            handler(request, response)
[aef164a]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()
[b828a96]104            logger.error("%s: %s" % (exctype.__name__, excval.message))
[1037115]105            response = make_empty_response(request)
[aef164a]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())
[1037115]112            response = make_empty_response(request)
[aef164a]113            response.operation_id = ipp.StatusCodes.INTERNAL_ERROR
114
[ef8df33]115        return response
[287d6ec]116
[1037115]117    def unknown_operation(self, request, response):
[7c143c9]118        logger.warning("unknown operation 0x%x" % request.operation_id)
[1037115]119        response = make_empty_response(request)
[793432f]120        response.operation_id = ipp.StatusCodes.OPERATION_NOT_SUPPORTED
[ef8df33]121        return response
[0ede474]122       
[9eeab06]123    ##### Printer Commands
124
[793432f]125    @handler_for(ipp.OperationCodes.PRINT_JOB)
[1037115]126    def print_job(self, request, response):
[7a7a09e]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
[1037115]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
[7a7a09e]170        """
171       
[b828a96]172        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]173
[793432f]174    @handler_for(ipp.OperationCodes.VALIDATE_JOB)
[1037115]175    def validate_job(self, request, response):
[94a4825]176
[b828a96]177        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]178
[793432f]179    @handler_for(ipp.OperationCodes.GET_JOBS)
[1037115]180    def get_jobs(self, request, response):
181        """3.2.6 Get-Jobs Operation
[6ed9d7a]182       
183        This REQUIRED operation allows a client to retrieve the list
184        of Job objects belonging to the target Printer object. The
185        client may also supply a list of Job attribute names and/or
186        attribute group names. A group of Job object attributes will
187        be returned for each Job object that is returned.
188
189        This operation is similar to the Get-Job-Attributes operation,
190        except that this Get-Jobs operation returns attributes from
191        possibly more than one object.
192
[1037115]193        Request
194        -------
195        Group 1: Operation Attributes
196            REQUIRED 'attributes-charset'
197            REQUIRED 'attributes-natural-language'
198            REQUIRED 'printer-uri' (uri)
199            OPTIONAL 'requesting-user-name' (name(MAX))
200            OPTIONAL 'limit' (integer(1:MAX))
201            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
202            OPTIONAL 'which-jobs' (type2 keyword)
203            OPTIONAL 'my-jobs' (boolean)
204
205        Response
206        --------
207        Group 1: Operation Attributes
208            OPTIONAL 'status-message' (text(255))
209            OPTIONAL 'detailed-status-message' (text(MAX))
210            REQUIRED 'attributes-charset'
211            REQUIRED 'attributes-natural-language'
212        Group 2: Unsupported Attributes
213        Groups 3 to N: Job Object Attributes
214
[6ed9d7a]215        """
[59a1d4a]216
[1037115]217        operation = request.attribute_groups[0]
[59a1d4a]218
[b01b6d1]219        # initialize operation attribute variables
220        printer_name = None
221        user = None
222        limit = None
223        attributes = None
224        which_jobs = None
225        my_jobs = None
226
[1037115]227        # requested printer uri
228        if 'printer-uri' not in operation:
229            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
[b01b6d1]230        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
[d994f15]231        if printer_uri not in self.printer.uris and printer_uri != "ipp://localhost/":
[b01b6d1]232            raise ipp.errors.ClientErrorAttributes(
233                str(operation['printer-uri']), operation['printer-uri'])
234
235        # optional attributes
236        if 'limit' in operation:
237            limit = verify_attribute(
238                operation['limit'], ipp.Limit)[0]
239           
240        if 'requested-attributes' in operation:
241            attributes = verify_attribute(
242                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
243           
244        if 'which-jobs' in operation:
245            which_jobs = verify_attribute(
246                operation['which-jobs'], ipp.WhichJobs)[0]
247           
248        if 'my-jobs' in operation:
249            my_jobs = verify_attribute(
250                operation['my-jobs'], ipp.MyJobs)[0]
251
252        if 'requesting-user-name' in operation:
253            user = verify_attribute(
254                operation['requesting-user-name'], ipp.RequestingUserName)[0]
255            # ignore if we're not filtering jobs by user
256            if not my_jobs:
257                user = None
258           
[1037115]259        # get the job attributes and add them to the response
[33ea505]260        job_attrs = self.printer.get_jobs(
[b01b6d1]261            which_jobs=which_jobs,
[33ea505]262            requesting_user_name=user,
263            requested_attributes=attributes)
264        for attrs in job_attrs:
[1037115]265            response.attribute_groups.append(ipp.AttributeGroup(
266                ipp.AttributeTags.JOB, attrs))
[287d6ec]267
[793432f]268    @handler_for(ipp.OperationCodes.PRINT_URI)
[1037115]269    def print_uri(self, request, response):
[b828a96]270        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]271
[793432f]272    @handler_for(ipp.OperationCodes.CREATE_JOB)
[1037115]273    def create_job(self, request, response):
[94a4825]274        """RFC 2911: 3.2.4 Create-Job Operation
275
276        This OPTIONAL operation is similar to the Print-Job operation
277        (section 3.2.1) except that in the Create-Job request, a
278        client does not supply document data or any reference to
279        document data. Also, the client does not supply any of the
280        'document-name', 'document- format', 'compression', or
281        'document-natural-language' operation attributes. This
282        operation is followed by one or more Send-Document or Send-URI
283        operations. In each of those operation requests, the client
284        OPTIONALLY supplies the 'document-name', 'document-format',
285        and 'document-natural-language' attributes for each document
286        in the multi-document Job object.
287
[ee8e6d0]288        Request
289        -------
290        Group 1: Operation Attributes
291            REQUIRED 'attributes-charset'
292            REQUIRED 'attributes-natural-language'
293            REQUIRED 'printer-uri' (uri)
294            OPTIONAL 'requesting-user-name' (name(MAX))
295            OPTIONAL 'job-name' (name(MAX))
296            OPTIONAL 'ipp-attribute-fidelity' (boolean)
297            OPTIONAL 'job-k-octets' (integer(0:MAX))
298            OPTIONAL 'job-impressions' (integer(0:MAX))
299            OPTIONAL 'job-media-sheets' (integer(0:MAX))
300        Group 2: Job Template Attributes
301
302        Response
303        --------
[1037115]304        Group 1: Operation Attributes
[ee8e6d0]305            OPTIONAL 'status-message' (text(255))
306            OPTIONAL 'detailed-status-message' (text(MAX))
307            REQUIRED 'attributes-charset'
308            REQUIRED 'attributes-natural-language'
309        Group 2: Unsupported Attributes
310        Group 3: Job Object Attributes
311            REQUIRED 'job-uri' (uri)
312            REQUIRED 'job-id' (integer(1:MAX))
313            REQUIRED 'job-state' (type1 enum)
314            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
315            OPTIONAL 'job-state-message' (text(MAX))
316            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
[94a4825]317       
318        """
[9eeab06]319
[ee8e6d0]320        operation = request.attribute_groups[0]
321
[b01b6d1]322        printer_uri = None
323        requesting_user_name = None
324        job_name = None
325        ipp_attribute_fidelity=None
326        job_k_octets = None
327
[ee8e6d0]328        # requested printer uri
329        if 'printer-uri' not in operation:
330            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
[b01b6d1]331        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
332        if printer_uri not in self.printer.uris:
333            raise ipp.errors.ClientErrorAttributes(
334                str(operation['printer-uri']), operation['printer-uri'])
335
336        if 'requesting-user-name' in operation:
337            user_name = verify_attribute(
338                operation['requesting-user-name'], ipp.RequestingUserName)[0]
339
340        if 'job-name' in operation:
341            job_name = verify_attribute(
342                operation['job-name'], ipp.JobName)[0]
343
344        if 'job-k-octets' in operation:
345            job_k_octets = verify_attribute(
346                operation['job-k-octets'], ipp.JobKOctets)[0]
347
348        if 'ipp-attribute-fidelity' in operation:
349            pass # don't care
350        if 'job-impressions' in operation:
351            pass # don't care
352        if 'job-media-sheets' in operation:
353            pass # don't care
[ee8e6d0]354
355        # get attributes from the printer and add to response
[33ea505]356        job_id = self.printer.create_job(
[b01b6d1]357            requesting_user_name=requesting_user_name,
358            job_name=job_name,
359            job_k_octets=job_k_octets)
[33ea505]360        attrs = self.printer.get_job_attributes(job_id)
[ee8e6d0]361        response.attribute_groups.append(ipp.AttributeGroup(
[33ea505]362            ipp.AttributeTags.JOB, attrs))
[94a4825]363   
[793432f]364    @handler_for(ipp.OperationCodes.PAUSE_PRINTER)
[1037115]365    def pause_printer(self, request, response):
[b828a96]366        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]367
[793432f]368    @handler_for(ipp.OperationCodes.RESUME_PRINTER)
[1037115]369    def resume_printer(self, request, response):
[b828a96]370        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]371
[793432f]372    @handler_for(ipp.OperationCodes.GET_PRINTER_ATTRIBUTES)
[1037115]373    def get_printer_attributes(self, request, response):
[cad7502]374        """RFC 2911: 3.2.5 Get-Printer-Attributes Operation
375
376        This REQUIRED operation allows a client to request the values
377        of the attributes of a Printer object.
378       
379        In the request, the client supplies the set of Printer
380        attribute names and/or attribute group names in which the
381        requester is interested. In the response, the Printer object
382        returns a corresponding attribute set with the appropriate
383        attribute values filled in.
384
[1037115]385        Request
386        -------
[cad7502]387
[1037115]388        Group 1: Operation Attributes
389            REQUIRED 'attributes-charset'
390            REQUIRED 'attributes-natural-language'
391            REQUIRED 'printer-uri' (uri)
392            OPTIONAL 'requesting-user-name' (name(MAX))
393            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
[ee8e6d0]394            OPTIONAL 'document-format' (mimeMediaType)
395           
[1037115]396        Response
397        --------
398
399        Group 1: Operation Attributes
400            OPTIONAL 'status-message' (text(255))
401            OPTIONAL 'detailed-status-message' (text(MAX))
402            REQUIRED 'attributes-charset'
403            REQUIRED 'attributes-natural-language'
404        Group 2: Unsupported Attributes
405        Group 3: Printer Object Attributes
[cad7502]406
407        """
408
[1037115]409        operation = request.attribute_groups[0]
[59a1d4a]410
[b01b6d1]411        printer_uri = None
412        requesting_user_name = None
413        requested_attributes = None
414        document_format = None
415
[1037115]416        # requested printer uri
417        if 'printer-uri' not in operation:
418            raise ipp.errors.ClientErrorBadRequest("Missing 'printer-uri' attribute")
[b01b6d1]419        printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
420        if printer_uri not in self.printer.uris:
421            raise ipp.errors.ClientErrorAttributes(
422                str(operation['printer-uri']), operation['printer-uri'])
423
424        # optional attributes
425        if 'requesting-user-name' in operation:
426            user_name = verify_attribute(
427                operation['requesting-user-name'], ipp.RequestingUserName)[0]
428           
429        if 'requested-attributes' in operation:
430            requested_attributes = verify_attribute(
431                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
432
433        if 'document-format' in operation:
434            pass # XXX: todo
[59a1d4a]435
[1037115]436        # get attributes from the printer and add to response
437        response.attribute_groups.append(ipp.AttributeGroup(
[b01b6d1]438            ipp.AttributeTags.PRINTER,
439            self.printer.get_printer_attributes(
440                requested_attributes=requested_attributes)))
[9eeab06]441
[793432f]442    @handler_for(ipp.OperationCodes.SET_PRINTER_ATTRIBUTES)
[1037115]443    def set_printer_attributes(self, request, response):
[b828a96]444        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]445
446    ##### Job Commands
447
[793432f]448    @handler_for(ipp.OperationCodes.CANCEL_JOB)
[1037115]449    def cancel_job(self, request, response):
[34a4e5d]450        """3.3.3 Cancel-Job Operation
451
452        This REQUIRED operation allows a client to cancel a Print Job from
453        the time the job is created up to the time it is completed, canceled,
454        or aborted. Since a Job might already be printing by the time a
455        Cancel-Job is received, some media sheet pages might be printed
456        before the job is actually terminated.
457
458        The IPP object MUST accept or reject the request based on the job's
459        current state and transition the job to the indicated new state as
460        follows:
461
462        Current State       New State           Response
463        -----------------------------------------------------------------
464        pending             canceled            successful-ok
465        pending-held        canceled            successful-ok
466        processing          canceled            successful-ok
467        processing          processing          successful-ok               See Rule 1
468        processing          processing          client-error-not-possible   See Rule 2
469        processing-stopped  canceled            successful-ok
470        processing-stopped  processing-stopped  successful-ok               See Rule 1
471        processing-stopped  processing-stopped  client-error-not-possible   See Rule 2
472        completed           completed           client-error-not-possible
473        canceled            canceled            client-error-not-possible
474        aborted             aborted             client-error-not-possible
475
476        Rule 1: If the implementation requires some measurable time to
477        cancel the job in the 'processing' or 'processing-stopped' job
478        states, the IPP object MUST add the 'processing-to-stop-point'
479        value to the job's 'job-state-reasons' attribute and then
480        transition the job to the 'canceled' state when the processing
481        ceases (see section 4.3.8).
482
483        Rule 2: If the Job object already has the
484        'processing-to-stop-point' value in its 'job-state-reasons'
485        attribute, then the Printer object MUST reject a Cancel-Job
486        operation.
487
488        Access Rights: The authenticated user (see section 8.3)
489        performing this operation must either be the job owner or an
490        operator or administrator of the Printer object (see Sections
491        1 and 8.5).  Otherwise, the IPP object MUST reject the
492        operation and return: 'client-error-forbidden',
493        'client-error-not-authenticated', or
494        'client-error-not-authorized' as appropriate.
495
496        Request
497        -------
498
499        Group 1: Operation Attributes
500            REQUIRED 'attributes-charset'
501            REQUIRED 'attributes-natural-language'
502            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
503              -or-   'job-uri' (uri)
504            OPTIONAL 'requesting-user-name' (name(MAX))
505            OPTIONAL 'message' (text(127))
506           
507        Response
508        --------
509
510        Group 1: Operation Attributes
511            OPTIONAL 'status-message' (text(255))
512            OPTIONAL 'detailed-status-message' (text(MAX))
513            REQUIRED 'attributes-charset'
514            REQUIRED 'attributes-natural-language'
515        Group 2: Unsupported Attributes
516
517        """
518
519        operation = request.attribute_groups[0]
520
521        job_id = None
522        printer_uri = None
523        requesting_user_name = None
524        message = None
525
526        # required attributes
527        if 'job-id' in operation and 'printer-uri' in operation:
528            job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
529            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
530            if printer_uri not in self.printer.uris:
531                raise ipp.errors.ClientErrorAttributes(
532                    str(operation['printer-uri']), operation['printer-uri'])
533
534        elif 'job-uri' in operation:
535            job_uri = verify_attribute(operation['job-uri'], ipp.JobUri)[0]
536            job_id = int(job_uri.split("/")[-1])
537
538        if 'requesting-user-name' in operation:
[33ea505]539            requesting_user_name = verify_attribute(
[34a4e5d]540                operation['requesting-user-name'], ipp.RequestingUserName)[0]
541
542        try:
[33ea505]543            self.printer.cancel_job(job_id, requesting_user_name=requesting_user_name)
[34a4e5d]544        except InvalidJobException:
545            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
[9eeab06]546
[793432f]547    @handler_for(ipp.OperationCodes.SEND_DOCUMENT)
[1037115]548    def send_document(self, request, response):
[b01b6d1]549        """3.3.1 Send-Document Operation
550       
551        This OPTIONAL operation allows a client to create a
552        multi-document Job object that is initially 'empty' (contains
553        no documents). In the Create-Job response, the Printer object
554        returns the Job object's URI (the 'job-uri' attribute) and the
555        Job object's 32-bit identifier (the 'job-id' attribute). For
556        each new document that the client desires to add, the client
557        uses a Send-Document operation. Each Send- Document Request
558        contains the entire stream of document data for one document.
559
560        If the Printer supports this operation but does not support
561        multiple documents per job, the Printer MUST reject subsequent
562        Send-Document operations supplied with data and return the
563        'server-error-multiple- document-jobs-not-supported'. However,
564        the Printer MUST accept the first document with a 'true' or
565        'false' value for the 'last-document' operation attribute (see
566        below), so that clients MAY always submit one document jobs
567        with a 'false' value for 'last-document' in the first
568        Send-Document and a 'true' for 'last-document' in the second
569        Send-Document (with no data).
570       
571        Since the Create-Job and the send operations (Send-Document or
572        Send- URI operations) that follow could occur over an
573        arbitrarily long period of time for a particular job, a client
574        MUST send another send operation within an IPP Printer defined
575        minimum time interval after the receipt of the previous
576        request for the job. If a Printer object supports the
577        Create-Job and Send-Document operations, the Printer object
578        MUST support the 'multiple-operation-time-out' attribute (see
579        section 4.4.31). This attribute indicates the minimum number
580        of seconds the Printer object will wait for the next send
581        operation before taking some recovery action.
582
583        An IPP object MUST recover from an errant client that does not
584        supply a send operation, sometime after the minimum time
585        interval specified by the Printer object's
586        'multiple-operation-time-out' attribute.
587
588        Access Rights: The authenticated user (see section 8.3)
589        performing this operation must either be the job owner (as
590        determined in the Create-Job operation) or an operator or
591        administrator of the Printer object (see Sections 1 and
592        8.5). Otherwise, the IPP object MUST reject the operation and
593        return: 'client-error-forbidden', 'client-
594        error-not-authenticated', or 'client-error-not-authorized' as
595        appropriate.
596
597        Request
598        -------
599
600        Group 1: Operation Attributes
601            REQUIRED 'attributes-charset'
602            REQUIRED 'attributes-natural-language'
[34a4e5d]603            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
604              -or-   'job-uri' (uri)
[b01b6d1]605            OPTIONAL 'requesting-user-name' (name(MAX))
606            OPTIONAL 'document-name' (name(MAX))
607            OPTIONAL 'compression' (type3 keyword)
608            OPTIONAL 'document-format' (mimeMediaType)
609            OPTIONAL 'document-natural-language' (naturalLanguage)
610            OPTIONAL 'last-document' (boolean)
611        Group 2: Document Content
612           
613        Response
614        --------
615
616        Group 1: Operation Attributes
617            OPTIONAL 'status-message' (text(255))
618            OPTIONAL 'detailed-status-message' (text(MAX))
619            REQUIRED 'attributes-charset'
620            REQUIRED 'attributes-natural-language'
621        Group 2: Unsupported Attributes
622        Group 3: Job Object Attributes
623            REQUIRED 'job-uri' (uri)
624            REQUIRED 'job-id' (integer(1:MAX))
625            REQUIRED 'job-state' (type1 enum)
626            REQUIRED 'job-state-reasons' (1setOf type2 keyword)
627            OPTIONAL 'job-state-message' (text(MAX))
628            OPTIONAL 'number-of-intervening-jobs' (integer(0:MAX))
629
630        """
631       
[e58af05]632        operation = request.attribute_groups[0]
633
[b01b6d1]634        job_id = None
635        printer_uri = None
636        requesting_user_name = None
637        document_name = None
638        compression = None
639        document_format = None
640        document_natural_language = None
641        last_document = None
[e58af05]642
[b01b6d1]643        # required attributes
[e58af05]644        if 'job-id' not in operation:
645            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
[b01b6d1]646        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
[e58af05]647
648        if 'last-document' not in operation:
649            raise ipp.errors.ClientErrorBadRequest("Missing 'last-document' attribute")
[b01b6d1]650        last_document = verify_attribute(operation['last-document'], ipp.LastDocument)[0]
651        if not last_document:
[e58af05]652            raise ipp.errors.ServerErrorMultipleJobsNotSupported
653
[b01b6d1]654        # optional attributes
655        if 'printer-uri' in operation:
656            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
657            if printer_uri not in self.printer.uris:
658                raise ipp.errors.ClientErrorAttributes(
659                    str(operation['printer-uri']), operation['printer-uri'])
660
661        if 'requesting-user-name' in operation:
662            user_name = verify_attribute(
663                operation['requesting-user-name'], ipp.RequestingUserName)[0]
664
665        if 'document-name' in operation:
666            document_name = verify_attribute(
667                operation['document-name'], ipp.DocumentName)[0]
668
669        if 'compression' in operation:
670            compression = verify_attribute(
671                operation['compression'], ipp.Compression)[0]
672
673        if 'document-format' in operation:
674            document_format = verify_attribute(
675                operation['document-format'], ipp.DocumentFormat)[0]
676
677        if 'document-natural-language' in operation:
678            document_natural_language = verify_attribute(
679                operation['document_natural_language'],
680                ipp.DocumentNaturalLanguage)[0]
681
[34a4e5d]682        try:
[33ea505]683            self.printer.send_document(
684                job_id,
[34a4e5d]685                request.data,
686                document_name=document_name,
687                document_format=document_format,
688                document_natural_language=document_natural_language,
[33ea505]689                requesting_user_name=user_name,
690                compression=compression,
[34a4e5d]691                last_document=last_document)
[33ea505]692            attrs = self.printer.get_job_attributes(job_id)
[34a4e5d]693        except InvalidJobException:
694            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
[b01b6d1]695
[e58af05]696        response.attribute_groups.append(ipp.AttributeGroup(
[34a4e5d]697            ipp.AttributeTags.JOB, attrs))
[9eeab06]698
[793432f]699    @handler_for(ipp.OperationCodes.SEND_URI)
[1037115]700    def send_uri(self, request, response):
[b828a96]701        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]702
[793432f]703    @handler_for(ipp.OperationCodes.GET_JOB_ATTRIBUTES)
[1037115]704    def get_job_attributes(self, request, response):
[b01b6d1]705        """3.3.4 Get-Job-Attributes Operation
706
707        This REQUIRED operation allows a client to request the values
708        of attributes of a Job object and it is almost identical to
709        the Get- Printer-Attributes operation (see section 3.2.5). The
710        only differences are that the operation is directed at a Job
711        object rather than a Printer object, there is no
712        'document-format' operation attribute used when querying a Job
713        object, and the returned attribute group is a set of Job
714        object attributes rather than a set of Printer object
715        attributes.
716
717        For Jobs, the possible names of attribute groups are:
718          - 'job-template': the subset of the Job Template attributes
719            that apply to a Job object (the first column of the table
720            in Section 4.2) that the implementation supports for Job
721            objects.
722          - 'job-description': the subset of the Job Description
723            attributes specified in Section 4.3 that the
724            implementation supports for Job objects.
725          - 'all': the special group 'all' that includes all
726            attributes that the implementation supports for Job
727            objects.
728
729        Since a client MAY request specific attributes or named
730        groups, there is a potential that there is some overlap. For
731        example, if a client requests, 'job-name' and
732        'job-description', the client is actually requesting the
733        'job-name' attribute once by naming it explicitly, and once by
734        inclusion in the 'job-description' group. In such cases, the
735        Printer object NEED NOT return the attribute only once in the
736        response even if it is requested multiple times. The client
737        SHOULD NOT request the same attribute in multiple ways.
738
739        It is NOT REQUIRED that a Job object support all attributes
740        belonging to a group (since some attributes are
741        OPTIONAL). However it is REQUIRED that each Job object support
742        all these group names.
743
744        Request
745        -------
746
747        Group 1: Operation Attributes
748            REQUIRED 'attributes-charset'
749            REQUIRED 'attributes-natural-language'
[34a4e5d]750            REQUIRED 'job-id' (integer(1:MAX)) and 'printer-uri' (uri)
751              -or-   'job-uri' (uri)
[b01b6d1]752            OPTIONAL 'requesting-user-name' (name(MAX))
753            OPTIONAL 'requested-attributes' (1setOf keyword)
754           
755        Response
756        --------
757
758        Group 1: Operation Attributes
759            OPTIONAL 'status-message' (text(255))
760            OPTIONAL 'detailed-status-message' (text(MAX))
761            REQUIRED 'attributes-charset'
762            REQUIRED 'attributes-natural-language'
763        Group 2: Unsupported Attributes
764        Group 3: Job Object Attributes
765
766        """
767       
[1037115]768        operation = request.attribute_groups[0]
769
[b01b6d1]770        job_id = None
771        printer_uri = None
772        requesting_user_name = None
773        requested_attributes = None
[1037115]774
[b01b6d1]775        # required attributes
[1037115]776        if 'job-id' not in operation:
777            raise ipp.errors.ClientErrorBadRequest("Missing 'job-id' attribute")
[b01b6d1]778        job_id = verify_attribute(operation['job-id'], ipp.JobId)[0]
779
780        # optional attributes
781        if 'printer-uri' in operation:
782            printer_uri = verify_attribute(operation['printer-uri'], ipp.PrinterUri)[0]
783            if printer_uri not in self.printer.uris:
784                raise ipp.errors.ClientErrorAttributes(
785                    str(operation['printer-uri']), operation['printer-uri'])
786
787        # optional attributes
788        if 'requesting-user-name' in operation:
789            user_name = verify_attribute(
790                operation['requesting-user-name'], ipp.RequestingUserName)[0]
791           
792        if 'requested-attributes' in operation:
793            requested_attributes = verify_attribute(
794                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
[1037115]795
796        # get the job attributes and add them to the response
[34a4e5d]797        try:
[33ea505]798            attrs = self.printer.get_job_attributes(
799                job_id,
800                requested_attributes=requested_attributes)
[34a4e5d]801        except InvalidJobException:
802            raise ipp.errors.ClientErrorNotFound("bad job: %d" % job_id)
803
[1037115]804        response.attribute_groups.append(ipp.AttributeGroup(
805            ipp.AttributeTags.JOB, attrs))
[9eeab06]806
[793432f]807    @handler_for(ipp.OperationCodes.SET_JOB_ATTRIBUTES)
[1037115]808    def set_job_attributes(self, request, response):
[b828a96]809        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]810
[793432f]811    @handler_for(ipp.OperationCodes.RESTART_JOB)
[1037115]812    def restart_job(self, request, response):
[b828a96]813        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]814
[793432f]815    @handler_for(ipp.OperationCodes.PROMOTE_JOB)
[1037115]816    def promote_job(self, request, response):
[b828a96]817        raise ipp.errors.ServerErrorOperationNotSupported
[9eeab06]818
819    ##### CUPS Specific Commands
820
[793432f]821    @handler_for(ipp.OperationCodes.CUPS_GET_DOCUMENT)
[1037115]822    def cups_get_document(self, request, response):
[b828a96]823        raise ipp.errors.ServerErrorOperationNotSupported
[e70c020]824
[793432f]825    @handler_for(ipp.OperationCodes.CUPS_GET_DEFAULT)
[1037115]826    def cups_get_default(self, request, response):
[0ede474]827        """The CUPS-Get-Default operation (0x4001) returns the default
828        printer URI and attributes.
829
[1037115]830        Request
831        -------
832
833        Group 1: Operation Attributes
834            REQUIRED 'attributes-charset'
835            REQUIRED 'attributes-natural-language'
836            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
837
838        Response
839        --------
840
841        Group 1: Operation Attributes
842            OPTIONAL 'status-message' (text(255))
843            REQUIRED 'attributes-charset'
844            REQUIRED 'attributes-natural-language'
845        Group 2: Printer Object Attributes
846
[0ede474]847        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
848
849        """
[287d6ec]850
[1037115]851        operation = request.attribute_groups[0]
[b01b6d1]852        requested_attributes = None
853       
854        if 'requested-attributes' in operation:
855            requested_attributes = verify_attribute(
856                operation['requested-attributes'], ipp.RequestedAttributes, length=None)
[1037115]857
858        # get attributes from the printer and add to response
[b01b6d1]859        attrs = self.printer.get_printer_attributes(
860            requested_attributes=requested_attributes)
[1037115]861        response.attribute_groups.append(ipp.AttributeGroup(
[b01b6d1]862            ipp.AttributeTags.PRINTER, attrs))
[0ede474]863
[793432f]864    @handler_for(ipp.OperationCodes.CUPS_GET_PRINTERS)
[1037115]865    def cups_get_printers(self, request, response):
[ef8df33]866        """The CUPS-Get-Printers operation (0x4002) returns the
867        printer attributes for every printer known to the system. This
868        may include printers that are not served directly by the
869        server.
[0ede474]870
871        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
872           
873        """
874
[1037115]875        # get attributes from the printer and add to response
[e58af05]876        response.attribute_groups.append(ipp.AttributeGroup(
[b01b6d1]877            ipp.AttributeTags.PRINTER, self.printer.get_printer_attributes()))
[287d6ec]878
[793432f]879    @handler_for(ipp.OperationCodes.CUPS_GET_CLASSES)
[1037115]880    def cups_get_classes(self, request, response):
[0ede474]881        """The CUPS-Get-Classes operation (0x4005) returns the printer
882        attributes for every printer class known to the system. This
883        may include printer classes that are not served directly by
884        the server.
[1037115]885
886        Request
887        -------
888
889        Group 1: Operation Attributes
890            REQUIRED 'attributes-charset'
891            REQUIRED 'attributes-natural-language'
892            OPTIONAL 'first-printer-name' (name(127)) CUPS 1.2/Mac OS X 10.5
893            OPTIONAL 'limit' (integer (1:MAX))
894            OPTIONAL 'printer-location' (text(127)) CUPS 1.1.7
895            OPTIONAL 'printer-type' (type2 enum) CUPS 1.1.7
896            OPTIONAL 'printer-type-mask' (type2 enum) CUPS 1.1.7
897            OPTIONAL 'requested-attributes' (1setOf keyword)
898            OPTOINAL 'requested-user-name' (name(127)) CUPS 1.2/Mac OS X 10.5
899            OPTIONAL 'requested-attributes' (1setOf type2 keyword)
900
901        Response
902        --------
903
904        Group 1: Operation Attributes
905            OPTIONAL 'status-message' (text(255))
906            REQUIRED 'attributes-charset'
907            REQUIRED 'attributes-natural-language'
908        Group 2: Printer Class Object Attributes
909
[0ede474]910        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
911
912        """
[ef8df33]913
[1037115]914        raise ipp.errors.ServerErrorOperationNotSupported
915
Note: See TracBrowser for help on using the repository browser.