source: server/lib/ippvalue.py @ a76f440

no-cups
Last change on this file since a76f440 was a6a1f43, checked in by Quentin Smith <quentin@…>, 14 years ago

Python <2.6 doesn't have the convenience method property.setter, so instead we'll use a function that emulates it

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