source: server/lib/gutenbach/ipp/core/request.py @ 793432f

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

Reorganization

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