root / pykota / trunk / bin / pykota @ 975

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

Better error handling.

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