source: server/lib/gutenbach/ipp/value.py @ 0c4f3bf

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

Rename value_tag to tag in ipp.value

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