source: server/lib/ippvalue.py @ fa0d0ef

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

Redo API in ippvalue.Value; various cases for packing/unpacking have not yet been tested

  • Property mode set to 100644
File size: 11.1 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(object):
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):
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            a non-binary value
34
35        If you create an empty Value instance, once you have set
36        value_tag and value, you can retrieve the packed value from
37        the packed_value property.
38
39        Arguments:
40
41            value_tag -- one byte, identifying the type of value
42
43            value -- variable size, containing the actual value.
44            It should be a string or number.
45        """
46
47        # make sure the arguments are valid
48        if value is not None:
49            assert value_tag is not None, \
50                   "value_tag must not be null because " + \
51                   "value is not null!"
52
53        # initialize member variables
54        self.value_tag = value_tag # one byte, the type of value
55        self.value     = value     # non-binary value of self.value
56
57    @classmethod
58    def unpack(cls, value_tag, packed_value):
59        """Unpack a binary IPP value
60
61        Unpacks a binary string into a Value object.
62
63        """
64        return cls(value_tag, cls._unpack(value_tag, packed_value))
65
66    @staticmethod
67    def _unpack(value_tag, packed_value):
68        """
69        Given self.value_tag and self.packed_value, unpack the binary
70        value into either a string or number.  These values MUST NOT
71        be null.
72
73        Returns: unpacked value
74
75        """
76
77        assert value_tag is not None, \
78               "Cannot unpack values with unspecified value tag!"
79        assert packed_value is not None, \
80               "Cannot unpack null values!"
81
82        value = None
83
84        # out-of-band value tags
85        if value_tag == OutOfBandTags.UNSUPPORTED or \
86               value_tag == OutOfBandTags.DEFAULT or \
87               value_tag == OutOfBandTags.UNKNOWN or \
88               value_tag == OutOfBandTags.NO_VALUE:
89            value_size = 0
90            value = ''
91
92        # integer value tags
93        elif value_tag == IntegerTags.INTEGER:
94            value = struct.unpack('>i', packed_value)[0]
95        elif value_tag == IntegerTags.BOOLEAN:
96            value = struct.unpack('>?', packed_value)[0]
97        elif value_tag == IntegerTags.ENUM:
98            value = struct.unpack('>i', packed_value)[0]
99
100       
101        elif value_tag == OctetStringTags.DATETIME:
102            # field  octets  contents                  range
103            # -----  ------  --------                  -----
104            #   1      1-2   year                      0..65536
105            #   2       3    month                     1..12
106            #   3       4    day                       1..31
107            #   4       5    hour                      0..23
108            #   5       6    minutes                   0..59
109            #   6       7    seconds                   0..60
110            #                (use 60 for leap-second)
111            #   7       8    deci-seconds              0..9
112            #   8       9    direction from UTC        '+' / '-'
113            #   9      10    hours from UTC            0..11
114            #  10      11    minutes from UTC          0..59
115
116            value = struct.unpack('>hbbbbbbcbb', packed_value)
117           
118        elif value_tag == OctetStringTags.RESOLUTION:
119            # OCTET-STRING consisting of nine octets of 2
120            # SIGNED-INTEGERs followed by a SIGNED-BYTE. The first
121            # SIGNED-INTEGER contains the value of cross feed
122            # direction resolution. The second SIGNED-INTEGER contains
123            # the value of feed direction resolution. The SIGNED-BYTE
124            # contains the units
125
126            value = struct.unpack('>iib', packed_value)
127           
128        elif value_tag == OctetStringTags.RANGE_OF_INTEGER:
129            # Eight octets consisting of 2 SIGNED-INTEGERs.  The first
130            # SIGNED-INTEGER contains the lower bound and the second
131            # SIGNED-INTEGER contains the upper bound.
132
133            value = struct.unpack('>ii', packed_value)
134
135        elif value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
136                 value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
137            a = struct.unpack('>h', packed_value[:2])[0]
138            b = struct.unpack('>%ss' % a, packed_value[2:a+2])[0]
139            c = struct.unpack('>h', packed_value[a+2:a+4])[0]
140            d = struct.unpack('>%ss' % c, packed_value[a+4:][0])
141            value = (a, b, c, d)
142
143        # character string value tags
144        elif value_tag == \
145                 CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
146                 value_tag == \
147                 CharacterStringTags.NAME_WITHOUT_LANGUAGE:
148            value = str(packed_value)
149        elif value_tag == CharacterStringTags.GENERIC or \
150                 value_tag == CharacterStringTags.KEYWORD or \
151                 value_tag == CharacterStringTags.URI or \
152                 value_tag == CharacterStringTags.URI_SCHEME or \
153                 value_tag == CharacterStringTags.CHARSET or \
154                 value_tag == CharacterStringTags.NATURAL_LANGUAGE or \
155                 value_tag == CharacterStringTags.MIME_MEDIA_TYPE:
156            value = str(packed_value)
157
158        # anything else that we didn't handle
159        else:
160            if value is None:
161                value = packed_value
162
163        return value
164
165    @property
166    def packed_value(self):
167        """
168        Given self.value_tag and self.value, pack the value into
169        binary form.  These values MUST NOT be null.
170
171        Returns: packed_value
172
173        """
174       
175        assert self.value_tag is not None, \
176               "cannot pack value with null value tag!"
177        assert self.value is not None, \
178               "cannot pack null value!"
179
180        packed_value = None
181
182        # out-of-band value tags
183        if self.value_tag == OutOfBandTags.UNSUPPORTED or \
184               self.value_tag == OutOfBandTags.DEFAULT or \
185               self.value_tag == OutOfBandTags.UNKNOWN or \
186               self.value_tag == OutOfBandTags.NO_VALUE:
187            packed_value = ''
188
189        # integer value tags
190        elif self.value_tag == IntegerTags.INTEGER:
191            packed_value = struct.pack('>i', self.value)
192        elif self.value_tag == IntegerTags.BOOLEAN:
193            packed_value = struct.pack('>?', self.value)
194        elif self.value_tag == IntegerTags.ENUM:
195            packed_value = struct.pack('>i', self.value)
196
197        # octet string value tags
198        elif self.value_tag == OctetStringTags.DATETIME:
199            # field  octets  contents                  range
200            # -----  ------  --------                  -----
201            #   1      1-2   year                      0..65536
202            #   2       3    month                     1..12
203            #   3       4    day                       1..31
204            #   4       5    hour                      0..23
205            #   5       6    minutes                   0..59
206            #   6       7    seconds                   0..60
207            #                (use 60 for leap-second)
208            #   7       8    deci-seconds              0..9
209            #   8       9    direction from UTC        '+' / '-'
210            #   9      10    hours from UTC            0..11
211            #  10      11    minutes from UTC          0..59
212
213            packed_value = struct.pack('>hbbbbbbcbb', *self.value)
214           
215        elif self.value_tag == OctetStringTags.RESOLUTION:
216            # OCTET-STRING consisting of nine octets of 2
217            # SIGNED-INTEGERs followed by a SIGNED-BYTE. The first
218            # SIGNED-INTEGER contains the value of cross feed
219            # direction resolution. The second SIGNED-INTEGER contains
220            # the value of feed direction resolution. The SIGNED-BYTE
221            # contains the units
222
223            packed_value = truct.pack('>iib', self.value)
224           
225        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
226            # Eight octets consisting of 2 SIGNED-INTEGERs.  The first
227            # SIGNED-INTEGER contains the lower bound and the second
228            # SIGNED-INTEGER contains the upper bound.
229
230            packed_value = struct.pack('>ii', self.value)
231
232        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
233                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
234           
235            a_bin = struct.pack('>h', self.value[0])
236            b_bin = struct.pack('>%ss' % self.value[0], self.value[1])
237            c_bin = struct.pack('>h', self.value[2])
238            d_bin = struct.pack('>%ss' % self.value[2], self.value[3])
239
240            packed_value = a_bin + b_bin + c_bin + d_bin
241
242        # character string value tags
243        elif self.value_tag == \
244                 CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
245                 self.value_tag == \
246                 CharacterStringTags.NAME_WITHOUT_LANGUAGE:
247
248            packed_value = struct.pack('>%ss' % len(self.value),
249                                       self.value)
250                   
251        elif self.value_tag == CharacterStringTags.GENERIC or \
252                 self.value_tag == CharacterStringTags.KEYWORD or \
253                 self.value_tag == CharacterStringTags.URI or \
254                 self.value_tag == CharacterStringTags.URI_SCHEME or \
255                 self.value_tag == CharacterStringTags.CHARSET or \
256                 self.value_tag == CharacterStringTags.NATURAL_LANGUAGE or \
257                 self.value_tag == CharacterStringTags.MIME_MEDIA_TYPE:
258           
259            packed_value = struct.pack('>%ss' % len(self.value),
260                                       self.value)
261
262        else:
263            packed_value = self.value
264
265        return packed_value
266
267    @packed_value.setter
268    def set_packed_value(self, packed_value):
269        """Replace a value using a new packed value
270
271        Unpacks a new packed_value (of the same value_tag).
272
273        """
274        self.value = self._unpack(self.value_tag, packed_value)
275
276    @property
277    def packed_value_size(self):
278        """
279        Get the size of the value in bytes.
280        """
281       
282        return len(self.packed_value)
283
284    @property
285    def total_size(self):
286        """
287        Get the total size of the IPP value.
288        """
289
290        # 1 byte for the tag
291        return self.packed_value_size + 1
292
293    def __str__(self):
294        return str(self.value)
295
296    def __repr__(self):
297        return '<IPPValue (%r, %r)>' % (self.value_tag, self.value)
Note: See TracBrowser for help on using the repository browser.