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

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

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

  • Property mode set to 100644
File size: 15.2 KB
RevLine 
[8979f90]1#!/usr/bin/python
2
3import sys, struct, logging
[2646571]4from ippconstants import *
[8979f90]5
6# initialize logger
7logger = logging.getLogger("ippLogger")
8
[ebf327d]9class Value():
[8979f90]10    """
[5cfb358]11    An IPP value consists of a tag and a value.
[8979f90]12
13    From RFC 2565:
14     -----------------------------------------------
15     |                   value-tag                 |   1 byte
16     -----------------------------------------------
[5cfb358]17     |            name-length  (value is 0x0000)   |   2 bytes
[8979f90]18     -----------------------------------------------
19     |              value-length  (value is v)     |   2 bytes
20     -----------------------------------------------
21     |                     value                   |   v bytes
22     -----------------------------------------------   
23    """
24
[5cfb358]25    def __init__(self, value_tag=None, value=None, unpack=True):
[8979f90]26        """
[5cfb358]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().
[8979f90]41
42        Arguments:
43
44            value_tag -- one byte, identifying the type of value
45
[5cfb358]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)
[8979f90]53        """
54
[5cfb358]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!"
[8979f90]63
[5cfb358]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        """
[8979f90]92
[5cfb358]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
[8979f90]100
101        # out-of-band value tags
[2646571]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:
[5cfb358]106            value_size = 0
107            value = ''
[8979f90]108
109        # integer value tags
[2646571]110        elif self.value_tag == IntegerTags.INTEGER:
[5cfb358]111            value_size = 4
112            value = struct.unpack('>i', self.binary_value)[0]
[2646571]113        elif self.value_tag == IntegerTags.BOOLEAN:
[5cfb358]114            value_size = 1
115            value = struct.unpack('>?', self.binary_value)[0]
[2646571]116        elif self.value_tag == IntegerTags.ENUM:
[5cfb358]117            value_size = 4
118            value = struct.unpack('>i', self.binary_value)[0]
[8979f90]119
120       
[2646571]121        elif self.value_tag == OctetStringTags.DATETIME:
[8979f90]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
[5cfb358]136            value_size = 11
137            value = struct.unpack('>hbbbbbbcbb', self.binary_value)[0]
[8979f90]138           
[2646571]139        elif self.value_tag == OctetStringTags.RESOLUTION:
[8979f90]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
[5cfb358]147            value_size = 9
148            value = struct.unpack('>iib', self.binary_value)
[8979f90]149           
[2646571]150        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
[8979f90]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
[5cfb358]155            value_size = 8
156            value = struct.unpack('>ii', self.binary_value)
[8979f90]157
[2646571]158        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
159                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
[5cfb358]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)
[8979f90]166
167        # character string value tags
[5cfb358]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)
[2646571]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:
[5cfb358]181            value_size = len(str(self.binary_value))
182            value = str(self.binary_value)
[8979f90]183
[5cfb358]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
[8979f90]220
221        # out-of-band value tags
[2646571]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:
[5cfb358]226            value_size = 0
227            binary_value = ''
[8979f90]228
229        # integer value tags
[2646571]230        elif self.value_tag == IntegerTags.INTEGER:
[5cfb358]231            value_size = 4
232            binary_value = struct.pack('>i', self.value)
[2646571]233        elif self.value_tag == IntegerTags.BOOLEAN:
[5cfb358]234            value_size = 1
235            binary_value = struct.pack('>?', self.value)
[2646571]236        elif self.value_tag == IntegerTags.ENUM:
[5cfb358]237            value_size = 4
238            binary_value = struct.pack('>i', self.value)
[8979f90]239
240        # octet string value tags
[2646571]241        elif self.value_tag == OctetStringTags.DATETIME:
[8979f90]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
[5cfb358]255
256            value_size = 11
257            binary_value = struct.pack('>hbbbbbbcbb', self.value)
[8979f90]258           
[2646571]259        elif self.value_tag == OctetStringTags.RESOLUTION:
[8979f90]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
[5cfb358]266
267            value_size = 9
268            binary_value = truct.pack('>iib', self.value)
[8979f90]269           
[2646571]270        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
[8979f90]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.
[5cfb358]274
275            value_size = 8
276            binary_value = struct.pack('>ii', self.value)
[8979f90]277
[2646571]278        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
279                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
[5cfb358]280           
[8979f90]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])
[5cfb358]285
286            value_size = 4 + self.value[0] + self.value[2]
287            binary_value = a_bin + b_bin + c_bin + d_bin
[8979f90]288
289        # character string value tags
[5cfb358]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                   
[2646571]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:
[5cfb358]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
[8979f90]407
[5cfb358]408    def __str__(self):
409        return self.value
Note: See TracBrowser for help on using the repository browser.