#! /usr/bin/env python # -*- coding: ISO-8859-15 -*- # PyKota accounting filter # # PyKota - Print Quotas for CUPS and LPRng # # (c) 2003 Jerome Alet # 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.48 2003/12/27 16:49:25 uid67467 # Should be ok now. # # Revision 1.47 2003/11/26 19:17:35 jalet # Printing on a printer not present in the Quota Storage now results # in the job being stopped or cancelled depending on the system. # # Revision 1.46 2003/11/24 14:25:02 jalet # Missing import in pykota filter # # 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. # # Revision 1.43 2003/11/06 22:33:25 jalet # French variable name # # Revision 1.42 2003/10/08 21:41:38 jalet # External policies for printers works ! # We can now auto-add users on first print, and do other useful things if needed. # # Revision 1.41 2003/10/07 09:07:27 jalet # Character encoding added to please latest version of Python # # Revision 1.40 2003/09/04 08:38:56 jalet # Added an exception catch to ensure clean close of database even in # case of TypeError too. # # Revision 1.39 2003/08/18 16:35:28 jalet # New pychecker pass, on the tools this time. # # Revision 1.38 2003/08/18 16:20:59 jalet # Improvement of the printing system detection code. # # Revision 1.37 2003/07/29 20:55:17 jalet # 1.14 is out ! # # Revision 1.36 2003/07/10 06:09:52 jalet # Incorrect documentation string # # Revision 1.35 2003/06/25 14:10:01 jalet # Hey, it may work (edpykota --reset excepted) ! # # Revision 1.34 2003/06/13 18:54:17 jalet # Bug with remote jobs and LPRng fixed. # # Revision 1.33 2003/05/28 13:51:38 jalet # Better handling of errors # # Revision 1.32 2003/05/27 23:00:20 jalet # Big rewrite of external accounting methods. # Should work well now. # # Revision 1.31 2003/04/30 13:36:39 jalet # Stupid accounting method was added. # # Revision 1.30 2003/04/29 22:03:38 jalet # Better error handling. # # 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 # methods in the future. # # Revision 1.27 2003/04/25 09:23:47 jalet # Debug message passed through ! # # Revision 1.26 2003/04/25 08:23:23 jalet # Multiple tries to get the printer's internal page counter, waits for # one minute maximum for the printer to warm up, actually. # # Revision 1.25 2003/04/24 11:53:48 jalet # Default policy for unknown users/groups is to DENY printing instead # of the previous default to ALLOW printing. This is to solve an accuracy # problem. If you set the policy to ALLOW, jobs printed by in nexistant user # (from PyKota's POV) will be charged to the next user who prints on the # same printer. # # Revision 1.24 2003/04/23 22:13:56 jalet # Preliminary support for LPRng added BUT STILL UNTESTED. # # Revision 1.23 2003/04/15 11:30:57 jalet # More work done on money print charging. # Minor bugs corrected. # All tools now access to the storage as priviledged users, repykota excepted. # # Revision 1.22 2003/04/15 11:09:04 jalet # Small bug was fixed when a printer was never used and its internal # page counter is not accessible. # # Revision 1.21 2003/04/12 17:20:14 jalet # Better formula for HP workaround # # Revision 1.20 2003/04/12 16:58:28 jalet # The workaround for HP printers was not correct, and there's probably no # correct way to workaround the problem because we can't save the internal # page counter in real time. The last job's size is unconditionnally set to # 5 pages in this case. # # Revision 1.19 2003/04/11 08:56:49 jalet # Comment # # Revision 1.18 2003/04/11 08:50:39 jalet # Workaround for the HP "feature" of saving the page counter to NVRAM # only every time 10 new pages are printed... # Workaround for printers with volatile page counters. # # Revision 1.17 2003/04/10 21:47:20 jalet # Job history added. Upgrade script neutralized for now ! # # Revision 1.16 2003/04/08 20:38:08 jalet # The last job Id is saved now for each printer, this will probably # allow other accounting methods in the future. # # Revision 1.15 2003/03/29 13:45:27 jalet # GPL paragraphs were incorrectly (from memory) copied into the sources. # Two README files were added. # Upgrade script for PostgreSQL pre 1.01 schema was added. # # Revision 1.14 2003/03/07 22:16:57 jalet # Algorithmically incorrect : last user quota wasn't updated if current # user wasn't allowed to print. # # Revision 1.13 2003/02/27 23:59:28 jalet # Stupid bug wrt exception handlingand value conversion # # Revision 1.12 2003/02/27 23:48:41 jalet # Correctly maps PyKota's log levels to syslog log levels # # Revision 1.11 2003/02/27 22:55:20 jalet # WARN log priority doesn't exist. # # Revision 1.10 2003/02/27 22:43:21 jalet # Missing import # # Revision 1.9 2003/02/27 22:40:26 jalet # Correctly handles cases where the printer is off. # # Revision 1.8 2003/02/09 12:56:53 jalet # Internationalization begins... # # Revision 1.7 2003/02/07 10:23:48 jalet # Avoid a possible future name clash # # Revision 1.6 2003/02/06 22:54:33 jalet # warnpykota should be ok # # Revision 1.5 2003/02/05 22:45:25 jalet # Forgotten import # # Revision 1.4 2003/02/05 22:42:51 jalet # Typo # # Revision 1.3 2003/02/05 22:38:39 jalet # Typo # # Revision 1.2 2003/02/05 22:16:20 jalet # DEVICE_URI is undefined outside of CUPS, i.e. for normal command line tools # # Revision 1.1 2003/02/05 21:28:17 jalet # Initial import into CVS # # # import sys import os from pykota.tool import PyKotaFilterOrBackend, PyKotaToolError from pykota.config import PyKotaConfigError from pykota.storage import PyKotaStorageError 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.""" if self.printingsystem == "CUPS" : return 0 elif self.printingsystem == "LPRNG" : return 0 else : # UNKNOWN return -1 def removeJob(self) : """Returns the exit code needed by the printing backend to refuse the job and remove it.""" if self.printingsystem == "CUPS" : return 1 elif self.printingsystem == "LPRNG" : return 3 else : # UNKNOWN return -1 def main(thefilter) : """Do it, and do it right !""" # # If this is a CUPS filter, we should act and die like a CUPS filter when needed if thefilter.printingsystem == "CUPS" : if len(sys.argv) not in (6, 7) : sys.stderr.write("ERROR: %s job-id user title copies options [file]\n" % sys.argv[0]) return thefilter.removeJob() # Get the last page counter and last username from the Quota Storage backend printer = thefilter.storage.getPrinter(thefilter.printername) if not printer.Exists : # The printer is unknown from the Quota Storage perspective. # we just cancel the job. thefilter.logger.log_message(_("Printer %s not registered in the PyKota system") % thefilter.printername, "error") return thefilter.removeJob() else : for dummy in range(2) : user = thefilter.storage.getUser(thefilter.username) if user.Exists : break else : # The user is unknown from the Quota Storage perspective # Depending on the default policy for this printer, we # either let the job pass through or reject it, but we # log a message in any case. (policy, args) = thefilter.config.getPrinterPolicy(thefilter.printername) if policy == "ALLOW" : action = "POLICY_ALLOW" elif policy == "EXTERNAL" : 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) : thefilter.logger.log_message(_("External policy %s for printer %s produced an error. Job rejected. Please check PyKota's configuration files.") % (commandline, thefilter.printername), "error") return thefilter.removeJob() else : # here we try a second time, because the goal # of the external action was to add the user # in the database. continue else : action = "POLICY_DENY" thefilter.logger.log_message(_("User %s not registered in the PyKota system, applying default policy (%s) for printer %s") % (thefilter.username, action, thefilter.printername), "warn") if action == "POLICY_DENY" : return thefilter.removeJob() # when we get there, the printer policy allows the job to pass break # if user exists, do accounting if user.Exists : # Now does the accounting and act depending on the result thefilter.logdebug("Does accounting for user %s on printer %s." % (user.Name, printer.Name)) action = thefilter.accounter.doAccounting(printer, user) # if not allowed to print then die, else proceed. if action == "DENY" : # No, just die cleanly thefilter.logdebug("Printing is denied.") return thefilter.removeJob() elif policy == "EXTERNAL" : thefilter.logger.log_message(_("External policy %s for printer %s couldn't add user %s. Job rejected.") % (commandline, thefilter.printername, thefilter.username), "error") return thefilter.removeJob() # pass the job untouched to the underlying layer thefilter.logdebug("Passing input data to next filter.") thefilter.accounter.filterInput(thefilter.inputfile) return thefilter.acceptJob() if __name__ == "__main__" : retcode = -1 try : # Initializes the current tool kotafilter = PyKotaFilter() retcode = main(kotafilter) 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() try : retcode = kotafilter.removeJob() except : retcode = -1 try : kotafilter.storage.close() except (TypeError, NameError, AttributeError) : pass sys.exit(retcode)