root / pykota / trunk / bin / pykota @ 1000

Revision 1000, 10.8 kB (checked in by jalet, 21 years ago)

Big rewrite of external accounting methods.
Should work well now.

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