Index: /pykota/trunk/conf/pykota.conf.sample
===================================================================
--- /pykota/trunk/conf/pykota.conf.sample (revision 964)
+++ /pykota/trunk/conf/pykota.conf.sample (revision 973)
@@ -87,5 +87,14 @@
 [lp]
 
-# How to query the lp printer for its page counter
+# What is the accounting backend to use
+# 
+# supported values :
+#
+#    - querying : asks the printer for its lifetime page counter
+#    - more to be added in the future
+#
+accounter: querying
+
+# How to query the lp printer for its page counter, if needed
 # Only snmp(community, oid) and external(command) are supported
 #
@@ -113,4 +122,5 @@
 requester: external(snmpget -c public -Ov %(printer)s 43.10.2.1.4.1.1 | cut -f 2,2 -d " ")
 
+
 # Default policy for inexistant users (e.g. root)
 # either allow or deny
Index: /pykota/trunk/setup.py
===================================================================
--- /pykota/trunk/setup.py (revision 952)
+++ /pykota/trunk/setup.py (revision 973)
@@ -23,4 +23,7 @@
 #
 # $Log$
+# Revision 1.13  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
 # Revision 1.12  2003/04/23 22:13:56  jalet
 # Preliminary support for LPRng added BUT STILL UNTESTED.
@@ -73,4 +76,5 @@
 import shutil
 from distutils.core import setup
+import ConfigParser
 
 sys.path.insert(0, "pykota")
@@ -155,4 +159,22 @@
             else :        
                 sys.stderr.write("WARNING : PyKota won't run without a configuration file !\n")
+    else :            
+        # Configuration file already exists. Check if this is an old version or not
+        # if the 'method: lazy' line is present, then the configuration file
+        # has to be updated.
+        oldconf = ConfigParser.ConfigParser()
+        oldconf.read(["/etc/pykota.conf"])
+        try :
+            if oldconf.get("global", "method", raw=1).lower().strip() == "lazy" :
+                sys.stdout.write("You have got an OLD PyKota configuration file !\n")
+                sys.stdout.write("The 'method' statement IS NOT SUPPORTED ANYMORE\nand was replaced with the 'accounter' statement.\n") 
+                sys.stdout.write("You have to manually set an 'accounter' statement,\neither globally or for each printer.\n")
+                sys.stdout.write("Please read the sample configuration file conf/pykota.conf.sample\n")
+                sys.stdout.write("to learn how to MANUALLY apply the modifications needed,\nafter the installation is done.\n")
+                sys.stdout.write("If you don't do this, then PyKota will stop working !\n")
+                answer = raw_input("Please, press ENTER when you'll have read the above paragraph.")
+        except ConfigParser.NoOptionError :
+            # New configuration file, OK
+            pass
     
     # checks if some needed Python modules are there or not.
@@ -189,5 +211,5 @@
       author_email = "alet@librelogiciel.com",
       url = "http://www.librelogiciel.com/software/",
-      packages = [ "pykota", "pykota.storages", "pykota.requesters", "pykota.loggers" ],
+      packages = [ "pykota", "pykota.storages", "pykota.requesters", "pykota.loggers", "pykota.accounters" ],
       scripts = [ "bin/pykota", "bin/edpykota", "bin/repykota", "bin/warnpykota" ],
       data_files = data_files)
Index: /pykota/trunk/pykota/tool.py
===================================================================
--- /pykota/trunk/pykota/tool.py (revision 956)
+++ /pykota/trunk/pykota/tool.py (revision 973)
@@ -21,4 +21,7 @@
 #
 # $Log$
+# Revision 1.39  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
 # Revision 1.38  2003/04/24 11:53:48  jalet
 # Default policy for unknown users/groups is to DENY printing instead
@@ -200,46 +203,4 @@
         self.storage = storage.openConnection(self.config, asadmin=asadmin)
         self.smtpserver = self.config.getSMTPServer()
-        
-    def extractInfoFromCupsOrLprng(self) :    
-        """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename) depending on the printing system in use (as seen by the print filter).
-        
-           Returns (None, None, None, None, None, None) if no printing system is recognized.
-        """
-        # Try to detect CUPS
-        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
-            if len(sys.argv) == 7 :
-                inputfile = sys.argv[6]
-            else :    
-                inputfile = None
-                
-            device_uri = os.environ.get("DEVICE_URI", "")
-            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
-            try :
-                (backend, destination) = device_uri.split(":", 1) 
-            except ValueError :    
-                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
-            while destination.startswith("/") :
-                destination = destination[1:]
-            printerhostname = destination.split("/")[0].split(":")[0]
-            return ("CUPS", printerhostname, os.environ.get("PRINTER"), sys.argv[2].strip(), sys.argv[1].strip(), inputfile)
-        else :    
-            # Try to detect LPRng
-            jseen = Jseen = Pseen = nseen = rseen = None
-            for arg in sys.argv :
-                if arg.startswith("-j") :
-                    jseen = arg[2:].strip()
-                elif arg.startswith("-J") :    
-                    Jseen = arg[2:].strip()
-                    if Jseen == "(STDIN)" :
-                        Jseen = None
-                elif arg.startswith("-n") :     
-                    nseen = arg[2:].strip()
-                elif arg.startswith("-P") :    
-                    Pseen = arg[2:].strip()
-                elif arg.startswith("-r") :    
-                    rseen = arg[2:].strip()
-            if jseen and Pseen and nseen and rseen :        
-                return ("LPRNG", rseen, Pseen, nseen, jseen, Jseen)
-        return (None, None, None, None, None, None)   # Unknown printing system          
         
     def display_version_and_quit(self) :
Index: /pykota/trunk/pykota/accounters/__init__.py
===================================================================
--- /pykota/trunk/pykota/accounters/__init__.py (revision 973)
+++ /pykota/trunk/pykota/accounters/__init__.py (revision 973)
@@ -0,0 +1,21 @@
+# PyKota
+#
+# PyKota : Print Quotas for CUPS and LPRng
+#
+# (c) 2003 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+#
Index: /pykota/trunk/pykota/accounters/querying.py
===================================================================
--- /pykota/trunk/pykota/accounters/querying.py (revision 973)
+++ /pykota/trunk/pykota/accounters/querying.py (revision 973)
@@ -0,0 +1,119 @@
+# PyKota
+#
+# PyKota - Print Quotas for CUPS and LPRng
+#
+# (c) 2003 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+#
+# $Log$
+# Revision 1.1  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
+#
+#
+
+from pykota.accounter import AccounterBase, PyKotaAccounterError
+from pykota.requester import openRequester, PyKotaRequesterError
+
+MAXTRIES = 6     # maximum number of tries to get the printer's internal page counter
+TIMETOSLEEP = 10 # number of seconds to sleep between two tries to get the printer's internal page counter
+
+class Accounter(AccounterBase) :
+    def doAccounting(self, printerid, userid) :
+        """Does print accounting and returns if the job status is ALLOW or DENY."""
+        # Get the page counter directly from the printer itself
+        # Tries MAXTRIES times, sleeping two seconds each time, in case the printer is sleeping.
+        # This was seen with my Apple LaserWriter 16/600 PS which doesn't answer before having warmed up.
+        global MAXTRIES, TIMETOSLEEP
+        requester = openRequester(self.filter.config, self.filter.printername)
+        for i in range(MAXTRIES) :
+            try :
+                counterbeforejob = requester.getPrinterPageCounter(self.filter.printerhostname)
+            except PyKotaRequesterError, msg :
+                # can't get actual page counter, assume printer is off or warming up
+                # log the message anyway.
+                self.filter.logger.log_message("%s" % msg, "warn")
+                counterbeforejob = None
+                printerIsOff = 1
+            else :    
+                # printer answered, it is on so we can exit the loop
+                printerIsOff = 0
+                break
+            time.sleep(TIMETOSLEEP)    
+        
+        # get last job information for this printer
+        pgc = self.filter.storage.getPrinterPageCounter(printerid)    
+        if pgc is None :
+            # The printer hasn't been used yet, from PyKota's point of view
+            lasthistoryid = None
+            lastjobid = self.filter.jobid
+            lastuserid = userid
+            lastusername = self.filter.username
+            lastpagecounter = counterbeforejob
+        else :    
+            # get last values from Quota Storage
+            (lasthistoryid, lastjobid, lastuserid, lastusername, lastpagecounter) = (pgc["id"], pgc["jobid"], pgc["userid"], pgc["username"], pgc["pagecounter"])
+            
+        # if printer is off then we assume the correct counter value is the last one
+        if printerIsOff :
+            counterbeforejob = lastpagecounter
+            
+        # if the internal lifetime page counter for this printer is 0    
+        # then this may be a printer with a volatile counter (never
+        # saved to NVRAM) which has just been switched off and then on
+        # so we use the last page counter from the Quota Storage instead
+        # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html
+        if counterbeforejob == 0 :
+            counterbeforejob = lastpagecounter
+            
+        # Computes the last job size as the difference between internal page
+        # counter in the printer and last page counter taken from the Quota
+        # Storage database for this particular printer
+        try :
+            jobsize = (counterbeforejob - lastpagecounter)    
+        except TypeError :    
+            # never used, and internal page counter not accessible
+            jobsize = 0
+            
+        if jobsize < 0 :
+            # Probably an HP printer which was switched off and back on, 
+            # its primary counter is only saved in a 10 increment, so
+            # it may be lower than the last page counter saved in the
+            # Quota Storage. 
+            # We unconditionnally set the last job's size to 
+            # abs(int((10 - abs(lastcounter(snmp) - lastcounter(storage)) / 2))
+            # For more accurate accounting, don't switch off your HP printers !
+            # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html
+            self.filter.logger.log_message(_("Error in page count value %i for user %s on printer %s") % (jobsize, lastusername, self.filter.printername), "error")
+            jobsize = abs(int((10 - abs(jobsize)) / 2))     # Workaround for HP printers' feature !
+            
+        # update the quota for the previous user on this printer 
+        self.filter.storage.updateUserPQuota(lastuserid, printerid, jobsize)
+        
+        # update the last job size in the history
+        self.filter.storage.updateJobSizeInHistory(lasthistoryid, jobsize)
+        
+        # warns the last user if he is over quota
+        self.filter.warnUserPQuota(lastusername, self.filter.printername)
+            
+        # Is the current user allowed to print at all ?
+        action = self.filter.warnUserPQuota(self.filter.username, self.filter.printername)
+        
+        # adds the current job to history    
+        self.filter.storage.addJobToHistory(self.filter.jobid, self.filter.storage.getUserId(self.filter.username), printerid, counterbeforejob, action)
+            
+        return action
Index: /pykota/trunk/pykota/accounter.py
===================================================================
--- /pykota/trunk/pykota/accounter.py (revision 973)
+++ /pykota/trunk/pykota/accounter.py (revision 973)
@@ -0,0 +1,59 @@
+# PyKota
+#
+# PyKota : Print Quotas for CUPS and LPRng
+#
+# (c) 2003 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+#
+# $Id$
+#
+# $Log$
+# Revision 1.1  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
+#
+#
+
+class PyKotaAccounterError(Exception):
+    """An exception for Accounter related stuff."""
+    def __init__(self, message = ""):
+        self.message = message
+        Exception.__init__(self, message)
+    def __repr__(self):
+        return self.message
+    __str__ = __repr__
+    
+class AccounterBase :    
+    """A class to account print usage by querying printers."""
+    def __init__(self, kotafilter) :
+        """Sets instance vars depending on the current printer."""
+        self.filter = kotafilter
+        
+    def doAccounting(self, printerid, userid) :    
+        """Does the real accounting."""
+        raise PyKotaAccounterError, "Accounter not implemented !"
+        
+def openAccounter(kotafilter) :
+    """Returns a connection handle to the appropriate accounter."""
+    backend = kotafilter.config.getAccounterBackend(kotafilter.printername)
+    try :
+        if not backend.isalpha() :
+            # don't trust user input
+            raise ImportError
+        exec "from pykota.accounters import %s as accounterbackend" % backend.lower()    
+    except ImportError :
+        raise PyKotaAccounterError, _("Unsupported accounter backend %s") % backend
+    else :    
+        return getattr(accounterbackend, "Accounter")(kotafilter)
Index: /pykota/trunk/pykota/config.py
===================================================================
--- /pykota/trunk/pykota/config.py (revision 956)
+++ /pykota/trunk/pykota/config.py (revision 973)
@@ -21,4 +21,7 @@
 #
 # $Log$
+# Revision 1.24  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
 # Revision 1.23  2003/04/24 11:53:48  jalet
 # Default policy for unknown users/groups is to DENY printing instead
@@ -139,14 +142,4 @@
         self.config = ConfigParser.ConfigParser()
         self.config.read([self.filename])
-        self.checkConfiguration()
-        
-    def checkConfiguration(self) :
-        """Checks if configuration is correct.
-        
-           raises PyKotaConfigError in case a problem is detected
-        """
-        validmethods = [ "lazy" ] # TODO add more methods            
-        if self.config.get("global", "method", raw=1).lower() not in validmethods :             
-            raise PyKotaConfigError, _("Option method only supports values in %s") % str(validmethods)
                         
     def getPrinterNames(self) :    
@@ -198,19 +191,41 @@
         return logger    
         
+    def getAccounterBackend(self, printer) :    
+        """Returns the accounter backend to use for a given printer.
+        
+           if it is not set, it defaults to 'querying' which means ask printer
+           for its internal lifetime page counter.
+        """   
+        validaccounters = [ "querying" ]     
+        try :
+            accounter = self.getPrinterOption(printer, "accounter").lower()
+        except PyKotaConfigError :    
+            accounter = "querying"
+        if accounter not in validaccounters :
+            raise PyKotaConfigError, _("Option accounter in section %s only supports values in %s") % (printer, str(validaccounters))
+        return accounter
+        
     def getRequesterBackend(self, printer) :    
         """Returns the requester backend to use for a given printer, with its arguments."""
-        fullrequester = self.getPrinterOption(printer, "requester")
-        try :
-            (requester, args) = [x.strip() for x in fullrequester.split('(', 1)]
-        except ValueError :    
-            raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer)
-        if args.endswith(')') :
-            args = args[:-1]
-        if not args :
-            raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer)
-        validrequesters = [ "snmp", "external" ] # TODO : add more requesters
-        if requester not in validrequesters :
-            raise PyKotaConfigError, _("Option requester for printer %s only supports values in %s") % (printer, str(validrequesters))
-        return (requester, args)
+        try :
+            fullrequester = self.getPrinterOption(printer, "requester")
+        except PyKotaConfigError :    
+            # No requester defined, maybe it is not needed if accounting method
+            # is not set to 'querying', but if we are called, then the accounting
+            # method really IS 'querying', and so there's a big problem.
+            raise PyKotaConfigError, _("Option requester for printer %s was not set") % printer
+        else :    
+            try :
+                (requester, args) = [x.strip() for x in fullrequester.split('(', 1)]
+            except ValueError :    
+                raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer)
+            if args.endswith(')') :
+                args = args[:-1]
+            if not args :
+                raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer)
+            validrequesters = [ "snmp", "external" ] # TODO : add more requesters
+            if requester not in validrequesters :
+                raise PyKotaConfigError, _("Option requester for printer %s only supports values in %s") % (printer, str(validrequesters))
+            return (requester, args)
         
     def getPrinterPolicy(self, printer) :    
Index: /pykota/trunk/pykota/version.py
===================================================================
--- /pykota/trunk/pykota/version.py (revision 962)
+++ /pykota/trunk/pykota/version.py (revision 973)
@@ -21,5 +21,5 @@
 #
 
-__version__ = "1.05alpha1-unofficial"
+__version__ = "1.05alpha2-unofficial"
 
 __doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""
Index: /pykota/trunk/bin/pykota
===================================================================
--- /pykota/trunk/bin/pykota (revision 966)
+++ /pykota/trunk/bin/pykota (revision 973)
@@ -23,4 +23,7 @@
 #
 # $Log$
+# Revision 1.29  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
 # Revision 1.28  2003/04/26 08:41:24  jalet
 # Small code reorganisation (UNTESTED) to allow pluggable accounting
@@ -133,8 +136,6 @@
 
 from pykota.tool import PyKotaTool, PyKotaToolError
-from pykota.requester import openRequester, PyKotaRequesterError
-
-MAXTRIES = 6     # maximum number of tries to get the printer's internal page counter
-TIMETOSLEEP = 10 # number of seconds to sleep between two tries to get the printer's internal page counter
+from pykota.storage import PyKotaStorageError
+from pykota.accounter import openAccounter, PyKotaAccounterError
 
 class PyKotaFilter(PyKotaTool) :    
@@ -143,6 +144,48 @@
         PyKotaTool.__init__(self)
         (self.printingsystem, self.printerhostname, self.printername, self.username, self.jobid, self.inputfile) = self.extractInfoFromCupsOrLprng()
-        self.requester = openRequester(self.config, self.printername)
-    
+        self.accounter = openAccounter(self)
+    
+    def extractInfoFromCupsOrLprng(self) :    
+        """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename) depending on the printing system in use (as seen by the print filter).
+        
+           Returns (None, None, None, None, None, None) if no printing system is recognized.
+        """
+        # Try to detect CUPS
+        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
+            if len(sys.argv) == 7 :
+                inputfile = sys.argv[6]
+            else :    
+                inputfile = None
+                
+            device_uri = os.environ.get("DEVICE_URI", "")
+            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
+            try :
+                (backend, destination) = device_uri.split(":", 1) 
+            except ValueError :    
+                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
+            while destination.startswith("/") :
+                destination = destination[1:]
+            printerhostname = destination.split("/")[0].split(":")[0]
+            return ("CUPS", printerhostname, os.environ.get("PRINTER"), sys.argv[2].strip(), sys.argv[1].strip(), inputfile)
+        else :    
+            # Try to detect LPRng
+            jseen = Jseen = Pseen = nseen = rseen = None
+            for arg in sys.argv :
+                if arg.startswith("-j") :
+                    jseen = arg[2:].strip()
+                elif arg.startswith("-J") :    
+                    Jseen = arg[2:].strip()
+                    if Jseen == "(STDIN)" :
+                        Jseen = None
+                elif arg.startswith("-n") :     
+                    nseen = arg[2:].strip()
+                elif arg.startswith("-P") :    
+                    Pseen = arg[2:].strip()
+                elif arg.startswith("-r") :    
+                    rseen = arg[2:].strip()
+            if jseen and Pseen and nseen and rseen :        
+                return ("LPRNG", rseen, Pseen, nseen, jseen, Jseen)
+        return (None, None, None, None, None, None)   # Unknown printing system          
+        
     def filterInput(self, inputfile) :
         """Transparent filter."""
@@ -182,6 +225,4 @@
 def main() :    
     """Do it, and do it right !"""
-    global MAXTRIES, TIMETOSLEEP
-    
     # Initializes the current tool
     kotafilter = PyKotaFilter()    
@@ -216,82 +257,6 @@
                 return kotafilter.removeJob()
         else :
-            # Get the page counter directly from the printer itself
-            # Tries MAXTRIES times, sleeping two seconds each time, in case the printer is sleeping.
-            # This was seen with my Apple LaserWriter 16/600 PS which doesn't answer before having warmed up.
-            for i in range(MAXTRIES) :
-                try :
-                    counterbeforejob = kotafilter.requester.getPrinterPageCounter(kotafilter.printerhostname)
-                except PyKotaRequesterError, msg :
-                    # can't get actual page counter, assume printer is off or warming up
-                    # log the message anyway.
-                    kotafilter.logger.log_message("%s" % msg, "warn")
-                    counterbeforejob = None
-                    printerIsOff = 1
-                else :    
-                    # printer answered, it is on so we can exit the loop
-                    printerIsOff = 0
-                    break
-                time.sleep(TIMETOSLEEP)    
-        
-            # get last job information for this printer
-            pgc = kotafilter.storage.getPrinterPageCounter(printerid)    
-            if pgc is None :
-                # The printer hasn't been used yet, from PyKota's point of view
-                lasthistoryid = None
-                lastjobid = kotafilter.jobid
-                lastuserid = userid
-                lastusername = kotafilter.username
-                lastpagecounter = counterbeforejob
-            else :    
-                # get last values from Quota Storage
-                (lasthistoryid, lastjobid, lastuserid, lastusername, lastpagecounter) = (pgc["id"], pgc["jobid"], pgc["userid"], pgc["username"], pgc["pagecounter"])
-                
-            # if printer is off then we assume the correct counter value is the last one
-            if printerIsOff :
-                counterbeforejob = lastpagecounter
-                
-            # if the internal lifetime page counter for this printer is 0    
-            # then this may be a printer with a volatile counter (never
-            # saved to NVRAM) which has just been switched off and then on
-            # so we use the last page counter from the Quota Storage instead
-            # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html
-            if counterbeforejob == 0 :
-                counterbeforejob = lastpagecounter
-                
-            # Computes the last job size as the difference between internal page
-            # counter in the printer and last page counter taken from the Quota
-            # Storage database for this particular printer
-            try :
-                jobsize = (counterbeforejob - lastpagecounter)    
-            except TypeError :    
-                # never used, and internal page counter not accessible
-                jobsize = 0
-                
-            if jobsize < 0 :
-                # Probably an HP printer which was switched off and back on, 
-                # its primary counter is only saved in a 10 increment, so
-                # it may be lower than the last page counter saved in the
-                # Quota Storage. 
-                # We unconditionnally set the last job's size to 
-                # abs(int((10 - abs(lastcounter(snmp) - lastcounter(storage)) / 2))
-                # For more accurate accounting, don't switch off your HP printers !
-                # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html
-                kotafilter.logger.log_message(_("Error in page count value %i for user %s on printer %s") % (jobsize, lastusername, kotafilter.printername), "error")
-                jobsize = abs(int((10 - abs(jobsize)) / 2))     # Workaround for HP printers' feature !
-                
-            # update the quota for the previous user on this printer 
-            kotafilter.storage.updateUserPQuota(lastuserid, printerid, jobsize)
-            
-            # update the last job size in the history
-            kotafilter.storage.updateJobSizeInHistory(lasthistoryid, jobsize)
-            
-            # warns the last user if he is over quota
-            kotafilter.warnUserPQuota(lastusername, kotafilter.printername)
-                
-            # Is the current user allowed to print at all ?
-            action = kotafilter.warnUserPQuota(kotafilter.username, kotafilter.printername)
-            
-            # adds the current job to history    
-            kotafilter.storage.addJobToHistory(kotafilter.jobid, kotafilter.storage.getUserId(kotafilter.username), printerid, counterbeforejob, action)
+            # Now does the accounting and act depending on the result
+            action = kotafilter.accounter.doAccounting(printerid, userid)
             
             # if not allowed to print then die, else proceed.
@@ -306,3 +271,10 @@
 
 if __name__ == "__main__" :    
-    sys.exit(main())
+    try :
+        retcode = main()
+    except (PyKotaToolError, PyKotaStorageError, PyKotaAccounterError), msg :
+        sys.stderr.write("ERROR : PyKota filter failed (%s)\n" % msg)
+        sys.stderr.flush()
+        retcode = -1
+    sys.exit(retcode)    
+    
Index: /pykota/trunk/NEWS
===================================================================
--- /pykota/trunk/NEWS (revision 970)
+++ /pykota/trunk/NEWS (revision 973)
@@ -22,4 +22,8 @@
 PyKota NEWS :
 
+    - 1.05alpha2 :
+    
+        - Pluggable accounting methods.
+        
     - 1.05alpha1 :
     
Index: /pykota/trunk/po/en/pykota.po
===================================================================
--- /pykota/trunk/po/en/pykota.po (revision 952)
+++ /pykota/trunk/po/en/pykota.po (revision 973)
@@ -21,4 +21,7 @@
 #
 # $Log$
+# Revision 1.22  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
 # Revision 1.21  2003/04/23 22:13:56  jalet
 # Preliminary support for LPRng added BUT STILL UNTESTED.
@@ -325,2 +328,11 @@
 msgid "Group %s not found in the PyKota Storage."
 msgstr ""
+
+msgid "Unsupported accounter backend %s"
+msgstr ""
+
+msgid "Option accounter in section %s only supports values in %s"
+msgstr ""
+
+msgid "Option requester for printer %s was not set"
+msgstr ""
Index: /pykota/trunk/po/fr/pykota.po
===================================================================
--- /pykota/trunk/po/fr/pykota.po (revision 952)
+++ /pykota/trunk/po/fr/pykota.po (revision 973)
@@ -21,4 +21,7 @@
 #
 # $Log$
+# Revision 1.21  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
 # Revision 1.20  2003/04/23 22:13:57  jalet
 # Preliminary support for LPRng added BUT STILL UNTESTED.
@@ -338,2 +341,10 @@
 msgid "Group %s not found in the PyKota Storage."
 msgstr "Groupe %s non trouv�ans le Quota Storage."
+
+msgid "Unsupported accounter backend %s"
+msgstr "Backend accounter %s non support�
+msgid "Option accounter in section %s only supports values in %s"
+msgstr "L'option accounter pour l'imprimante %s supporte seulement les valeurs %s"
+
+msgid "Option requester for printer %s was not set"
+msgstr "L'option requester pour l'imprimante %s n'a pas � d�nie"
Index: /pykota/trunk/po/pykota.pot
===================================================================
--- /pykota/trunk/po/pykota.pot (revision 952)
+++ /pykota/trunk/po/pykota.pot (revision 973)
@@ -21,4 +21,7 @@
 #
 # $Log$
+# Revision 1.22  2003/04/29 18:37:54  jalet
+# Pluggable accounting methods (actually doesn't support external scripts)
+#
 # Revision 1.21  2003/04/23 22:13:56  jalet
 # Preliminary support for LPRng added BUT STILL UNTESTED.
@@ -325,2 +328,11 @@
 msgid "Group %s not found in the PyKota Storage."
 msgstr ""
+
+msgid "Unsupported accounter backend %s"
+msgstr ""
+
+msgid "Option accounter in section %s only supports values in %s"
+msgstr ""
+
+msgid "Option requester for printer %s was not set"
+msgstr ""
