root / pykota / trunk / bin / pykota @ 956

Revision 956, 11.6 kB (checked in by jalet, 21 years ago)

Default policy for unknown users/groups is to DENY printing instead
of the previous default to ALLOW printing. This is to solve an accuracy
problem. If you set the policy to ALLOW, jobs printed by in nexistant user
(from PyKota's POV) will be charged to the next user who prints on the
same printer.

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