source: server/lib/ipprequest.py @ 0e5cdb3

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

Use new Value interface

  • Property mode set to 100644
File size: 9.4 KB
Line 
1#!/usr/bin/python
2
3import sys, struct, logging
4from ippattributegroup import AttributeGroup
5from ippattribute import Attribute
6from ippvalue import Value
7
8# initialize logger
9logger = logging.getLogger("ippLogger")
10
11class Request():
12    """
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    # either give the version, operation_id, request_id,
36    # attribute_sequence, and data, or a file handler (request) which
37    # can be read from to get the request
38    def __init__(self, version=None, operation_id=None, request_id=None,
39                 attribute_groups=[], data=None, request=None, length=sys.maxint):
40        """
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        if request is None:
69            # make sure the version number isn't empty
70            assert version is not None
71            # make sure verison is a tuple of length 2
72            assert isinstance(version, tuple)
73            assert len(version) == 2
74            # make sure the operation id isn't empty
75            assert operation_id is not None
76            # make sure the request id isn't empty
77            assert request_id is not None
78            # make sure attribute_groups is a list of Attributes
79            assert len(attribute_groups) > 0
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        """
201        Packs the value data into binary data.
202        """
203
204        # convert the version, operation id, and request id to binary
205        preattributes = struct.pack('>bbhi',
206                                    self.version[0],
207                                    self.version[1],
208                                    self.operation_id,
209                                    self.request_id)
210
211        # convert the attribute groups to binary
212        attribute_groups = ''.join([a.packed_value for a in self.attribute_groups])
213
214        # conver the end-of-attributes-tag to binary
215        end_of_attributes_tag = struct.pack('>b', AttributeTags.END)
216
217        # convert the data to binary
218        if self.data is not None:
219            data = ''.join([struct.pack('>b', x) for x in self.data])
220        else:
221            data = ''
222
223        # append everything together and return it
224        return preattributes + attribute_groups + end_of_attributes_tag + data
Note: See TracBrowser for help on using the repository browser.