source: server/lib/gutenbach/ipp/core/value.py @ ffbe41d

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

Clean up core ipp code a bit

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