root / pykota / trunk / bin / pykota @ 976

Revision 976, 10.4 kB (checked in by jalet, 21 years ago)

Stupid accounting method was added.

  • 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.31  2003/04/30 13:36:39  jalet
26# Stupid accounting method was added.
27#
28# Revision 1.30  2003/04/29 22:03:38  jalet
29# Better error handling.
30#
31# Revision 1.29  2003/04/29 18:37:54  jalet
32# Pluggable accounting methods (actually doesn't support external scripts)
33#
34# Revision 1.28  2003/04/26 08:41:24  jalet
35# Small code reorganisation (UNTESTED) to allow pluggable accounting
36# methods in the future.
37#
38# Revision 1.27  2003/04/25 09:23:47  jalet
39# Debug message passed through !
40#
41# Revision 1.26  2003/04/25 08:23:23  jalet
42# Multiple tries to get the printer's internal page counter, waits for
43# one minute maximum for the printer to warm up, actually.
44#
45# Revision 1.25  2003/04/24 11:53:48  jalet
46# Default policy for unknown users/groups is to DENY printing instead
47# of the previous default to ALLOW printing. This is to solve an accuracy
48# problem. If you set the policy to ALLOW, jobs printed by in nexistant user
49# (from PyKota's POV) will be charged to the next user who prints on the
50# same printer.
51#
52# Revision 1.24  2003/04/23 22:13:56  jalet
53# Preliminary support for LPRng added BUT STILL UNTESTED.
54#
55# Revision 1.23  2003/04/15 11:30:57  jalet
56# More work done on money print charging.
57# Minor bugs corrected.
58# All tools now access to the storage as priviledged users, repykota excepted.
59#
60# Revision 1.22  2003/04/15 11:09:04  jalet
61# Small bug was fixed when a printer was never used and its internal
62# page counter is not accessible.
63#
64# Revision 1.21  2003/04/12 17:20:14  jalet
65# Better formula for HP workaround
66#
67# Revision 1.20  2003/04/12 16:58:28  jalet
68# The workaround for HP printers was not correct, and there's probably no
69# correct way to workaround the problem because we can't save the internal
70# page counter in real time. The last job's size is unconditionnally set to
71# 5 pages in this case.
72#
73# Revision 1.19  2003/04/11 08:56:49  jalet
74# Comment
75#
76# Revision 1.18  2003/04/11 08:50:39  jalet
77# Workaround for the HP "feature" of saving the page counter to NVRAM
78# only every time 10 new pages are printed...
79# Workaround for printers with volatile page counters.
80#
81# Revision 1.17  2003/04/10 21:47:20  jalet
82# Job history added. Upgrade script neutralized for now !
83#
84# Revision 1.16  2003/04/08 20:38:08  jalet
85# The last job Id is saved now for each printer, this will probably
86# allow other accounting methods in the future.
87#
88# Revision 1.15  2003/03/29 13:45:27  jalet
89# GPL paragraphs were incorrectly (from memory) copied into the sources.
90# Two README files were added.
91# Upgrade script for PostgreSQL pre 1.01 schema was added.
92#
93# Revision 1.14  2003/03/07 22:16:57  jalet
94# Algorithmically incorrect : last user quota wasn't updated if current
95# user wasn't allowed to print.
96#
97# Revision 1.13  2003/02/27 23:59:28  jalet
98# Stupid bug wrt exception handlingand value conversion
99#
100# Revision 1.12  2003/02/27 23:48:41  jalet
101# Correctly maps PyKota's log levels to syslog log levels
102#
103# Revision 1.11  2003/02/27 22:55:20  jalet
104# WARN log priority doesn't exist.
105#
106# Revision 1.10  2003/02/27 22:43:21  jalet
107# Missing import
108#
109# Revision 1.9  2003/02/27 22:40:26  jalet
110# Correctly handles cases where the printer is off.
111#
112# Revision 1.8  2003/02/09 12:56:53  jalet
113# Internationalization begins...
114#
115# Revision 1.7  2003/02/07 10:23:48  jalet
116# Avoid a possible future name clash
117#
118# Revision 1.6  2003/02/06 22:54:33  jalet
119# warnpykota should be ok
120#
121# Revision 1.5  2003/02/05 22:45:25  jalet
122# Forgotten import
123#
124# Revision 1.4  2003/02/05 22:42:51  jalet
125# Typo
126#
127# Revision 1.3  2003/02/05 22:38:39  jalet
128# Typo
129#
130# Revision 1.2  2003/02/05 22:16:20  jalet
131# DEVICE_URI is undefined outside of CUPS, i.e. for normal command line tools
132#
133# Revision 1.1  2003/02/05 21:28:17  jalet
134# Initial import into CVS
135#
136#
137#
138
139import sys
140import os
141import time
142
143from pykota.tool import PyKotaTool, PyKotaToolError
144from pykota.config import PyKotaConfigError
145from pykota.storage import PyKotaStorageError
146from pykota.accounter import openAccounter, PyKotaAccounterError
147
148class PyKotaFilter(PyKotaTool) :   
149    """Class for the PyKota filter."""
150    def __init__(self) :
151        PyKotaTool.__init__(self)
152        (self.printingsystem, self.printerhostname, self.printername, self.username, self.jobid, self.inputfile) = self.extractInfoFromCupsOrLprng()
153        self.accounter = openAccounter(self)
154   
155    def extractInfoFromCupsOrLprng(self) :   
156        """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename) depending on the printing system in use (as seen by the print filter).
157       
158           Returns (None, None, None, None, None, None) if no printing system is recognized.
159        """
160        # Try to detect CUPS
161        if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :
162            if len(sys.argv) == 7 :
163                inputfile = sys.argv[6]
164            else :   
165                inputfile = None
166               
167            device_uri = os.environ.get("DEVICE_URI", "")
168            # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp
169            try :
170                (backend, destination) = device_uri.split(":", 1) 
171            except ValueError :   
172                raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri
173            while destination.startswith("/") :
174                destination = destination[1:]
175            printerhostname = destination.split("/")[0].split(":")[0]
176            return ("CUPS", printerhostname, os.environ.get("PRINTER"), sys.argv[2].strip(), sys.argv[1].strip(), inputfile)
177        else :   
178            # Try to detect LPRng
179            jseen = Jseen = Pseen = nseen = rseen = None
180            for arg in sys.argv :
181                if arg.startswith("-j") :
182                    jseen = arg[2:].strip()
183                elif arg.startswith("-J") :   
184                    Jseen = arg[2:].strip()
185                    if Jseen == "(STDIN)" :
186                        Jseen = None
187                elif arg.startswith("-n") :     
188                    nseen = arg[2:].strip()
189                elif arg.startswith("-P") :   
190                    Pseen = arg[2:].strip()
191                elif arg.startswith("-r") :   
192                    rseen = arg[2:].strip()
193            if jseen and Pseen and nseen and rseen :       
194                return ("LPRNG", rseen, Pseen, nseen, jseen, Jseen)
195        return (None, None, None, None, None, None)   # Unknown printing system         
196       
197    def acceptJob(self) :       
198        """Returns the exit code needed by the printing backend to accept the job and print it."""
199        if self.printingsystem == "CUPS" :
200            return 0
201        elif self.printingsystem == "LPRNG" :   
202            return 0
203        else :   
204            # UNKNOWN
205            return -1
206           
207    def removeJob(self) :           
208        """Returns the exit code needed by the printing backend to refuse the job and remove it."""
209        if self.printingsystem == "CUPS" :
210            return 1
211        elif self.printingsystem == "LPRNG" :   
212            return 3
213        else :   
214            # UNKNOWN
215            return -1
216           
217def main() :   
218    """Do it, and do it right !"""
219    # Initializes the current tool
220    kotafilter = PyKotaFilter()   
221   
222    #
223    # If this is a CUPS filter, we should act and die like a CUPS filter when needed
224    if kotafilter.printingsystem == "CUPS" :
225        if len(sys.argv) not in (6, 7) :   
226            sys.stderr.write("ERROR: %s job-id user title copies options [file]\n" % sys.argv[0])
227            return kotafilter.removeJob()
228   
229    # Get the last page counter and last username from the Quota Storage backend
230    printerid = kotafilter.storage.getPrinterId(kotafilter.printername)
231    if printerid is None :
232        # The printer is unknown from the Quota Storage perspective
233        # we let the job pass through, but log a warning message
234        kotafilter.logger.log_message(_("Printer %s not registered in the PyKota system") % kotafilter.printername, "warn")
235    else :   
236        userid = kotafilter.storage.getUserId(kotafilter.username)
237        if userid is None :
238            # The user is unknown from the Quota Storage perspective
239            # Depending on the default policy for this printer, we
240            # either let the job pass through or reject it, but we
241            # log a message in any case.
242            policy = kotafilter.config.getPrinterPolicy(kotafilter.printername)
243            if policy == "ALLOW" :
244                action = "POLICY_ALLOW"
245            else :   
246                action = "POLICY_DENY"
247            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")
248            if action == "POLICY_DENY" :
249                return kotafilter.removeJob()
250        else :
251            # Now does the accounting and act depending on the result
252            action = kotafilter.accounter.doAccounting(printerid, userid)
253           
254            # if not allowed to print then die, else proceed.
255            if action == "DENY" :
256                # No, just die cleanly
257                return kotafilter.removeJob()
258       
259    # pass the job untouched to the underlying layer
260    kotafilter.accounter.filterInput(kotafilter.inputfile)     
261   
262    return kotafilter.acceptJob()
263
264if __name__ == "__main__" :   
265    try :
266        retcode = main()
267    except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError), msg :
268        sys.stderr.write("ERROR : PyKota filter failed (%s)\n" % msg)
269        sys.stderr.flush()
270        retcode = -1
271    sys.exit(retcode)   
272   
Note: See TracBrowser for help on using the browser.