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
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.tag_size     = 1    # length of the tag in bytes
68        self.value_size   = None # size of self.value in bytes
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
75                self.value_size, self.value = self.unpack()
76            else:
77                self.value_tag = value_tag
78                self.value = value
79                self.value_size, self.binary_value = self.pack()
80
81            self.verify()
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        """
93
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
101
102        # out-of-band value tags
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:
107            value_size = 0
108            value = ''
109
110        # integer value tags
111        elif self.value_tag == IntegerTags.INTEGER:
112            value_size = 4
113            value = struct.unpack('>i', self.binary_value)[0]
114        elif self.value_tag == IntegerTags.BOOLEAN:
115            value_size = 1
116            value = struct.unpack('>?', self.binary_value)[0]
117        elif self.value_tag == IntegerTags.ENUM:
118            value_size = 4
119            value = struct.unpack('>i', self.binary_value)[0]
120
121       
122        elif self.value_tag == OctetStringTags.DATETIME:
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
137            value_size = 11
138            value = struct.unpack('>hbbbbbbcbb', self.binary_value)[0]
139           
140        elif self.value_tag == OctetStringTags.RESOLUTION:
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
148            value_size = 9
149            value = struct.unpack('>iib', self.binary_value)
150           
151        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
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
156            value_size = 8
157            value = struct.unpack('>ii', self.binary_value)
158
159        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
160                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
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)
167
168        # character string value tags
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)
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:
182            value_size = len(str(self.binary_value))
183            value = str(self.binary_value)
184
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
218
219        # out-of-band value tags
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:
224            value_size = 0
225            binary_value = ''
226
227        # integer value tags
228        elif self.value_tag == IntegerTags.INTEGER:
229            value_size = 4
230            binary_value = struct.pack('>i', self.value)
231        elif self.value_tag == IntegerTags.BOOLEAN:
232            value_size = 1
233            binary_value = struct.pack('>?', self.value)
234        elif self.value_tag == IntegerTags.ENUM:
235            value_size = 4
236            binary_value = struct.pack('>i', self.value)
237
238        # octet string value tags
239        elif self.value_tag == OctetStringTags.DATETIME:
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
253
254            value_size = 11
255            binary_value = struct.pack('>hbbbbbbcbb', self.value)
256           
257        elif self.value_tag == OctetStringTags.RESOLUTION:
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
264
265            value_size = 9
266            binary_value = truct.pack('>iib', self.value)
267           
268        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
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.
272
273            value_size = 8
274            binary_value = struct.pack('>ii', self.value)
275
276        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
277                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
278           
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])
283
284            value_size = 4 + self.value[0] + self.value[2]
285            binary_value = a_bin + b_bin + c_bin + d_bin
286
287        # character string value tags
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                   
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:
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
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!"
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
369    def getValueSize(self):
370        """
371        Get the size of the value in bytes.
372        """
373       
374        return self.value_size
375
376    def getTagSize(self):
377        """
378        Get the size of the value tag in bytes.
379        """
380
381        return self.tag_size
382
383    def setValue(self, value):
384        """
385        Set the non-binary value.
386        """
387
388        self.value = value
389
390    def setBinaryValue(self, binary_value):
391        """
392        Set the binary value.
393        """
394
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
405    def setValueSize(self, size):
406        """
407        Set the size of the value in bytes.
408        """
409       
410        self.value_size = size
411
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
419    def __str__(self):
420        return self.value
Note: See TracBrowser for help on using the repository browser.