Changeset 646 for tea4cups

Show
Ignore:
Timestamp:
06/14/05 21:26:27 (19 years ago)
Author:
jerome
Message:

Updated the IPP parser to the latest version

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • tea4cups/trunk/tea4cups

    r644 r646  
    5858    pass 
    5959 
    60 # Some IPP constants 
    61 OPERATION_ATTRIBUTES_TAG = 0x01 
    62 JOB_ATTRIBUTES_TAG = 0x02 
    63 END_OF_ATTRIBUTES_TAG = 0x03 
    64 PRINTER_ATTRIBUTES_TAG = 0x04 
    65 UNSUPPORTED_ATTRIBUTES_TAG = 0x05 
    66  
    67 class IPPMessage : 
    68     """A class for IPP message files. 
    69  
     60class IPPRequest : 
     61    """A class for IPP requests. 
     62     
    7063       Usage : 
    71  
     64        
    7265         fp = open("/var/spool/cups/c00001", "rb") 
    73          message = IPPMessage(fp.read()) 
     66         message = IPPRequest(fp.read()) 
    7467         fp.close() 
    75          print "IPP version : %s" % message.version 
    76          print "IPP operation Id : %s" % message.operation_id 
    77          print "IPP request Id : %s" % message.request_id 
    78          for attrtype in ("operation", "job", "printer", "unsupported") : 
     68         message.parse() 
     69         # print message.dump() # dumps an equivalent to the original IPP message 
     70         # print str(message)   # returns a string of text with the same content as below 
     71         print "IPP version : %s.%s" % message.version 
     72         print "IPP operation Id : 0x%04x" % message.operation_id 
     73         print "IPP request Id : 0x%08x" % message.request_id 
     74         for attrtype in message.attributes_types : 
    7975             attrdict = getattr(message, "%s_attributes" % attrtype) 
    8076             if attrdict : 
     
    8278                 for key in attrdict.keys() : 
    8379                     print "  %s : %s" % (key, attrdict[key]) 
     80         if message.data :             
     81             print "IPP datas : ", repr(message.data)             
    8482    """ 
    85     attributes_types = ("operation", "job", "printer", "unsupported") 
    86     def __init__(self, data, debug=0) : 
    87         """Initializes and parses IPP Message object. 
    88  
     83    attributes_types = ("operation", "job", "printer", "unsupported", \ 
     84                                     "subscription", "event_notification") 
     85    def __init__(self, data="", version=None, operation_id=None, \ 
     86                                              request_id=None, debug=0) : 
     87        """Initializes an IPP Message object. 
     88         
    8989           Parameters : 
    90  
    91              data : the IPP Message's content. 
     90            
     91             data : the complete IPP Message's content. 
    9292             debug : a boolean value to output debug info on stderr. 
    9393        """ 
    9494        self.debug = debug 
    95         self.data = data 
     95        self._data = data 
     96        self.parsed = 0 
     97         
     98        # Initializes message 
     99        if version is not None : 
     100            try : 
     101                self.version = [int(p) for p in version.split(".")] 
     102            except AttributeError : 
     103                if len(version) == 2 : # 2-tuple 
     104                    self.version = version 
     105                else :     
     106                    try : 
     107                        self.version = [int(p) for p in str(float(version)).split(".")] 
     108                    except : 
     109                        self.version = (1, 1) # default version number 
     110        self.operation_id = operation_id 
     111        self.request_id = request_id 
     112        self.data = "" 
     113         
     114        # Initialize attributes mappings 
    96115        for attrtype in self.attributes_types : 
    97116            setattr(self, "%s_attributes" % attrtype, {}) 
    98         self.tags = [ None ] * 256      # by default all tags reserved 
    99  
     117             
     118        # Initialize tags     
     119        self.tags = [ None ] * 256 # by default all tags reserved 
     120         
    100121        # Delimiter tags 
    101         self.tags[OPERATION_ATTRIBUTES_TAG] = "operation-attributes-tag" 
    102         self.tags[JOB_ATTRIBUTES_TAG] = "job-attributes-tag" 
    103         self.tags[END_OF_ATTRIBUTES_TAG] = "end-of-attributes-tag" 
    104         self.tags[PRINTER_ATTRIBUTES_TAG] = "printer-attributes-tag" 
    105         self.tags[UNSUPPORTED_ATTRIBUTES_TAG] = "unsupported-attributes-tag" 
    106  
     122        self.tags[0x01] = "operation-attributes-tag" 
     123        self.tags[0x02] = "job-attributes-tag" 
     124        self.tags[0x03] = "end-of-attributes-tag" 
     125        self.tags[0x04] = "printer-attributes-tag" 
     126        self.tags[0x05] = "unsupported-attributes-tag" 
     127        self.tags[0x06] = "subscription-attributes-tag" 
     128        self.tags[0x07] = "event-notification-attributes-tag" 
     129         
    107130        # out of band values 
    108131        self.tags[0x10] = "unsupported" 
     
    110133        self.tags[0x12] = "unknown" 
    111134        self.tags[0x13] = "no-value" 
    112  
     135        self.tags[0x15] = "not-settable" 
     136        self.tags[0x16] = "delete-attribute" 
     137        self.tags[0x17] = "admin-define" 
     138   
    113139        # integer values 
    114140        self.tags[0x20] = "generic-integer" 
     
    116142        self.tags[0x22] = "boolean" 
    117143        self.tags[0x23] = "enum" 
    118  
     144         
    119145        # octetString 
    120146        self.tags[0x30] = "octetString-with-an-unspecified-format" 
     
    122148        self.tags[0x32] = "resolution" 
    123149        self.tags[0x33] = "rangeOfInteger" 
    124         self.tags[0x34] = "reserved-for-collection" 
     150        self.tags[0x34] = "begCollection" # TODO : find sample files for testing 
    125151        self.tags[0x35] = "textWithLanguage" 
    126152        self.tags[0x36] = "nameWithLanguage" 
    127  
     153        self.tags[0x37] = "endCollection" 
     154         
    128155        # character strings 
    129         self.tags[0x20] = "generic-character-string" 
     156        self.tags[0x40] = "generic-character-string" 
    130157        self.tags[0x41] = "textWithoutLanguage" 
    131158        self.tags[0x42] = "nameWithoutLanguage" 
    132         # self.tags[0x43] = "reserved" 
    133159        self.tags[0x44] = "keyword" 
    134160        self.tags[0x45] = "uri" 
     
    137163        self.tags[0x48] = "naturalLanguage" 
    138164        self.tags[0x49] = "mimeMediaType" 
    139  
    140         # now parses the IPP message 
    141         self.parse() 
    142  
    143     def printInfo(self, msg) : 
     165        self.tags[0x4a] = "memberAttrName" 
     166         
     167        # Reverse mapping to generate IPP messages 
     168        self.dictags = {} 
     169        for i in range(len(self.tags)) : 
     170            value = self.tags[i] 
     171            if value is not None : 
     172                self.dictags[value] = i 
     173         
     174    def printInfo(self, msg) :     
    144175        """Prints a debug message.""" 
    145176        if self.debug : 
    146177            sys.stderr.write("%s\n" % msg) 
    147178            sys.stderr.flush() 
    148  
    149     def parseTag(self) : 
    150         """Extracts information from an IPP tag.""" 
    151         pos = self.position 
    152         tagtype = self.tags[ord(self.data[pos])] 
    153         pos += 1 
    154         posend = pos2 = pos + 2 
    155         namelength = unpack(">H", self.data[pos:pos2])[0] 
    156         if not namelength : 
    157             name = self._curname 
    158         else : 
    159             posend += namelength 
    160             self._curname = name = self.data[pos2:posend] 
    161         pos2 = posend + 2 
    162         valuelength = unpack(">H", self.data[posend:pos2])[0] 
    163         posend = pos2 + valuelength 
    164         value = self.data[pos2:posend] 
    165         if tagtype in ("integer", "enum") : 
    166             value = unpack(">I", value)[0] 
    167         oldval = self._curdict.setdefault(name, []) 
    168         oldval.append((tagtype, value)) 
    169         self.printInfo("%s(%s) %s" % (name, tagtype, value)) 
    170         return posend - self.position 
    171  
    172     def operation_attributes_tag(self) : 
    173         """Indicates that the parser enters into an operation-attributes-tag group.""" 
    174         self.printInfo("Start of operation_attributes_tag") 
    175         self._curdict = self.operation_attributes 
    176         return self.parseTag() 
    177  
    178     def job_attributes_tag(self) : 
    179         """Indicates that the parser enters into a job-attributes-tag group.""" 
    180         self.printInfo("Start of job_attributes_tag") 
    181         self._curdict = self.job_attributes 
    182         return self.parseTag() 
    183  
    184     def printer_attributes_tag(self) : 
    185         """Indicates that the parser enters into a printer-attributes-tag group.""" 
    186         self.printInfo("Start of printer_attributes_tag") 
    187         self._curdict = self.printer_attributes 
    188         return self.parseTag() 
    189  
    190     def unsupported_attributes_tag(self) : 
    191         """Indicates that the parser enters into an unsupported-attributes-tag group.""" 
    192         self.printInfo("Start of unsupported_attributes_tag") 
    193         self._curdict = self.unsupported_attributes 
    194         return self.parseTag() 
    195  
     179             
     180    def __str__(self) :         
     181        """Returns the parsed IPP message in a readable form.""" 
     182        if not self.parsed : 
     183            return "" 
     184        else :     
     185            buffer = [] 
     186            buffer.append("IPP version : %s.%s" % self.version) 
     187            buffer.append("IPP operation Id : 0x%04x" % self.operation_id) 
     188            buffer.append("IPP request Id : 0x%08x" % self.request_id) 
     189            for attrtype in self.attributes_types : 
     190                attrdict = getattr(self, "%s_attributes" % attrtype) 
     191                if attrdict : 
     192                    buffer.append("%s attributes :" % attrtype.title()) 
     193                    for key in attrdict.keys() : 
     194                        buffer.append("  %s : %s" % (key, attrdict[key])) 
     195            if self.data :             
     196                buffer.append("IPP datas : %s" % repr(message.data)) 
     197            return "\n".join(buffer) 
     198         
     199    def dump(self) :     
     200        """Generates an IPP Message. 
     201         
     202           Returns the message as a string of text. 
     203        """     
     204        buffer = [] 
     205        if None not in (self.version, self.operation_id, self.request_id) : 
     206            buffer.append(chr(self.version[0]) + chr(self.version[1])) 
     207            buffer.append(pack(">H", self.operation_id)) 
     208            buffer.append(pack(">I", self.request_id)) 
     209            for attrtype in self.attributes_types : 
     210                tagprinted = 0 
     211                for (attrname, value) in getattr(self, "%s_attributes" % attrtype).items() : 
     212                    if not tagprinted : 
     213                        buffer.append(chr(self.dictags["%s-attributes-tag" % attrtype])) 
     214                        tagprinted = 1 
     215                    if type(value) != type([]) : 
     216                        value = [ value ] 
     217                    for (vtype, val) in value : 
     218                        buffer.append(chr(self.dictags[vtype])) 
     219                        buffer.append(pack(">H", len(attrname))) 
     220                        buffer.append(attrname) 
     221                        if vtype in ("integer", "enum") : 
     222                            buffer.append(pack(">H", 4)) 
     223                            buffer.append(pack(">I", val)) 
     224                        elif vtype == "boolean" : 
     225                            buffer.append(pack(">H", 1)) 
     226                            buffer.append(chr(val)) 
     227                        else :     
     228                            buffer.append(pack(">H", len(val))) 
     229                            buffer.append(val) 
     230            buffer.append(chr(self.dictags["end-of-attributes-tag"])) 
     231        buffer.append(self.data)     
     232        return "".join(buffer) 
     233             
    196234    def parse(self) : 
    197         """Parses an IPP Message. 
    198  
     235        """Parses an IPP Request. 
     236         
    199237           NB : Only a subset of RFC2910 is implemented. 
    200238        """ 
    201239        self._curname = None 
    202240        self._curdict = None 
    203         self.version = "%s.%s" % (ord(self.data[0]), ord(self.data[1])) 
    204         self.operation_id = "0x%04x" % unpack(">H", self.data[2:4])[0] 
    205         self.request_id = "0x%08x" % unpack(">I", self.data[4:8])[0] 
     241        self.version = (ord(self._data[0]), ord(self._data[1])) 
     242        self.operation_id = unpack(">H", self._data[2:4])[0] 
     243        self.request_id = unpack(">I", self._data[4:8])[0] 
    206244        self.position = 8 
    207         try : 
    208             tag = ord(self.data[self.position]) 
    209             while tag != END_OF_ATTRIBUTES_TAG : 
     245        endofattributes = self.dictags["end-of-attributes-tag"] 
     246        maxdelimiter = self.dictags["event-notification-attributes-tag"] 
     247        try : 
     248            tag = ord(self._data[self.position]) 
     249            while tag != endofattributes : 
    210250                self.position += 1 
    211251                name = self.tags[tag] 
     
    214254                    if func is not None : 
    215255                        self.position += func() 
    216                         if ord(self.data[self.position]) > UNSUPPORTED_ATTRIBUTES_TAG : 
     256                        if ord(self._data[self.position]) > maxdelimiter : 
    217257                            self.position -= 1 
    218258                            continue 
    219                 tag = ord(self.data[self.position]) 
     259                tag = ord(self._data[self.position]) 
    220260        except IndexError : 
    221261            raise IPPError, "Unexpected end of IPP message." 
    222  
     262             
    223263        # Now transform all one-element lists into single values 
    224264        for attrtype in self.attributes_types : 
     
    227267                if len(value) == 1 : 
    228268                    attrdict[key] = value[0] 
     269        self.data = self._data[self.position+1:]             
     270        self.parsed = 1             
     271         
     272    def parseTag(self) :     
     273        """Extracts information from an IPP tag.""" 
     274        pos = self.position 
     275        tagtype = self.tags[ord(self._data[pos])] 
     276        pos += 1 
     277        posend = pos2 = pos + 2 
     278        namelength = unpack(">H", self._data[pos:pos2])[0] 
     279        if not namelength : 
     280            name = self._curname 
     281        else :     
     282            posend += namelength 
     283            self._curname = name = self._data[pos2:posend] 
     284        pos2 = posend + 2 
     285        valuelength = unpack(">H", self._data[posend:pos2])[0] 
     286        posend = pos2 + valuelength 
     287        value = self._data[pos2:posend] 
     288        if tagtype in ("integer", "enum") : 
     289            value = unpack(">I", value)[0] 
     290        elif tagtype == "boolean" :     
     291            value = ord(value) 
     292        oldval = self._curdict.setdefault(name, []) 
     293        oldval.append((tagtype, value)) 
     294        self.printInfo("%s(%s) : %s" % (name, tagtype, value)) 
     295        return posend - self.position 
     296         
     297    def operation_attributes_tag(self) :  
     298        """Indicates that the parser enters into an operation-attributes-tag group.""" 
     299        self.printInfo("Start of operation_attributes_tag") 
     300        self._curdict = self.operation_attributes 
     301        return self.parseTag() 
     302         
     303    def job_attributes_tag(self) :  
     304        """Indicates that the parser enters into a job-attributes-tag group.""" 
     305        self.printInfo("Start of job_attributes_tag") 
     306        self._curdict = self.job_attributes 
     307        return self.parseTag() 
     308         
     309    def printer_attributes_tag(self) :  
     310        """Indicates that the parser enters into a printer-attributes-tag group.""" 
     311        self.printInfo("Start of printer_attributes_tag") 
     312        self._curdict = self.printer_attributes 
     313        return self.parseTag() 
     314         
     315    def unsupported_attributes_tag(self) :  
     316        """Indicates that the parser enters into an unsupported-attributes-tag group.""" 
     317        self.printInfo("Start of unsupported_attributes_tag") 
     318        self._curdict = self.unsupported_attributes 
     319        return self.parseTag() 
     320         
     321    def subscription_attributes_tag(self) :  
     322        """Indicates that the parser enters into a subscription-attributes-tag group.""" 
     323        self.printInfo("Start of subscription_attributes_tag") 
     324        self._curdict = self.subscription_attributes 
     325        return self.parseTag() 
     326         
     327    def event_notification_attributes_tag(self) :  
     328        """Indicates that the parser enters into an event-notification-attributes-tag group.""" 
     329        self.printInfo("Start of event_notification_attributes_tag") 
     330        self._curdict = self.event_notification_attributes 
     331        return self.parseTag() 
    229332 
    230333class FakeConfig : 
     
    433536        self.Directory = self.getPrintQueueOption(self.PrinterName, "directory") 
    434537        self.DataFile = os.path.join(self.Directory, "%s-%s-%s-%s" % (self.myname, self.PrinterName, self.UserName, self.JobId)) 
    435         (ippfilename, ippmessage) = self.parseIPPMessageFile() 
     538        (ippfilename, ippmessage) = self.parseIPPRequestFile() 
    436539        self.ControlFile = ippfilename 
    437540        (chtype, self.ClientHost) = ippmessage.operation_attributes.get("job-originating-host-name", \ 
     
    466569        return dirvalues 
    467570 
    468     def parseIPPMessageFile(self) : 
     571    def parseIPPRequestFile(self) : 
    469572        """Parses the IPP message file and returns a tuple (filename, parsedvalue).""" 
    470573        cupsdconf = self.getCupsConfigDirectives(["RequestRoot"]) 
     
    483586            self.logDebug("Parsing of IPP message file %s begins." % ippmessagefile) 
    484587            try : 
    485                 ippmessage = IPPMessage(ippdatafile.read()) 
     588                ippmessage = IPPRequest(ippdatafile.read()) 
     589                ippmessage.parse() 
    486590            except IPPError, msg : 
    487591                self.logInfo("Error while parsing %s : %s" % (ippmessagefile, msg), "warn")