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

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

Checkpoint, creating classes for specific IPP attributes

  • Property mode set to 100644
File size: 14.1 KB
RevLine 
[738d179]1from gutenbach.server.printer import GutenbachPrinter
[b2e077a]2import gutenbach.ipp as ipp
[aef164a]3import gutenbach.ipp.constants as consts
[287d6ec]4import logging
[5e44432]5import traceback
[aded2d1]6import sys
[287d6ec]7
[d04a689]8# initialize logger
9logger = logging.getLogger(__name__)
[287d6ec]10
11def handler_for(operation):
12    """A decorator method to mark a function with the operation id
13    that it handles.  This value will be stored in
14    'func.ipp_operation'.
15
16    """
17   
18    def f(func):
19        func.ipp_operation = operation
20        return func
21    return f
22
[b2e077a]23class GutenbachRequestHandler(object):
[287d6ec]24
[b2e077a]25    def __init__(self):
26        self.printers = {
27            "test": GutenbachPrinter(name="test")
28            }
29        self.default = "test"
[287d6ec]30   
[ef8df33]31    def handle(self, request):
[b2e077a]32        # look up the handler
33        handler = None
[9eeab06]34        handler_name = None
[287d6ec]35        for d in dir(self):
36            if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id:
[9eeab06]37                handler_name = d
[b2e077a]38                break
39        # we couldn't find a handler, so default to unknown operation
[9eeab06]40        if handler_name is None:
41            handler_name = "unknown_operation"
[b2e077a]42        # call the handler
[9eeab06]43        handler = getattr(self, handler_name)
[7bd1035]44        logger.info("Handling request of type '%s'" % handler_name)
[aef164a]45
46        # Try to handle the request
47        try:
48            response = handler(request)
49
50        # Handle any errors that occur.  If an exception occurs that
51        # is an IPP error, then we can get the error code from the
52        # exception itself.
53        except ipp.errors.IPPException:
54            exctype, excval, exctb = sys.exc_info()
55            logger.error(traceback.format_exc())
56            response = ipp.ops.make_empty_response(request)
57            excval.update_response(response)
58
59        # If it wasn't an IPP error, then it's our fault, so mark it
60        # as an internal server error
61        except Exception:
62            logger.error(traceback.format_exc())
63            response = ipp.ops.make_empty_response(request)
64            response.operation_id = ipp.StatusCodes.INTERNAL_ERROR
65
[ef8df33]66        return response
[287d6ec]67
[ef8df33]68    def unknown_operation(self, request):
[9eeab06]69        logger.warning("Received unknown operation 0x%x" % request.operation_id)
[ef8df33]70        response = ipp.ops.make_empty_response(request)
[aef164a]71        response.operation_id = consts.StatusCodes.OPERATION_NOT_SUPPORTED
[ef8df33]72        return response
[0ede474]73       
[9eeab06]74    ##### Printer Commands
75
[94a4825]76    @handler_for(consts.Operations.PRINT_JOB)
[ef8df33]77    def print_job(self, request):
[7a7a09e]78        """RFC 2911: 3.2.1 Print-Job Operation
79
80        This REQUIRED operation allows a client to submit a print job
81        with only one document and supply the document data (rather
82        than just a reference to the data). See Section 15 for the
83        suggested steps for processing create operations and their
84        Operation and Job Template attributes.
85
86        """
87       
[94a4825]88        raise NotImplementedError
[9eeab06]89
[94a4825]90    @handler_for(consts.Operations.VALIDATE_JOB)
[ef8df33]91    def validate_job(self, request):
[94a4825]92
93        raise NotImplementedError
[9eeab06]94
[aef164a]95    @handler_for(consts.Operations.GET_JOBS)
[ef8df33]96    def get_jobs(self, request):
[6ed9d7a]97        """RFC 2911: 3.2.6 Get-Jobs Operation
98       
99        This REQUIRED operation allows a client to retrieve the list
100        of Job objects belonging to the target Printer object. The
101        client may also supply a list of Job attribute names and/or
102        attribute group names. A group of Job object attributes will
103        be returned for each Job object that is returned.
104
105        This operation is similar to the Get-Job-Attributes operation,
106        except that this Get-Jobs operation returns attributes from
107        possibly more than one object.
108
109        """
[59a1d4a]110
111        # verify the request and get an attribute dictionary
[ef8df33]112        req_dict = ipp.ops.verify_get_jobs_request(request)
[59a1d4a]113
114        # lookup printer name
[ef8df33]115        printer_name = req_dict['printer-uri']
[5fe360e]116        if printer_name not in self.printers:
[aded2d1]117            raise ipp.errors.ClientErrorAttributes(
[5fe360e]118                "Invalid printer uri: %s" % printer_name,
119                [request.attribute_groups[0].attributes[2]])
[6ed9d7a]120
[59a1d4a]121        # get the job attributes
[ef8df33]122        jobs = [job.get_job_attributes(request) for job in \
123                self.printers[printer_name].get_jobs()]
[59a1d4a]124
125        # build the response
[ef8df33]126        response = ipp.ops.make_get_jobs_response(jobs, request)
127        return response
[287d6ec]128
[94a4825]129    @handler_for(consts.Operations.PRINT_URI)
[ef8df33]130    def print_uri(self, request):
[94a4825]131        raise NotImplementedError
[9eeab06]132
[94a4825]133    @handler_for(consts.Operations.CREATE_JOB)
[ef8df33]134    def create_job(self, request):
[94a4825]135        """RFC 2911: 3.2.4 Create-Job Operation
136
137        This OPTIONAL operation is similar to the Print-Job operation
138        (section 3.2.1) except that in the Create-Job request, a
139        client does not supply document data or any reference to
140        document data. Also, the client does not supply any of the
141        'document-name', 'document- format', 'compression', or
142        'document-natural-language' operation attributes. This
143        operation is followed by one or more Send-Document or Send-URI
144        operations. In each of those operation requests, the client
145        OPTIONALLY supplies the 'document-name', 'document-format',
146        and 'document-natural-language' attributes for each document
147        in the multi-document Job object.
148
149        If a Printer object supports the Create-Job operation, it MUST
150        also support the Send-Document operation and also MAY support
151        the Send-URI operation.
152       
153        If the Printer object supports this operation, it MUST support
154        the 'multiple-operation-time-out' Printer attribute (see
155        section 4.4.31).  If the Printer object supports this
156        operation, then it MUST support the
157        'multiple-document-jobs-supported' Printer Description
158        attribute (see section 4.4.16) and indicate whether or not it
159        supports multiple-document jobs.
160       
161        If the Printer object supports this operation and supports
162        multiple documents in a job, then it MUST support the
163        'multiple-document- handling' Job Template job attribute with
164        at least one value (see section 4.2.4) and the associated
165        'multiple-document-handling- default' and
166        'multiple-document-handling-supported' Job Template Printer
167        attributes (see section 4.2).
168       
169        After the Create-Job operation has completed, the value of the
170        'job- state' attribute is similar to the 'job-state' after a
171        Print-Job, even though no document-data has arrived. A Printer
172        MAY set the 'job-data-insufficient' value of the job's
173        'job-state-reason' attribute to indicate that processing
174        cannot begin until sufficient data has arrived and set the
175        'job-state' to either 'pending' or 'pending-held'. A
176        non-spooling printer that doesn't implement the 'pending' job
177        state may even set the 'job-state' to 'processing', even
178        though there is not yet any data to process. See sections
179        4.3.7 and 4.3.8.
180       
181        """
[9eeab06]182
[94a4825]183        raise NotImplementedError
184   
185    @handler_for(consts.Operations.PAUSE_PRINTER)
[ef8df33]186    def pause_printer(self, request):
[94a4825]187        raise NotImplementedError
[9eeab06]188
[94a4825]189    @handler_for(consts.Operations.RESUME_PRINTER)
[ef8df33]190    def resume_printer(self, request):
[94a4825]191        raise NotImplementedError
[9eeab06]192
[aef164a]193    @handler_for(consts.Operations.GET_PRINTER_ATTRIBUTES)
[ef8df33]194    def get_printer_attributes(self, request):
[cad7502]195        """RFC 2911: 3.2.5 Get-Printer-Attributes Operation
196
197        This REQUIRED operation allows a client to request the values
198        of the attributes of a Printer object.
199       
200        In the request, the client supplies the set of Printer
201        attribute names and/or attribute group names in which the
202        requester is interested. In the response, the Printer object
203        returns a corresponding attribute set with the appropriate
204        attribute values filled in.
205
206        For Printer objects, the possible names of attribute groups are:
207       
208        - 'job-template': the subset of the Job Template attributes
209          that apply to a Printer object (the last two columns of the
210          table in Section 4.2) that the implementation supports for
211          Printer objects.
212
213        - 'printer-description': the subset of the attributes
214          specified in Section 4.4 that the implementation supports
215          for Printer objects.
216
217        - 'all': the special group 'all' that includes all attributes
218          that the implementation supports for Printer objects.
219       
220        Since a client MAY request specific attributes or named
221        groups, there is a potential that there is some overlap. For
222        example, if a client requests, 'printer-name' and 'all', the
223        client is actually requesting the 'printer-name' attribute
224        twice: once by naming it explicitly, and once by inclusion in
225        the 'all' group. In such cases, the Printer object NEED NOT
226        return each attribute only once in the response even if it is
227        requested multiple times. The client SHOULD NOT request the
228        same attribute in multiple ways.
229
230        It is NOT REQUIRED that a Printer object support all
231        attributes belonging to a group (since some attributes are
232        OPTIONAL). However, it is REQUIRED that each Printer object
233        support all group names.
234
235        """
236
[59a1d4a]237        # verify the request and get the attributes dictionary
[ef8df33]238        req_dict = ipp.ops.verify_get_printer_attributes_request(request)
[59a1d4a]239
240        # lookup the printer name
[ef8df33]241        printer_name = req_dict['printer-uri']
[5fe360e]242        if printer_name not in self.printers:
[aded2d1]243            raise ipp.errors.ClientErrorAttributes(
[5fe360e]244                "Invalid printer uri: %s" % printer_name,
245                [request.attribute_groups[0].attributes[2]])
[59a1d4a]246
247        # bulid response
[ef8df33]248        response = ipp.ops.make_get_printer_attributes_response(
249            self.printers[printer_name].get_printer_attributes(request), request)
250        return response
[9eeab06]251
[94a4825]252    @handler_for(consts.Operations.SET_PRINTER_ATTRIBUTES)
[ef8df33]253    def set_printer_attributes(self, request):
[94a4825]254        raise NotImplementedError
[9eeab06]255
256    ##### Job Commands
257
[94a4825]258    @handler_for(consts.Operations.CANCEL_JOB)
[ef8df33]259    def cancel_job(self, request):
[94a4825]260        raise NotImplementedError
[9eeab06]261
[94a4825]262    @handler_for(consts.Operations.SEND_DOCUMENT)
[ef8df33]263    def send_document(self, request):
[94a4825]264        raise NotImplementedError
[9eeab06]265
[94a4825]266    @handler_for(consts.Operations.SEND_URI)
[ef8df33]267    def send_uri(self, request):
[94a4825]268        raise NotImplementedError
[9eeab06]269
[94a4825]270    @handler_for(consts.Operations.GET_JOB_ATTRIBUTES)
[ef8df33]271    def get_job_attributes(self, request):
[59a1d4a]272       
273        # verify the request and get the attributes dictionary
[ef8df33]274        req_dict = ipp.ops.verify_get_jobs_request(request)
[5fe360e]275       
[59a1d4a]276        # lookup the printer name
277        printer_name = req_dict['printer-uri']
[5fe360e]278        if printer_name not in self.printers:
[aded2d1]279            raise ipp.errors.ClientErrorAttributes(
[5fe360e]280                "Invalid printer uri: %s" % printer_name,
281                [request.attribute_groups[0].attributes[2]])
[59a1d4a]282
283        # lookup the job id
284        job_id = req_dict['job-id']
285        try: job = self.printers[printer_name].get_job(job_id)
[5fe360e]286        except InvalidJobException:
[aded2d1]287            raise ipp.errors.ClientErrorAttributes(
[5fe360e]288                "Invalid job id: %d" % job_id,
289                [request.attribute_groups[0].attributes[2]]) # XXX: this is wrong
290
291        # XXX: we need to honor the things that the request actually asks for
[59a1d4a]292        # build the response
[ef8df33]293        response = ipp.ops.make_get_job_attributes_response(
294            job.get_job_attributes(request), request)
295        return response
[9eeab06]296
[94a4825]297    @handler_for(consts.Operations.SET_JOB_ATTRIBUTES)
[ef8df33]298    def set_job_attributes(self, request):
[94a4825]299        raise NotImplementedError
[9eeab06]300
[94a4825]301    @handler_for(consts.Operations.RESTART_JOB)
[ef8df33]302    def restart_job(self, request):
[94a4825]303        raise NotImplementedError
[9eeab06]304
[94a4825]305    @handler_for(consts.Operations.PROMOTE_JOB)
[ef8df33]306    def promote_job(self, request):
[94a4825]307        raise NotImplementedError
[9eeab06]308
309    ##### CUPS Specific Commands
310
[94a4825]311    @handler_for(consts.Operations.CUPS_GET_DOCUMENT)
[e70c020]312    def cups_get_document(self, request):
[94a4825]313        raise NotImplementedError
[e70c020]314
[aef164a]315    @handler_for(consts.Operations.CUPS_GET_DEFAULT)
[ef8df33]316    def cups_get_default(self, request):
[0ede474]317        """The CUPS-Get-Default operation (0x4001) returns the default
318        printer URI and attributes.
319
320        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
321
322        """
[287d6ec]323
[59a1d4a]324        # verify the request and get the attributes dictionary
[ef8df33]325        req_dict = ipp.ops.verify_cups_get_default_request(request)
[59a1d4a]326        # build the response
[ef8df33]327        response = ipp.ops.make_get_printer_attributes_response(
328            self.printers[self.default].get_printer_attributes(request), request)
329        return response
[0ede474]330
[aef164a]331    @handler_for(consts.Operations.CUPS_GET_PRINTERS)
[ef8df33]332    def cups_get_printers(self, request):
333        """The CUPS-Get-Printers operation (0x4002) returns the
334        printer attributes for every printer known to the system. This
335        may include printers that are not served directly by the
336        server.
[0ede474]337
338        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
339           
340        """
341
[59a1d4a]342        # verify the request and get the attributes dictionary
[ef8df33]343        req_dict = ipp.ops.verify_cups_get_printers_request(request)
[59a1d4a]344        # get the printer attributes
[ef8df33]345        attrs = [self.printers[printer].get_printer_attributes(request) \
346                 for printer in self.printers]
[59a1d4a]347        # build the response
[ef8df33]348        response = ipp.ops.make_cups_get_printers_response(attrs, request)
349        return response
[287d6ec]350
[aef164a]351    @handler_for(consts.Operations.CUPS_GET_CLASSES)
[ef8df33]352    def cups_get_classes(self, request):
[0ede474]353        """The CUPS-Get-Classes operation (0x4005) returns the printer
354        attributes for every printer class known to the system. This
355        may include printer classes that are not served directly by
356        the server.
[ef8df33]357       
[0ede474]358        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
359
360        """
[ef8df33]361
[59a1d4a]362        # verify the request and get the attributes dictionaryu
[ef8df33]363        req_dict = ipp.ops.verify_cups_get_classes_request(request)
[59a1d4a]364        # build the response
[ef8df33]365        response = ipp.ops.make_cups_get_classes_response(request)
366        return response
Note: See TracBrowser for help on using the repository browser.