source: server/lib/gutenbach/ipp/request.py @ 7bd1035

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

Move actual server code out of server/requests.py and into server/server.py

  • Property mode set to 100644
File size: 10.2 KB
RevLine 
[7a1c039]1from .attribute import Attribute
[d04a689]2from .attributegroup import AttributeGroup
[7a1c039]3from .constants import AttributeTags
[d04a689]4from .value import Value
5import sys
6import struct
7import logging
[8403f61]8
9# initialize logger
[7a1c039]10logger = logging.getLogger(__name__)
[4ec7caa]11
[ebf327d]12class Request():
[5c5fe6d]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    -----------------------------------------------
[5c5fe6d]33
[84e8137]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):
[5c5fe6d]41        """Create a Request.  Takes either the segments of the request
[ebf327d]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
[5c5fe6d]66
[84e8137]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
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                       
[0e5cdb3]149                        ippvalue = Value.unpack(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
[0e5cdb3]166                        ippvalue = Value.unpack(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
[0e5cdb3]198    @property
199    def packed_value(self):
[5c5fe6d]200        """Packs the value data into binary data.
201       
[d56a0bc]202        """
203
[1b9d629]204        # make sure the version number isn't empty
205        assert self.version is not None
206        # make sure verison is a tuple of length 2
207        assert isinstance(self.version, tuple)
208        assert len(self.version) == 2
209        # make sure the operation id isn't empty
210        assert self.operation_id is not None
211        # make sure the request id isn't empty
212        assert self.request_id is not None
213        # make sure attribute_groups is a list of Attributes
214        assert len(self.attribute_groups) > 0
215        for a in self.attribute_groups: assert isinstance(a, AttributeGroup)
216
[d56a0bc]217        # convert the version, operation id, and request id to binary
218        preattributes = struct.pack('>bbhi',
219                                    self.version[0],
220                                    self.version[1],
221                                    self.operation_id,
222                                    self.request_id)
223
[8e43aa8]224        # convert the attribute groups to binary
[0e5cdb3]225        attribute_groups = ''.join([a.packed_value for a in self.attribute_groups])
[d56a0bc]226
227        # conver the end-of-attributes-tag to binary
[2646571]228        end_of_attributes_tag = struct.pack('>b', AttributeTags.END)
[d56a0bc]229
230        # convert the data to binary
[8403f61]231        if self.data is not None:
232            data = ''.join([struct.pack('>b', x) for x in self.data])
233        else:
234            data = ''
[d56a0bc]235
236        # append everything together and return it
[8e43aa8]237        return preattributes + attribute_groups + end_of_attributes_tag + data
[94211df]238
239    def __repr__(self):
[7bd1035]240        val = '<IPPRequest (version=%r, ' % [self.version]
[9eeab06]241        val += 'operation_id=%x, ' % self.operation_id
242        val += 'request_id=%r, ' % self.request_id
243        val += 'attribute_groups=%r)>' % self.attribute_groups
244        return val
Note: See TracBrowser for help on using the repository browser.