root / pykota / trunk / bin / pykota @ 1026

Revision 1026, 10.9 kB (checked in by jalet, 21 years ago)

Bug with remote jobs and LPRng fixed.

  • 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.34  2003/06/13 18:54:17  jalet
26# Bug with remote jobs and LPRng fixed.
27#
28# Revision 1.33  2003/05/28 13:51:38  jalet
29# Better handling of errors
30#
31# Revision 1.32  2003/05/27 23:00:20  jalet
32# Big rewrite of external accounting methods.
33# Should work well now.
34#
35# Revision 1.31  2003/04/30 13:36:39  jalet
36# Stupid accounting method was added.
37#
38# Revision 1.30  2003/04/29 22:03:38  jalet
39# Better error handling.
40#
41# Revision 1.29  2003/04/29 18:37:54  jalet
42# Pluggable accounting methods (actually doesn't support external scripts)
43#
44# Revision 1.28  2003/04/26 08:41:24  jalet
45# Small code reorganisation (UNTESTED) to allow pluggable accounting
46# methods in the future.
47#
48# Revision 1.27  2003/04/25 09:23:47  jalet
49# Debug message passed through !
50#
51# Revision 1.26  2003/04/25 08:23:23  jalet
52# Multiple tries to get the printer's internal page counter, waits for
53# one minute maximum for the printer to warm up, actually.
54#
55# Revision 1.25  2003/04/24 11:53:48  jalet
56# Default policy for unknown users/groups is to DENY printing instead
57# of the previous default to ALLOW printing. This is to solve an accuracy
58# problem. If you set the policy to ALLOW, jobs printed by in nexistant user
59# (from PyKota's POV) will be charged to the next user who prints on the
60# same printer.
61#
62# Revision 1.24  2003/04/23 22:13:56  jalet
63# Preliminary support for LPRng added BUT STILL UNTESTED.
64#
65# Revision 1.23  2003/04/15 11:30:57  jalet
66# More work done on money print charging.
67# Minor bugs corrected.
68# All tools now access to the storage as priviledged users, repykota excepted.
69#
70# Revision 1.22  2003/04/15 11:09:04  jalet
71# Small bug was fixed when a printer was never used and its internal
72# page counter is not accessible.
73#
74# Revision 1.21  2003/04/12 17:20:14  jalet
75# Better formula for HP workaround
76#
77# Revision 1.20  2003/04/12 16:58:28  jalet
78# The workaround for HP printers was not correct, and there's probably no
79# correct way to workaround the problem because we can't save the internal
80# page counter in real time. The last job's size is unconditionnally set to
81# 5 pages in this case.
82#
83# Revision 1.19  2003/04/11 08:56:49  jalet
84# Comment
85#
86# Revision 1.18  2003/04/11 08:50:39  jalet
87# Workaround for the HP "feature" of saving the page counter to NVRAM
88# only every time 10 new pages are printed...
89# Workaround for printers with volatile page counters.
90#
91# Revision 1.17  2003/04/10 21:47:20  jalet
92# Job history added. Upgrade script neutralized for now !
93#
94# Revision 1.16  2003/04/08 20:38:08  jalet
95# The last job Id is saved now for each printer, this will probably
96# allow other accounting methods in the future.
97#
98# Revision 1.15  2003/03/29 13:45:27  jalet
99# GPL paragraphs were incorrectly (from memory) copied into the sources.
100# Two README files were added.
101# Upgrade script for PostgreSQL pre 1.01 schema was added.
102#
103# Revision 1.14  2003/03/07 22:16:57  jalet
104# Algorithmically incorrect : last user quota wasn't updated if current
105# user wasn't allowed to print.
106#
107# Revision 1.13  2003/02/27 23:59:28  jalet
108# Stupid bug wrt exception handlingand value conversion
109#
110# Revision 1.12  2003/02/27 23:48:41  jalet
111# Correctly maps PyKota's log levels to syslog log levels
112#
113# Revision 1.11  2003/02/27 22:55:20  jalet
114# WARN log priority doesn't exist.
115#
116# Revision 1.10  2003/02/27 22:43:21  jalet
117# Missing import
118#
119# Revision 1.9  2003/02/27 22:40:26  jalet
120# Correctly handles cases where the printer is off.
121#
122# Revision 1.8  2003/02/09 12:56:53  jalet
123# Internationalization begins...
124#
125# Revision 1.7  2003/02/07 10:23:48  jalet
126# Avoid a possible future name clash
127#
128# Revision 1.6  2003/02/06 22:54:33  jalet
129# warnpykota should be ok
130#
131# Revision 1.5  2003/02/05 22:45:25  jalet
132# Forgotten import
133#
134# Revision 1.4  2003/02/05 22:42:51  jalet
135# Typo
136#
137# Revision 1.3  2003/02/05 22:38:39  jalet
138# Typo
139#
140# Revision 1.2  2003/02/05 22:16:20  jalet
141# DEVICE_URI is undefined outside of CUPS, i.e. for normal command line tools
142#
143# Revision 1.1  2003/02/05 21:28:17  jalet
144# Initial import into CVS
145#
146#
147#
148
149import sys
150import os
151import time
152
153from pykota.tool import PyKotaTool, PyKotaToolError
154from pykota.config import PyKotaConfigError
155from pykota.storage import PyKotaStorageError
156from pykota.accounter import openAccounter, PyKotaAccounterError
157
158class PyKotaFilter(PyKotaTool) :   
159    """Class for the PyKota filter."""
160    def __init__(self) :
161        PyKotaTool.__init__(self)
162        (self.printingsystem, self.printerhostname, self.printername, self.username, self.jobid, self.inputfile, self.copies) = self.extractInfoFromCupsOrLprng()
163        self.accounter = openAccounter(self)
164   
165    def extractInfoFromCupsOrLprng(self) :   
166        """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename) depending on the printing system in use (as seen by the print filter).
167       
168           Returns (None, None, None, None, None, None) if no printing system is recognized.
169        """
170        # Try to detect CUPS
171        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
172            if len(sys.argv) == 7 :
173                inputfile = sys.argv[6]
174            else :   
175                inputfile = None
176               
177            device_uri = os.environ.get("DEVICE_URI", "")
178            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
179            try :
180                (backend, destination) = device_uri.split(":", 1) 
181            except ValueError :   
182                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
183            while destination.startswith("/") :
184                destination = destination[1:]
185            printerhostname = destination.split("/")[0].split(":")[0]
186            return ("CUPS", printerhostname, os.environ.get("PRINTER"), sys.argv[2].strip(), sys.argv[1].strip(), inputfile, int(sys.argv[4].strip()))
187        else :   
188            # Try to detect LPRng
189            jseen = Pseen = nseen = rseen = Kseen = None
190            for arg in sys.argv :
191                if arg.startswith("-j") :
192                    jseen = arg[2:].strip()
193                elif arg.startswith("-n") :     
194                    nseen = arg[2:].strip()
195                elif arg.startswith("-P") :   
196                    Pseen = arg[2:].strip()
197                elif arg.startswith("-r") :   
198                    rseen = arg[2:].strip()
199                elif arg.startswith("-K") or arg.startswith("-#") :   
200                    Kseen = int(arg[2:].strip())
201            if Kseen is None :       
202                Kseen = 1       # we assume the user wants at least one copy...
203            if jseen and Pseen and nseen and rseen :       
204                # job is always in stdin (None)
205                return ("LPRNG", rseen, Pseen, nseen, jseen, None, 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.