source: server/lib/gutenbach/ipp/value.py @ b828a96

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

Use classes for standard IPP attributes

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