Index: pykota/trunk/conf/pykota.conf.sample
===================================================================
--- pykota/trunk/conf/pykota.conf.sample (revision 1193)
+++ pykota/trunk/conf/pykota.conf.sample (revision 1196)
@@ -340,12 +340,24 @@
 # if he is unknown. Example :
 # 
-#       policy: external(/usr/bin/edpykota --add --printer %(printer)s --softlimit 50 --hardlimit 60 %(user)s) 
-#
-#       NB : '%(user)s' and '%(printer)s' will be automatically replaced 
-#            by the user and printer names. 
+#       policy: external(/usr/bin/edpykota --add --printer %(printer)s --softlimit 50 --hardlimit 60 %(user)s >/dev/null) 
 #
 # Of course you can launch any command of your choice with this, e.g. :
 #
-#       policy: external(/usr/local/bin/myadminscript.sh %(user)s)
+#       policy: external(/usr/local/bin/myadminscript.sh %(user)s >/dev/null)
+
+# You can use :
+#
+#       '%(username)s'          will contain the user's name
+#       '%(printername)s'       will contain the printer's name
+#
+#   On your command line, to pass arguments to your command.
+#
+#   NB : Don't forget to redirect your command's standard output somewhere 
+#        (e.g. >/dev/null) so that there's no perturbation to the underlying
+#        layer (filter or backend)
+#
+# If the user still doesn't exist after external policy command was 
+# launched (the external command didn't add it), or if an error occured
+# during the execution of the external policy command, the job is rejected.
 #
 policy: deny
Index: pykota/trunk/pykota/tool.py
===================================================================
--- pykota/trunk/pykota/tool.py (revision 1193)
+++ pykota/trunk/pykota/tool.py (revision 1196)
@@ -22,4 +22,9 @@
 #
 # $Log$
+# Revision 1.57  2003/11/19 23:19:38  jalet
+# Code refactoring work.
+# Explicit redirection to /dev/null has to be set in external policy now, just
+# like in external mailto.
+#
 # Revision 1.56  2003/11/19 07:40:20  jalet
 # Missing import statement.
@@ -234,4 +239,5 @@
 
 from pykota import version, config, storage, logger
+from pykota.accounter import openAccounter
 
 class PyKotaToolError(Exception):
@@ -514,7 +520,8 @@
         return action
     
-    def externalMailTo(self, cmd, action, user, printername, message) :
+    def externalMailTo(self, cmd, action, user, printer, message) :
         """Warns the user with an external command."""
         username = user.Name
+        printername = printer.Name
         email = user.Email or user.Name
         if "@" not in email :
@@ -522,4 +529,10 @@
         os.system(cmd % locals())
     
+    def formatCommandLine(self, cmd, user, printer) :
+        """Executes an external command."""
+        username = user.Name
+        printername = printer.Name
+        return cmd % locals()
+        
     def warnGroupPQuota(self, grouppquota) :
         """Checks a group quota and send messages if quota is exceeded on current printer."""
@@ -542,5 +555,5 @@
                         self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printer.Name))
                     else :    
-                        self.externalMailTo(arguments, action, user, printer.Name, message)
+                        self.externalMailTo(arguments, action, user, printer, message)
         elif action == "WARN" :    
             adminmessage = _("Print Quota low for group %s on printer %s") % (group.Name, printer.Name)
@@ -557,5 +570,5 @@
                         self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
                     else :    
-                        self.externalMailTo(arguments, action, user, printer.Name, message)
+                        self.externalMailTo(arguments, action, user, printer, message)
         return action        
         
@@ -578,5 +591,5 @@
                     self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
                 else :    
-                    self.externalMailTo(arguments, action, user, printer.Name, message)
+                    self.externalMailTo(arguments, action, user, printer, message)
             if mailto in [ "BOTH", "ADMIN" ] :
                 self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
@@ -592,6 +605,88 @@
                     self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message)
                 else :    
-                    self.externalMailTo(arguments, action, user, printer.Name, message)
+                    self.externalMailTo(arguments, action, user, printer, message)
             if mailto in [ "BOTH", "ADMIN" ] :
                 self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
         return action        
+        
+class PyKotaFilterOrBackend(PyKotaTool) :    
+    """Class for the PyKota filter or backend."""
+    def __init__(self) :
+        PyKotaTool.__init__(self)
+        (self.printingsystem, \
+         self.printerhostname, \
+         self.printername, \
+         self.username, \
+         self.jobid, \
+         self.inputfile, \
+         self.copies, \
+         self.title, \
+         self.options, \
+         self.originalbackend) = self.extractInfoFromCupsOrLprng()
+        self.accounter = openAccounter(self)
+    
+    def extractInfoFromCupsOrLprng(self) :    
+        """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename, title, options, backend).
+        
+           Returns (None, None, None, None, 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
+                
+            # check that the DEVICE_URI environment variable's value is 
+            # prefixed with "cupspykota:" otherwise don't touch it.
+            # If this is the case, we have to remove the prefix from 
+            # the environment before launching the real backend in cupspykota
+            device_uri = os.environ.get("DEVICE_URI", "")
+            if device_uri.startswith("cupspykota:") :
+                fulldevice_uri = device_uri[:]
+                device_uri = fulldevice_uri[len("cupspykota:"):]
+                if device_uri.startswith("//") :    # lpd (at least)
+                    device_uri = device_uri[2:]
+                os.environ["DEVICE_URI"] = device_uri   # TODO : side effect !
+            # 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, \
+                    int(sys.argv[4].strip()), \
+                    sys.argv[3], \
+                    sys.argv[5], \
+                    backend)
+        else :    
+            # Try to detect LPRng
+            jseen = Pseen = nseen = rseen = Kseen = None
+            for arg in sys.argv :
+                if arg.startswith("-j") :
+                    jseen = arg[2:].strip()
+                elif arg.startswith("-n") :     
+                    nseen = arg[2:].strip()
+                elif arg.startswith("-P") :    
+                    Pseen = arg[2:].strip()
+                elif arg.startswith("-r") :    
+                    rseen = arg[2:].strip()
+                elif arg.startswith("-K") or arg.startswith("-#") :    
+                    Kseen = int(arg[2:].strip())
+            if Kseen is None :        
+                Kseen = 1       # we assume the user wants at least one copy...
+            if (rseen is None) and jseen and Pseen and nseen :    
+                self.logger.log_message(_("Printer hostname undefined, set to 'localhost'"), "warn")
+                rseen = "localhost"
+            if jseen and Pseen and nseen and rseen :        
+                # job is always in stdin (None)
+                return ("LPRNG", rseen, Pseen, nseen, jseen, None, Kseen, None, None, None)
+        self.logger.log_message(_("Printing system unknown, args=%s") % " ".join(sys.argv), "warn")
+        return (None, None, None, None, None, None, None, None, None, None)   # Unknown printing system
Index: pykota/trunk/bin/cupspykota
===================================================================
--- pykota/trunk/bin/cupspykota (revision 1191)
+++ pykota/trunk/bin/cupspykota (revision 1196)
@@ -24,4 +24,9 @@
 #
 # $Log$
+# Revision 1.11  2003/11/19 23:19:35  jalet
+# Code refactoring work.
+# Explicit redirection to /dev/null has to be set in external policy now, just
+# like in external mailto.
+#
 # Revision 1.10  2003/11/18 17:54:24  jalet
 # SIGTERMs are now transmitted to original backends.
@@ -69,9 +74,9 @@
 import signal
 
-from pykota.tool import PyKotaTool, PyKotaToolError
+from pykota.tool import PyKotaFilterOrBackend, PyKotaToolError
 from pykota.config import PyKotaConfigError
 from pykota.storage import PyKotaStorageError
-from pykota.accounter import openAccounter, PyKotaAccounterError
-from pykota.requester import openRequester, PyKotaRequesterError
+from pykota.accounter import PyKotaAccounterError
+from pykota.requester import PyKotaRequesterError
 
 class PyKotaPopen3(popen2.Popen3) :
@@ -96,72 +101,4 @@
             os._exit(1)
     
-class PyKotaBackend(PyKotaTool) :    
-    """Class for the PyKota backend."""
-    def __init__(self) :
-        PyKotaTool.__init__(self)
-        (self.printingsystem, \
-         self.printerhostname, \
-         self.printername, \
-         self.username, \
-         self.jobid, \
-         self.inputfile, \
-         self.copies, \
-         self.title, \
-         self.options, \
-         self.originalbackend) = self.extractCUPSInfo()
-        self.accounter = openAccounter(self)
-    
-    def extractCUPSInfo(self) :    
-        """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename, title, options, backend).
-        
-           Returns (None, None, None, None, 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
-                
-            # the DEVICE_URI environment variable's value is 
-            # prefixed with "cupspykota:" otherwise we wouldn't
-            # be called. We have to remove this from the environment 
-            # before launching the real backend.
-            fulldevice_uri = os.environ.get("DEVICE_URI", "")
-            device_uri = fulldevice_uri[len("cupspykota:"):]
-            if device_uri.startswith("//") :    # lpd (at least)
-                device_uri = device_uri[2:]
-            os.environ["DEVICE_URI"] = 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, \
-                    int(sys.argv[4].strip()), \
-                    sys.argv[3], \
-                    sys.argv[5], \
-                    backend)
-        else :    
-            self.logger.log_message(_("Printing system unknown, args=%s") % " ".join(sys.argv), "warn")
-            return (None, None, None, None, None, None, None, None, None, None)   # Unknown printing system          
-        
-def format_commandline(prt, usr, cmdline) :            
-    """Passes printer and user names on the command line."""
-    printer = prt.Name
-    user = usr.Name
-    # we don't want the external command's standard 
-    # output to break the print job's data, but we
-    # want to keep its standard error
-    return "%s >/dev/null" % (cmdline % locals())
-
 gotSigTerm = 0
 def sigterm_handler(signum, frame) :
@@ -208,5 +145,5 @@
                     action = "POLICY_ALLOW"
                 elif policy == "EXTERNAL" :    
-                    commandline = format_commandline(printer, user, args)
+                    commandline = thebackend.formatCommandLine(args, user, printer)
                     thebackend.logger.log_message(_("User %s not registered in the PyKota system, applying external policy (%s) for printer %s") % (thebackend.username, commandline, thebackend.printername), "info")
                     if os.system(commandline) :
@@ -472,7 +409,7 @@
         try :
             # Initializes the backend
-            kotabackend = PyKotaBackend()    
+            kotabackend = PyKotaFilterOrBackend()    
             retcode = main(kotabackend)
-        except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg :
+        except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, PyKotaRequesterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg :
             sys.stderr.write("ERROR : cupspykota backend failed (%s)\n" % msg)
             sys.stderr.flush()
Index: pykota/trunk/bin/pykota
===================================================================
--- pykota/trunk/bin/pykota (revision 1177)
+++ pykota/trunk/bin/pykota (revision 1196)
@@ -24,4 +24,9 @@
 #
 # $Log$
+# Revision 1.45  2003/11/19 23:19:37  jalet
+# Code refactoring work.
+# Explicit redirection to /dev/null has to be set in external policy now, just
+# like in external mailto.
+#
 # Revision 1.44  2003/11/08 16:05:31  jalet
 # CUPS backend added for people to experiment.
@@ -186,60 +191,9 @@
 from pykota.config import PyKotaConfigError
 from pykota.storage import PyKotaStorageError
-from pykota.accounter import openAccounter, PyKotaAccounterError
-
-class PyKotaFilter(PyKotaTool) :    
-    """Class for the PyKota filter."""
-    def __init__(self) :
-        PyKotaTool.__init__(self)
-        (self.printingsystem, self.printerhostname, self.printername, self.username, self.jobid, self.inputfile, self.copies) = self.extractInfoFromCupsOrLprng()
-        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, 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, int(sys.argv[4].strip()))
-        else :    
-            # Try to detect LPRng
-            jseen = Pseen = nseen = rseen = Kseen = None
-            for arg in sys.argv :
-                if arg.startswith("-j") :
-                    jseen = arg[2:].strip()
-                elif arg.startswith("-n") :     
-                    nseen = arg[2:].strip()
-                elif arg.startswith("-P") :    
-                    Pseen = arg[2:].strip()
-                elif arg.startswith("-r") :    
-                    rseen = arg[2:].strip()
-                elif arg.startswith("-K") or arg.startswith("-#") :    
-                    Kseen = int(arg[2:].strip())
-            if Kseen is None :        
-                Kseen = 1       # we assume the user wants at least one copy...
-            if (rseen is None) and jseen and Pseen and nseen :    
-                self.logger.log_message(_("Printer hostname undefined, set to 'localhost'"), "warn")
-                rseen = "localhost"
-            if jseen and Pseen and nseen and rseen :        
-                # job is always in stdin (None)
-                return ("LPRNG", rseen, Pseen, nseen, jseen, None, Kseen)
-        self.logger.log_message(_("Printing system unknown, args=%s") % " ".join(sys.argv), "warn")
-        return (None, None, None, None, None, None, None)   # Unknown printing system          
-        
+from pykota.accounter import PyKotaAccounterError
+from pykota.requester import PyKotaRequesterError
+
+class PyKotaFilter(PyKotaFilterOrBackend) :        
+    """A class for the pykota filter."""
     def acceptJob(self) :        
         """Returns the exit code needed by the printing backend to accept the job and print it."""
@@ -262,13 +216,4 @@
             return -1
             
-def format_commandline(prt, usr, cmdline) :            
-    """Passes printer and user names on the command line."""
-    printer = prt.Name
-    user = usr.Name
-    # we don't want the external command's standard 
-    # output to break the print job's data, but we
-    # want to keep its standard error
-    return "%s >/dev/null" % (cmdline % locals())
-
 def main(thefilter) :    
     """Do it, and do it right !"""
@@ -300,5 +245,5 @@
                     action = "POLICY_ALLOW"
                 elif policy == "EXTERNAL" :    
-                    commandline = format_commandline(printer, user, args)
+                    commandline = thefilter.formatCommandLine(args, user, printer)
                     thefilter.logger.log_message(_("User %s not registered in the PyKota system, applying external policy (%s) for printer %s") % (thefilter.username, commandline, thefilter.printername), "info")
                     if os.system(commandline) :
@@ -342,5 +287,5 @@
         kotafilter = PyKotaFilter()    
         retcode = main(kotafilter)
-    except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg :
+    except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, PyKotaRequesterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg :
         sys.stderr.write("ERROR : PyKota filter failed (%s)\n" % msg)
         sys.stderr.flush()
Index: pykota/trunk/NEWS
===================================================================
--- pykota/trunk/NEWS (revision 1194)
+++ pykota/trunk/NEWS (revision 1196)
@@ -23,4 +23,7 @@
 
     - 1.16alpha10 :
+        
+        - Some modifications done to external policy handling.
+          See sample configuration file for details.
     
         - Spanish translation added.
