Index: /pykota/trunk/conf/pykota.conf.sample
===================================================================
--- /pykota/trunk/conf/pykota.conf.sample (revision 1499)
+++ /pykota/trunk/conf/pykota.conf.sample (revision 1502)
@@ -455,5 +455,6 @@
 # PYKOTAPRECOMPUTEDJOBSIZE : contains the precomputed job's size
 # PYKOTAPRECOMPUTEDJOBPRICE : contains the precomputed job's price
-#
+# PYKOTAJOBORIGINATINGHOSTNAME : contains the client's hostname if 
+#                                it is possible to retrieve it.
 
 # PreHook : gets executed after being sure the user, printer and user quota
Index: /pykota/trunk/pykota/storages/sql.py
===================================================================
--- /pykota/trunk/pykota/storages/sql.py (revision 1451)
+++ /pykota/trunk/pykota/storages/sql.py (revision 1502)
@@ -22,4 +22,7 @@
 #
 # $Log$
+# Revision 1.39  2004/05/26 14:50:12  jalet
+# First try at saving the job-originating-hostname in the database
+#
 # Revision 1.38  2004/05/06 12:37:47  jalet
 # pkpgcounter : comments
@@ -143,5 +146,5 @@
         """Extracts a printer's last job information."""
         lastjob = StorageLastJob(self, printer)
-        result = self.doSearch("SELECT jobhistory.id, jobid, userid, username, pagecounter, jobsize, jobprice, filename, title, copies, options, jobdate FROM jobhistory, users WHERE printerid=%s AND userid=users.id ORDER BY jobdate DESC LIMIT 1" % self.doQuote(printer.ident))
+        result = self.doSearch("SELECT jobhistory.id, jobid, userid, username, pagecounter, jobsize, jobprice, filename, title, copies, options, hostname, jobdate FROM jobhistory, users WHERE printerid=%s AND userid=users.id ORDER BY jobdate DESC LIMIT 1" % self.doQuote(printer.ident))
         if result :
             fields = result[0]
@@ -158,4 +161,5 @@
             lastjob.JobOptions = fields.get("options")
             lastjob.JobDate = fields.get("jobdate")
+            lastjob.JobHostName = fields.get("hostname")
             lastjob.Exists = 1
         return lastjob
@@ -335,14 +339,14 @@
         self.doModify("UPDATE jobhistory SET jobsize=%s, jobprice=%s WHERE id=%s" % (self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(lastjob.ident)))
         
-    def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None) :    
+    def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None, clienthost=None) :    
         """Adds a job in a printer's history."""
         if (not self.disablehistory) or (not printer.LastJob.Exists) :
             if jobsize is not None :
-                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options)))
+                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, hostname) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost)))
             else :    
-                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, filename, title, copies, options) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options)))
+                self.doModify("INSERT INTO jobhistory (userid, printerid, jobid, pagecounter, action, filename, title, copies, options, hostname) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(printer.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost)))
         else :        
             # here we explicitly want to reset jobsize to NULL if needed
-            self.doModify("UPDATE jobhistory SET userid=%s, jobid=%s, pagecounter=%s, action=%s, jobsize=%s, jobprice=%s, filename=%s, title=%s, copies=%s, options=%s, jobdate=now() WHERE id=%s" % (self.doQuote(user.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(printer.LastJob.ident)))
+            self.doModify("UPDATE jobhistory SET userid=%s, jobid=%s, pagecounter=%s, action=%s, jobsize=%s, jobprice=%s, filename=%s, title=%s, copies=%s, options=%s, hostname=%s, jobdate=now() WHERE id=%s" % (self.doQuote(user.ident), self.doQuote(jobid), self.doQuote(pagecounter), self.doQuote(action), self.doQuote(jobsize), self.doQuote(jobprice), self.doQuote(filename), self.doQuote(title), self.doQuote(copies), self.doQuote(options), self.doQuote(clienthost), self.doQuote(printer.LastJob.ident)))
             
     def writeUserPQuotaLimits(self, userpquota, softlimit, hardlimit) :
@@ -368,5 +372,5 @@
         self.doModify("DELETE FROM printergroupsmembers WHERE groupid=%s AND printerid=%s" % (self.doQuote(pgroup.ident), self.doQuote(printer.ident)))
         
-    def retrieveHistory(self, user=None, printer=None, datelimit=None, limit=100) :    
+    def retrieveHistory(self, user=None, printer=None, datelimit=None, hostname=None, limit=100) :    
         """Retrieves all print jobs for user on printer (or all) before date, limited to first 100 results."""
         query = "SELECT jobhistory.*,username,printername FROM jobhistory,users,printers WHERE users.id=userid AND printers.id=printerid"
@@ -376,4 +380,6 @@
         if (printer is not None) and printer.Exists :
             where.append("printerid=%s" % self.doQuote(printer.ident))
+        if hostname is not None :    
+            where.append("hostname=%s" % self.doQuote(hostname))
         if datelimit is not None :    
             where.append("jobdate<=%s" % self.doQuote(datelimit))
@@ -399,4 +405,5 @@
                 job.JobOptions = fields.get("options")
                 job.JobDate = fields.get("jobdate")
+                job.JobHostName = fields.get("hostname")
                 job.UserName = fields.get("username")
                 job.PrinterName = fields.get("printername")
Index: /pykota/trunk/pykota/storages/ldapstorage.py
===================================================================
--- /pykota/trunk/pykota/storages/ldapstorage.py (revision 1451)
+++ /pykota/trunk/pykota/storages/ldapstorage.py (revision 1502)
@@ -22,4 +22,7 @@
 #
 # $Log$
+# Revision 1.64  2004/05/26 14:50:01  jalet
+# First try at saving the job-originating-hostname in the database
+#
 # Revision 1.63  2004/05/06 12:37:46  jalet
 # pkpgcounter : comments
@@ -591,5 +594,5 @@
             lastjob.lastjobident = result[0][0]
             lastjobident = result[0][1]["pykotaLastJobIdent"][0]
-            result = self.doSearch("objectClass=pykotaJob", ["pykotaUserName", "pykotaJobId", "pykotaPrinterPageCounter", "pykotaJobSize", "pykotaAction", "pykotaJobPrice", "pykotaFileName", "pykotaTitle", "pykotaCopies", "pykotaOptions", "createTimestamp"], base="cn=%s,%s" % (lastjobident, self.info["jobbase"]), scope=ldap.SCOPE_BASE)
+            result = self.doSearch("objectClass=pykotaJob", ["pykotaHostName", "pykotaUserName", "pykotaJobId", "pykotaPrinterPageCounter", "pykotaJobSize", "pykotaAction", "pykotaJobPrice", "pykotaFileName", "pykotaTitle", "pykotaCopies", "pykotaOptions", "createTimestamp"], base="cn=%s,%s" % (lastjobident, self.info["jobbase"]), scope=ldap.SCOPE_BASE)
             if result :
                 fields = result[0][1]
@@ -605,4 +608,5 @@
                 lastjob.JobCopies = int(fields.get("pykotaCopies", [0])[0])
                 lastjob.JobOptions = fields.get("pykotaOptions", [""])[0]
+                lastjob.JobHostName = fields.get("pykotaHostName", [""])[0]
                 date = fields.get("createTimestamp", ["19700101000000"])[0]
                 year = int(date[:4])
@@ -912,5 +916,5 @@
         self.doModify(lastjob.ident, fields)         
         
-    def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None) :
+    def writeJobNew(self, printer, user, jobid, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None, clienthost=None) :
         """Adds a job in a printer's history."""
         if (not self.disablehistory) or (not printer.LastJob.Exists) :
@@ -932,4 +936,5 @@
                    "pykotaCopies" : str(copies), 
                    "pykotaOptions" : str(options), 
+                   "pykotaHostName" : str(clienthost), 
                  }
         if (not self.disablehistory) or (not printer.LastJob.Exists) :
@@ -997,5 +1002,5 @@
             self.doModify(pgroup.ident, fields)         
             
-    def retrieveHistory(self, user=None, printer=None, datelimit=None, limit=100) :    
+    def retrieveHistory(self, user=None, printer=None, datelimit=None, hostname=None, limit=100) :    
         """Retrieves all print jobs for user on printer (or all) before date, limited to first 100 results."""
         precond = "(objectClass=pykotaJob)"
@@ -1005,4 +1010,6 @@
         if (printer is not None) and printer.Exists :
             where.append("(pykotaPrinterName=%s)" % printer.Name)
+        if hostname is not None :
+            where.append("(pykotaHostName=%s)" % hostname)
         if where :    
             where = "(&%s)" % "".join([precond] + where)
@@ -1010,5 +1017,5 @@
             where = precond
         jobs = []    
-        result = self.doSearch(where, fields=["pykotaUserName", "pykotaPrinterName", "pykotaJobId", "pykotaPrinterPageCounter", "pykotaAction", "pykotaJobSize", "pykotaJobPrice", "pykotaFileName", "pykotaTitle", "pykotaCopies", "pykotaOptions", "createTimestamp"], base=self.info["jobbase"])
+        result = self.doSearch(where, fields=["pykotaHostName", "pykotaUserName", "pykotaPrinterName", "pykotaJobId", "pykotaPrinterPageCounter", "pykotaAction", "pykotaJobSize", "pykotaJobPrice", "pykotaFileName", "pykotaTitle", "pykotaCopies", "pykotaOptions", "createTimestamp"], base=self.info["jobbase"])
         if result :
             for (ident, fields) in result :
@@ -1024,4 +1031,5 @@
                 job.JobCopies = int(fields.get("pykotaCopies", [0])[0])
                 job.JobOptions = fields.get("pykotaOptions", [""])[0]
+                job.JobHostName = fields.get("pykotaHostName", [""])[0]
                 date = fields.get("createTimestamp", ["19700101000000"])[0]
                 year = int(date[:4])
Index: /pykota/trunk/pykota/storage.py
===================================================================
--- /pykota/trunk/pykota/storage.py (revision 1418)
+++ /pykota/trunk/pykota/storage.py (revision 1502)
@@ -22,4 +22,7 @@
 #
 # $Log$
+# Revision 1.52  2004/05/26 14:49:57  jalet
+# First try at saving the job-originating-hostname in the database
+#
 # Revision 1.51  2004/03/24 15:15:24  jalet
 # Began integration of Henrik Janhagen's work on quota-then-balance
@@ -300,7 +303,7 @@
             raise AttributeError, name
             
-    def addJobToHistory(self, jobid, user, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None) :
+    def addJobToHistory(self, jobid, user, pagecounter, action, jobsize=None, jobprice=None, filename=None, title=None, copies=None, options=None, clienthost=None) :
         """Adds a job to the printer's history."""
-        self.parent.writeJobNew(self, user, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options)
+        self.parent.writeJobNew(self, user, jobid, pagecounter, action, jobsize, jobprice, filename, title, copies, options, clienthost)
         # TODO : update LastJob object ? Probably not needed.
         
@@ -454,4 +457,5 @@
         self.JobCopies = None
         self.JobOptions = None
+        self.JobHostName = None
         
     def __getattr__(self, name) :    
Index: /pykota/trunk/pykota/version.py
===================================================================
--- /pykota/trunk/pykota/version.py (revision 1500)
+++ /pykota/trunk/pykota/version.py (revision 1502)
@@ -22,5 +22,5 @@
 #
 
-__version__ = "1.19alpha15_unofficial"
+__version__ = "1.19alpha16_unofficial"
 
 __doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""
Index: /pykota/trunk/cgi-bin/printquota.cgi
===================================================================
--- /pykota/trunk/cgi-bin/printquota.cgi (revision 1419)
+++ /pykota/trunk/cgi-bin/printquota.cgi (revision 1502)
@@ -23,4 +23,7 @@
 #
 # $Log$
+# Revision 1.27  2004/05/26 14:49:40  jalet
+# First try at saving the job-originating-hostname in the database
+#
 # Revision 1.26  2004/03/24 19:37:04  jalet
 # Doesn't retrieve users or printers objects to display the history,
@@ -266,5 +269,5 @@
             else :
                 self.report.append('<table class="pykotatable" border="1">')
-                headers = ["Date", "User", "Printer", "PageCounter", "JobId", "JobSize", "JobPrice", "Copies", "Title", "Filename", "Options", "Action"]
+                headers = ["Date", "User", "Printer", "PageCounter", "JobId", "JobSize", "JobPrice", "Copies", "Title", "Filename", "Options", "HostName", "Action"]
                 self.report.append('<tr class="pykotacolsheader">%s</tr>' % "".join(["<th>%s</th>" % h for h in headers]))
                 oddeven = 0
@@ -280,5 +283,5 @@
                         oddevenclass = "warn"
                     username = '<a href="%s?%s">%s</a>' % (os.environ.get("SCRIPT_NAME", ""), urllib.urlencode({"history" : 1, "username" : job.UserName}), job.UserName)
-                    self.report.append('<tr class="%s">%s</tr>' % (oddevenclass, "".join(["<td>%s</td>" % h for h in (job.JobDate[:19], username, job.PrinterName, job.PrinterPageCounter, job.JobId, job.JobSize, job.JobPrice, job.JobCopies, job.JobTitle, job.JobFileName, job.JobOptions, job.JobAction)])))
+                    self.report.append('<tr class="%s">%s</tr>' % (oddevenclass, "".join(["<td>%s</td>" % h for h in (job.JobDate[:19], username, job.PrinterName, job.PrinterPageCounter, job.JobId, job.JobSize, job.JobPrice, job.JobCopies, job.JobTitle, job.JobFileName, job.JobOptions, job.JobHostName, job.JobAction)])))
                 self.report.append('</table>')
                 dico = { "history" : 1,
Index: /pykota/trunk/bin/cupspykota
===================================================================
--- /pykota/trunk/bin/cupspykota (revision 1499)
+++ /pykota/trunk/bin/cupspykota (revision 1502)
@@ -24,4 +24,7 @@
 #
 # $Log$
+# Revision 1.53  2004/05/26 14:49:35  jalet
+# First try at saving the job-originating-hostname in the database
+#
 # Revision 1.52  2004/05/25 09:15:13  jalet
 # accounter.py : old code deleted
@@ -252,4 +255,50 @@
         return 0
         
+    def getPageLogLocation(self) :
+        """Retrieves CUPS' page_log file location."""
+        location = None
+        cupsroot = os.environ.get("CUPS_SERVERROOT", "/etc/cups")
+        cupsdconf = os.path.join(cupsroot, "cupsd.conf")
+        try :
+            conffile = open(cupsdconf, "r")
+        except IOError :    
+            return # file doesn't exist or can't be read
+        else :    
+            for line in conffile.readlines() :
+                linecopy = line.strip().lower()
+                if linecopy.startswith("pagelog ") :
+                    try :
+                        location = line.split()[1]
+                    except :    
+                        pass # ignore errors, we take the last value in any case.
+            conffile.close()            
+            return location            
+            
+    def getJobOriginatingHostname(self, printername, username, jobid) :
+        """Retrieves the job-originating-hostname from the CUPS page_log file if possible."""
+        pagelogpath = self.getPageLogLocation() or "/var/log/cups/page_log"
+        try :
+            pagelog = open(pagelogpath, "r")
+        except IOError :    
+            return # no page log or can't read it, originating hostname unknown yet
+        else :    
+            # TODO : read backward so we could take first value seen
+            # TODO : here we read forward so we must take the last value seen
+            prefix = "%s %s %s" % (printername, username, jobid)
+            matchingline = None
+            while 1 :
+                line = pagelog.readline()
+                if not line :
+                    break
+                else :
+                    line = line.strip()
+                    if line.startswith(prefix) :    
+                        matchingline = line # no break, because we read forward
+            pagelog.close()        
+            if matchingline is None :
+                return # correct line not found, job-originating-hostname unknown
+            else :    
+                return matchingline.split()[-1]
+                
     def doWork(self, policy, printer, user, userpquota) :    
         """Most of the work is done here."""
@@ -262,4 +311,8 @@
             # exports user information with initial values
             self.exportUserInfo(userpquota)
+            
+            # tries to extract job-originating-hostname 
+            clienthost = self.getJobOriginatingHostname(printer.Name, user.Name, self.jobid)
+            os.putenv("PYKOTAJOBORIGINATINGHOSTNAME", str(clienthost or ""))
             
             # enters first phase
@@ -317,5 +370,5 @@
             
             # adds the current job to history    
-            printer.addJobToHistory(self.jobid, user, self.accounter.getLastPageCounter(), action, jobsize, jobprice, self.preserveinputfile, self.title, self.copies, self.options)
+            printer.addJobToHistory(self.jobid, user, self.accounter.getLastPageCounter(), action, jobsize, jobprice, self.preserveinputfile, self.title, self.copies, self.options, clienthost)
             self.logdebug("Job added to history.")
             
Index: /pykota/trunk/NEWS
===================================================================
--- /pykota/trunk/NEWS (revision 1500)
+++ /pykota/trunk/NEWS (revision 1502)
@@ -23,4 +23,9 @@
 
 
+    - 1.19alpha16 :
+    
+        - The job's originating hostname is now stored in the database,
+          and exported as PYKOTAJOBORIGINATINGHOSTNAME in the environment.
+        
     - 1.19alpha15 :
     
