root / pykota / trunk / bin / pykota @ 1004

Revision 1004, 11.0 kB (checked in by jalet, 21 years ago)

Better handling of errors

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2
3# PyKota accounting filter
4#
5# PyKota - Print Quotas for CUPS and LPRng
6#
7# (c) 2003 Jerome Alet <alet@librelogiciel.com>
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21#
22# $Id$
23#
24# $Log$
25# Revision 1.33  2003/05/28 13:51:38  jalet
26# Better handling of errors
27#
28# Revision 1.32  2003/05/27 23:00:20  jalet
29# Big rewrite of external accounting methods.
30# Should work well now.
31#
32# Revision 1.31  2003/04/30 13:36:39  jalet
33# Stupid accounting method was added.
34#
35# Revision 1.30  2003/04/29 22:03:38  jalet
36# Better error handling.
37#
38# Revision 1.29  2003/04/29 18:37:54  jalet
39# Pluggable accounting methods (actually doesn't support external scripts)
40#
41# Revision 1.28  2003/04/26 08:41:24  jalet
42# Small code reorganisation (UNTESTED) to allow pluggable accounting
43# methods in the future.
44#
45# Revision 1.27  2003/04/25 09:23:47  jalet
46# Debug message passed through !
47#
48# Revision 1.26  2003/04/25 08:23:23  jalet
49# Multiple tries to get the printer's internal page counter, waits for
50# one minute maximum for the printer to warm up, actually.
51#
52# Revision 1.25  2003/04/24 11:53:48  jalet
53# Default policy for unknown users/groups is to DENY printing instead
54# of the previous default to ALLOW printing. This is to solve an accuracy
55# problem. If you set the policy to ALLOW, jobs printed by in nexistant user
56# (from PyKota's POV) will be charged to the next user who prints on the
57# same printer.
58#
59# Revision 1.24  2003/04/23 22:13:56  jalet
60# Preliminary support for LPRng added BUT STILL UNTESTED.
61#
62# Revision 1.23  2003/04/15 11:30:57  jalet
63# More work done on money print charging.
64# Minor bugs corrected.
65# All tools now access to the storage as priviledged users, repykota excepted.
66#
67# Revision 1.22  2003/04/15 11:09:04  jalet
68# Small bug was fixed when a printer was never used and its internal
69# page counter is not accessible.
70#
71# Revision 1.21  2003/04/12 17:20:14  jalet
72# Better formula for HP workaround
73#
74# Revision 1.20  2003/04/12 16:58:28  jalet
75# The workaround for HP printers was not correct, and there's probably no
76# correct way to workaround the problem because we can't save the internal
77# page counter in real time. The last job's size is unconditionnally set to
78# 5 pages in this case.
79#
80# Revision 1.19  2003/04/11 08:56:49  jalet
81# Comment
82#
83# Revision 1.18  2003/04/11 08:50:39  jalet
84# Workaround for the HP "feature" of saving the page counter to NVRAM
85# only every time 10 new pages are printed...
86# Workaround for printers with volatile page counters.
87#
88# Revision 1.17  2003/04/10 21:47:20  jalet
89# Job history added. Upgrade script neutralized for now !
90#
91# Revision 1.16  2003/04/08 20:38:08  jalet
92# The last job Id is saved now for each printer, this will probably
93# allow other accounting methods in the future.
94#
95# Revision 1.15  2003/03/29 13:45:27  jalet
96# GPL paragraphs were incorrectly (from memory) copied into the sources.
97# Two README files were added.
98# Upgrade script for PostgreSQL pre 1.01 schema was added.
99#
100# Revision 1.14  2003/03/07 22:16:57  jalet
101# Algorithmically incorrect : last user quota wasn't updated if current
102# user wasn't allowed to print.
103#
104# Revision 1.13  2003/02/27 23:59:28  jalet
105# Stupid bug wrt exception handlingand value conversion
106#
107# Revision 1.12  2003/02/27 23:48:41  jalet
108# Correctly maps PyKota's log levels to syslog log levels
109#
110# Revision 1.11  2003/02/27 22:55:20  jalet
111# WARN log priority doesn't exist.
112#
113# Revision 1.10  2003/02/27 22:43:21  jalet
114# Missing import
115#
116# Revision 1.9  2003/02/27 22:40:26  jalet
117# Correctly handles cases where the printer is off.
118#
119# Revision 1.8  2003/02/09 12:56:53  jalet
120# Internationalization begins...
121#
122# Revision 1.7  2003/02/07 10:23:48  jalet
123# Avoid a possible future name clash
124#
125# Revision 1.6  2003/02/06 22:54:33  jalet
126# warnpykota should be ok
127#
128# Revision 1.5  2003/02/05 22:45:25  jalet
129# Forgotten import
130#
131# Revision 1.4  2003/02/05 22:42:51  jalet
132# Typo
133#
134# Revision 1.3  2003/02/05 22:38:39  jalet
135# Typo
136#
137# Revision 1.2  2003/02/05 22:16:20  jalet
138# DEVICE_URI is undefined outside of CUPS, i.e. for normal command line tools
139#
140# Revision 1.1  2003/02/05 21:28:17  jalet
141# Initial import into CVS
142#
143#
144#
145
146import sys
147import os
148import time
149
150from pykota.tool import PyKotaTool, PyKotaToolError
151from pykota.config import PyKotaConfigError
152from pykota.storage import PyKotaStorageError
153from pykota.accounter import openAccounter, PyKotaAccounterError
154
155class PyKotaFilter(PyKotaTool) :   
156    """Class for the PyKota filter."""
157    def __init__(self) :
158        PyKotaTool.__init__(self)
159        (self.printingsystem, self.printerhostname, self.printername, self.username, self.jobid, self.inputfile, self.copies) = self.extractInfoFromCupsOrLprng()
160        self.accounter = openAccounter(self)
161   
162    def extractInfoFromCupsOrLprng(self) :   
163        """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename) depending on the printing system in use (as seen by the print filter).
164       
165           Returns (None, None, None, None, None, None) if no printing system is recognized.
166        """
167        # Try to detect CUPS
168        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
169            if len(sys.argv) == 7 :
170                inputfile = sys.argv[6]
171            else :   
172                inputfile = None
173               
174            device_uri = os.environ.get("DEVICE_URI", "")
175            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
176            try :
177                (backend, destination) = device_uri.split(":", 1) 
178            except ValueError :   
179                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
180            while destination.startswith("/") :
181                destination = destination[1:]
182            printerhostname = destination.split("/")[0].split(":")[0]
183            return ("CUPS", printerhostname, os.environ.get("PRINTER"), sys.argv[2].strip(), sys.argv[1].strip(), inputfile, int(sys.argv[4].strip()))
184        else :   
185            # Try to detect LPRng
186            jseen = Jseen = Pseen = nseen = rseen = Kseen = None
187            for arg in sys.argv :
188                if arg.startswith("-j") :
189                    jseen = arg[2:].strip()
190                elif arg.startswith("-J") :   
191                    Jseen = arg[2:].strip()
192                    if Jseen == "(STDIN)" :
193                        Jseen = None
194                elif arg.startswith("-n") :     
195                    nseen = arg[2:].strip()
196                elif arg.startswith("-P") :   
197                    Pseen = arg[2:].strip()
198                elif arg.startswith("-r") :   
199                    rseen = arg[2:].strip()
200                elif arg.startswith("-K") or arg.startswith("-#") :   
201                    Kseen = int(arg[2:].strip())
202            if Kseen is None :       
203                Kseen = 1       # we assume the user wants at least one copy...
204            if jseen and Pseen and nseen and rseen :       
205                return ("LPRNG", rseen, Pseen, nseen, jseen, Jseen, Kseen)
206        return (None, None, None, None, None, None, None)   # Unknown printing system         
207       
208    def acceptJob(self) :       
209        """Returns the exit code needed by the printing backend to accept the job and print it."""
210        if self.printingsystem == "CUPS" :
211            return 0
212        elif self.printingsystem == "LPRNG" :   
213            return 0
214        else :   
215            # UNKNOWN
216            return -1
217           
218    def removeJob(self) :           
219        """Returns the exit code needed by the printing backend to refuse the job and remove it."""
220        if self.printingsystem == "CUPS" :
221            return 1
222        elif self.printingsystem == "LPRNG" :   
223            return 3
224        else :   
225            # UNKNOWN
226            return -1
227           
228def main() :   
229    """Do it, and do it right !"""
230    # Initializes the current tool
231    kotafilter = PyKotaFilter()   
232   
233    #
234    # If this is a CUPS filter, we should act and die like a CUPS filter when needed
235    if kotafilter.printingsystem == "CUPS" :
236        if len(sys.argv) not in (6, 7) :   
237            sys.stderr.write("ERROR: %s job-id user title copies options [file]\n" % sys.argv[0])
238            return kotafilter.removeJob()
239   
240    # Get the last page counter and last username from the Quota Storage backend
241    printerid = kotafilter.storage.getPrinterId(kotafilter.printername)
242    if printerid is None :
243        # The printer is unknown from the Quota Storage perspective
244        # we let the job pass through, but log a warning message
245        kotafilter.logger.log_message(_("Printer %s not registered in the PyKota system") % kotafilter.printername, "warn")
246    else :   
247        userid = kotafilter.storage.getUserId(kotafilter.username)
248        if userid is None :
249            # The user is unknown from the Quota Storage perspective
250            # Depending on the default policy for this printer, we
251            # either let the job pass through or reject it, but we
252            # log a message in any case.
253            policy = kotafilter.config.getPrinterPolicy(kotafilter.printername)
254            if policy == "ALLOW" :
255                action = "POLICY_ALLOW"
256            else :   
257                action = "POLICY_DENY"
258            kotafilter.logger.log_message(_("User %s not registered in the PyKota system, applying default policy (%s) for printer %s") % (kotafilter.username, action, kotafilter.printername), "warn")
259            if action == "POLICY_DENY" :
260                return kotafilter.removeJob()
261        else :
262            # Now does the accounting and act depending on the result
263            action = kotafilter.accounter.doAccounting(printerid, userid)
264           
265            # if not allowed to print then die, else proceed.
266            if action == "DENY" :
267                # No, just die cleanly
268                return kotafilter.removeJob()
269       
270    # pass the job untouched to the underlying layer
271    kotafilter.accounter.filterInput(kotafilter.inputfile)     
272   
273    return kotafilter.acceptJob()
274
275if __name__ == "__main__" :   
276    retcode = -1
277    try :
278        retcode = main()
279    except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, AttributeError, KeyError, IndexError, ValueError, IOError), msg :
280        sys.stderr.write("ERROR : PyKota filter failed (%s)\n" % msg)
281        sys.stderr.flush()
282    sys.exit(retcode)   
283   
Note: See TracBrowser for help on using the browser.