source: server/lib/ipprequest.py @ 569c377

no-cups
Last change on this file since 569c377 was 569c377, checked in by Quentin Smith <quentin@…>, 13 years ago

Err, make it work

  • Property mode set to 100644
File size: 9.4 KB
RevLine 
[c216863]1#!/usr/bin/python
2
[8403f61]3import sys, struct, logging
[ebf327d]4from ippattributegroup import AttributeGroup
5from ippattribute import Attribute
6from ippvalue import Value
[569c377]7from ippconstants import *
[8403f61]8
9# initialize logger
10logger = logging.getLogger("ippLogger")
[4ec7caa]11
[ebf327d]12class Request():
[84e8137]13    """
14    From RFC 2565:
[c216863]15   
[84e8137]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    """
[c216863]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
[8e43aa8]39    def __init__(self, version=None, operation_id=None, request_id=None,
[89fe6da]40                 attribute_groups=[], data=None, request=None, length=sys.maxint):
[84e8137]41        """
[ebf327d]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.
[84e8137]45
46        Keyword arguments for passing in the segments of the request:
47       
[4ec7caa]48            version -- a tuple of two signed chars, identifying the
49                       major version and minor version numbers of the
50                       request
[84e8137]51                           
[4ec7caa]52            operation_id -- a signed short, identifying the id of the
[84e8137]53                            requested operation
54
[4ec7caa]55            request_id -- a signed int, identifying the id of the
[84e8137]56                          request itself.
[c216863]57
[ebf327d]58            attribute_groups -- a list of Attributes, at least length 1
[84e8137]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
[4ec7caa]72            # make sure verison is a tuple of length 2
73            assert isinstance(version, tuple)
74            assert len(version) == 2
[84e8137]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
[ebf327d]79            # make sure attribute_groups is a list of Attributes
[8e43aa8]80            assert len(attribute_groups) > 0
[ebf327d]81            for a in attribute_groups: assert isinstance(a, AttributeGroup)
[84e8137]82           
83        # if the request isn't None, then we'll read directly from
84        # that file handle
[c216863]85        if request is not None:
[4ec7caa]86            # read the version-number (two signed chars)
[d56a0bc]87            self.version        = struct.unpack('>bb', request.read(2))
[89fe6da]88            length -= 2
[8403f61]89            logger.debug("version-number : (0x%X, 0x%X)" % self.version)
[84e8137]90
91            # read the operation-id (or status-code, but that's only
[4ec7caa]92            # for a response) (signed short)
[8403f61]93            self.operation_id   = struct.unpack('>h', request.read(2))[0]
[89fe6da]94            length -= 2
[8403f61]95            logger.debug("operation-id : 0x%X" % self.operation_id)
[84e8137]96
[4ec7caa]97            # read the request-id (signed int)
[8403f61]98            self.request_id     = struct.unpack('>i', request.read(4))[0]
[89fe6da]99            length -= 4
[8403f61]100            logger.debug("request-id : 0x%X" % self.request_id)
[84e8137]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)
[8e43aa8]104            self.attribute_groups = []
[84e8137]105
106            # read in the next byte
[8403f61]107            next_byte = struct.unpack('>b', request.read(1))[0]
[89fe6da]108            length -=1
[8403f61]109            logger.debug("next byte : 0x%X" % next_byte)
[84e8137]110
111            # as long as the next byte isn't signaling the end of the
112            # attributes, keep looping and parsing attributes
[2646571]113            while next_byte != AttributeTags.END:
[84e8137]114               
[8403f61]115                attribute_group_tag = next_byte
116                logger.debug("attribute-tag : %i" % attribute_group_tag)
117
[8e43aa8]118                attributes = []
119
[8403f61]120                next_byte = struct.unpack('>b', request.read(1))[0]
[89fe6da]121                length -= 1
[8403f61]122                logger.debug("next byte : 0x%X" % next_byte)
[84e8137]123
[8403f61]124                while next_byte > 0x0F:
125                   
[4ec7caa]126                    # read in the value tag (signed char)
[8403f61]127                    value_tag     = next_byte
128                    logger.debug("value-tag : 0x%X" % value_tag)
129                   
[4ec7caa]130                    # read in the length of the name (signed short)
[8403f61]131                    name_length   = struct.unpack('>h', request.read(2))[0]
[89fe6da]132                    length -= 2
[8403f61]133                    logger.debug("name-length : %i" % name_length)
[84e8137]134                   
[2646571]135                    if name_length != AttributeTags.ZERO_NAME_LENGTH:
[8403f61]136                        # read the name (a string of name_length bytes)
137                        name          = request.read(name_length)
[89fe6da]138                        length -= name_length
[8403f61]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]
[89fe6da]143                        length -= 2
[8403f61]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)
[89fe6da]148                        length -= value_length
149                       
[0e5cdb3]150                        ippvalue = Value.unpack(value_tag, value)
[aaa1da3]151                        logger.debug("value : %s" % ippvalue.value)
[8403f61]152
[ebf327d]153                        # create a new Attribute from the data we just
[8403f61]154                        # read in, and add it to our attributes list
[ebf327d]155                        attributes.append(Attribute(name, [ippvalue]))
[8403f61]156
157                    else:
158                        # read in the length of the value (signed short)
159                        value_length  = struct.unpack('>h', request.read(2))[0]
[89fe6da]160                        length -= 2
[8403f61]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)
[89fe6da]165                        length -= value_length
[aaa1da3]166
[0e5cdb3]167                        ippvalue = Value.unpack(value_tag, value)
[aaa1da3]168                        logger.debug("value : %s" % ippvalue.value)
[8403f61]169
170                        # add another value to the last attribute
[aaa1da3]171                        attributes[-1].values.append(ippvalue)
[8403f61]172
173                    # read another byte
174                    next_byte = struct.unpack('>b', request.read(1))[0]
[89fe6da]175                    length -= 1
[c216863]176
[ebf327d]177                self.attribute_groups.append(AttributeGroup(
178                    attribute_group_tag, attributes))
[8e43aa8]179
[84e8137]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
[89fe6da]182            assert length >= 0
183            self.data = request.read(length)
[8403f61]184            logger.debug("data : %s" % self.data)
[c216863]185
[84e8137]186        # otherwise, just set the class variables to the keyword
187        # arguments passed in
[c216863]188        else:
[8e43aa8]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
[c216863]193            self.data = data
[d56a0bc]194
[c5e88d0]195    def getAttributeGroup(self, tag):
196        return filter(lambda x: x.attribute_group_tag == tag,
197                      self.attribute_groups)
198
[0e5cdb3]199    @property
200    def packed_value(self):
[d56a0bc]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
[8e43aa8]212        # convert the attribute groups to binary
[0e5cdb3]213        attribute_groups = ''.join([a.packed_value for a in self.attribute_groups])
[d56a0bc]214
215        # conver the end-of-attributes-tag to binary
[2646571]216        end_of_attributes_tag = struct.pack('>b', AttributeTags.END)
[d56a0bc]217
218        # convert the data to binary
[8403f61]219        if self.data is not None:
220            data = ''.join([struct.pack('>b', x) for x in self.data])
221        else:
222            data = ''
[d56a0bc]223
224        # append everything together and return it
[8e43aa8]225        return preattributes + attribute_groups + end_of_attributes_tag + data
Note: See TracBrowser for help on using the repository browser.