source: server/lib/ipprequest.py @ cf1d291

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

Update references to IPPTags and add import statements for IPPAttribute, IPPAttributeGroup, IPPValue

  • Property mode set to 100644
File size: 9.4 KB
Line 
1#!/usr/bin/python
2
3import sys, struct, logging
4from ippattributegroup import IPPAttributeGroup
5from ippattribute import IPPAttribute
6from ippvalue import IPPValue
7
8# initialize logger
9logger = logging.getLogger("ippLogger")
10
11class IPPRequest():
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 an IPPRequest.  Takes either the segments of the
42        request separately, or a file handle for the request to parse.
43        If the file handle is passed in, all other arguments are
44        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 IPPAttributes, 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 IPPAttributes
80            assert len(attribute_groups) > 0
81            for a in attribute_groups: assert isinstance(a, IPPAttributeGroup)
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 = IPPValue(value_tag, value)
151                        logger.debug("value : %s" % ippvalue.value)
152
153                        # create a new IPPAttribute from the data we just
154                        # read in, and add it to our attributes list
155                        attributes.append(IPPAttribute(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 = IPPValue(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(IPPAttributeGroup(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    def toBinaryData(self):
199        """
200        Packs the value data into binary data.
201        """
202
203        # convert the version, operation id, and request id to binary
204        preattributes = struct.pack('>bbhi',
205                                    self.version[0],
206                                    self.version[1],
207                                    self.operation_id,
208                                    self.request_id)
209
210        # convert the attribute groups to binary
211        attribute_groups = ''.join([a.toBinaryData() for a in self.attribute_groups])
212
213        # conver the end-of-attributes-tag to binary
214        end_of_attributes_tag = struct.pack('>b', AttributeTags.END)
215
216        # convert the data to binary
217        if self.data is not None:
218            data = ''.join([struct.pack('>b', x) for x in self.data])
219        else:
220            data = ''
221
222        # append everything together and return it
223        return preattributes + attribute_groups + end_of_attributes_tag + data
Note: See TracBrowser for help on using the repository browser.