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
Line 
1from gutenbach.server.printer import GutenbachPrinter
2import gutenbach.ipp as ipp
3import gutenbach.ipp.constants as consts
4import logging
5import traceback
6import sys
7
8# initialize logger
9logger = logging.getLogger(__name__)
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
23class GutenbachRequestHandler(object):
24
25    def __init__(self):
26        self.printers = {
27            "test": GutenbachPrinter(name="test")
28            }
29        self.default = "test"
30   
31    def handle(self, request):
32        # look up the handler
33        handler = None
34        handler_name = None
35        for d in dir(self):
36            if getattr(getattr(self, d), "ipp_operation", None) == request.operation_id:
37                handler_name = d
38                break
39        # we couldn't find a handler, so default to unknown operation
40        if handler_name is None:
41            handler_name = "unknown_operation"
42        # call the handler
43        handler = getattr(self, handler_name)
44        logger.info("Handling request of type '%s'" % handler_name)
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
66        return response
67
68    def unknown_operation(self, request):
69        logger.warning("Received unknown operation 0x%x" % request.operation_id)
70        response = ipp.ops.make_empty_response(request)
71        response.operation_id = consts.StatusCodes.OPERATION_NOT_SUPPORTED
72        return response
73       
74    ##### Printer Commands
75
76    @handler_for(consts.Operations.PRINT_JOB)
77    def print_job(self, request):
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       
88        raise NotImplementedError
89
90    @handler_for(consts.Operations.VALIDATE_JOB)
91    def validate_job(self, request):
92
93        raise NotImplementedError
94
95    @handler_for(consts.Operations.GET_JOBS)
96    def get_jobs(self, request):
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        """
110
111        # verify the request and get an attribute dictionary
112        req_dict = ipp.ops.verify_get_jobs_request(request)
113
114        # lookup printer name
115        printer_name = req_dict['printer-uri']
116        if printer_name not in self.printers:
117            raise ipp.errors.ClientErrorAttributes(
118                "Invalid printer uri: %s" % printer_name,
119                [request.attribute_groups[0].attributes[2]])
120
121        # get the job attributes
122        jobs = [job.get_job_attributes(request) for job in \
123                self.printers[printer_name].get_jobs()]
124
125        # build the response
126        response = ipp.ops.make_get_jobs_response(jobs, request)
127        return response
128
129    @handler_for(consts.Operations.PRINT_URI)
130    def print_uri(self, request):
131        raise NotImplementedError
132
133    @handler_for(consts.Operations.CREATE_JOB)
134    def create_job(self, request):
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        """
182
183        raise NotImplementedError
184   
185    @handler_for(consts.Operations.PAUSE_PRINTER)
186    def pause_printer(self, request):
187        raise NotImplementedError
188
189    @handler_for(consts.Operations.RESUME_PRINTER)
190    def resume_printer(self, request):
191        raise NotImplementedError
192
193    @handler_for(consts.Operations.GET_PRINTER_ATTRIBUTES)
194    def get_printer_attributes(self, request):
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
237        # verify the request and get the attributes dictionary
238        req_dict = ipp.ops.verify_get_printer_attributes_request(request)
239
240        # lookup the printer name
241        printer_name = req_dict['printer-uri']
242        if printer_name not in self.printers:
243            raise ipp.errors.ClientErrorAttributes(
244                "Invalid printer uri: %s" % printer_name,
245                [request.attribute_groups[0].attributes[2]])
246
247        # bulid response
248        response = ipp.ops.make_get_printer_attributes_response(
249            self.printers[printer_name].get_printer_attributes(request), request)
250        return response
251
252    @handler_for(consts.Operations.SET_PRINTER_ATTRIBUTES)
253    def set_printer_attributes(self, request):
254        raise NotImplementedError
255
256    ##### Job Commands
257
258    @handler_for(consts.Operations.CANCEL_JOB)
259    def cancel_job(self, request):
260        raise NotImplementedError
261
262    @handler_for(consts.Operations.SEND_DOCUMENT)
263    def send_document(self, request):
264        raise NotImplementedError
265
266    @handler_for(consts.Operations.SEND_URI)
267    def send_uri(self, request):
268        raise NotImplementedError
269
270    @handler_for(consts.Operations.GET_JOB_ATTRIBUTES)
271    def get_job_attributes(self, request):
272       
273        # verify the request and get the attributes dictionary
274        req_dict = ipp.ops.verify_get_jobs_request(request)
275       
276        # lookup the printer name
277        printer_name = req_dict['printer-uri']
278        if printer_name not in self.printers:
279            raise ipp.errors.ClientErrorAttributes(
280                "Invalid printer uri: %s" % printer_name,
281                [request.attribute_groups[0].attributes[2]])
282
283        # lookup the job id
284        job_id = req_dict['job-id']
285        try: job = self.printers[printer_name].get_job(job_id)
286        except InvalidJobException:
287            raise ipp.errors.ClientErrorAttributes(
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
292        # build the response
293        response = ipp.ops.make_get_job_attributes_response(
294            job.get_job_attributes(request), request)
295        return response
296
297    @handler_for(consts.Operations.SET_JOB_ATTRIBUTES)
298    def set_job_attributes(self, request):
299        raise NotImplementedError
300
301    @handler_for(consts.Operations.RESTART_JOB)
302    def restart_job(self, request):
303        raise NotImplementedError
304
305    @handler_for(consts.Operations.PROMOTE_JOB)
306    def promote_job(self, request):
307        raise NotImplementedError
308
309    ##### CUPS Specific Commands
310
311    @handler_for(consts.Operations.CUPS_GET_DOCUMENT)
312    def cups_get_document(self, request):
313        raise NotImplementedError
314
315    @handler_for(consts.Operations.CUPS_GET_DEFAULT)
316    def cups_get_default(self, request):
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        """
323
324        # verify the request and get the attributes dictionary
325        req_dict = ipp.ops.verify_cups_get_default_request(request)
326        # build the response
327        response = ipp.ops.make_get_printer_attributes_response(
328            self.printers[self.default].get_printer_attributes(request), request)
329        return response
330
331    @handler_for(consts.Operations.CUPS_GET_PRINTERS)
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.
337
338        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
339           
340        """
341
342        # verify the request and get the attributes dictionary
343        req_dict = ipp.ops.verify_cups_get_printers_request(request)
344        # get the printer attributes
345        attrs = [self.printers[printer].get_printer_attributes(request) \
346                 for printer in self.printers]
347        # build the response
348        response = ipp.ops.make_cups_get_printers_response(attrs, request)
349        return response
350
351    @handler_for(consts.Operations.CUPS_GET_CLASSES)
352    def cups_get_classes(self, request):
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.
357       
358        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
359
360        """
361
362        # verify the request and get the attributes dictionaryu
363        req_dict = ipp.ops.verify_cups_get_classes_request(request)
364        # build the response
365        response = ipp.ops.make_cups_get_classes_response(request)
366        return response
Note: See TracBrowser for help on using the repository browser.