root / pykota / trunk / bin / pykota @ 909

Revision 909, 10.2 kB (checked in by jalet, 21 years ago)

Better formula for HP workaround

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