source: server/lib/ippvalue.py @ c269bc7

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

Update API for Value, Attribute, and AttributeGroup?

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