source: server/lib/ipprequest.py @ 8979f90

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

Put various IPP classes in their own files

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