source: server/lib/gutenbach/server/requests.py @ 0ede474

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

Add documentation to the currently supported CUPS handlers

  • Property mode set to 100644
File size: 15.9 KB
Line 
1import BaseHTTPServer
2import gutenbach.ipp as ipp
3import gutenbach.ipp.constants as const
4from gutenbach.server.printer import GutenbachPrinter
5from gutenbach.server.exceptions import MalformedIPPRequestException
6import logging
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, response):
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.debug("Sending request to handler '%s'" % handler_name)
45        handler(request, response)
46
47    def unknown_operation(self, request, response):
48        logger.warning("Received unknown operation 0x%x" % request.operation_id)
49        response.operation_id = const.StatusCodes.OPERATION_NOT_SUPPORTED
50
51    ##### Helper functions
52
53    def _get_printer_attributes(self, printer, request, response):
54        response.attribute_groups.append(ipp.AttributeGroup(
55            const.AttributeTags.PRINTER,
56            printer.get_printer_attributes(request)))
57
58    def _get_job_attributes(self, job_id, printer, request, response):
59        response.attribute_groups.append(ipp.AttributeGroup(
60            const.AttributeTags.JOB,
61            job.get_job_attributes(request)))
62
63    def _get_printer_name(self, request):
64        # make sure the first group is an OPERATION group
65        group_tag = request.attribute_groups[0].tag
66        if group_tag != const.AttributeTags.OPERATION:
67            raise MalformedIPPRequestException, \
68                  "Expected OPERATION group tag, got %d\n", group_tag
69
70        # make sure the printer-uri value is appropriate
71        printer_name_attr = request.attribute_groups[0]['printer-uri']
72        printer_name_value_tag = printer_name_attr.values[0].value_tag
73        if printer_name_value_tag != const.CharacterStringTags.URI:
74            raise MalformedIPPRequestException, \
75                  "Expected URI value tag, got %s" % printer_name_value_tag
76
77        # actually get the printer name
78        printer_name_value = printer_name_attr.values[0].value
79        # XXX: hack -- CUPS will strip the port from the request, so
80        # we can't do an exact comparison (also the hostname might be
81        # different, depending on the CNAME or whether it's localhost)
82        printer_name = printer_name_value.split("/")[-1]
83
84        # make sure the printer name is valid
85        if printer_name not in self.printers:
86            raise ValueError, "Invalid printer uri: %s" % printer_name_value
87
88        return printer_name
89
90    def _get_job_id(self, request):
91        pass
92       
93    ##### Printer Commands
94
95    def print_job(self, request, response):
96        pass
97
98    def validate_job(self, request, response):
99        pass
100
101    @handler_for(const.Operations.GET_JOBS)
102    def get_jobs(self, request, response):
103        printer_name = self._get_printer_name(request)
104        # Each job will append a new job attribute group.
105        for job in self.printers[printer_name].get_jobs():
106            self._get_job_attributes(job, request, response)
107        response.operation_id = const.StatusCodes.OK
108
109    def print_uri(self, request, response):
110        pass
111
112    def create_job(self, request, response):
113        pass
114
115    def pause_printer(self, request, response):
116        pass
117
118    def resume_printer(self, request, response):
119        pass
120
121    @handler_for(const.Operations.GET_PRINTER_ATTRIBUTES)
122    def get_printer_attributes(self, request, response):
123        # this is just like cups_get_default, except the printer name
124        # is given
125        printer_name = self._get_printer_name(request)
126        self._get_printer_attributes(self.printers[printer_name], request, response)
127        response.operation_id = const.StatusCodes.OK
128
129    def set_printer_attributes(self, request, response):
130        pass
131
132    ##### Job Commands
133
134    def cancel_job(self, request, response):
135        pass
136
137    def send_document(self, request, response):
138        pass
139
140    def send_uri(self, request, response):
141        pass
142
143    def get_job_attributes(self, request, response):
144        printer_name = self._get_printer_name(request)
145        job_id = self._get_job_id(request)
146        self._get_job_attributes(
147            self.printers[printer_name].get_job(job_id), request, response)
148
149    def set_job_attributes(self, request, response):
150        pass
151
152    def cups_get_document(self, request, response):
153        pass
154
155    def restart_job(self, request, response):
156        pass
157
158    def promote_job(self, request, response):
159        pass
160
161
162    ##### CUPS Specific Commands
163
164    @handler_for(const.Operations.CUPS_GET_DEFAULT)
165    def cups_get_default(self, request, response):
166        """The CUPS-Get-Default operation (0x4001) returns the default
167        printer URI and attributes.
168
169        CUPS-Get-Default Request
170        ------------------------
171
172        The following groups of attributes are supplied as part of the
173        CUPS-Get-Default request:
174
175        Group 1: Operation Attributes
176            Natural Language and Character Set:
177                The 'attributes-charset' and
178                'attributes-natural-language' attributes as described
179                in section 3.1.4.1 of the IPP Model and Semantics
180                document.
181            'requested-attributes' (1setOf keyword):
182                The client OPTIONALLY supplies a set of attribute
183                names and/or attribute group names in whose values the
184                requester is interested. If the client omits this
185                attribute, the server responds as if this attribute
186                had been supplied with a value of 'all'.
187       
188        CUPS-Get-Default Response
189        -------------------------
190
191        The following groups of attributes are send as part of the
192        CUPS-Get-Default Response:
193
194        Group 1: Operation Attributes
195            Status Message:
196                The standard response status message.
197            Natural Language and Character Set:
198                The 'attributes-charset' and
199                'attributes-natural-language' attributes as described
200                in section 3.1.4.2 of the IPP Model and Semantics
201                document.
202
203        Group 2: Printer Object Attributes
204            The set of requested attributes and their current values.
205
206        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_DEFAULT )
207
208        """
209           
210        self._get_printer_attributes(self.printers[self.default], request, response)
211        response.operation_id = const.StatusCodes.OK
212
213    @handler_for(const.Operations.CUPS_GET_PRINTERS)
214    def cups_get_printers(self, request, response):
215        """
216        The CUPS-Get-Printers operation (0x4002) returns the printer
217        attributes for every printer known to the system. This may
218        include printers that are not served directly by the server.
219
220        CUPS-Get-Printers Request
221        -------------------------
222       
223        The following groups of attributes are supplied as part of the
224        CUPS-Get-Printers request:
225
226        Group 1: Operation Attributes
227            Natural Language and Character Set:
228                The 'attributes-charset' and
229                'attributes-natural-language' attributes as described
230                in section 3.1.4.1 of the IPP Model and Semantics
231                document.
232            'first-printer-name' (name(127)):CUPS 1.2/Mac OS X 10.5
233                The client OPTIONALLY supplies this attribute to
234                select the first printer that is returned.
235            'limit' (integer (1:MAX)):
236                The client OPTIONALLY supplies this attribute limiting
237                the number of printers that are returned.
238            'printer-location' (text(127)): CUPS 1.1.7
239                The client OPTIONALLY supplies this attribute to
240                select which printers are returned.
241            'printer-type' (type2 enum): CUPS 1.1.7
242                The client OPTIONALLY supplies a printer type
243                enumeration to select which printers are returned.
244            'printer-type-mask' (type2 enum): CUPS 1.1.7
245                The client OPTIONALLY supplies a printer type mask
246                enumeration to select which bits are used in the
247                'printer-type' attribute.
248            'requested-attributes' (1setOf keyword) :
249                The client OPTIONALLY supplies a set of attribute
250                names and/or attribute group names in whose values the
251                requester is interested. If the client omits this
252                attribute, the server responds as if this attribute
253                had been supplied with a value of 'all'.
254            'requested-user-name' (name(127)) : CUPS 1.2/Mac OS X 10.5
255                The client OPTIONALLY supplies a user name that is
256                used to filter the returned printers.
257
258        CUPS-Get-Printers Response
259        --------------------------
260
261        The following groups of attributes are send as part of the
262        CUPS-Get-Printers Response:
263
264        Group 1: Operation Attributes
265            Status Message:
266                The standard response status message.
267            Natural Language and Character Set:
268                The 'attributes-charset' and
269                'attributes-natural-language' attributes as described
270                in section 3.1.4.2 of the IPP Model and Semantics
271                document.
272               
273        Group 2: Printer Object Attributes
274            The set of requested attributes and their current values
275            for each printer.
276
277        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_PRINTERS )
278           
279        """
280
281        # Each printer will append a new printer attribute group.
282        for printer in self.printers:
283            self._get_printer_attributes(self.printers[printer], request, response)
284        response.operation_id = const.StatusCodes.OK
285
286    @handler_for(const.Operations.CUPS_GET_CLASSES)
287    def cups_get_classes(self, request, response):
288        """The CUPS-Get-Classes operation (0x4005) returns the printer
289        attributes for every printer class known to the system. This
290        may include printer classes that are not served directly by
291        the server.
292
293        CUPS-Get-Classes Request
294        ------------------------
295
296        The following groups of attributes are supplied as part of the
297        CUPS-Get-Classes request:
298
299        Group 1: Operation Attributes
300            Natural Language and Character Set:
301                The 'attributes-charset' and
302                'attributes-natural-language' attributes as described
303                in section 3.1.4.1 of the IPP Model and Semantics
304                document.
305            'first-printer-name' (name(127)):CUPS 1.2/Mac OS X 10.5
306                The client OPTIONALLY supplies this attribute to
307                select the first printer that is returned.
308            'limit' (integer (1:MAX)):
309                The client OPTIONALLY supplies this attribute limiting
310                the number of printer classes that are returned.
311            'printer-location' (text(127)): CUPS 1.1.7
312                The client OPTIONALLY supplies this attribute to
313                select which printer classes are returned.
314            'printer-type' (type2 enum): CUPS 1.1.7
315                The client OPTIONALLY supplies a printer type
316                enumeration to select which printer classes are
317                returned.
318            'printer-type-mask' (type2 enum): CUPS 1.1.7
319                The client OPTIONALLY supplies a printer type mask
320                enumeration to select which bits are used in the
321                'printer-type' attribute.
322            'requested-attributes' (1setOf keyword) :
323                The client OPTIONALLY supplies a set of attribute
324                names and/or attribute group names in whose values the
325                requester is interested. If the client omits this
326                attribute, the server responds as if this attribute
327                had been supplied with a value of 'all'.
328            'requested-user-name' (name(127)) : CUPS 1.2/Mac OS X 10.5
329                The client OPTIONALLY supplies a user name that is
330                used to filter the returned printers.
331               
332        CUPS-Get-Classes Response
333        -------------------------
334
335        The following groups of attributes are send as part of the
336        CUPS-Get-Classes Response:
337
338        Group 1: Operation Attributes
339            Status Message:
340                The standard response status message.
341            Natural Language and Character Set:
342                The 'attributes-charset' and
343                'attributes-natural-language' attributes as described
344                in section 3.1.4.2 of the IPP Model and Semantics
345                document.
346
347        Group 2: Printer Class Object Attributes
348            The set of requested attributes and their current values
349            for each printer class.
350
351        (Source: http://www.cups.org/documentation.php/spec-ipp.html#CUPS_GET_CLASSES )
352
353        """
354       
355        # We have no printer classes, so we don't need to do anything
356        response.operation_id = const.StatusCodes.OK
357
358class GutenbachIPPServer(BaseHTTPServer.BaseHTTPRequestHandler):
359    def setup(self):
360        self.root = GutenbachRequestHandler()
361        BaseHTTPServer.BaseHTTPRequestHandler.setup(self)
362
363    def handle_one_request(self):
364        self.raw_requestline = self.rfile.readline()
365        if not self.raw_requestline:
366            self.close_connection = 1
367            return
368        if not self.parse_request(): # An error code has been sent, just exit
369            return
370        self.handle_ipp()
371
372    def handle_ipp(self):
373        # Receive a request
374        length = int(self.headers.getheader('content-length', 0))
375        request = ipp.Request(request=self.rfile, length=length)
376        logger.debug("Received request: %s" % repr(request))
377
378        # Operation attributes -- typically the same for any request
379        attributes = [
380            ipp.Attribute(
381                'attributes-charset',
382                [ipp.Value(ipp.Tags.CHARSET, 'utf-8')]),
383            ipp.Attribute(
384                'attributes-natural-language',
385                [ipp.Value(ipp.Tags.NATURAL_LANGUAGE, 'en-us')])
386            ]
387        # Put the operation attributes in a group
388        attribute_group = ipp.AttributeGroup(
389            const.AttributeTags.OPERATION,
390            attributes)
391
392        # Set up the default response -- handlers will override these
393        # values if they need to
394        response_kwargs = {}
395        response_kwargs['version']          = request.version
396        response_kwargs['operation_id']     = const.StatusCodes.INTERNAL_ERROR
397        response_kwargs['request_id']       = request.request_id
398        response_kwargs['attribute_groups'] = [attribute_group]
399        response = ipp.Request(**response_kwargs)
400
401        # Get the handler and pass it the request and response objects
402        self.root.handle(request, response)
403        logger.debug("Sending response: %s" % repr(response))
404
405        # Send the response across HTTP
406        self.send_response(200, "Gutenbach IPP Response")
407        self.send_header("Content-Type", "application/ipp")
408        self.send_header("Connection", "close")
409        self.end_headers()
410        self.wfile.write(response.packed_value)
Note: See TracBrowser for help on using the repository browser.