root / pykota / trunk / bin / pykota @ 901

Revision 901, 9.6 kB (checked in by jalet, 21 years ago)

Workaround for the HP "feature" of saving the page counter to NVRAM
only every time 10 new pages are printed...
Workaround for printers with volatile page counters.

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