source: server/lib/ippvalue.py @ 5cfb358

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

Change structure of ippvalue.Value to make it easier to manipulate.

  • Property mode set to 100644
File size: 15.2 KB
Line 
1#!/usr/bin/python
2
3import sys, struct, logging
4from ippconstants import *
5
6# initialize logger
7logger = logging.getLogger("ippLogger")
8
9class Value():
10    """
11    An IPP value consists of a tag and a value.
12
13    From RFC 2565:
14     -----------------------------------------------
15     |                   value-tag                 |   1 byte
16     -----------------------------------------------
17     |            name-length  (value is 0x0000)   |   2 bytes
18     -----------------------------------------------
19     |              value-length  (value is v)     |   2 bytes
20     -----------------------------------------------
21     |                     value                   |   v bytes
22     -----------------------------------------------   
23    """
24
25    def __init__(self, value_tag=None, value=None, unpack=True):
26        """
27        Initialize a Value.  There are three different ways you can
28        call this method:
29
30            Value() -- creates an empty Value instance
31
32            Value(value_tag, value) -- creates a Value instance from
33            binary values
34
35            Value(value_tag, value, unpack=False) -- creates a Value
36            instance from non-binary values
37
38        If you create an empty Value instance, once you have set
39        value_tag and value, you should call pack(), or if you set
40        value_tag and binary_value, call unpack().
41
42        Arguments:
43
44            value_tag -- one byte, identifying the type of value
45
46            value -- variable size, containing the actual value.  If
47            unpack is True, then this should be a binary value,
48            otherwise it should be a string or number.
49
50            unpack -- boolean which indicates whether the value passed
51            in is binary (i.e., should be unpacked) or not (i.e., does
52            not need to be unpacked)
53        """
54
55        # make sure the arguments are valid
56        if value_tag is not None:
57            assert value is not None, \
58                   "value must not be null because " + \
59                   "value_tag is not null!"
60        elif value_tag is None:
61            assert value is None, \
62                   "value must be null because value_tag is null!"
63
64        # initialize member variables
65        self.value_tag    = None # one byte, the type of value
66        self.value        = None # non-binary value of self.value
67        self.value_size   = None # size of self.value
68        self.binary_value = None # binary value of self.value
69
70        if value_tag is not None and value is not None:
71            if unpack:
72                self.value_tag = value_tag
73                self.binary_value = value
74                self.unpack()
75                self.pack()
76            else:
77                self.value_tag = value_tag
78                self.value = value
79                self.pack()
80                self.unpack()
81
82    def unpack(self):
83        """
84        Given self.value_tag and self.binary_value, unpack the binary
85        value into either a string or number.  These values MUST NOT
86        be null.  Furthermore, if self.value and self.value_size are
87        set, this method will check to make sure that unpacking
88        self.binary_value matches up.
89
90        Returns: tuple of (value_size, value)
91        """
92
93        assert self.value_tag is not None, \
94               "Cannot unpack values with unspecified value tag!"
95        assert self.binary_value is not None, \
96               "Cannot unpack null values!"
97
98        value_size = None
99        value = None
100
101        # out-of-band value tags
102        if self.value_tag == OutOfBandTags.UNSUPPORTED or \
103               self.value_tag == OutOfBandTags.DEFAULT or \
104               self.value_tag == OutOfBandTags.UNKNOWN or \
105               self.value_tag == OutOfBandTags.NO_VALUE:
106            value_size = 0
107            value = ''
108
109        # integer value tags
110        elif self.value_tag == IntegerTags.INTEGER:
111            value_size = 4
112            value = struct.unpack('>i', self.binary_value)[0]
113        elif self.value_tag == IntegerTags.BOOLEAN:
114            value_size = 1
115            value = struct.unpack('>?', self.binary_value)[0]
116        elif self.value_tag == IntegerTags.ENUM:
117            value_size = 4
118            value = struct.unpack('>i', self.binary_value)[0]
119
120       
121        elif self.value_tag == OctetStringTags.DATETIME:
122            # field  octets  contents                  range
123            # -----  ------  --------                  -----
124            #   1      1-2   year                      0..65536
125            #   2       3    month                     1..12
126            #   3       4    day                       1..31
127            #   4       5    hour                      0..23
128            #   5       6    minutes                   0..59
129            #   6       7    seconds                   0..60
130            #                (use 60 for leap-second)
131            #   7       8    deci-seconds              0..9
132            #   8       9    direction from UTC        '+' / '-'
133            #   9      10    hours from UTC            0..11
134            #  10      11    minutes from UTC          0..59
135
136            value_size = 11
137            value = struct.unpack('>hbbbbbbcbb', self.binary_value)[0]
138           
139        elif self.value_tag == OctetStringTags.RESOLUTION:
140            # OCTET-STRING consisting of nine octets of 2
141            # SIGNED-INTEGERs followed by a SIGNED-BYTE. The first
142            # SIGNED-INTEGER contains the value of cross feed
143            # direction resolution. The second SIGNED-INTEGER contains
144            # the value of feed direction resolution. The SIGNED-BYTE
145            # contains the units
146
147            value_size = 9
148            value = struct.unpack('>iib', self.binary_value)
149           
150        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
151            # Eight octets consisting of 2 SIGNED-INTEGERs.  The first
152            # SIGNED-INTEGER contains the lower bound and the second
153            # SIGNED-INTEGER contains the upper bound.
154
155            value_size = 8
156            value = struct.unpack('>ii', self.binary_value)
157
158        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
159                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
160            a = struct.unpack('>h', self.binary_value[:2])[0]
161            b = struct.unpack('>%ss' % a, self.binary_value[2:a+2])[0]
162            c = struct.unpack('>h', self.binary_value[a+2:a+4])[0]
163            d = struct.unpack('>%ss' % c, self.binary_value[a+4:][0])
164            value_size = 4 + a + c
165            value = (a, b, c, d)
166
167        # character string value tags
168        elif self.value_tag == \
169                 CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
170                 self.value_tag == \
171                 CharacterStringTags.NAME_WITHOUT_LANGUAGE:
172            value_size = len(str(self.binary_value))
173            value = str(self.binary_value)
174        elif self.value_tag == CharacterStringTags.GENERIC or \
175                 self.value_tag == CharacterStringTags.KEYWORD or \
176                 self.value_tag == CharacterStringTags.URI or \
177                 self.value_tag == CharacterStringTags.URI_SCHEME or \
178                 self.value_tag == CharacterStringTags.CHARSET or \
179                 self.value_tag == CharacterStringTags.NATURAL_LANGUAGE or \
180                 self.value_tag == CharacterStringTags.MIME_MEDIA_TYPE:
181            value_size = len(str(self.binary_value))
182            value = str(self.binary_value)
183
184        # anything else that we didn't handle
185        else:
186            if value_size is None and value is None:
187                value_size = len(self.binary_value)
188                value = self.binary_value
189
190        if self.value_size is not None:
191            assert value_size == self.value_size, \
192                   "unpacked value_size is not the same " + \
193                   "as self.value_size!"
194        if self.value is not None:
195            assert value == self.value, \
196                   "unpacked value is not the same as self.value!"
197
198        self.value_size = value_size
199        self.value = value
200       
201        return value_size, value
202
203    def pack(self):
204        """
205        Given self.value_tag and self.value, pack the value into
206        binary formo.  These values MUST NOT be null.  Furthermore, if
207        self.binary_value and self.value_size are set, this method
208        will check to make sure that packing self.value matches up.
209
210        Returns: tuple of (value_size, binary_value)
211        """
212       
213        assert self.value_tag is not None, \
214               "cannot pack value with null value tag!"
215        assert self.value is not None, \
216               "cannot pack null value!"
217
218        value_size = None
219        binary_value = None
220
221        # out-of-band value tags
222        if self.value_tag == OutOfBandTags.UNSUPPORTED or \
223               self.value_tag == OutOfBandTags.DEFAULT or \
224               self.value_tag == OutOfBandTags.UNKNOWN or \
225               self.value_tag == OutOfBandTags.NO_VALUE:
226            value_size = 0
227            binary_value = ''
228
229        # integer value tags
230        elif self.value_tag == IntegerTags.INTEGER:
231            value_size = 4
232            binary_value = struct.pack('>i', self.value)
233        elif self.value_tag == IntegerTags.BOOLEAN:
234            value_size = 1
235            binary_value = struct.pack('>?', self.value)
236        elif self.value_tag == IntegerTags.ENUM:
237            value_size = 4
238            binary_value = struct.pack('>i', self.value)
239
240        # octet string value tags
241        elif self.value_tag == OctetStringTags.DATETIME:
242            # field  octets  contents                  range
243            # -----  ------  --------                  -----
244            #   1      1-2   year                      0..65536
245            #   2       3    month                     1..12
246            #   3       4    day                       1..31
247            #   4       5    hour                      0..23
248            #   5       6    minutes                   0..59
249            #   6       7    seconds                   0..60
250            #                (use 60 for leap-second)
251            #   7       8    deci-seconds              0..9
252            #   8       9    direction from UTC        '+' / '-'
253            #   9      10    hours from UTC            0..11
254            #  10      11    minutes from UTC          0..59
255
256            value_size = 11
257            binary_value = struct.pack('>hbbbbbbcbb', self.value)
258           
259        elif self.value_tag == OctetStringTags.RESOLUTION:
260            # OCTET-STRING consisting of nine octets of 2
261            # SIGNED-INTEGERs followed by a SIGNED-BYTE. The first
262            # SIGNED-INTEGER contains the value of cross feed
263            # direction resolution. The second SIGNED-INTEGER contains
264            # the value of feed direction resolution. The SIGNED-BYTE
265            # contains the units
266
267            value_size = 9
268            binary_value = truct.pack('>iib', self.value)
269           
270        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
271            # Eight octets consisting of 2 SIGNED-INTEGERs.  The first
272            # SIGNED-INTEGER contains the lower bound and the second
273            # SIGNED-INTEGER contains the upper bound.
274
275            value_size = 8
276            binary_value = struct.pack('>ii', self.value)
277
278        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
279                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
280           
281            a_bin = struct.pack('>h', self.value[0])
282            b_bin = struct.pack('>%ss' % self.value[0], self.value[1])
283            c_bin = struct.pack('>h', self.value[2])
284            d_bin = struct.pack('>%ss' % self.value[2], self.value[3])
285
286            value_size = 4 + self.value[0] + self.value[2]
287            binary_value = a_bin + b_bin + c_bin + d_bin
288
289        # character string value tags
290        elif self.value_tag == \
291                 CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
292                 self.value_tag == \
293                 CharacterStringTags.NAME_WITHOUT_LANGUAGE:
294
295            value_size = len(self.value)
296            binary_value = struct.pack('>%ss' % len(self.value),
297                                       self.value)
298                   
299        elif self.value_tag == CharacterStringTags.GENERIC or \
300                 self.value_tag == CharacterStringTags.KEYWORD or \
301                 self.value_tag == CharacterStringTags.URI or \
302                 self.value_tag == CharacterStringTags.URI_SCHEME or \
303                 self.value_tag == CharacterStringTags.CHARSET or \
304                 self.value_tag == CharacterStringTags.NATURAL_LANGUAGE or \
305                 self.value_tag == CharacterStringTags.MIME_MEDIA_TYPE:
306           
307            value_size = len(self.value)
308            binary_value = struct.pack('>%ss' % len(self.value),
309                                       self.value)
310
311        else:
312            value_size = len(self.value)
313            binary_value = self.value
314
315        if self.value_size is not None:
316            assert value_size == self.value_size, \
317                   "packed value size is not the same as " + \
318                   "self.value_size!"
319        if self.binary_value is not None:
320            assert binary_value == self.binary_value, \
321                   "packed binary value is not the same as " + \
322                   "self.binary_value!"
323
324        self.value_size = value_size
325        self.binary_value = binary_value
326
327        return value_size, binary_value
328
329    def verify(self):
330        """
331        Verifies that the binary and non-binary values for this Value
332        instance line up.  All of self.value, self.binary_value,
333        self.value_size, and self.value_tag must be defined.
334
335        This function will throw an assertion error if the Value
336        instance is not valid.
337        """
338
339        assert self.value is not None, \
340               "value is null!"
341        assert self.binary_value is not None, \
342               "binary value is null!"
343        assert self.value_size is not None, \
344               "value size is unknown!"
345        assert self.value_tag is not None, \
346               "value type is unknown!"
347
348        self.pack()
349        self.unpack()
350
351    def getValue(self):
352        """
353        Get the non-binary value.
354        """
355
356        return self.value
357
358    def getBinaryValue(self):
359        """
360        Get the binary value.
361        """
362       
363        return self.binary_value
364
365    def getValueTag(self):
366        """
367        Get the value tag (type of value). This is an integer
368        corresponding to a value in ippconstants.
369        """
370       
371        return self.value_tag
372
373    def getSize(self):
374        """
375        Get the size of the value in bytes.
376        """
377       
378        return self.value_size
379
380    def setValue(self, value):
381        """
382        Set the non-binary value.
383        """
384       
385        self.value = value
386
387    def setBinaryValue(self, binary_value):
388        """
389        Set the binary value.
390        """
391        self.binary_value = binary_value
392
393    def setValueTag(self, value_tag):
394        """
395        Set the type (tag) of the value.  This should correspond to an
396        integer defined in ippconstants.
397        """
398       
399        self.value_tag = value_tag
400
401    def setSize(self, size):
402        """
403        Set the size of the value in bytes.
404        """
405       
406        self.value_size = size
407
408    def __str__(self):
409        return self.value
Note: See TracBrowser for help on using the repository browser.