source: server/lib/ippvalue.py @ 569c377

no-cups
Last change on this file since 569c377 was 569c377, checked in by Quentin Smith <quentin@…>, 13 years ago

Err, make it work

  • Property mode set to 100644
File size: 11.2 KB
RevLine 
[8979f90]1#!/usr/bin/python
2
3import sys, struct, logging
[2646571]4from ippconstants import *
[8979f90]5
6# initialize logger
7logger = logging.getLogger("ippLogger")
8
[a6a1f43]9def setter(prop):
10    def f(func):
11        return property(prop.fget, func, prop.fdel, prop.__doc__)
12    return f
13
[fa0d0ef]14class Value(object):
[8979f90]15    """
[5cfb358]16    An IPP value consists of a tag and a value.
[8979f90]17
18    From RFC 2565:
19     -----------------------------------------------
20     |                   value-tag                 |   1 byte
21     -----------------------------------------------
[5cfb358]22     |            name-length  (value is 0x0000)   |   2 bytes
[8979f90]23     -----------------------------------------------
24     |              value-length  (value is v)     |   2 bytes
25     -----------------------------------------------
26     |                     value                   |   v bytes
27     -----------------------------------------------   
28    """
29
[fa0d0ef]30    def __init__(self, value_tag=None, value=None):
[8979f90]31        """
[5cfb358]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
[fa0d0ef]38            a non-binary value
[5cfb358]39
40        If you create an empty Value instance, once you have set
[fa0d0ef]41        value_tag and value, you can retrieve the packed value from
42        the packed_value property.
[8979f90]43
44        Arguments:
45
46            value_tag -- one byte, identifying the type of value
47
[fa0d0ef]48            value -- variable size, containing the actual value.
49            It should be a string or number.
[8979f90]50        """
51
[5cfb358]52        # make sure the arguments are valid
[fa0d0ef]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!"
[8979f90]57
[5cfb358]58        # initialize member variables
[fa0d0ef]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
[5cfb358]68        """
[fa0d0ef]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
[5cfb358]75        value into either a string or number.  These values MUST NOT
[fa0d0ef]76        be null.
77
78        Returns: unpacked value
[5cfb358]79
80        """
[8979f90]81
[fa0d0ef]82        assert value_tag is not None, \
[5cfb358]83               "Cannot unpack values with unspecified value tag!"
[fa0d0ef]84        assert packed_value is not None, \
[5cfb358]85               "Cannot unpack null values!"
86
87        value = None
[8979f90]88
89        # out-of-band value tags
[fa0d0ef]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:
[5cfb358]94            value_size = 0
95            value = ''
[8979f90]96
97        # integer value tags
[fa0d0ef]98        elif value_tag == IntegerTags.INTEGER:
99            value = struct.unpack('>i', packed_value)[0]
100        elif value_tag == IntegerTags.BOOLEAN:
[569c377]101            value = struct.unpack('>b', packed_value)[0]
[fa0d0ef]102        elif value_tag == IntegerTags.ENUM:
103            value = struct.unpack('>i', packed_value)[0]
[8979f90]104
105       
[fa0d0ef]106        elif value_tag == OctetStringTags.DATETIME:
[8979f90]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
[fa0d0ef]121            value = struct.unpack('>hbbbbbbcbb', packed_value)
[8979f90]122           
[fa0d0ef]123        elif value_tag == OctetStringTags.RESOLUTION:
[8979f90]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
[fa0d0ef]131            value = struct.unpack('>iib', packed_value)
[8979f90]132           
[fa0d0ef]133        elif value_tag == OctetStringTags.RANGE_OF_INTEGER:
[8979f90]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
[fa0d0ef]138            value = struct.unpack('>ii', packed_value)
[8979f90]139
[fa0d0ef]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])
[5cfb358]146            value = (a, b, c, d)
[8979f90]147
148        # character string value tags
[fa0d0ef]149        elif value_tag == \
[5cfb358]150                 CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
[fa0d0ef]151                 value_tag == \
[5cfb358]152                 CharacterStringTags.NAME_WITHOUT_LANGUAGE:
[fa0d0ef]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)
[8979f90]162
[5cfb358]163        # anything else that we didn't handle
164        else:
[fa0d0ef]165            if value is None:
166                value = packed_value
[5cfb358]167
[fa0d0ef]168        return value
[5cfb358]169
[fa0d0ef]170    @property
171    def packed_value(self):
[5cfb358]172        """
173        Given self.value_tag and self.value, pack the value into
[fa0d0ef]174        binary form.  These values MUST NOT be null.
175
176        Returns: packed_value
[5cfb358]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
[fa0d0ef]185        packed_value = None
[8979f90]186
187        # out-of-band value tags
[2646571]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:
[fa0d0ef]192            packed_value = ''
[8979f90]193
194        # integer value tags
[2646571]195        elif self.value_tag == IntegerTags.INTEGER:
[fa0d0ef]196            packed_value = struct.pack('>i', self.value)
[2646571]197        elif self.value_tag == IntegerTags.BOOLEAN:
[569c377]198            packed_value = struct.pack('>b', self.value)
[2646571]199        elif self.value_tag == IntegerTags.ENUM:
[fa0d0ef]200            packed_value = struct.pack('>i', self.value)
[8979f90]201
202        # octet string value tags
[2646571]203        elif self.value_tag == OctetStringTags.DATETIME:
[8979f90]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
[5cfb358]217
[fa0d0ef]218            packed_value = struct.pack('>hbbbbbbcbb', *self.value)
[8979f90]219           
[2646571]220        elif self.value_tag == OctetStringTags.RESOLUTION:
[8979f90]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
[5cfb358]227
[fa0d0ef]228            packed_value = truct.pack('>iib', self.value)
[8979f90]229           
[2646571]230        elif self.value_tag == OctetStringTags.RANGE_OF_INTEGER:
[8979f90]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.
[5cfb358]234
[fa0d0ef]235            packed_value = struct.pack('>ii', self.value)
[8979f90]236
[2646571]237        elif self.value_tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
238                 self.value_tag == OctetStringTags.NAME_WITH_LANGUAGE:
[5cfb358]239           
[8979f90]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])
[5cfb358]244
[fa0d0ef]245            packed_value = a_bin + b_bin + c_bin + d_bin
[8979f90]246
247        # character string value tags
[5cfb358]248        elif self.value_tag == \
249                 CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
250                 self.value_tag == \
251                 CharacterStringTags.NAME_WITHOUT_LANGUAGE:
252
[fa0d0ef]253            packed_value = struct.pack('>%ss' % len(self.value),
[5cfb358]254                                       self.value)
255                   
[2646571]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:
[5cfb358]263           
[fa0d0ef]264            packed_value = struct.pack('>%ss' % len(self.value),
[5cfb358]265                                       self.value)
266
267        else:
[fa0d0ef]268            packed_value = self.value
[5cfb358]269
[fa0d0ef]270        return packed_value
[5cfb358]271
[a6a1f43]272    @setter(packed_value)
273    def packed_value(self, packed_value):
[fa0d0ef]274        """Replace a value using a new packed value
[5cfb358]275
[fa0d0ef]276        Unpacks a new packed_value (of the same value_tag).
[5cfb358]277
278        """
[fa0d0ef]279        self.value = self._unpack(self.value_tag, packed_value)
[5cfb358]280
[fa0d0ef]281    @property
282    def packed_value_size(self):
[5cfb358]283        """
284        Get the size of the value in bytes.
285        """
286       
[fa0d0ef]287        return len(self.packed_value)
[8979f90]288
[fa0d0ef]289    @property
290    def total_size(self):
[c269bc7]291        """
292        Get the total size of the IPP value.
293        """
294
[fa0d0ef]295        # 1 byte for the tag
296        return self.packed_value_size + 1
[c269bc7]297
[5cfb358]298    def __str__(self):
[fa0d0ef]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.