source: server/lib/ipprequest.py @ a76f440

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

Change IPPValue, IPPAttribute, IPPAttributeGroup, and IPPRequest to Value, Attribute, AttributeGroup?, and Request, respectively, to make things a little less verbose

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