root / pykota / trunk / pykota / config.py @ 1227

Revision 1227, 19.5 kB (checked in by jalet, 20 years ago)

Added 'utolower' configuration option to convert all usernames to
lowercase when printing. All database accesses are still and will
remain case sensitive though.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# PyKota
2# -*- coding: ISO-8859-15 -*-
3#
4# PyKota : Print Quotas for CUPS and LPRng
5#
6# (c) 2003 Jerome Alet <alet@librelogiciel.com>
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20#
21# $Id$
22#
23# $Log$
24# Revision 1.41  2003/11/29 20:06:20  jalet
25# Added 'utolower' configuration option to convert all usernames to
26# lowercase when printing. All database accesses are still and will
27# remain case sensitive though.
28#
29# Revision 1.40  2003/11/18 23:43:12  jalet
30# Mailto can be any external command now, as usual.
31#
32# Revision 1.39  2003/10/08 21:41:38  jalet
33# External policies for printers works !
34# We can now auto-add users on first print, and do other useful things if needed.
35#
36# Revision 1.38  2003/10/07 22:06:05  jalet
37# Preliminary code to disable job history
38#
39# Revision 1.37  2003/10/07 09:07:28  jalet
40# Character encoding added to please latest version of Python
41#
42# Revision 1.36  2003/10/02 20:23:18  jalet
43# Storage caching mechanism added.
44#
45# Revision 1.35  2003/07/29 09:54:03  jalet
46# Added configurable LDAP mail attribute support
47#
48# Revision 1.34  2003/07/28 09:11:12  jalet
49# PyKota now tries to add its attributes intelligently in existing LDAP
50# directories.
51#
52# Revision 1.33  2003/07/16 21:53:07  jalet
53# Really big modifications wrt new configuration file's location and content.
54#
55# Revision 1.32  2003/07/08 19:43:51  jalet
56# Configurable warning messages.
57# Poor man's treshold value added.
58#
59# Revision 1.31  2003/07/07 11:49:24  jalet
60# Lots of small fixes with the help of PyChecker
61#
62# Revision 1.30  2003/06/25 14:10:01  jalet
63# Hey, it may work (edpykota --reset excepted) !
64#
65# Revision 1.29  2003/06/14 22:44:21  jalet
66# More work on LDAP storage backend.
67#
68# Revision 1.28  2003/06/10 16:37:54  jalet
69# Deletion of the second user which is not needed anymore.
70# Added a debug configuration field in /etc/pykota.conf
71# All queries can now be sent to the logger in debug mode, this will
72# greatly help improve performance when time for this will come.
73#
74# Revision 1.27  2003/05/27 23:00:21  jalet
75# Big rewrite of external accounting methods.
76# Should work well now.
77#
78# Revision 1.26  2003/04/30 19:53:58  jalet
79# 1.05
80#
81# Revision 1.25  2003/04/30 13:36:40  jalet
82# Stupid accounting method was added.
83#
84# Revision 1.24  2003/04/29 18:37:54  jalet
85# Pluggable accounting methods (actually doesn't support external scripts)
86#
87# Revision 1.23  2003/04/24 11:53:48  jalet
88# Default policy for unknown users/groups is to DENY printing instead
89# of the previous default to ALLOW printing. This is to solve an accuracy
90# problem. If you set the policy to ALLOW, jobs printed by in nexistant user
91# (from PyKota's POV) will be charged to the next user who prints on the
92# same printer.
93#
94# Revision 1.22  2003/04/23 22:13:57  jalet
95# Preliminary support for LPRng added BUT STILL UNTESTED.
96#
97# Revision 1.21  2003/03/29 13:45:27  jalet
98# GPL paragraphs were incorrectly (from memory) copied into the sources.
99# Two README files were added.
100# Upgrade script for PostgreSQL pre 1.01 schema was added.
101#
102# Revision 1.20  2003/03/29 13:08:28  jalet
103# Configuration is now expected to be found in /etc/pykota.conf instead of
104# in /etc/cups/pykota.conf
105# Installation script can move old config files to the new location if needed.
106# Better error handling if configuration file is absent.
107#
108# Revision 1.19  2003/03/16 09:56:52  jalet
109# Mailto option now accepts some additional values which all mean that
110# nobody will receive any email message.
111# Mailto option now works. Version 1.01 is now officially out.
112#
113# Revision 1.18  2003/03/16 08:00:50  jalet
114# Default hard coded options are now used if they are not set in the
115# configuration file.
116#
117# Revision 1.17  2003/03/15 23:01:28  jalet
118# New mailto option in configuration file added.
119# No time to test this tonight (although it should work).
120#
121# Revision 1.16  2003/02/17 23:01:56  jalet
122# Typos
123#
124# Revision 1.15  2003/02/17 22:55:01  jalet
125# More options can now be set per printer or globally :
126#
127#       admin
128#       adminmail
129#       gracedelay
130#       requester
131#
132# the printer option has priority when both are defined.
133#
134# Revision 1.14  2003/02/17 22:05:50  jalet
135# Storage backend now supports admin and user passwords (untested)
136#
137# Revision 1.13  2003/02/10 11:47:39  jalet
138# Moved some code down into the requesters
139#
140# Revision 1.12  2003/02/10 10:36:33  jalet
141# Small problem wrt external requester
142#
143# Revision 1.11  2003/02/10 08:50:45  jalet
144# External requester seems to be finally ok now
145#
146# Revision 1.10  2003/02/10 08:19:57  jalet
147# tell ConfigParser to return raw data, this allows our own strings
148# interpolations in the requester
149#
150# Revision 1.9  2003/02/10 00:44:38  jalet
151# Typos
152#
153# Revision 1.8  2003/02/10 00:42:17  jalet
154# External requester should be ok (untested)
155# New syntax for configuration file wrt requesters
156#
157# Revision 1.7  2003/02/09 13:05:43  jalet
158# Internationalization continues...
159#
160# Revision 1.6  2003/02/07 22:00:09  jalet
161# Bad cut&paste
162#
163# Revision 1.5  2003/02/06 23:58:05  jalet
164# repykota should be ok
165#
166# Revision 1.4  2003/02/06 09:19:02  jalet
167# More robust behavior (hopefully) when the user or printer is not managed
168# correctly by the Quota System : e.g. cupsFilter added in ppd file, but
169# printer and/or user not 'yet?' in storage.
170#
171# Revision 1.3  2003/02/05 23:26:22  jalet
172# Incorrect handling of grace delay
173#
174# Revision 1.2  2003/02/05 23:09:20  jalet
175# Name conflict
176#
177# Revision 1.1  2003/02/05 21:28:17  jalet
178# Initial import into CVS
179#
180#
181#
182
183import os
184import ConfigParser
185
186class PyKotaConfigError(Exception):
187    """An exception for PyKota config related stuff."""
188    def __init__(self, message = ""):
189        self.message = message
190        Exception.__init__(self, message)
191    def __repr__(self):
192        return self.message
193    __str__ = __repr__
194   
195class PyKotaConfig :
196    """A class to deal with PyKota's configuration."""
197    def __init__(self, directory) :
198        """Reads and checks the configuration file."""
199        self.filename = os.path.join(directory, "pykota.conf")
200        if not os.path.isfile(self.filename) :
201            raise PyKotaConfigError, _("Configuration file %s not found.") % self.filename
202        self.config = ConfigParser.ConfigParser()
203        self.config.read([self.filename])
204           
205    def isTrue(self, option) :       
206        """Returns 1 if option is set to true, else 0."""
207        if (option is not None) and (option.upper().strip() in ['Y', 'YES', '1', 'ON', 'T', 'TRUE']) :
208            return 1
209        else :   
210            return 0
211                       
212    def getPrinterNames(self) :   
213        """Returns the list of configured printers, i.e. all sections names minus 'global'."""
214        return [pname for pname in self.config.sections() if pname != "global"]
215       
216    def getGlobalOption(self, option, ignore=0) :   
217        """Returns an option from the global section, or raises a PyKotaConfigError if ignore is not set, else returns None."""
218        try :
219            return self.config.get("global", option, raw=1)
220        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
221            if ignore :
222                return
223            else :
224                raise PyKotaConfigError, _("Option %s not found in section global of %s") % (option, self.filename)
225               
226    def getPrinterOption(self, printername, option) :   
227        """Returns an option from the printer section, or the global section, or raises a PyKotaConfigError."""
228        globaloption = self.getGlobalOption(option, ignore=1)
229        try :
230            return self.config.get(printername, option, raw=1)
231        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
232            if globaloption is not None :
233                return globaloption
234            else :
235                raise PyKotaConfigError, _("Option %s not found in section %s of %s") % (option, printername, self.filename)
236       
237    def getStorageBackend(self) :   
238        """Returns the storage backend information as a Python mapping."""       
239        backendinfo = {}
240        for option in [ "storagebackend", "storageserver", \
241                        "storagename", "storageuser", \
242                      ] :
243            backendinfo[option] = self.getGlobalOption(option)
244        backendinfo["storageuserpw"] = self.getGlobalOption("storageuserpw", ignore=1)  # password is optional
245        backendinfo["storageadmin"] = None
246        backendinfo["storageadminpw"] = None
247        adminconf = ConfigParser.ConfigParser()
248        adminconf.read(["/etc/pykota/pykotadmin.conf"])
249        if adminconf.sections() : # were we able to read the file ?
250            try :
251                backendinfo["storageadmin"] = adminconf.get("global", "storageadmin", raw=1)
252            except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
253                raise PyKotaConfigError, _("Option %s not found in section global of %s") % ("storageadmin", "/etc/pykota/pykotadmin.conf")
254            try :
255                backendinfo["storageadminpw"] = adminconf.get("global", "storageadminpw", raw=1)
256            except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
257                pass # Password is optional
258        return backendinfo
259       
260    def getLDAPInfo(self) :   
261        """Returns some hints for the LDAP backend."""       
262        ldapinfo = {}
263        for option in [ "userbase", "userrdn", \
264                        "balancebase", "balancerdn", \
265                        "groupbase", "grouprdn", "groupmembers", \
266                        "printerbase", "printerrdn", \
267                        "userquotabase", "groupquotabase", \
268                        "jobbase", "lastjobbase", \
269                        "newuser", "newgroup", \
270                        "usermail", \
271                      ] :
272            ldapinfo[option] = self.getGlobalOption(option).strip()
273        for field in ["newuser", "newgroup"] :
274            if ldapinfo[field].lower().startswith('attach(') :
275                ldapinfo[field] = ldapinfo[field][7:-1]
276        return ldapinfo
277       
278    def getLoggingBackend(self) :   
279        """Returns the logging backend information."""
280        validloggers = [ "stderr", "system" ] 
281        try :
282            logger = self.getGlobalOption("logger").lower()
283        except PyKotaConfigError :   
284            logger = "system"
285        if logger not in validloggers :             
286            raise PyKotaConfigError, _("Option logger only supports values in %s") % str(validloggers)
287        return logger   
288       
289    def getAccounterBackend(self, printername) :   
290        """Returns the accounter backend to use for a given printer.
291       
292           if it is not set, it defaults to 'querying' which means ask printer
293           for its internal lifetime page counter.
294        """   
295        validaccounters = [ "querying", "stupid", "external" ]     
296        try :
297            fullaccounter = self.getPrinterOption(printername, "accounter").strip()
298        except PyKotaConfigError :   
299            fullaccounter = "querying"
300        if fullaccounter.lower().startswith("external") :   
301            try :
302                (accounter, args) = [x.strip() for x in fullaccounter.split('(', 1)]
303            except ValueError :   
304                raise PyKotaConfigError, _("Invalid external accounter %s for printer %s") % (fullaccounter, printername)
305            if args.endswith(')') :
306                args = args[:-1]
307            if not args :
308                raise PyKotaConfigError, _("Invalid external accounter %s for printer %s") % (fullaccounter, printername)
309            return (accounter.lower(), args)   
310        elif fullaccounter.lower() not in validaccounters :
311            raise PyKotaConfigError, _("Option accounter in section %s only supports values in %s") % (printername, str(validaccounters))
312        else :   
313            return (fullaccounter.lower(), None)
314       
315    def getRequesterBackend(self, printername) :   
316        """Returns the requester backend to use for a given printer, with its arguments."""
317        try :
318            fullrequester = self.getPrinterOption(printername, "requester")
319        except PyKotaConfigError :   
320            # No requester defined, maybe it is not needed if accounting method
321            # is not set to 'querying', but if we are called, then the accounting
322            # method really IS 'querying', and so there's a big problem.
323            raise PyKotaConfigError, _("Option requester for printer %s was not set") % printername
324        else :   
325            try :
326                (requester, args) = [x.strip() for x in fullrequester.split('(', 1)]
327            except ValueError :   
328                raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printername)
329            if args.endswith(')') :
330                args = args[:-1]
331            if not args :
332                raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printername)
333            validrequesters = [ "snmp", "external" ] # TODO : add more requesters
334            requester = requester.lower()
335            if requester not in validrequesters :
336                raise PyKotaConfigError, _("Option requester for printer %s only supports values in %s") % (printername, str(validrequesters))
337            return (requester, args)
338       
339    def getPrinterPolicy(self, printername) :   
340        """Returns the default policy for the current printer."""
341        validpolicies = [ "ALLOW", "DENY", "EXTERNAL" ]     
342        try :
343            fullpolicy = self.getPrinterOption(printername, "policy")
344        except PyKotaConfigError :   
345            return ("DENY", None)
346        else :   
347            try :
348                policy = [x.strip() for x in fullpolicy.split('(', 1)]
349            except ValueError :   
350                raise PyKotaConfigError, _("Invalid policy %s for printer %s") % (fullpolicy, printername)
351            if len(policy) == 1 :   
352                policy.append("")
353            (policy, args) = policy   
354            if args.endswith(')') :
355                args = args[:-1]
356            policy = policy.upper()   
357            if (policy == "EXTERNAL") and not args :
358                raise PyKotaConfigError, _("Invalid policy %s for printer %s") % (fullpolicy, printername)
359            if policy not in validpolicies :
360                raise PyKotaConfigError, _("Option policy in section %s only supports values in %s") % (printername, str(validpolicies))
361            return (policy, args)
362       
363    def getSMTPServer(self) :   
364        """Returns the SMTP server to use to send messages to users."""
365        try :
366            return self.getGlobalOption("smtpserver")
367        except PyKotaConfigError :   
368            return "localhost"
369       
370    def getAdminMail(self, printername) :   
371        """Returns the Email address of the Print Quota Administrator."""
372        try :
373            return self.getPrinterOption(printername, "adminmail")
374        except PyKotaConfigError :   
375            return "root@localhost"
376       
377    def getAdmin(self, printername) :   
378        """Returns the full name of the Print Quota Administrator."""
379        try :
380            return self.getPrinterOption(printername, "admin")
381        except PyKotaConfigError :   
382            return "root"
383       
384    def getMailTo(self, printername) :   
385        """Returns the recipient of email messages."""
386        validmailtos = [ "EXTERNAL", "NOBODY", "NONE", "NOONE", "BITBUCKET", "DEVNULL", "BOTH", "USER", "ADMIN" ]
387        try :
388            fullmailto = self.getPrinterOption(printername, "mailto")
389        except PyKotaConfigError :   
390            return ("BOTH", None)
391        else :   
392            try :
393                mailto = [x.strip() for x in fullmailto.split('(', 1)]
394            except ValueError :   
395                raise PyKotaConfigError, _("Invalid option mailto %s for printer %s") % (fullmailto, printername)
396            if len(mailto) == 1 :   
397                mailto.append("")
398            (mailto, args) = mailto   
399            if args.endswith(')') :
400                args = args[:-1]
401            mailto = mailto.upper()   
402            if (mailto == "EXTERNAL") and not args :
403                raise PyKotaConfigError, _("Invalid option mailto %s for printer %s") % (fullmailto, printername)
404            if mailto not in validmailtos :
405                raise PyKotaConfigError, _("Option mailto in section %s only supports values in %s") % (printername, str(validmailtos))
406            return (mailto, args)
407       
408    def getGraceDelay(self, printername) :   
409        """Returns the grace delay in days."""
410        try :
411            gd = self.getPrinterOption(printername, "gracedelay")
412        except PyKotaConfigError :   
413            gd = 7
414        try :
415            return int(gd)
416        except (TypeError, ValueError) :   
417            raise PyKotaConfigError, _("Invalid grace delay %s") % gd
418           
419    def getPoorMan(self) :   
420        """Returns the poor man's treshold."""
421        try :
422            pm = self.getGlobalOption("poorman")
423        except PyKotaConfigError :   
424            pm = 1.0
425        try :
426            return float(pm)
427        except (TypeError, ValueError) :   
428            raise PyKotaConfigError, _("Invalid poor man's treshold %s") % pm
429           
430    def getPoorWarn(self) :   
431        """Returns the poor man's warning message."""
432        try :
433            return self.getGlobalOption("poorwarn")
434        except PyKotaConfigError :   
435            return _("Your Print Quota account balance is Low.\nSoon you'll not be allowed to print anymore.\nPlease contact the Print Quota Administrator to solve the problem.")
436           
437    def getHardWarn(self, printername) :   
438        """Returns the hard limit error message."""
439        try :
440            return self.getPrinterOption(printername, "hardwarn")
441        except PyKotaConfigError :   
442            return _("You are not allowed to print anymore because\nyour Print Quota is exceeded on printer %s.") % printername
443           
444    def getSoftWarn(self, printername) :   
445        """Returns the soft limit error message."""
446        try :
447            return self.getPrinterOption(printername, "softwarn")
448        except PyKotaConfigError :   
449            return _("You will soon be forbidden to print anymore because\nyour Print Quota is almost reached on printer %s.") % printername
450           
451    def getDebug(self) :         
452        """Returns 1 if debugging is activated, else 0."""
453        return self.isTrue(self.getGlobalOption("debug", ignore=1))
454           
455    def getCaching(self) :         
456        """Returns 1 if database caching is enabled, else 0."""
457        return self.isTrue(self.getGlobalOption("storagecaching", ignore=1))
458           
459    def getDisableHistory(self) :         
460        """Returns 1 if we want to disable history, else 0."""
461        return self.isTrue(self.getGlobalOption("disablehistory", ignore=1))
462           
463    def getUserNameToLower(self) :         
464        """Returns 1 if we want to convert usernames to lowercase when printing, else 0."""
465        return self.isTrue(self.getGlobalOption("utolower", ignore=1))
Note: See TracBrowser for help on using the browser.