root / pykota / trunk / bin / pykota @ 1271

Revision 1271, 11.7 kB (checked in by jalet, 20 years ago)

Major code refactoring, it's way cleaner, and now allows automated addition
of printers on first print.

  • 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# -*- coding: ISO-8859-15 -*-
3
4# PyKota accounting filter
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003-2004 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22#
23# $Id$
24#
25# $Log$
26# Revision 1.50  2004/01/11 23:22:42  jalet
27# Major code refactoring, it's way cleaner, and now allows automated addition
28# of printers on first print.
29#
30# Revision 1.49  2004/01/08 14:10:32  jalet
31# Copyright year changed.
32#
33# Revision 1.48  2003/12/27 16:49:25  uid67467
34# Should be ok now.
35#
36# Revision 1.47  2003/11/26 19:17:35  jalet
37# Printing on a printer not present in the Quota Storage now results
38# in the job being stopped or cancelled depending on the system.
39#
40# Revision 1.46  2003/11/24 14:25:02  jalet
41# Missing import in pykota filter
42#
43# Revision 1.45  2003/11/19 23:19:37  jalet
44# Code refactoring work.
45# Explicit redirection to /dev/null has to be set in external policy now, just
46# like in external mailto.
47#
48# Revision 1.44  2003/11/08 16:05:31  jalet
49# CUPS backend added for people to experiment.
50#
51# Revision 1.43  2003/11/06 22:33:25  jalet
52# French variable name
53#
54# Revision 1.42  2003/10/08 21:41:38  jalet
55# External policies for printers works !
56# We can now auto-add users on first print, and do other useful things if needed.
57#
58# Revision 1.41  2003/10/07 09:07:27  jalet
59# Character encoding added to please latest version of Python
60#
61# Revision 1.40  2003/09/04 08:38:56  jalet
62# Added an exception catch to ensure clean close of database even in
63# case of TypeError too.
64#
65# Revision 1.39  2003/08/18 16:35:28  jalet
66# New pychecker pass, on the tools this time.
67#
68# Revision 1.38  2003/08/18 16:20:59  jalet
69# Improvement of the printing system detection code.
70#
71# Revision 1.37  2003/07/29 20:55:17  jalet
72# 1.14 is out !
73#
74# Revision 1.36  2003/07/10 06:09:52  jalet
75# Incorrect documentation string
76#
77# Revision 1.35  2003/06/25 14:10:01  jalet
78# Hey, it may work (edpykota --reset excepted) !
79#
80# Revision 1.34  2003/06/13 18:54:17  jalet
81# Bug with remote jobs and LPRng fixed.
82#
83# Revision 1.33  2003/05/28 13:51:38  jalet
84# Better handling of errors
85#
86# Revision 1.32  2003/05/27 23:00:20  jalet
87# Big rewrite of external accounting methods.
88# Should work well now.
89#
90# Revision 1.31  2003/04/30 13:36:39  jalet
91# Stupid accounting method was added.
92#
93# Revision 1.30  2003/04/29 22:03:38  jalet
94# Better error handling.
95#
96# Revision 1.29  2003/04/29 18:37:54  jalet
97# Pluggable accounting methods (actually doesn't support external scripts)
98#
99# Revision 1.28  2003/04/26 08:41:24  jalet
100# Small code reorganisation (UNTESTED) to allow pluggable accounting
101# methods in the future.
102#
103# Revision 1.27  2003/04/25 09:23:47  jalet
104# Debug message passed through !
105#
106# Revision 1.26  2003/04/25 08:23:23  jalet
107# Multiple tries to get the printer's internal page counter, waits for
108# one minute maximum for the printer to warm up, actually.
109#
110# Revision 1.25  2003/04/24 11:53:48  jalet
111# Default policy for unknown users/groups is to DENY printing instead
112# of the previous default to ALLOW printing. This is to solve an accuracy
113# problem. If you set the policy to ALLOW, jobs printed by in nexistant user
114# (from PyKota's POV) will be charged to the next user who prints on the
115# same printer.
116#
117# Revision 1.24  2003/04/23 22:13:56  jalet
118# Preliminary support for LPRng added BUT STILL UNTESTED.
119#
120# Revision 1.23  2003/04/15 11:30:57  jalet
121# More work done on money print charging.
122# Minor bugs corrected.
123# All tools now access to the storage as priviledged users, repykota excepted.
124#
125# Revision 1.22  2003/04/15 11:09:04  jalet
126# Small bug was fixed when a printer was never used and its internal
127# page counter is not accessible.
128#
129# Revision 1.21  2003/04/12 17:20:14  jalet
130# Better formula for HP workaround
131#
132# Revision 1.20  2003/04/12 16:58:28  jalet
133# The workaround for HP printers was not correct, and there's probably no
134# correct way to workaround the problem because we can't save the internal
135# page counter in real time. The last job's size is unconditionnally set to
136# 5 pages in this case.
137#
138# Revision 1.19  2003/04/11 08:56:49  jalet
139# Comment
140#
141# Revision 1.18  2003/04/11 08:50:39  jalet
142# Workaround for the HP "feature" of saving the page counter to NVRAM
143# only every time 10 new pages are printed...
144# Workaround for printers with volatile page counters.
145#
146# Revision 1.17  2003/04/10 21:47:20  jalet
147# Job history added. Upgrade script neutralized for now !
148#
149# Revision 1.16  2003/04/08 20:38:08  jalet
150# The last job Id is saved now for each printer, this will probably
151# allow other accounting methods in the future.
152#
153# Revision 1.15  2003/03/29 13:45:27  jalet
154# GPL paragraphs were incorrectly (from memory) copied into the sources.
155# Two README files were added.
156# Upgrade script for PostgreSQL pre 1.01 schema was added.
157#
158# Revision 1.14  2003/03/07 22:16:57  jalet
159# Algorithmically incorrect : last user quota wasn't updated if current
160# user wasn't allowed to print.
161#
162# Revision 1.13  2003/02/27 23:59:28  jalet
163# Stupid bug wrt exception handlingand value conversion
164#
165# Revision 1.12  2003/02/27 23:48:41  jalet
166# Correctly maps PyKota's log levels to syslog log levels
167#
168# Revision 1.11  2003/02/27 22:55:20  jalet
169# WARN log priority doesn't exist.
170#
171# Revision 1.10  2003/02/27 22:43:21  jalet
172# Missing import
173#
174# Revision 1.9  2003/02/27 22:40:26  jalet
175# Correctly handles cases where the printer is off.
176#
177# Revision 1.8  2003/02/09 12:56:53  jalet
178# Internationalization begins...
179#
180# Revision 1.7  2003/02/07 10:23:48  jalet
181# Avoid a possible future name clash
182#
183# Revision 1.6  2003/02/06 22:54:33  jalet
184# warnpykota should be ok
185#
186# Revision 1.5  2003/02/05 22:45:25  jalet
187# Forgotten import
188#
189# Revision 1.4  2003/02/05 22:42:51  jalet
190# Typo
191#
192# Revision 1.3  2003/02/05 22:38:39  jalet
193# Typo
194#
195# Revision 1.2  2003/02/05 22:16:20  jalet
196# DEVICE_URI is undefined outside of CUPS, i.e. for normal command line tools
197#
198# Revision 1.1  2003/02/05 21:28:17  jalet
199# Initial import into CVS
200#
201#
202#
203
204import sys
205import os
206
207from pykota.tool import PyKotaFilterOrBackend, PyKotaToolError
208from pykota.config import PyKotaConfigError
209from pykota.storage import PyKotaStorageError
210from pykota.accounter import PyKotaAccounterError
211from pykota.requester import PyKotaRequesterError
212
213class PyKotaFilter(PyKotaFilterOrBackend) :       
214    """A class for the pykota filter."""
215    def acceptJob(self) :       
216        """Returns the exit code needed by the printing backend to accept the job and print it."""
217        if self.printingsystem == "CUPS" :
218            return 0
219        elif self.printingsystem == "LPRNG" :   
220            return 0
221        else :   
222            # UNKNOWN
223            return -1
224           
225    def removeJob(self) :           
226        """Returns the exit code needed by the printing backend to refuse the job and remove it."""
227        if self.printingsystem == "CUPS" :
228            return 1
229        elif self.printingsystem == "LPRNG" :   
230            return 3
231        else :   
232            # UNKNOWN
233            return -1
234       
235    def doWork(self, policy, printer, user, userpquota) :   
236        """Most of the work is done here."""
237        # Two different values possible for policy here :
238        # ALLOW means : Either printer, user or user print quota doesn't exist,
239        #               but the job should be allowed anyway.
240        # OK means : Both printer, user and user print quota exist, job should
241        #            be allowed if current user is allowed to print on this printer
242        if policy == "OK" :
243            self.logdebug("Does accounting for user %s on printer %s." % (user.Name, printer.Name))
244            action = self.accounter.doAccounting(userpquota)
245            if action == "DENY" :
246                # Just die.
247                self.logdebug("Printing is denied.")
248                return self.removeJob()
249           
250        # pass the job's data to the next filter
251        self.logdebug("Passing input data to next filter.")
252        mustclose = 0   
253        if self.inputfile is not None :   
254            if hasattr(self.inputfile, "read") :
255                infile = self.inputfile
256            else :   
257                infile = open(self.inputfile, "rb")
258            mustclose = 1
259        else :   
260            infile = sys.stdin
261        data = infile.read(256*1024)   
262        while data :
263            sys.stdout.write(data)
264            data = infile.read(256*1024)
265        if mustclose :   
266            infile.close()
267       
268        return self.acceptJob()
269       
270    def mainWork(self) :   
271        # If this is a CUPS filter, we should act and die like a CUPS filter when needed
272        if self.printingsystem == "CUPS" :
273            if len(sys.argv) not in (6, 7) :   
274                sys.stderr.write("ERROR: %s job-id user title copies options [file]\n" % sys.argv[0])
275                return self.removeJob()
276               
277        if self.accounter.isDelayed :       
278            # Here we need to update the last user's print quota       
279            # because hardware accounting is delayed by one print job
280            # in the pykota filter.
281            printer = self.storage.getPrinter(self.printername)       
282            if not printer.Exists :
283                self.logger.log_message(_("Printer %s not registered in the PyKota system") % self.printername, "info")
284            else :   
285                if not printer.LastJob.Exists :
286                    self.logger.log_message(_("Printer %s was never used") % self.printername, "info")
287                else :
288                    self.logdebug("Updating print quota for last user %s on printer %s" % (printer.LastJob.User.Name, printer.Name))
289                    lastuserpquota = self.storage.getUserPQuota(printer.LastJob.User, printer)
290                    self.accounter.counterbefore = printer.LastJob.PrinterPageCounter
291                    self.accounter.counterafter = self.accounter.getPrinterInternalPageCounter() or 0
292                    lastjobsize = self.accounter.getJobSize()
293                    self.logdebug("Last Job size : %i" % lastjobsize)
294                    printer.LastJob.setSize(lastjobsize)
295                    self.logdebug("Updating user %s's quota on printer %s" % (lastuserpquota.User.Name, printer.Name))
296                    lastuserpquota.increasePagesUsage(lastjobsize)
297                    self.warnUserPQuota(lastuserpquota)
298                   
299        # then deal with current print job as usual           
300        PyKotaFilterOrBackend.mainWork(self)       
301           
302if __name__ == "__main__" :   
303    retcode = -1
304    try :
305        # Initializes the current tool
306        kotafilter = PyKotaFilter()   
307        retcode = kotafilter.mainWork()
308    except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, PyKotaRequesterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg :
309        sys.stderr.write("ERROR : PyKota filter failed (%s)\n" % msg)
310        sys.stderr.flush()
311        try :
312            retcode = kotafilter.removeJob()
313        except :
314            retcode = -1
315
316    try :
317        kotafilter.storage.close()
318    except (TypeError, NameError, AttributeError) :   
319        pass
320       
321    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.