source: server/lib/ipp/request.py @ 94211df

no-cups
Last change on this file since 94211df was 94211df, checked in by Quentin Smith <quentin@…>, 14 years ago

Improve the repr of IPP request parts

  • Property mode set to 100644
File size: 9.6 KB
Line 
1#!/usr/bin/python
2
3import sys, struct, logging
4from .attributegroup import AttributeGroup
5from .attribute import Attribute
6from .value import Value
7from .constants import AttributeTags
8
9# initialize logger
10logger = logging.getLogger(__name__)
11
12class Request():
13    """
14    From RFC 2565:
15   
16    The encoding for an operation request or response consists of:
17    -----------------------------------------------
18    |                  version-number             |   2 bytes  - required
19    -----------------------------------------------
20    |               operation-id (request)        |
21    |                      or                     |   2 bytes  - required
22    |               status-code (response)        |
23    -----------------------------------------------
24    |                   request-id                |   4 bytes  - required
25    -----------------------------------------------------------
26    |               xxx-attributes-tag            |   1 byte  |
27    -----------------------------------------------           |-0 or more
28    |             xxx-attribute-sequence          |   n bytes |
29    -----------------------------------------------------------
30    |              end-of-attributes-tag          |   1 byte   - required
31    -----------------------------------------------
32    |                     data                    |   q bytes  - optional
33    -----------------------------------------------
34    """
35
36    # either give the version, operation_id, request_id,
37    # attribute_sequence, and data, or a file handler (request) which
38    # can be read from to get the request
39    def __init__(self, version=None, operation_id=None, request_id=None,
40                 attribute_groups=[], data=None, request=None, length=sys.maxint):
41        """
42        Create a Request.  Takes either the segments of the request
43        separately, or a file handle for the request to parse.  If the
44        file handle is passed in, all other arguments are ignored.
45
46        Keyword arguments for passing in the segments of the request:
47       
48            version -- a tuple of two signed chars, identifying the
49                       major version and minor version numbers of the
50                       request
51                           
52            operation_id -- a signed short, identifying the id of the
53                            requested operation
54
55            request_id -- a signed int, identifying the id of the
56                          request itself.
57
58            attribute_groups -- a list of Attributes, at least length 1
59
60            data -- (optional) variable length, containing the actual
61                    data of the request
62
63        Keyword arguments for passing in the raw request:
64
65            request -- a file handle that supports the read()
66                       operation
67        """
68
69        if request is None:
70            # make sure the version number isn't empty
71            assert version is not None
72            # make sure verison is a tuple of length 2
73            assert isinstance(version, tuple)
74            assert len(version) == 2
75            # make sure the operation id isn't empty
76            assert operation_id is not None
77            # make sure the request id isn't empty
78            assert request_id is not None
79            # make sure attribute_groups is a list of Attributes
80            assert len(attribute_groups) > 0
81            for a in attribute_groups: assert isinstance(a, AttributeGroup)
82           
83        # if the request isn't None, then we'll read directly from
84        # that file handle
85        if request is not None:
86            # read the version-number (two signed chars)
87            self.version        = struct.unpack('>bb', request.read(2))
88            length -= 2
89            logger.debug("version-number : (0x%X, 0x%X)" % self.version)
90
91            # read the operation-id (or status-code, but that's only
92            # for a response) (signed short)
93            self.operation_id   = struct.unpack('>h', request.read(2))[0]
94            length -= 2
95            logger.debug("operation-id : 0x%X" % self.operation_id)
96
97            # read the request-id (signed int)
98            self.request_id     = struct.unpack('>i', request.read(4))[0]
99            length -= 4
100            logger.debug("request-id : 0x%X" % self.request_id)
101
102            # now we have to read in the attributes.  Each attribute
103            # has a tag (1 byte) and a sequence of values (n bytes)
104            self.attribute_groups = []
105
106            # read in the next byte
107            next_byte = struct.unpack('>b', request.read(1))[0]
108            length -=1
109            logger.debug("next byte : 0x%X" % next_byte)
110
111            # as long as the next byte isn't signaling the end of the
112            # attributes, keep looping and parsing attributes
113            while next_byte != AttributeTags.END:
114               
115                attribute_group_tag = next_byte
116                logger.debug("attribute-tag : %i" % attribute_group_tag)
117
118                attributes = []
119
120                next_byte = struct.unpack('>b', request.read(1))[0]
121                length -= 1
122                logger.debug("next byte : 0x%X" % next_byte)
123
124                while next_byte > 0x0F:
125                   
126                    # read in the value tag (signed char)
127                    value_tag     = next_byte
128                    logger.debug("value-tag : 0x%X" % value_tag)
129                   
130                    # read in the length of the name (signed short)
131                    name_length   = struct.unpack('>h', request.read(2))[0]
132                    length -= 2
133                    logger.debug("name-length : %i" % name_length)
134                   
135                    if name_length != AttributeTags.ZERO_NAME_LENGTH:
136                        # read the name (a string of name_length bytes)
137                        name          = request.read(name_length)
138                        length -= name_length
139                        logger.debug("name : %s" % name)
140                   
141                        # read in the length of the value (signed short)
142                        value_length  = struct.unpack('>h', request.read(2))[0]
143                        length -= 2
144                        logger.debug("value-length : %i" % value_length)
145                   
146                        # read in the value (string of value_length bytes)
147                        value         = request.read(value_length)
148                        length -= value_length
149                       
150                        ippvalue = Value.unpack(value_tag, value)
151                        logger.debug("value : %s" % ippvalue.value)
152
153                        # create a new Attribute from the data we just
154                        # read in, and add it to our attributes list
155                        attributes.append(Attribute(name, [ippvalue]))
156
157                    else:
158                        # read in the length of the value (signed short)
159                        value_length  = struct.unpack('>h', request.read(2))[0]
160                        length -= 2
161                        logger.debug("value-length : %i" % value_length)
162                   
163                        # read in the value (string of value_length bytes)
164                        value         = request.read(value_length)
165                        length -= value_length
166
167                        ippvalue = Value.unpack(value_tag, value)
168                        logger.debug("value : %s" % ippvalue.value)
169
170                        # add another value to the last attribute
171                        attributes[-1].values.append(ippvalue)
172
173                    # read another byte
174                    next_byte = struct.unpack('>b', request.read(1))[0]
175                    length -= 1
176
177                self.attribute_groups.append(AttributeGroup(
178                    attribute_group_tag, attributes))
179
180            # once we hit the end-of-attributes tag, the only thing
181            # left is the data, so go ahead and read all of it
182            assert length >= 0
183            self.data = request.read(length)
184            logger.debug("data : %s" % self.data)
185
186        # otherwise, just set the class variables to the keyword
187        # arguments passed in
188        else:
189            self.version = (version[0], version[1])
190            self.operation_id = operation_id
191            self.request_id = request_id
192            self.attribute_groups = attribute_groups
193            self.data = data
194
195    def getAttributeGroup(self, tag):
196        return filter(lambda x: x.attribute_group_tag == tag,
197                      self.attribute_groups)
198
199    @property
200    def packed_value(self):
201        """
202        Packs the value data into binary data.
203        """
204
205        # convert the version, operation id, and request id to binary
206        preattributes = struct.pack('>bbhi',
207                                    self.version[0],
208                                    self.version[1],
209                                    self.operation_id,
210                                    self.request_id)
211
212        # convert the attribute groups to binary
213        attribute_groups = ''.join([a.packed_value for a in self.attribute_groups])
214
215        # conver the end-of-attributes-tag to binary
216        end_of_attributes_tag = struct.pack('>b', AttributeTags.END)
217
218        # convert the data to binary
219        if self.data is not None:
220            data = ''.join([struct.pack('>b', x) for x in self.data])
221        else:
222            data = ''
223
224        # append everything together and return it
225        return preattributes + attribute_groups + end_of_attributes_tag + data
226
227    def __repr__(self):
228        return '<IPPRequest (version=%r, operation_id=%x, request_id=%r, attribute_groups=%r)>' % (self.version, self.operation_id, self.request_id, self.attribute_groups)
Note: See TracBrowser for help on using the repository browser.