root / pykota / trunk / bin / pykota @ 1113

Revision 1113, 11.3 kB (checked in by jalet, 21 years ago)

1.14 is out !

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