root / pykota / trunk / bin / pykota @ 1041

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

Hey, it may work (edpykota --reset excepted) !

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