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
RevLine 
[c216863]1#!/usr/bin/python
2
[8403f61]3import sys, struct, logging
4
5# initialize logger
6logger = logging.getLogger("ippLogger")
[4ec7caa]7
[c216863]8class IPPRequest():
[84e8137]9    """
10    From RFC 2565:
[c216863]11   
[84e8137]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    """
[c216863]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
[8e43aa8]35    def __init__(self, version=None, operation_id=None, request_id=None,
[89fe6da]36                 attribute_groups=[], data=None, request=None, length=sys.maxint):
[84e8137]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       
[4ec7caa]45            version -- a tuple of two signed chars, identifying the
46                       major version and minor version numbers of the
47                       request
[84e8137]48                           
[4ec7caa]49            operation_id -- a signed short, identifying the id of the
[84e8137]50                            requested operation
51
[4ec7caa]52            request_id -- a signed int, identifying the id of the
[84e8137]53                          request itself.
[c216863]54
[8e43aa8]55            attribute_groups -- a list of IPPAttributes, at least length 1
[84e8137]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
[4ec7caa]69            # make sure verison is a tuple of length 2
70            assert isinstance(version, tuple)
71            assert len(version) == 2
[84e8137]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
[8e43aa8]76            # make sure attribute_groups is a list of IPPAttributes
77            assert len(attribute_groups) > 0
[c5e88d0]78            for a in attribute_groups: assert isinstance(a, IPPAttributeGroup)
[84e8137]79           
80        # if the request isn't None, then we'll read directly from
81        # that file handle
[c216863]82        if request is not None:
[4ec7caa]83            # read the version-number (two signed chars)
[d56a0bc]84            self.version        = struct.unpack('>bb', request.read(2))
[89fe6da]85            length -= 2
[8403f61]86            logger.debug("version-number : (0x%X, 0x%X)" % self.version)
[84e8137]87
88            # read the operation-id (or status-code, but that's only
[4ec7caa]89            # for a response) (signed short)
[8403f61]90            self.operation_id   = struct.unpack('>h', request.read(2))[0]
[89fe6da]91            length -= 2
[8403f61]92            logger.debug("operation-id : 0x%X" % self.operation_id)
[84e8137]93
[4ec7caa]94            # read the request-id (signed int)
[8403f61]95            self.request_id     = struct.unpack('>i', request.read(4))[0]
[89fe6da]96            length -= 4
[8403f61]97            logger.debug("request-id : 0x%X" % self.request_id)
[84e8137]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)
[8e43aa8]101            self.attribute_groups = []
[84e8137]102
103            # read in the next byte
[8403f61]104            next_byte = struct.unpack('>b', request.read(1))[0]
[89fe6da]105            length -=1
[8403f61]106            logger.debug("next byte : 0x%X" % next_byte)
[84e8137]107
108            # as long as the next byte isn't signaling the end of the
109            # attributes, keep looping and parsing attributes
[4ec7caa]110            while next_byte != IPPTags.END_OF_ATTRIBUTES_TAG:
[84e8137]111               
[8403f61]112                attribute_group_tag = next_byte
113                logger.debug("attribute-tag : %i" % attribute_group_tag)
114
[8e43aa8]115                attributes = []
116
[8403f61]117                next_byte = struct.unpack('>b', request.read(1))[0]
[89fe6da]118                length -= 1
[8403f61]119                logger.debug("next byte : 0x%X" % next_byte)
[84e8137]120
[8403f61]121                while next_byte > 0x0F:
122                   
[4ec7caa]123                    # read in the value tag (signed char)
[8403f61]124                    value_tag     = next_byte
125                    logger.debug("value-tag : 0x%X" % value_tag)
126                   
[4ec7caa]127                    # read in the length of the name (signed short)
[8403f61]128                    name_length   = struct.unpack('>h', request.read(2))[0]
[89fe6da]129                    length -= 2
[8403f61]130                    logger.debug("name-length : %i" % name_length)
[84e8137]131                   
[8403f61]132                    if name_length != IPPTags.ZERO_NAME_LENGTH:
133                        # read the name (a string of name_length bytes)
134                        name          = request.read(name_length)
[89fe6da]135                        length -= name_length
[8403f61]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]
[89fe6da]140                        length -= 2
[8403f61]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)
[89fe6da]145                        length -= value_length
146                       
[aaa1da3]147                        ippvalue = IPPValue(value_tag, value)
148                        logger.debug("value : %s" % ippvalue.value)
[8403f61]149
150                        # create a new IPPAttribute from the data we just
151                        # read in, and add it to our attributes list
[aaa1da3]152                        attributes.append(IPPAttribute(name, [ippvalue]))
[8403f61]153
154                    else:
155                        # read in the length of the value (signed short)
156                        value_length  = struct.unpack('>h', request.read(2))[0]
[89fe6da]157                        length -= 2
[8403f61]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)
[89fe6da]162                        length -= value_length
[aaa1da3]163
164                        ippvalue = IPPValue(value_tag, value)
165                        logger.debug("value : %s" % ippvalue.value)
[8403f61]166
167                        # add another value to the last attribute
[aaa1da3]168                        attributes[-1].values.append(ippvalue)
[8403f61]169
170                    # read another byte
171                    next_byte = struct.unpack('>b', request.read(1))[0]
[89fe6da]172                    length -= 1
[c216863]173
[8e43aa8]174                self.attribute_groups.append(IPPAttributeGroup(attribute_group_tag, attributes))
175
[84e8137]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
[89fe6da]178            assert length >= 0
179            self.data = request.read(length)
[8403f61]180            logger.debug("data : %s" % self.data)
[c216863]181
[84e8137]182        # otherwise, just set the class variables to the keyword
183        # arguments passed in
[c216863]184        else:
[8e43aa8]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
[c216863]189            self.data = data
[d56a0bc]190
[c5e88d0]191    def getAttributeGroup(self, tag):
192        return filter(lambda x: x.attribute_group_tag == tag,
193                      self.attribute_groups)
194
[d56a0bc]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
[8e43aa8]207        # convert the attribute groups to binary
208        attribute_groups = ''.join([a.toBinaryData() for a in self.attribute_groups])
[d56a0bc]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
[8403f61]214        if self.data is not None:
215            data = ''.join([struct.pack('>b', x) for x in self.data])
216        else:
217            data = ''
[d56a0bc]218
219        # append everything together and return it
[8e43aa8]220        return preattributes + attribute_groups + end_of_attributes_tag + data
Note: See TracBrowser for help on using the repository browser.