Index: /pykota/trunk/conf/pykota.conf.sample
===================================================================
--- /pykota/trunk/conf/pykota.conf.sample (revision 1188)
+++ /pykota/trunk/conf/pykota.conf.sample (revision 1192)
@@ -185,5 +185,5 @@
 #
 # Who should we send an email to in case a quota is reached ?
-# possible values are : DevNull, User, Admin, Both
+# possible values are : DevNull, User, Admin, Both, External(some command)
 # The Both value means that the User and the Admin will receive 
 # an email message.
@@ -192,4 +192,21 @@
 # If both are defined, the printer option has priority.
 # If the value is not set, then the default BOTH applies.
+#
+#   For mailto: external(/usr/bin/mycommand)
+#
+#   You can use :
+#
+#       '%(action)s'            will contain either WARN or DENY
+#       '%(username)s'          will contain the user's name
+#       '%(printername)s'       will contain the printer's name
+#       '%(email)s'             will contain the user's email address
+#       '%(message)s'           will contain the message if you want 
+#                               use it.
+#
+#   On your command line, to pass arguments to your command.
+#   Example :
+#
+#       mailto: external(/usr/bin/callpager %(username)s "Quota problem on %(printername)s")
+#
 mailto: both
 
Index: /pykota/trunk/pykota/tool.py
===================================================================
--- /pykota/trunk/pykota/tool.py (revision 1171)
+++ /pykota/trunk/pykota/tool.py (revision 1192)
@@ -22,4 +22,7 @@
 #
 # $Log$
+# Revision 1.55  2003/11/18 23:43:12  jalet
+# Mailto can be any external command now, as usual.
+#
 # Revision 1.54  2003/10/24 21:52:46  jalet
 # Now can force language when coming from CGI script.
@@ -506,4 +509,12 @@
         return action
     
+    def externalMailTo(self, cmd, action, user, printername, message) :
+        """Warns the user with an external command."""
+        username = user.Name
+        email = user.Email or user.Name
+        if "@" not in email :
+            email = "%s@%s" % (email, self.smtpserver)
+        os.system(cmd % locals())
+    
     def warnGroupPQuota(self, grouppquota) :
         """Checks a group quota and send messages if quota is exceeded on current printer."""
@@ -512,5 +523,5 @@
         admin = self.config.getAdmin(printer.Name)
         adminmail = self.config.getAdminMail(printer.Name)
-        mailto = self.config.getMailTo(printer.Name)
+        (mailto, arguments) = self.config.getMailTo(printer.Name)
         action = self.checkGroupPQuota(grouppquota)
         if action.startswith("POLICY_") :
@@ -521,7 +532,10 @@
             if mailto in [ "BOTH", "ADMIN" ] :
                 self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
-            if mailto in [ "BOTH", "USER" ] :
+            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
                 for user in self.storage.getGroupMembers(group) :
-                    self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printer.Name))
+                    if mailto != "EXTERNAL" :
+                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printer.Name))
+                    else :    
+                        self.externalMailTo(arguments, action, user, printer.Name, message)
         elif action == "WARN" :    
             adminmessage = _("Print Quota low for group %s on printer %s") % (group.Name, printer.Name)
@@ -533,7 +547,10 @@
             else :     
                 message = self.config.getSoftWarn(printer.Name)
-            if mailto in [ "BOTH", "USER" ] :
+            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
                 for user in self.storage.getGroupMembers(group) :
-                    self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
+                    if mailto != "EXTERNAL" :
+                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
+                    else :    
+                        self.externalMailTo(arguments, action, user, printer.Name, message)
         return action        
         
@@ -544,5 +561,5 @@
         admin = self.config.getAdmin(printer.Name)
         adminmail = self.config.getAdminMail(printer.Name)
-        mailto = self.config.getMailTo(printer.Name)
+        (mailto, arguments) = self.config.getMailTo(printer.Name)
         action = self.checkUserPQuota(userpquota)
         if action.startswith("POLICY_") :
@@ -551,6 +568,10 @@
             adminmessage = _("Print Quota exceeded for user %s on printer %s") % (user.Name, printer.Name)
             self.logger.log_message(adminmessage)
-            if mailto in [ "BOTH", "USER" ] :
-                self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printer.Name))
+            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
+                message = self.config.getHardWarn(printer.Name)
+                if mailto != "EXTERNAL" :
+                    self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
+                else :    
+                    self.externalMailTo(arguments, action, user, printer.Name, message)
             if mailto in [ "BOTH", "ADMIN" ] :
                 self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
@@ -558,10 +579,13 @@
             adminmessage = _("Print Quota low for user %s on printer %s") % (user.Name, printer.Name)
             self.logger.log_message(adminmessage)
-            if mailto in [ "BOTH", "USER" ] :
+            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
                 if user.LimitBy and (user.LimitBy.lower() == "balance") : 
                     message = self.config.getPoorWarn()
                 else :     
                     message = self.config.getSoftWarn(printer.Name)
-                self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message)
+                if mailto != "EXTERNAL" :    
+                    self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message)
+                else :    
+                    self.externalMailTo(arguments, action, user, printer.Name, message)
             if mailto in [ "BOTH", "ADMIN" ] :
                 self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
Index: /pykota/trunk/pykota/config.py
===================================================================
--- /pykota/trunk/pykota/config.py (revision 1152)
+++ /pykota/trunk/pykota/config.py (revision 1192)
@@ -22,4 +22,7 @@
 #
 # $Log$
+# Revision 1.40  2003/11/18 23:43:12  jalet
+# Mailto can be any external command now, as usual.
+#
 # Revision 1.39  2003/10/08 21:41:38  jalet
 # External policies for printers works !
@@ -209,14 +212,14 @@
                 raise PyKotaConfigError, _("Option %s not found in section global of %s") % (option, self.filename)
                 
-    def getPrinterOption(self, printer, option) :    
+    def getPrinterOption(self, printername, option) :    
         """Returns an option from the printer section, or the global section, or raises a PyKotaConfigError."""
         globaloption = self.getGlobalOption(option, ignore=1)
         try :
-            return self.config.get(printer, option, raw=1)
+            return self.config.get(printername, option, raw=1)
         except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :    
             if globaloption is not None :
                 return globaloption
             else :
-                raise PyKotaConfigError, _("Option %s not found in section %s of %s") % (option, printer, self.filename)
+                raise PyKotaConfigError, _("Option %s not found in section %s of %s") % (option, printername, self.filename)
         
     def getStorageBackend(self) :    
@@ -272,5 +275,5 @@
         return logger    
         
-    def getAccounterBackend(self, printer) :    
+    def getAccounterBackend(self, printername) :    
         """Returns the accounter backend to use for a given printer.
         
@@ -280,5 +283,5 @@
         validaccounters = [ "querying", "stupid", "external" ]     
         try :
-            fullaccounter = self.getPrinterOption(printer, "accounter").strip()
+            fullaccounter = self.getPrinterOption(printername, "accounter").strip()
         except PyKotaConfigError :    
             fullaccounter = "querying"
@@ -287,44 +290,44 @@
                 (accounter, args) = [x.strip() for x in fullaccounter.split('(', 1)]
             except ValueError :    
-                raise PyKotaConfigError, _("Invalid external accounter %s for printer %s") % (fullaccounter, printer)
+                raise PyKotaConfigError, _("Invalid external accounter %s for printer %s") % (fullaccounter, printername)
             if args.endswith(')') :
                 args = args[:-1]
             if not args :
-                raise PyKotaConfigError, _("Invalid external accounter %s for printer %s") % (fullaccounter, printer)
+                raise PyKotaConfigError, _("Invalid external accounter %s for printer %s") % (fullaccounter, printername)
             return (accounter.lower(), args)    
         elif fullaccounter.lower() not in validaccounters :
-            raise PyKotaConfigError, _("Option accounter in section %s only supports values in %s") % (printer, str(validaccounters))
+            raise PyKotaConfigError, _("Option accounter in section %s only supports values in %s") % (printername, str(validaccounters))
         else :    
             return (fullaccounter.lower(), None)
         
-    def getRequesterBackend(self, printer) :    
+    def getRequesterBackend(self, printername) :    
         """Returns the requester backend to use for a given printer, with its arguments."""
         try :
-            fullrequester = self.getPrinterOption(printer, "requester")
+            fullrequester = self.getPrinterOption(printername, "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
+            raise PyKotaConfigError, _("Option requester for printer %s was not set") % printername
         else :    
             try :
                 (requester, args) = [x.strip() for x in fullrequester.split('(', 1)]
             except ValueError :    
-                raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer)
+                raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printername)
             if args.endswith(')') :
                 args = args[:-1]
             if not args :
-                raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer)
+                raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printername)
             validrequesters = [ "snmp", "external" ] # TODO : add more requesters
             requester = requester.lower()
             if requester not in validrequesters :
-                raise PyKotaConfigError, _("Option requester for printer %s only supports values in %s") % (printer, str(validrequesters))
+                raise PyKotaConfigError, _("Option requester for printer %s only supports values in %s") % (printername, str(validrequesters))
             return (requester, args)
         
-    def getPrinterPolicy(self, printer) :    
+    def getPrinterPolicy(self, printername) :    
         """Returns the default policy for the current printer."""
         validpolicies = [ "ALLOW", "DENY", "EXTERNAL" ]     
         try :
-            fullpolicy = self.getPrinterOption(printer, "policy")
+            fullpolicy = self.getPrinterOption(printername, "policy")
         except PyKotaConfigError :    
             return ("DENY", None)
@@ -333,5 +336,5 @@
                 policy = [x.strip() for x in fullpolicy.split('(', 1)]
             except ValueError :    
-                raise PyKotaConfigError, _("Invalid policy %s for printer %s") % (fullpolicy, printer)
+                raise PyKotaConfigError, _("Invalid policy %s for printer %s") % (fullpolicy, printername)
             if len(policy) == 1 :    
                 policy.append("")
@@ -341,7 +344,7 @@
             policy = policy.upper()    
             if (policy == "EXTERNAL") and not args :
-                raise PyKotaConfigError, _("Invalid policy %s for printer %s") % (fullpolicy, printer)
+                raise PyKotaConfigError, _("Invalid policy %s for printer %s") % (fullpolicy, printername)
             if policy not in validpolicies :
-                raise PyKotaConfigError, _("Option policy in section %s only supports values in %s") % (printer, str(validpolicies))
+                raise PyKotaConfigError, _("Option policy in section %s only supports values in %s") % (printername, str(validpolicies))
             return (policy, args)
         
@@ -353,33 +356,46 @@
             return "localhost"
         
-    def getAdminMail(self, printer) :    
+    def getAdminMail(self, printername) :    
         """Returns the Email address of the Print Quota Administrator."""
         try :
-            return self.getPrinterOption(printer, "adminmail")
+            return self.getPrinterOption(printername, "adminmail")
         except PyKotaConfigError :    
             return "root@localhost"
         
-    def getAdmin(self, printer) :    
+    def getAdmin(self, printername) :    
         """Returns the full name of the Print Quota Administrator."""
         try :
-            return self.getPrinterOption(printer, "admin")
+            return self.getPrinterOption(printername, "admin")
         except PyKotaConfigError :    
             return "root"
         
-    def getMailTo(self, printer) :    
+    def getMailTo(self, printername) :    
         """Returns the recipient of email messages."""
-        validmailtos = [ "NOBODY", "NONE", "NOONE", "BITBUCKET", "DEVNULL", "BOTH", "USER", "ADMIN" ]
-        try :
-            mailto = self.getPrinterOption(printer, "mailto").upper()
-        except PyKotaConfigError :    
-            mailto = "BOTH"
-        if mailto not in validmailtos :
-            raise PyKotaConfigError, _("Option mailto in section %s only supports values in %s") % (printer, str(validmailtos))
-        return mailto    
-        
-    def getGraceDelay(self, printer) :    
+        validmailtos = [ "EXTERNAL", "NOBODY", "NONE", "NOONE", "BITBUCKET", "DEVNULL", "BOTH", "USER", "ADMIN" ]
+        try :
+            fullmailto = self.getPrinterOption(printername, "mailto")
+        except PyKotaConfigError :    
+            return ("BOTH", None)
+        else :    
+            try :
+                mailto = [x.strip() for x in fullmailto.split('(', 1)]
+            except ValueError :    
+                raise PyKotaConfigError, _("Invalid option mailto %s for printer %s") % (fullmailto, printername)
+            if len(mailto) == 1 :    
+                mailto.append("")
+            (mailto, args) = mailto    
+            if args.endswith(')') :
+                args = args[:-1]
+            mailto = mailto.upper()    
+            if (mailto == "EXTERNAL") and not args :
+                raise PyKotaConfigError, _("Invalid option mailto %s for printer %s") % (fullmailto, printername)
+            if mailto not in validmailtos :
+                raise PyKotaConfigError, _("Option mailto in section %s only supports values in %s") % (printername, str(validmailtos))
+            return (mailto, args)
+        
+    def getGraceDelay(self, printername) :    
         """Returns the grace delay in days."""
         try :
-            gd = self.getPrinterOption(printer, "gracedelay")
+            gd = self.getPrinterOption(printername, "gracedelay")
         except PyKotaConfigError :    
             gd = 7
@@ -407,17 +423,17 @@
             return _("Your Print Quota account balance is Low.\nSoon you'll not be allowed to print anymore.\nPlease contact the Print Quota Administrator to solve the problem.")
             
-    def getHardWarn(self, printer) :    
+    def getHardWarn(self, printername) :    
         """Returns the hard limit error message."""
         try :
-            return self.getPrinterOption(printer, "hardwarn")
-        except PyKotaConfigError :    
-            return _("You are not allowed to print anymore because\nyour Print Quota is exceeded on printer %s.") % printer
-            
-    def getSoftWarn(self, printer) :    
+            return self.getPrinterOption(printername, "hardwarn")
+        except PyKotaConfigError :    
+            return _("You are not allowed to print anymore because\nyour Print Quota is exceeded on printer %s.") % printername
+            
+    def getSoftWarn(self, printername) :    
         """Returns the soft limit error message."""
         try :
-            return self.getPrinterOption(printer, "softwarn")
-        except PyKotaConfigError :    
-            return _("You will soon be forbidden to print anymore because\nyour Print Quota is almost reached on printer %s.") % printer
+            return self.getPrinterOption(printername, "softwarn")
+        except PyKotaConfigError :    
+            return _("You will soon be forbidden to print anymore because\nyour Print Quota is almost reached on printer %s.") % printername
             
     def getDebug(self) :          
Index: /pykota/trunk/pykota/version.py
===================================================================
--- /pykota/trunk/pykota/version.py (revision 1189)
+++ /pykota/trunk/pykota/version.py (revision 1192)
@@ -22,5 +22,5 @@
 #
 
-__version__ = "1.16alpha8_unofficial"
+__version__ = "1.16alpha9_unofficial"
 
 __doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""
Index: /pykota/trunk/NEWS
===================================================================
--- /pykota/trunk/NEWS (revision 1191)
+++ /pykota/trunk/NEWS (revision 1192)
@@ -22,4 +22,11 @@
 PyKota NEWS :
 
+    - 1.16alpha9 :
+    
+        - mailto can be set to external(...) to warn users above
+          quota with any command of your choice (e.g. linpopup).
+          Several parameters are available, see sample configuration
+          file for details.
+          
     - 1.16alpha8 :
     
Index: /pykota/trunk/po/en/pykota.po
===================================================================
--- /pykota/trunk/po/en/pykota.po (revision 1152)
+++ /pykota/trunk/po/en/pykota.po (revision 1192)
@@ -21,4 +21,7 @@
 #
 # $Log$
+# Revision 1.36  2003/11/18 23:43:11  jalet
+# Mailto can be any external command now, as usual.
+#
 # Revision 1.35  2003/10/08 21:41:38  jalet
 # External policies for printers works !
@@ -443,2 +446,5 @@
 msgstr ""
 
+msgid "Invalid option mailto %s for printer %s"
+msgstr ""
+
Index: /pykota/trunk/po/fr/pykota.po
===================================================================
--- /pykota/trunk/po/fr/pykota.po (revision 1152)
+++ /pykota/trunk/po/fr/pykota.po (revision 1192)
@@ -21,4 +21,7 @@
 #
 # $Log$
+# Revision 1.35  2003/11/18 23:43:12  jalet
+# Mailto can be any external command now, as usual.
+#
 # Revision 1.34  2003/10/08 21:41:38  jalet
 # External policies for printers works !
@@ -453,2 +456,5 @@
 msgstr "L'action externe %s sur l'imprimante %s n'a pas pu ajouter l'utilisateur %s. Job rejet�
 
+msgid "Invalid option mailto %s for printer %s"
+msgstr "Option mailto %s invalide pour l'imprimante %s"
+
Index: /pykota/trunk/po/pykota.pot
===================================================================
--- /pykota/trunk/po/pykota.pot (revision 1152)
+++ /pykota/trunk/po/pykota.pot (revision 1192)
@@ -21,4 +21,7 @@
 #
 # $Log$
+# Revision 1.36  2003/11/18 23:43:11  jalet
+# Mailto can be any external command now, as usual.
+#
 # Revision 1.35  2003/10/08 21:41:38  jalet
 # External policies for printers works !
@@ -443,2 +446,5 @@
 msgstr ""
 
+msgid "Invalid option mailto %s for printer %s"
+msgstr ""
+
