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
RevLine 
[7a1c039]1from .constants import OutOfBandTags, IntegerTags, OctetStringTags, CharacterStringTags
[d04a689]2import sys
3import struct
4import logging
[8979f90]5
6# initialize logger
[7a1c039]7logger = logging.getLogger(__name__)
[8979f90]8
[fa0d0ef]9class Value(object):
[5c5fe6d]10    """An IPP value consists of a tag and a value.
[8979f90]11
12    From RFC 2565:
13     -----------------------------------------------
14     |                   value-tag                 |   1 byte
15     -----------------------------------------------
[5cfb358]16     |            name-length  (value is 0x0000)   |   2 bytes
[8979f90]17     -----------------------------------------------
18     |              value-length  (value is v)     |   2 bytes
19     -----------------------------------------------
20     |                     value                   |   v bytes
21     -----------------------------------------------   
[5c5fe6d]22
[8979f90]23    """
24
[0c4f3bf]25    def __init__(self, tag=None, value=None):
[5c5fe6d]26        """Initialize a Value.  There are three different ways you can
[5cfb358]27        call this method:
28
29            Value() -- creates an empty Value instance
30
[0c4f3bf]31            Value(tag, value) -- creates a Value instance from
[fa0d0ef]32            a non-binary value
[5cfb358]33
34        If you create an empty Value instance, once you have set
[0c4f3bf]35        tag and value, you can retrieve the packed value from
[fa0d0ef]36        the packed_value property.
[8979f90]37
38        Arguments:
39
[0c4f3bf]40            tag -- one byte, identifying the type of value
[8979f90]41
[fa0d0ef]42            value -- variable size, containing the actual value.
43            It should be a string or number.
[5c5fe6d]44
[8979f90]45        """
46
[5cfb358]47        # make sure the arguments are valid
[fa0d0ef]48        if value is not None:
[0c4f3bf]49            assert tag is not None, \
50                   "tag must not be null because " + \
[fa0d0ef]51                   "value is not null!"
[8979f90]52
[5cfb358]53        # initialize member variables
[0c4f3bf]54        self.tag = tag # one byte, the type of value
[fa0d0ef]55        self.value     = value     # non-binary value of self.value
56
[b828a96]57    def __cmp__(self, other):
58        eq = (self.value == other.value) and (self.tag == other.tag)
59        return 0 if eq else 1
60
[fa0d0ef]61    @classmethod
[0c4f3bf]62    def unpack(cls, tag, packed_value):
[5c5fe6d]63        """Unpack a binary IPP value into a Value object.
[fa0d0ef]64
[5cfb358]65        """
[0c4f3bf]66        return cls(tag, cls._unpack(tag, packed_value))
[fa0d0ef]67
68    @staticmethod
[0c4f3bf]69    def _unpack(tag, packed_value):
70        """Given self.tag and self.packed_value, unpack the
[5c5fe6d]71        binary value into either a string or number.  These values
72        MUST NOT be null.
[fa0d0ef]73
74        Returns: unpacked value
[5cfb358]75
76        """
[8979f90]77
[0c4f3bf]78        assert tag is not None, \
[5cfb358]79               "Cannot unpack values with unspecified value tag!"
[fa0d0ef]80        assert packed_value is not None, \
[5cfb358]81               "Cannot unpack null values!"
82
83        value = None
[8979f90]84
85        # out-of-band value tags
[0c4f3bf]86        if tag == OutOfBandTags.UNSUPPORTED or \
87               tag == OutOfBandTags.DEFAULT or \
88               tag == OutOfBandTags.UNKNOWN or \
89               tag == OutOfBandTags.NO_VALUE:
[5cfb358]90            value_size = 0
91            value = ''
[8979f90]92
93        # integer value tags
[0c4f3bf]94        elif tag == IntegerTags.INTEGER:
[fa0d0ef]95            value = struct.unpack('>i', packed_value)[0]
[0c4f3bf]96        elif tag == IntegerTags.BOOLEAN:
[569c377]97            value = struct.unpack('>b', packed_value)[0]
[0c4f3bf]98        elif tag == IntegerTags.ENUM:
[fa0d0ef]99            value = struct.unpack('>i', packed_value)[0]
[8979f90]100
101       
[0c4f3bf]102        elif tag == OctetStringTags.DATETIME:
[8979f90]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
[fa0d0ef]117            value = struct.unpack('>hbbbbbbcbb', packed_value)
[8979f90]118           
[0c4f3bf]119        elif tag == OctetStringTags.RESOLUTION:
[8979f90]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
[fa0d0ef]127            value = struct.unpack('>iib', packed_value)
[8979f90]128           
[0c4f3bf]129        elif tag == OctetStringTags.RANGE_OF_INTEGER:
[8979f90]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
[fa0d0ef]134            value = struct.unpack('>ii', packed_value)
[8979f90]135
[0c4f3bf]136        elif tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
137                 tag == OctetStringTags.NAME_WITH_LANGUAGE:
[fa0d0ef]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])
[5cfb358]142            value = (a, b, c, d)
[8979f90]143
144        # character string value tags
[0c4f3bf]145        elif tag == \
[5cfb358]146                 CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
[0c4f3bf]147                 tag == \
[5cfb358]148                 CharacterStringTags.NAME_WITHOUT_LANGUAGE:
[fa0d0ef]149            value = str(packed_value)
[0c4f3bf]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:
[fa0d0ef]157            value = str(packed_value)
[8979f90]158
[5cfb358]159        # anything else that we didn't handle
160        else:
[fa0d0ef]161            if value is None:
162                value = packed_value
[5cfb358]163
[fa0d0ef]164        return value
[5cfb358]165
[fa0d0ef]166    @property
167    def packed_value(self):
[0c4f3bf]168        """Given self.tag and self.value, pack the value into
[fa0d0ef]169        binary form.  These values MUST NOT be null.
170
171        Returns: packed_value
[5cfb358]172
173        """
174       
[0c4f3bf]175        assert self.tag is not None, \
[5cfb358]176               "cannot pack value with null value tag!"
177        assert self.value is not None, \
178               "cannot pack null value!"
179
[fa0d0ef]180        packed_value = None
[8979f90]181
182        # out-of-band value tags
[0c4f3bf]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:
[fa0d0ef]187            packed_value = ''
[8979f90]188
189        # integer value tags
[0c4f3bf]190        elif self.tag == IntegerTags.INTEGER:
[fa0d0ef]191            packed_value = struct.pack('>i', self.value)
[0c4f3bf]192        elif self.tag == IntegerTags.BOOLEAN:
[569c377]193            packed_value = struct.pack('>b', self.value)
[0c4f3bf]194        elif self.tag == IntegerTags.ENUM:
[fa0d0ef]195            packed_value = struct.pack('>i', self.value)
[8979f90]196
197        # octet string value tags
[0c4f3bf]198        elif self.tag == OctetStringTags.DATETIME:
[8979f90]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
[5cfb358]212
[fa0d0ef]213            packed_value = struct.pack('>hbbbbbbcbb', *self.value)
[8979f90]214           
[0c4f3bf]215        elif self.tag == OctetStringTags.RESOLUTION:
[8979f90]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
[5cfb358]222
[556a285]223            packed_value = truct.pack('>iib', *self.value)
[8979f90]224           
[0c4f3bf]225        elif self.tag == OctetStringTags.RANGE_OF_INTEGER:
[8979f90]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.
[5cfb358]229
[556a285]230            packed_value = struct.pack('>ii', *self.value)
[8979f90]231
[0c4f3bf]232        elif self.tag == OctetStringTags.TEXT_WITH_LANGUAGE or \
233                 self.tag == OctetStringTags.NAME_WITH_LANGUAGE:
[5cfb358]234           
[8979f90]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])
[5cfb358]239
[fa0d0ef]240            packed_value = a_bin + b_bin + c_bin + d_bin
[8979f90]241
242        # character string value tags
[0c4f3bf]243        elif self.tag == \
[5cfb358]244                 CharacterStringTags.TEXT_WITHOUT_LANGUAGE or \
[0c4f3bf]245                 self.tag == \
[5cfb358]246                 CharacterStringTags.NAME_WITHOUT_LANGUAGE:
247
[fa0d0ef]248            packed_value = struct.pack('>%ss' % len(self.value),
[5cfb358]249                                       self.value)
250                   
[0c4f3bf]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:
[5cfb358]258           
[fa0d0ef]259            packed_value = struct.pack('>%ss' % len(self.value),
[5cfb358]260                                       self.value)
261
262        else:
[fa0d0ef]263            packed_value = self.value
[5cfb358]264
[fa0d0ef]265        return packed_value
[5cfb358]266
[5c5fe6d]267    @packed_value.setter
[a6a1f43]268    def packed_value(self, packed_value):
[fa0d0ef]269        """Replace a value using a new packed value
[5cfb358]270
[0c4f3bf]271        Unpacks a new packed_value (of the same tag).
[5cfb358]272
273        """
[0c4f3bf]274        self.value = self._unpack(self.tag, packed_value)
[5cfb358]275
[fa0d0ef]276    @property
277    def packed_value_size(self):
[5c5fe6d]278        """Get the size of the value in bytes.
279       
[5cfb358]280        """
281       
[fa0d0ef]282        return len(self.packed_value)
[8979f90]283
[fa0d0ef]284    @property
285    def total_size(self):
[5c5fe6d]286        """Get the total size of the IPP value.
287       
[c269bc7]288        """
289
[fa0d0ef]290        # 1 byte for the tag
291        return self.packed_value_size + 1
[c269bc7]292
[0c4f3bf]293    @property
294    def pyobj(self):
295        return (self.tag, self.value)
296
[5cfb358]297    def __str__(self):
[fa0d0ef]298        return str(self.value)
299
300    def __repr__(self):
[0c4f3bf]301        return '<IPPValue (%x, %r)>' % (self.tag, self.value)
Note: See TracBrowser for help on using the repository browser.