root / pykota / trunk / bin / pykota @ 915

Revision 915, 10.7 kB (checked in by jalet, 21 years ago)

More work done on money print charging.
Minor bugs corrected.
All tools now access to the storage as priviledged users, repykota excepted.

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