source: server/lib/ipprequest.py @ 84e8137

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

Add comments and docstrings to ipprequest.py

  • Property mode set to 100644
File size: 10.7 KB
Line 
1#!/usr/bin/python
2
3import sys
4
5# various tags
6zero_name_length                  = 0x00
7operation_attributes_tag          = 0x01
8job_attributes_tag                = 0x02
9end_of_attributes_tag             = 0x03
10printer_attributes_tag            = 0x04
11unsupported_attributes_tag        = 0x05
12
13# "out of band" value tags
14oob_unsupported_value_tag         = 0x10
15oob_default_value_tag             = 0x11
16oob_unknown_value_tag             = 0x12
17oob_no_value_tag                  = 0x13
18
19# integer value tags
20generic_integer_value_tag         = 0x20
21integer_value_tag                 = 0x21
22boolean_value_tag                 = 0x22
23enum_value_tag                    = 0x23
24                 
25# octetString value tags
26unspecified_octetString_value_tag = 0x30
27dateTime_value_tag                = 0x31
28resolution_value_tag              = 0x32
29rangeOfInteger_value_tag          = 0x33
30collection_value_tag              = 0x34
31textWithLanguage_value_tag        = 0x35
32nameWithLanguage_value_tag        = 0x36
33
34# character-string value tags
35generic_char_string_value_tag     = 0x40
36textWithoutLanguage_value_tag     = 0x41
37nameWithoutLanguage_value_tag     = 0x42
38keyword_value_tag                 = 0x44
39uri_value_tag                     = 0x45
40uriScheme_value_tag               = 0x46
41charset_value_tag                 = 0x47
42naturalLanguage_value_tag         = 0x48
43mimeMediaType_value_tag           = 0x49                                   
44
45class IPPValue():
46    """
47    An IPP value consists of a tag and a value, and optionally, a name.
48    """
49
50    def __init__(self, value_tag, name, value):
51        """
52        Initialize an IPPValue:
53
54        Arguments:
55
56            value_tag -- one byte, identifying the type of value
57
58            name -- (optional) variable size, identifying the name of
59                    this value
60
61            value -- variable size, containing the actual value
62        """
63
64        # make sure value_tag isn't empty
65        assert value_tag is not None
66        # make sure the size of value_tag is one byte
67        assert sys.getsizeof(value_tag) == 1
68        # make sure value isn't empty
69        assert value is not None
70
71        self.value_tag = value_tag
72        self.name = name
73        self.value = value
74
75class IPPAttribute():
76     """
77     From RFC 2565:
78
79     Each attribute consists of:
80     -----------------------------------------------
81     |                   value-tag                 |   1 byte
82     -----------------------------------------------
83     |               name-length  (value is u)     |   2 bytes
84     -----------------------------------------------
85     |                     name                    |   u bytes
86     -----------------------------------------------
87     |              value-length  (value is v)     |   2 bytes
88     -----------------------------------------------
89     |                     value                   |   v bytes
90     -----------------------------------------------
91
92     An additional value consists of:
93     -----------------------------------------------------------
94     |                   value-tag                 |   1 byte  |
95     -----------------------------------------------           |
96     |            name-length  (value is 0x0000)   |   2 bytes |
97     -----------------------------------------------           |-0 or more
98     |              value-length (value is w)      |   2 bytes |
99     -----------------------------------------------           |
100     |                     value                   |   w bytes |
101     -----------------------------------------------------------
102     """
103
104     def __init__(self, attribute_tag, values):
105         """
106         Initialize an IPPAttribute.
107
108         Arguments:
109
110             attribute_tag -- one byte, identifying the type of attribute
111
112             values -- a list of IPPValues.  May not be empty.
113         """
114
115         # make sure attribute_tag isn't empty
116         assert attribute_tag is not None
117         # make sure attribute_tag is of the right size
118         assert sys.getsizeof(attribute_tag) == 1
119         
120         # make sure the list of values isn't empty
121         assert len(values) > 0
122         # make sure each value is an IPPValue
123         for value in values: assert isinstance(value, IPPValue)
124       
125         self.attribute_tag = attribute_tag
126         self.values = values
127
128class IPPRequest():
129    """
130    From RFC 2565:
131   
132    The encoding for an operation request or response consists of:
133    -----------------------------------------------
134    |                  version-number             |   2 bytes  - required
135    -----------------------------------------------
136    |               operation-id (request)        |
137    |                      or                     |   2 bytes  - required
138    |               status-code (response)        |
139    -----------------------------------------------
140    |                   request-id                |   4 bytes  - required
141    -----------------------------------------------------------
142    |               xxx-attributes-tag            |   1 byte  |
143    -----------------------------------------------           |-0 or more
144    |             xxx-attribute-sequence          |   n bytes |
145    -----------------------------------------------------------
146    |              end-of-attributes-tag          |   1 byte   - required
147    -----------------------------------------------
148    |                     data                    |   q bytes  - optional
149    -----------------------------------------------
150    """
151
152    # either give the version, operation_id, request_id,
153    # attribute_sequence, and data, or a file handler (request) which
154    # can be read from to get the request
155    def __init__(self, version=None, operation_id=None, request_id=None, attributes=[], data=None, request=None):
156        """
157        Create an IPPRequest.  Takes either the segments of the
158        request separately, or a file handle for the request to parse.
159        If the file handle is passed in, all other arguments are
160        ignored.
161
162        Keyword arguments for passing in the segments of the request:
163       
164            version -- two bytes, identifying the version number of
165                       the request
166                           
167            operation_id -- two bytes, identifying the id of the
168                            requested operation
169
170            request_id -- four bytes, identifying the id of the
171                          request itself.
172
173            attributes -- a list of IPPAttributes.  May be empty.
174
175            data -- (optional) variable length, containing the actual
176                    data of the request
177
178        Keyword arguments for passing in the raw request:
179
180            request -- a file handle that supports the read()
181                       operation
182        """
183
184        if request is None:
185            # make sure the version number isn't empty
186            assert version is not None
187            # make sure the version number is two bytes long
188            assert sys.getsizeof(version) == 2
189            # make sure the operation id isn't empty
190            assert operation_id is not None
191            # make sure the operation id is two bytes long
192            assert sys.getsizeof(operation_id) == 2
193            # make sure the request id isn't empty
194            assert request_id is not None
195            # make sure the request id is four bytes long
196            assert sys.getsizeof(request_id) == 4
197           
198        # if the request isn't None, then we'll read directly from
199        # that file handle
200        if request is not None:
201            # read the version-number (two bytes)
202            self.version        = request.read(2)
203
204            # read the operation-id (or status-code, but that's only
205            # for a response) (two bytes)
206            self.operation_id   = request.read(2)
207
208            # read the request-id (4 bytes)
209            self.request_id     = request.read(4)
210
211            # now we have to read in the attributes.  Each attribute
212            # has a tag (1 byte) and a sequence of values (n bytes)
213            self.attributes     = []
214
215            # read in the next byte
216            next_byte = request.read(1)
217
218            # as long as the next byte isn't signaling the end of the
219            # attributes, keep looping and parsing attributes
220            while next_byte != end_of_attributes_tag:
221               
222                # if the next byte is an attribute tag, then we're at
223                # the start of a new attribute
224                if next_byte <= 0x0F:
225
226                    attribute_tag = next_byte
227                    # read in the value tag (one byte)
228                    value_tag     = request.read(1)
229                    # read in the length of the name (two bytes)
230                    name_length   = request.read(2)
231                    # read the name (name_length bytes)
232                    name          = request.read(name_length)
233                    # read in the length of the value (two bytes)
234                    value_length  = request.read(2)
235                    # read in the value (value_length bytes)
236                    value         = request.read(value_length)
237
238                    # create a new IPPAttribute from the data we just
239                    # read in, and add it to our attributes list
240                    self.attributes.append(IPPAttribute(
241                        attribute_tag,
242                        [IPPValue(value_tag, name, value)]))
243
244                # otherwise, we're still in the process of reading the
245                # current attribute (now we'll read in another value)
246                else:
247                   
248                    value_tag     = next_byte
249                    # read in the length of the name (two bytes) --
250                    # this should be 0x0
251                    name_length   = request.read(2)
252                    assert name_length == zero_name_length
253                    # name should be empty
254                    name          = None
255                    # read in the length of the value (two bytes)
256                    value_length  = request.read(2)
257                    # read in the value (value_length bytes)
258                    value         = request.read(value_length)
259
260                    # add another value to the last attribute
261                    self.attributes[-1].values.append(IPPValue(value_tag, name, value))
262
263                # read another byte
264                next_byte = request.read(1)
265
266            # once we hit the end-of-attributes tag, the only thing
267            # left is the data, so go ahead and read all of it
268            self.data = request.read()
269
270        # otherwise, just set the class variables to the keyword
271        # arguments passed in
272        else:
273            self.version = version
274            self.operation_id = operation_id
275            self.request_id = request_id
276            self.attributes = attributes
277            self.data = data
Note: See TracBrowser for help on using the repository browser.