Index: pykota/trunk/bin/cupspykota
===================================================================
--- pykota/trunk/bin/cupspykota (revision 2239)
+++ pykota/trunk/bin/cupspykota (revision 2254)
@@ -40,5 +40,5 @@
 from pykota.storage import PyKotaStorageError
 from pykota.accounter import PyKotaAccounterError
-from pykota.ipp import IPPMessage, PyKotaIPPError
+from pykota.ipp import IPPMessage, IPPError
     
 class PyKotaPopen4(popen2.Popen4) :
@@ -215,10 +215,17 @@
                 ippdatafile.close()
             self.dropPriv()    
-            clienthost = ippmessage.get("job-originating-host-name") 
-            billingcode = ippmessage.get("job-billing")
+            
+            try :
+                (chtype, clienthost) = ippmessage.operation_attributes.get("job-originating-host-name", \
+                                          ippmessage.job_attributes.get("job-originating-host-name", (None, None)))
+                (jbtype, billingcode) = ippmessage.job_attributes.get("job-billing", (None, None))
+            except AttributeError :    
+                clienthost = None
+                billingcode = None
             if clienthost is None :
                 (billingcode, clienthost) = self.getJobInfosFromPageLog(cupsdconf, printer.Name, user.Name, self.jobid)
             self.logdebug("Client Hostname : %s" % (clienthost or "Unknown"))    
             self.logdebug("Billing Code : %s" % (billingcode or "None"))    
+            
             os.environ["PYKOTAJOBORIGINATINGHOSTNAME"] = str(clienthost or "")
             os.environ["PYKOTAJOBBILLING"] = str(billingcode or "")
Index: pykota/trunk/NEWS
===================================================================
--- pykota/trunk/NEWS (revision 2241)
+++ pykota/trunk/NEWS (revision 2254)
@@ -22,4 +22,9 @@
 PyKota NEWS :
        
+    - 1.23alpha2 :
+    
+        - Major rewrite of the IPP message parsing code to definitely
+          fix the job-originating-hostname IPP attribute problem.
+        
     - 1.23alpha1 :   
     
Index: pykota/trunk/pykota/ipp.py
===================================================================
--- pykota/trunk/pykota/ipp.py (revision 2190)
+++ pykota/trunk/pykota/ipp.py (revision 2254)
@@ -2,7 +2,9 @@
 # -*- coding: ISO-8859-15 -*-
 #
-# PyKota - Print Quotas for CUPS and LPRng
-#
-# (c) 2003-2004 Jerome Alet <alet@librelogiciel.com>
+# PyKota
+#
+# PyKota : Print Quotas for CUPS and LPRng
+#
+# (c) 2003, 2004, 2005 Jerome Alet <alet@librelogiciel.com>
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -32,6 +34,6 @@
 UNSUPPORTED_ATTRIBUTES_TAG = 0x05
 
-class PyKotaIPPError(Exception):
-    """An exception for PyKota IPP related stuff."""
+class IPPError(Exception):
+    """An exception for IPP related stuff."""
     def __init__(self, message = ""):
         self.message = message
@@ -42,18 +44,43 @@
 
 class IPPMessage :
-    """A class for IPP message files."""
-    def __init__(self, data) :
-        """Initializes an IPP Message object."""
+    """A class for IPP message files.
+    
+       Usage :
+       
+         fp = open("/var/spool/cups/c00001", "rb")
+         message = IPPMessage(fp.read())
+         fp.close()
+         print "IPP version : %s" % message.version
+         print "IPP operation Id : %s" % message.operation_id
+         print "IPP request Id : %s" % message.request_id
+         for attrtype in ("operation", "job", "printer", "unsupported") :
+             attrdict = getattr(message, "%s_attributes" % attrtype)
+             if attrdict :
+                 print "%s attributes :" % attrtype.title()
+                 for key in attrdict.keys() :
+                     print "  %s : %s" % (key, attrdict[key])
+    """
+    def __init__(self, data, debug=0) :
+        """Initializes and parses IPP Message object.
+        
+           Parameters :
+           
+             data : the IPP Message's content.
+             debug : a boolean value to output debug info on stderr.
+        """
+        self.debug = debug
         self.data = data
-        self._attributes = {}
-        self.curname = None
+        self.operation_attributes = {}
+        self.job_attributes = {}
+        self.printer_attributes = {}
+        self.unsupported_attributes = {}
         self.tags = [ None ] * 256      # by default all tags reserved
         
         # Delimiter tags
-        self.tags[0x01] = "operation-attributes-tag"
-        self.tags[0x02] = "job-attributes-tag"
-        self.tags[0x03] = "end-of-attributes-tag"
-        self.tags[0x04] = "printer-attributes-tag"
-        self.tags[0x05] = "unsupported-attributes-tag"
+        self.tags[OPERATION_ATTRIBUTES_TAG] = "operation-attributes-tag"
+        self.tags[JOB_ATTRIBUTES_TAG] = "job-attributes-tag"
+        self.tags[END_OF_ATTRIBUTES_TAG] = "end-of-attributes-tag"
+        self.tags[PRINTER_ATTRIBUTES_TAG] = "printer-attributes-tag"
+        self.tags[UNSUPPORTED_ATTRIBUTES_TAG] = "unsupported-attributes-tag"
         
         # out of band values
@@ -93,56 +120,55 @@
         self.parse()
         
-    def __getattr__(self, attrname) :    
-        """Allows self.attributes to return the attributes names."""
-        if attrname == "attributes" :
-            keys = self._attributes.keys()
-            keys.sort()
-            return keys
-        raise AttributeError, attrname
+    def printInfo(self, msg) :    
+        """Prints a debug message."""
+        if self.debug :
+            sys.stderr.write("%s\n" % msg)
+            sys.stderr.flush()
             
-    def __getitem__(self, ippattrname) :    
-        """Fakes a dictionnary d['key'] notation."""
-        value = self._attributes.get(ippattrname)
-        if value is not None :
-            if len(value) == 1 :
-                value = value[0]
-        return value        
-    get = __getitem__    
-    
-    def keys(self) :
-        """Fakes a dictionnary .keys() method."""
-        return self._attributes.keys()
-        
     def parseTag(self) :    
         """Extracts information from an IPP tag."""
         pos = self.position
-        valuetag = self.tags[ord(self.data[pos])]
-        # print valuetag.get("name")
+        tagtype = self.tags[ord(self.data[pos])]
         pos += 1
         posend = pos2 = pos + 2
         namelength = unpack(">H", self.data[pos:pos2])[0]
         if not namelength :
-            name = self.curname
+            name = self._curname
         else :    
             posend += namelength
-            self.curname = name = self.data[pos2:posend]
+            self._curname = name = self.data[pos2:posend]
         pos2 = posend + 2
         valuelength = unpack(">H", self.data[posend:pos2])[0]
         posend = pos2 + valuelength
         value = self.data[pos2:posend]
-        oldval = self._attributes.setdefault(name, [])
-        oldval.append(value)
+        if tagtype in ("integer", "enum") :
+            value = unpack(">I", value)[0]
+        oldval = self._curdict.setdefault(name, [])
+        oldval.append((tagtype, value))
+        self.printInfo("%s(%s) %s" % (name, tagtype, value))
         return posend - self.position
         
     def operation_attributes_tag(self) : 
         """Indicates that the parser enters into an operation-attributes-tag group."""
+        self.printInfo("Start of operation_attributes_tag")
+        self._curdict = self.operation_attributes
         return self.parseTag()
         
     def job_attributes_tag(self) : 
-        """Indicates that the parser enters into an operation-attributes-tag group."""
+        """Indicates that the parser enters into a job-attributes-tag group."""
+        self.printInfo("Start of job_attributes_tag")
+        self._curdict = self.job_attributes
         return self.parseTag()
         
     def printer_attributes_tag(self) : 
-        """Indicates that the parser enters into an operation-attributes-tag group."""
+        """Indicates that the parser enters into a printer-attributes-tag group."""
+        self.printInfo("Start of printer_attributes_tag")
+        self._curdict = self.printer_attributes
+        return self.parseTag()
+        
+    def unsupported_attributes_tag(self) : 
+        """Indicates that the parser enters into an unsupported-attributes-tag group."""
+        self.printInfo("Start of unsupported_attributes_tag")
+        self._curdict = self.unsupported_attributes
         return self.parseTag()
         
@@ -151,6 +177,7 @@
         
            NB : Only a subset of RFC2910 is implemented.
-           We are only interested in textual informations for now anyway.
         """
+        self._curname = None
+        self._curdict = None
         self.version = "%s.%s" % (ord(self.data[0]), ord(self.data[1]))
         self.operation_id = "0x%04x" % unpack(">H", self.data[2:4])[0]
@@ -171,13 +198,27 @@
                 tag = ord(self.data[self.position])
         except IndexError :
-            raise PyKotaIPPError, "Unexpected end of IPP message."
+            raise IPPError, "Unexpected end of IPP message."
+            
+        # Now transform all one-element lists into single values
+        for attrtype in ("operation", "job", "printer") :
+            attrdict = getattr(self, "%s_attributes" % attrtype)
+            for (key, value) in attrdict.items() :
+                if len(value) == 1 :
+                    attrdict[key] = value[0]
             
 if __name__ == "__main__" :            
-    if len(sys.argv) < 2 :
-        print "usage : python ipp.py /var/spool/cups/c00005 (for example)\n"
+    if (len(sys.argv) < 2) or (sys.argv[1] == "--debug") :
+        print "usage : python ipp.py /var/spool/cups/c00005 [--debug] (for example)\n"
     else :    
-        infile = open(sys.argv[1])
-        message = IPPMessage(infile.read())
+        infile = open(sys.argv[1], "rb")
+        message = IPPMessage(infile.read(), debug=(sys.argv[-1]=="--debug"))
         infile.close()
-        for key in message.keys() :
-            print "%s : %s" % (key, message[key])
+        print "IPP version : %s" % message.version
+        print "IPP operation Id : %s" % message.operation_id
+        print "IPP request Id : %s" % message.request_id
+        for attrtype in ("operation", "job", "printer", "unsupported") :
+            attrdict = getattr(message, "%s_attributes" % attrtype)
+            if attrdict :
+                print "%s attributes :" % attrtype.title()
+                for key in attrdict.keys() :
+                    print "  %s : %s" % (key, attrdict[key])
Index: pykota/trunk/pykota/version.py
===================================================================
--- pykota/trunk/pykota/version.py (revision 2239)
+++ pykota/trunk/pykota/version.py (revision 2254)
@@ -22,5 +22,5 @@
 #
 
-__version__ = "1.23alpha1_unofficial"
+__version__ = "1.23alpha2_unofficial"
 
 __doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""
