root / pykota / trunk / bin / pykota @ 908

Revision 908, 10.1 kB (checked in by jalet, 21 years ago)

The workaround for HP printers was not correct, and there's probably no
correct way to workaround the problem because we can't save the internal
page counter in real time. The last job's size is unconditionnally set to
5 pages in this case.

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