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

Revision 2370, 21.3 kB (checked in by jerome, 19 years ago)

Fix for unquoted string which prevented the LDAP backend to work since
the new billingcodebase directive was expected.
Severity: Major for LDAP users.

  • 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-2004 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20#
21# $Id$
22#
23#
24
25import os
26import ConfigParser
27
28class PyKotaConfigError(Exception):
29    """An exception for PyKota config related stuff."""
30    def __init__(self, message = ""):
31        self.message = message
32        Exception.__init__(self, message)
33    def __repr__(self):
34        return self.message
35    __str__ = __repr__
36   
37class PyKotaConfig :
38    """A class to deal with PyKota's configuration."""
39    def __init__(self, directory) :
40        """Reads and checks the configuration file."""
41        self.isAdmin = 0
42        self.directory = directory
43        self.filename = os.path.join(directory, "pykota.conf")
44        if not os.path.isfile(self.filename) :
45            raise PyKotaConfigError, _("Configuration file %s not found.") % self.filename
46        self.config = ConfigParser.ConfigParser()
47        self.config.read([self.filename])
48           
49    def isTrue(self, option) :       
50        """Returns 1 if option is set to true, else 0."""
51        if (option is not None) and (option.strip().upper() in ['Y', 'YES', '1', 'ON', 'T', 'TRUE']) :
52            return 1
53        else :   
54            return 0
55                       
56    def isFalse(self, option) :       
57        """Returns 1 if option is set to false, else 0."""
58        if (option is not None) and (option.strip().upper() in ['N', 'NO', '0', 'OFF', 'F', 'FALSE']) :
59            return 1
60        else :   
61            return 0
62                       
63    def getPrinterNames(self) :   
64        """Returns the list of configured printers, i.e. all sections names minus 'global'."""
65        return [pname for pname in self.config.sections() if pname != "global"]
66       
67    def getGlobalOption(self, option, ignore=0) :   
68        """Returns an option from the global section, or raises a PyKotaConfigError if ignore is not set, else returns None."""
69        try :
70            return self.config.get("global", option, raw=1)
71        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
72            if ignore :
73                return
74            else :
75                raise PyKotaConfigError, _("Option %s not found in section global of %s") % (option, self.filename)
76               
77    def getPrinterOption(self, printername, option) :   
78        """Returns an option from the printer section, or the global section, or raises a PyKotaConfigError."""
79        globaloption = self.getGlobalOption(option, ignore=1)
80        try :
81            return self.config.get(printername, option, raw=1)
82        except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
83            if globaloption is not None :
84                return globaloption
85            else :
86                raise PyKotaConfigError, _("Option %s not found in section %s of %s") % (option, printername, self.filename)
87       
88    def getStorageBackend(self) :   
89        """Returns the storage backend information as a Python mapping."""       
90        backendinfo = {}
91        for option in [ "storagebackend", "storageserver", \
92                        "storagename", "storageuser", \
93                      ] :
94            backendinfo[option] = self.getGlobalOption(option)
95        backendinfo["storageuserpw"] = self.getGlobalOption("storageuserpw", ignore=1)  # password is optional
96        backendinfo["storageadmin"] = None
97        backendinfo["storageadminpw"] = None
98        adminconf = ConfigParser.ConfigParser()
99        filename = os.path.join(self.directory, "pykotadmin.conf")
100        adminconf.read([filename])
101        if adminconf.sections() : # were we able to read the file ?
102            try :
103                backendinfo["storageadmin"] = adminconf.get("global", "storageadmin", raw=1)
104            except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
105                raise PyKotaConfigError, _("Option %s not found in section global of %s") % ("storageadmin", filename)
106            else :   
107                self.isAdmin = 1 # We are a PyKota administrator
108            try :
109                backendinfo["storageadminpw"] = adminconf.get("global", "storageadminpw", raw=1)
110            except (ConfigParser.NoSectionError, ConfigParser.NoOptionError) :   
111                pass # Password is optional
112        return backendinfo
113       
114    def getLDAPInfo(self) :   
115        """Returns some hints for the LDAP backend."""       
116        ldapinfo = {}
117        for option in [ "userbase", "userrdn", \
118                        "balancebase", "balancerdn", \
119                        "groupbase", "grouprdn", "groupmembers", \
120                        "printerbase", "printerrdn", \
121                        "userquotabase", "groupquotabase", \
122                        "jobbase", "lastjobbase", "billingcodebase", \
123                        "newuser", "newgroup", \
124                        "usermail", \
125                      ] :
126            ldapinfo[option] = self.getGlobalOption(option).strip()
127        for field in ["newuser", "newgroup"] :
128            if ldapinfo[field].lower().startswith('attach(') :
129                ldapinfo[field] = ldapinfo[field][7:-1]
130               
131        # should we use TLS, by default (if unset) value is NO       
132        ldapinfo["ldaptls"] = self.isTrue(self.getGlobalOption("ldaptls", ignore=1))
133        ldapinfo["cacert"] = self.getGlobalOption("cacert", ignore=1)
134        if ldapinfo["cacert"] :
135            ldapinfo["cacert"] = ldapinfo["cacert"].strip()
136        if ldapinfo["ldaptls"] :   
137            if not os.access(ldapinfo["cacert"] or "", os.R_OK) :
138                raise PyKotaConfigError, _("Option ldaptls is set, but certificate %s is not readable.") % str(ldapinfo["cacert"])
139        return ldapinfo
140       
141    def getLoggingBackend(self) :   
142        """Returns the logging backend information."""
143        validloggers = [ "stderr", "system" ] 
144        try :
145            logger = self.getGlobalOption("logger").lower()
146        except PyKotaConfigError :   
147            logger = "system"
148        if logger not in validloggers :             
149            raise PyKotaConfigError, _("Option logger only supports values in %s") % str(validloggers)
150        return logger   
151       
152    def getLogoURL(self) :
153        """Returns the URL to use for the logo in the CGI scripts."""
154        url = self.getGlobalOption("logourl", ignore=1) or \
155                   "http://www.librelogiciel.com/software/PyKota/pykota.png"
156        return url.strip()           
157       
158    def getLogoLink(self) :
159        """Returns the URL to go to when the user clicks on the logo in the CGI scripts."""
160        url = self.getGlobalOption("logolink", ignore=1) or \
161                   "http://www.librelogiciel.com/software/"
162        return url.strip()           
163   
164    def getAccounterBackend(self, printername) :   
165        """Returns the accounter backend to use for a given printer.
166       
167           if it is not set, it defaults to 'hardware' which means ask printer
168           for its internal lifetime page counter.
169        """   
170        validaccounters = [ "hardware", "software" ]     
171        fullaccounter = self.getPrinterOption(printername, "accounter").strip()
172        flower = fullaccounter.lower()
173        if flower.startswith("software") or flower.startswith("hardware") :   
174            try :
175                (accounter, args) = [x.strip() for x in fullaccounter.split('(', 1)]
176            except ValueError :   
177                raise PyKotaConfigError, _("Invalid accounter %s for printer %s") % (fullaccounter, printername)
178            if args.endswith(')') :
179                args = args[:-1].strip()
180            if (accounter == "hardware") and not args :
181                raise PyKotaConfigError, _("Invalid accounter %s for printer %s") % (fullaccounter, printername)
182            return (accounter.lower(), args)   
183        else :
184            raise PyKotaConfigError, _("Option accounter in section %s only supports values in %s") % (printername, str(validaccounters))
185       
186    def getPreHook(self, printername) :   
187        """Returns the prehook command line to launch, or None if unset."""
188        try :
189            return self.getPrinterOption(printername, "prehook").strip()
190        except PyKotaConfigError :   
191            return      # No command to launch in the pre-hook
192           
193    def getPostHook(self, printername) :   
194        """Returns the posthook command line to launch, or None if unset."""
195        try :
196            return self.getPrinterOption(printername, "posthook").strip()
197        except PyKotaConfigError :   
198            return      # No command to launch in the post-hook
199           
200    def getStripTitle(self, printername) :   
201        """Returns the striptitle directive's content, or None if unset."""
202        try :
203            return self.getPrinterOption(printername, "striptitle").strip()
204        except PyKotaConfigError :   
205            return      # No prefix to strip off
206           
207    def getPrinterEnforcement(self, printername) :   
208        """Returns if quota enforcement should be strict or laxist for the current printer."""
209        validenforcements = [ "STRICT", "LAXIST" ]     
210        try :
211            enforcement = self.getPrinterOption(printername, "enforcement")
212        except PyKotaConfigError :   
213            return "LAXIST"
214        else :   
215            enforcement = enforcement.upper()
216            if enforcement not in validenforcements :
217                raise PyKotaConfigError, _("Option enforcement in section %s only supports values in %s") % (printername, str(validenforcements))
218            return enforcement   
219           
220    def getPrinterOnAccounterError(self, printername) :   
221        """Returns what must be done whenever the accounter fails."""
222        validactions = [ "CONTINUE", "STOP" ]     
223        try :
224            action = self.getPrinterOption(printername, "onaccountererror")
225        except PyKotaConfigError :   
226            return "STOP"
227        else :   
228            action = action.upper()
229            if action not in validactions :
230                raise PyKotaConfigError, _("Option onaccountererror in section %s only supports values in %s") % (printername, str(validactions))
231            return action 
232           
233    def getPrinterPolicy(self, printername) :   
234        """Returns the default policy for the current printer."""
235        validpolicies = [ "ALLOW", "DENY", "EXTERNAL" ]     
236        try :
237            fullpolicy = self.getPrinterOption(printername, "policy")
238        except PyKotaConfigError :   
239            return ("DENY", None)
240        else :   
241            try :
242                policy = [x.strip() for x in fullpolicy.split('(', 1)]
243            except ValueError :   
244                raise PyKotaConfigError, _("Invalid policy %s for printer %s") % (fullpolicy, printername)
245            if len(policy) == 1 :   
246                policy.append("")
247            (policy, args) = policy   
248            if args.endswith(')') :
249                args = args[:-1]
250            policy = policy.upper()   
251            if (policy == "EXTERNAL") and not args :
252                raise PyKotaConfigError, _("Invalid policy %s for printer %s") % (fullpolicy, printername)
253            if policy not in validpolicies :
254                raise PyKotaConfigError, _("Option policy in section %s only supports values in %s") % (printername, str(validpolicies))
255            return (policy, args)
256       
257    def getCrashRecipient(self) :   
258        """Returns the email address of the software crash messages recipient."""
259        try :
260            return self.getGlobalOption("crashrecipient")
261        except :   
262            return
263           
264    def getSMTPServer(self) :   
265        """Returns the SMTP server to use to send messages to users."""
266        try :
267            return self.getGlobalOption("smtpserver")
268        except PyKotaConfigError :   
269            return "localhost"
270       
271    def getMailDomain(self) :   
272        """Returns the mail domain to use to send messages to users."""
273        try :
274            return self.getGlobalOption("maildomain")
275        except PyKotaConfigError :   
276            return 
277       
278    def getAdminMail(self, printername) :   
279        """Returns the Email address of the Print Quota Administrator."""
280        try :
281            return self.getPrinterOption(printername, "adminmail")
282        except PyKotaConfigError :   
283            return "root@localhost"
284       
285    def getAdmin(self, printername) :   
286        """Returns the full name of the Print Quota Administrator."""
287        try :
288            return self.getPrinterOption(printername, "admin")
289        except PyKotaConfigError :   
290            return "root"
291       
292    def getMailTo(self, printername) :   
293        """Returns the recipient of email messages."""
294        validmailtos = [ "EXTERNAL", "NOBODY", "NONE", "NOONE", "BITBUCKET", "DEVNULL", "BOTH", "USER", "ADMIN" ]
295        try :
296            fullmailto = self.getPrinterOption(printername, "mailto")
297        except PyKotaConfigError :   
298            return ("BOTH", None)
299        else :   
300            try :
301                mailto = [x.strip() for x in fullmailto.split('(', 1)]
302            except ValueError :   
303                raise PyKotaConfigError, _("Invalid option mailto %s for printer %s") % (fullmailto, printername)
304            if len(mailto) == 1 :   
305                mailto.append("")
306            (mailto, args) = mailto   
307            if args.endswith(')') :
308                args = args[:-1]
309            mailto = mailto.upper()   
310            if (mailto == "EXTERNAL") and not args :
311                raise PyKotaConfigError, _("Invalid option mailto %s for printer %s") % (fullmailto, printername)
312            if mailto not in validmailtos :
313                raise PyKotaConfigError, _("Option mailto in section %s only supports values in %s") % (printername, str(validmailtos))
314            return (mailto, args)
315       
316    def getMaxDenyBanners(self, printername) :   
317        """Returns the maximum number of deny banners to be printed for a particular user on a particular printer."""
318        try :
319            maxdb = self.getPrinterOption(printername, "maxdenybanners")
320        except PyKotaConfigError :   
321            return 0 # default value is to forbid printing a deny banner.
322        try :
323            value = int(maxdb.strip())
324            if value < 0 :
325                raise ValueError
326        except (TypeError, ValueError) :   
327            raise PyKotaConfigError, _("Invalid maximal deny banners counter %s") % maxdb
328        else :   
329            return value
330           
331    def getGraceDelay(self, printername) :   
332        """Returns the grace delay in days."""
333        try :
334            gd = self.getPrinterOption(printername, "gracedelay")
335        except PyKotaConfigError :   
336            gd = 7      # default value of 7 days
337        try :
338            return int(gd)
339        except (TypeError, ValueError) :   
340            raise PyKotaConfigError, _("Invalid grace delay %s") % gd
341           
342    def getPoorMan(self) :   
343        """Returns the poor man's threshold."""
344        try :
345            pm = self.getGlobalOption("poorman")
346        except PyKotaConfigError :   
347            pm = 1.0    # default value of 1 unit
348        try :
349            return float(pm)
350        except (TypeError, ValueError) :   
351            raise PyKotaConfigError, _("Invalid poor man's threshold %s") % pm
352           
353    def getPoorWarn(self) :   
354        """Returns the poor man's warning message."""
355        try :
356            return self.getGlobalOption("poorwarn")
357        except PyKotaConfigError :   
358            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.")
359           
360    def getHardWarn(self, printername) :   
361        """Returns the hard limit error message."""
362        try :
363            return self.getPrinterOption(printername, "hardwarn")
364        except PyKotaConfigError :   
365            return _("You are not allowed to print anymore because\nyour Print Quota is exceeded on printer %s.") % printername
366           
367    def getSoftWarn(self, printername) :   
368        """Returns the soft limit error message."""
369        try :
370            return self.getPrinterOption(printername, "softwarn")
371        except PyKotaConfigError :   
372            return _("You will soon be forbidden to print anymore because\nyour Print Quota is almost reached on printer %s.") % printername
373           
374    def getPrivacy(self) :       
375        """Returns 1 if privacy is activated, else 0."""
376        return self.isTrue(self.getGlobalOption("privacy", ignore=1))
377       
378    def getDebug(self) :         
379        """Returns 1 if debugging is activated, else 0."""
380        return self.isTrue(self.getGlobalOption("debug", ignore=1))
381           
382    def getCaching(self) :         
383        """Returns 1 if database caching is enabled, else 0."""
384        return self.isTrue(self.getGlobalOption("storagecaching", ignore=1))
385           
386    def getLDAPCache(self) :         
387        """Returns 1 if low-level LDAP caching is enabled, else 0."""
388        return self.isTrue(self.getGlobalOption("ldapcache", ignore=1))
389           
390    def getDisableHistory(self) :         
391        """Returns 1 if we want to disable history, else 0."""
392        return self.isTrue(self.getGlobalOption("disablehistory", ignore=1))
393           
394    def getUserNameToLower(self) :         
395        """Returns 1 if we want to convert usernames to lowercase when printing, else 0."""
396        return self.isTrue(self.getGlobalOption("utolower", ignore=1))
397       
398    def getRejectUnknown(self) :         
399        """Returns 1 if we want to reject the creation of unknown users or groups, else 0."""
400        return self.isTrue(self.getGlobalOption("reject_unknown", ignore=1))
401       
402    def getDenyDuplicates(self, printername) :         
403        """Returns 1 or a command if we want to deny duplicate jobs, else 0."""
404        try : 
405            denyduplicates = self.getPrinterOption(printername, "denyduplicates")
406        except PyKotaConfigError :   
407            return 0
408        else :   
409            if self.isTrue(denyduplicates) :
410                return 1
411            elif self.isFalse(denyduplicates) :
412                return 0
413            else :   
414                # it's a command to run.
415                return denyduplicates
416       
417    def getWinbindSeparator(self) :         
418        """Returns the winbind separator's value if it is set, else None."""
419        return self.getGlobalOption("winbind_separator", ignore=1)
420
421    def getAccountBanner(self, printername) :
422        """Returns which banner(s) to account for: NONE, BOTH, STARTING, ENDING."""
423        validvalues = [ "NONE", "BOTH", "STARTING", "ENDING" ]     
424        try :
425            value = self.getPrinterOption(printername, "accountbanner")
426        except PyKotaConfigError :   
427            return "BOTH"       # Default value of BOTH
428        else :   
429            value = value.strip().upper()
430            if value not in validvalues :
431                raise PyKotaConfigError, _("Option accountbanner in section %s only supports values in %s") % (printername, str(validvalues))
432            return value 
433
434    def getStartingBanner(self, printername) :
435        """Returns the startingbanner value if set, else None."""
436        try :
437            return self.getPrinterOption(printername, "startingbanner").strip()
438        except PyKotaConfigError :
439            return None
440
441    def getEndingBanner(self, printername) :
442        """Returns the endingbanner value if set, else None."""
443        try :
444            return self.getPrinterOption(printername, "endingbanner").strip()
445        except PyKotaConfigError :
446            return None
447           
448    def getTrustJobSize(self, printername) :
449        """Returns the normalized value of the trustjobsize's directive."""
450        try :
451            value = self.getPrinterOption(printername, "trustjobsize").strip().upper()
452        except PyKotaConfigError :
453            return (None, "YES")
454        else :   
455            if value == "YES" :
456                return (None, "YES")
457            try :   
458                (limit, replacement) = [p.strip() for p in value.split(">")[1].split(":")]
459                limit = int(limit)
460                try :
461                    replacement = int(replacement) 
462                except ValueError :   
463                    if replacement != "PRECOMPUTED" :
464                        raise
465                if limit < 0 :
466                    raise ValueError
467                if (replacement != "PRECOMPUTED") and (replacement < 0) :
468                    raise ValueError
469            except (IndexError, ValueError, TypeError) :
470                raise PyKotaConfigError, _("Option trustjobsize for printer %s is incorrect") % printername
471            return (limit, replacement)   
Note: See TracBrowser for help on using the browser.