root / pykota / trunk / bin / pykota @ 952

Revision 952, 11.2 kB (checked in by jalet, 21 years ago)

Preliminary support for LPRng added BUT STILL UNTESTED.

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