root / pykota / trunk / pykota / tool.py @ 2860

Revision 2860, 34.0 kB (checked in by jerome, 18 years ago)

Removed unnecessary warning message.

  • 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, 2005, 2006 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 sys
26import os
27import pwd
28import fnmatch
29import getopt
30import smtplib
31import gettext
32import locale
33import socket
34import time
35from email.MIMEText import MIMEText
36from email.Header import Header
37
38from mx import DateTime
39
40from pykota import config, storage, logger
41from pykota.version import __version__, __author__, __years__, __gplblurb__
42
43def N_(message) :
44    """Fake translation marker for translatable strings extraction."""
45    return message
46
47class PyKotaToolError(Exception):
48    """An exception for PyKota related stuff."""
49    def __init__(self, message = ""):
50        self.message = message
51        Exception.__init__(self, message)
52    def __repr__(self):
53        return self.message
54    __str__ = __repr__
55   
56class PyKotaCommandLineError(PyKotaToolError) :   
57    """An exception for Pykota command line tools."""
58    pass
59   
60def crashed(message="Bug in PyKota") :   
61    """Minimal crash method."""
62    import traceback
63    lines = []
64    for line in traceback.format_exception(*sys.exc_info()) :
65        lines.extend([l for l in line.split("\n") if l])
66    msg = "ERROR: ".join(["%s\n" % l for l in (["ERROR: PyKota v%s" % __version__, message] + lines)])
67    sys.stderr.write(msg)
68    sys.stderr.flush()
69    return msg
70
71class Percent :
72    """A class to display progress."""
73    def __init__(self, app, size=None) :
74        """Initializes the engine."""
75        self.app = app
76        self.size = None
77        if size :
78            self.setSize(size)
79        self.previous = None
80        self.before = time.time()
81       
82    def setSize(self, size) :     
83        """Sets the total size."""
84        self.number = 0
85        self.size = size
86        if size :
87            self.factor = 100.0 / float(size)
88       
89    def display(self, msg) :   
90        """Displays the value."""
91        self.app.display(msg)
92       
93    def oneMore(self) :   
94        """Increments internal counter."""
95        if self.size :
96            self.number += 1
97            percent = "%.02f" % (float(self.number) * self.factor)
98            if percent != self.previous : # optimize for large number of items
99                self.display("\r%s%%" % percent)
100                self.previous = percent
101           
102    def done(self) :         
103        """Displays the 'done' message."""
104        after = time.time()
105        if self.size :
106            speed = self.size / (after - self.before)
107            self.display("\r100.00%%\r        \r%s. %s : %.2f %s.\n" \
108                     % (_("Done"), _("Average speed"), speed, _("entries per second")))
109        else :             
110            self.display("\r100.00%%\r        \r%s.\n" % _("Done"))
111       
112class Tool :
113    """Base class for tools with no database access."""
114    def __init__(self, lang="", charset=None, doc="PyKota v%(__version__)s (c) %(__years__)s %(__author__)s") :
115        """Initializes the command line tool."""
116        # did we drop priviledges ?
117        self.privdropped = 0
118       
119        # locale stuff
120        self.defaultToCLocale = 0
121        try :
122            locale.setlocale(locale.LC_ALL, lang)
123        except (locale.Error, IOError) :
124            # locale.setlocale(locale.LC_ALL, "C")
125            self.defaultToCLocale = 1
126        try :
127            gettext.install("pykota")
128        except :
129            gettext.NullTranslations().install()
130           
131        # We can force the charset.   
132        # The CHARSET environment variable is set by CUPS when printing.
133        # Else we use the current locale's one.
134        # If nothing is set, we use ISO-8859-15 widely used in western Europe.
135        localecharset = None
136        try :
137            try :
138                localecharset = locale.nl_langinfo(locale.CODESET)
139            except AttributeError :   
140                try :
141                    localecharset = locale.getpreferredencoding()
142                except AttributeError :   
143                    try :
144                        localecharset = locale.getlocale()[1]
145                        localecharset = localecharset or locale.getdefaultlocale()[1]
146                    except ValueError :   
147                        pass        # Unknown locale, strange...
148        except locale.Error :           
149            pass
150        self.charset = charset or os.environ.get("CHARSET") or localecharset or "ISO-8859-15"
151   
152        # pykota specific stuff
153        self.documentation = doc
154       
155    def deferredInit(self) :       
156        """Deferred initialization."""
157        # try to find the configuration files in user's 'pykota' home directory.
158        try :
159            self.pykotauser = pwd.getpwnam("pykota")
160        except KeyError :   
161            self.pykotauser = None
162            confdir = "/etc/pykota"
163            missingUser = 1
164        else :   
165            confdir = self.pykotauser[5]
166            missingUser = 0
167           
168        self.config = config.PyKotaConfig(confdir)
169        self.debug = self.config.getDebug()
170        self.smtpserver = self.config.getSMTPServer()
171        self.maildomain = self.config.getMailDomain()
172        self.logger = logger.openLogger(self.config.getLoggingBackend())
173           
174        # now drop priviledge if possible
175        self.dropPriv()   
176       
177        # We NEED this here, even when not in an accounting filter/backend   
178        self.softwareJobSize = 0
179        self.softwareJobPrice = 0.0
180       
181        if self.defaultToCLocale :
182            self.printInfo("Incorrect locale settings. PyKota falls back to the default locale.", "warn")
183        if missingUser :     
184            self.printInfo("The 'pykota' system account is missing. Configuration files were searched in /etc/pykota instead.", "warn")
185       
186        self.logdebug("Charset in use : %s" % self.charset)
187        arguments = " ".join(['"%s"' % arg for arg in sys.argv])
188        self.logdebug("Command line arguments : %s" % arguments)
189       
190    def dropPriv(self) :   
191        """Drops priviledges."""
192        uid = os.geteuid()
193        if uid :
194            try :
195                username = pwd.getpwuid(uid)[0]
196            except (KeyError, IndexError), msg :   
197                self.printInfo(_("Strange problem with uid(%s) : %s") % (uid, msg), "warn")
198            else :
199                self.logdebug(_("Running as user '%s'.") % username)
200        else :
201            if self.pykotauser is None :
202                self.logdebug(_("No user named 'pykota'. Not dropping priviledges."))
203            else :   
204                try :
205                    os.setegid(self.pykotauser[3])
206                    os.seteuid(self.pykotauser[2])
207                except OSError, msg :   
208                    self.printInfo(_("Impossible to drop priviledges : %s") % msg, "warn")
209                else :   
210                    self.logdebug(_("Priviledges dropped. Now running as user 'pykota'."))
211                    self.privdropped = 1
212           
213    def regainPriv(self) :   
214        """Drops priviledges."""
215        if self.privdropped :
216            try :
217                os.seteuid(0)
218                os.setegid(0)
219            except OSError, msg :   
220                self.printInfo(_("Impossible to regain priviledges : %s") % msg, "warn")
221            else :   
222                self.logdebug(_("Regained priviledges. Now running as root."))
223                self.privdropped = 0
224       
225    def getCharset(self) :   
226        """Returns the charset in use."""
227        return self.charset
228       
229    def UTF8ToUserCharset(self, text) :
230        """Converts from UTF-8 to user's charset."""
231        if text is not None :
232            try :
233                return unicode(text, "UTF-8").encode(self.charset) 
234            except (UnicodeError, TypeError) :   
235                try :
236                    # Incorrect locale settings ?
237                    return unicode(text, "UTF-8").encode("ISO-8859-15") 
238                except (UnicodeError, TypeError) :   
239                    try :
240                        return text.encode(self.charset) 
241                    except (UnicodeError, TypeError, AttributeError) :
242                        pass
243        return text
244       
245    def userCharsetToUTF8(self, text) :
246        """Converts from user's charset to UTF-8."""
247        if text is not None :
248            try :
249                return unicode(text, self.charset).encode("UTF-8") 
250            except (UnicodeError, TypeError) :   
251                try :
252                    # Incorrect locale settings ?
253                    return unicode(text, "ISO-8859-15").encode("UTF-8") 
254                except (UnicodeError, TypeError) :   
255                    try :
256                        return text.encode("UTF-8") 
257                    except (UnicodeError, TypeError, AttributeError) :
258                        pass
259        return text
260       
261    def display(self, message) :
262        """Display a message but only if stdout is a tty."""
263        if sys.stdout.isatty() :
264            sys.stdout.write(message)
265            sys.stdout.flush()
266           
267    def logdebug(self, message) :   
268        """Logs something to debug output if debug is enabled."""
269        if self.debug :
270            self.logger.log_message(message, "debug")
271           
272    def printInfo(self, message, level="info") :       
273        """Sends a message to standard error."""
274        sys.stderr.write("%s: %s\n" % (level.upper(), message))
275        sys.stderr.flush()
276       
277    def matchString(self, s, patterns) :
278        """Returns True if the string s matches one of the patterns, else False."""
279        if not patterns :
280            return True # No pattern, always matches.
281        else :   
282            for pattern in patterns :
283                if fnmatch.fnmatchcase(s, pattern) :
284                    return True
285            return False
286       
287    def sanitizeNames(self, options, names) :
288        """Ensures that an user can only see the datas he is allowed to see, by modifying the list of names."""
289        if not self.config.isAdmin :
290            username = pwd.getpwuid(os.geteuid())[0]
291            if not options["list"] :
292                raise PyKotaCommandLineError, "%s : %s" % (username, _("You're not allowed to use this command."))
293            else :
294                if options["groups"] :
295                    user = self.storage.getUser(username)
296                    if user.Exists :
297                        return [ g.Name for g in self.storage.getUserGroups(user) ]
298                return [ username ]
299        elif not names :       
300            return ["*"]
301        else :   
302            return names
303       
304    def display_version_and_quit(self) :
305        """Displays version number, then exists successfully."""
306        try :
307            self.clean()
308        except AttributeError :   
309            pass
310        print __version__
311        sys.exit(0)
312   
313    def display_usage_and_quit(self) :
314        """Displays command line usage, then exists successfully."""
315        try :
316            self.clean()
317        except AttributeError :   
318            pass
319        print _(self.documentation) % globals()
320        print __gplblurb__
321        print
322        print _("Please report bugs to :"), __author__
323        sys.exit(0)
324       
325    def crashed(self, message="Bug in PyKota") :   
326        """Outputs a crash message, and optionally sends it to software author."""
327        msg = crashed(message)
328        fullmessage = "========== Traceback :\n\n%s\n\n========== sys.argv :\n\n%s\n\n========== Environment :\n\n%s\n" % \
329                        (msg, \
330                         "\n".join(["    %s" % repr(a) for a in sys.argv]), \
331                         "\n".join(["    %s=%s" % (k, v) for (k, v) in os.environ.items()]))
332        try :
333            crashrecipient = self.config.getCrashRecipient()
334            if crashrecipient :
335                admin = self.config.getAdminMail("global") # Nice trick, isn't it ?
336                server = smtplib.SMTP(self.smtpserver)
337                msg = MIMEText(fullmessage, _charset=self.charset)
338                msg["Subject"] = str(Header("PyKota v%s crash traceback !" \
339                                        % __version__, charset=self.charset))
340                msg["From"] = admin
341                msg["To"] = crashrecipient
342                msg["Cc"] = admin
343                server.sendmail(admin, [admin, crashrecipient], msg.as_string())
344                server.quit()
345        except :
346            pass
347        return fullmessage   
348       
349    def parseCommandline(self, argv, short, long, allownothing=0) :
350        """Parses the command line, controlling options."""
351        # split options in two lists: those which need an argument, those which don't need any
352        short = "%sA:" % short
353        long.append("arguments=")
354        withoutarg = []
355        witharg = []
356        lgs = len(short)
357        i = 0
358        while i < lgs :
359            ii = i + 1
360            if (ii < lgs) and (short[ii] == ':') :
361                # needs an argument
362                witharg.append(short[i])
363                ii = ii + 1 # skip the ':'
364            else :
365                # doesn't need an argument
366                withoutarg.append(short[i])
367            i = ii
368               
369        for option in long :
370            if option[-1] == '=' :
371                # needs an argument
372                witharg.append(option[:-1])
373            else :
374                # doesn't need an argument
375                withoutarg.append(option)
376       
377        # then we parse the command line
378        done = 0
379        while not done :
380            # we begin with all possible options unset
381            parsed = {}
382            for option in withoutarg + witharg :
383                parsed[option] = None
384            args = []       # to not break if something unexpected happened
385            try :
386                options, args = getopt.getopt(argv, short, long)
387                if options :
388                    for (o, v) in options :
389                        # we skip the '-' chars
390                        lgo = len(o)
391                        i = 0
392                        while (i < lgo) and (o[i] == '-') :
393                            i = i + 1
394                        o = o[i:]
395                        if o in witharg :
396                            # needs an argument : set it
397                            parsed[o] = v
398                        elif o in withoutarg :
399                            # doesn't need an argument : boolean
400                            parsed[o] = 1
401                        else :
402                            # should never occur
403                            raise PyKotaCommandLineError, "Unexpected problem when parsing command line"
404                elif (not args) and (not allownothing) and sys.stdin.isatty() : # no option and no argument, we display help if we are a tty
405                    self.display_usage_and_quit()
406            except getopt.error, msg :
407                raise PyKotaCommandLineError, str(msg)
408            else :   
409                if parsed["arguments"] or parsed["A"] :
410                    # arguments are in a file, we ignore all other arguments
411                    # and reset the list of arguments to the lines read from
412                    # the file.
413                    argsfile = open(parsed["arguments"] or parsed["A"], "r")
414                    argv = [ l.strip() for l in argsfile.readlines() ]
415                    argsfile.close()
416                    for i in range(len(argv)) :
417                        argi = argv[i]
418                        if argi.startswith('"') and argi.endswith('"') :
419                            argv[i] = argi[1:-1]
420                else :   
421                    done = 1
422        return (parsed, args)
423   
424class PyKotaTool(Tool) :   
425    """Base class for all PyKota command line tools."""
426    def __init__(self, lang="", charset=None, doc="PyKota v%(__version__)s (c) %(__years__)s %(__author__)s") :
427        """Initializes the command line tool and opens the database."""
428        Tool.__init__(self, lang, charset, doc)
429       
430    def deferredInit(self) :   
431        """Deferred initialization."""
432        Tool.deferredInit(self)
433        self.storage = storage.openConnection(self)
434        if self.config.isAdmin : # TODO : We don't know this before, fix this !
435            self.logdebug("Beware : running as a PyKota administrator !")
436        else :   
437            self.logdebug("Don't Panic : running as a mere mortal !")
438       
439    def clean(self) :   
440        """Ensures that the database is closed."""
441        try :
442            self.storage.close()
443        except (TypeError, NameError, AttributeError) :   
444            pass
445           
446    def isValidName(self, name) :
447        """Checks if a user or printer name is valid."""
448        invalidchars = "/@?*,;&|"
449        for c in list(invalidchars) :
450            if c in name :
451                return 0
452        return 1       
453       
454    def sendMessage(self, adminmail, touser, fullmessage) :
455        """Sends an email message containing headers to some user."""
456        try :   
457            server = smtplib.SMTP(self.smtpserver)
458        except socket.error, msg :   
459            self.printInfo(_("Impossible to connect to SMTP server : %s") % msg, "error")
460        else :
461            try :
462                server.sendmail(adminmail, [touser], fullmessage)
463            except smtplib.SMTPException, answer :   
464                for (k, v) in answer.recipients.items() :
465                    self.printInfo(_("Impossible to send mail to %s, error %s : %s") % (k, v[0], v[1]), "error")
466            server.quit()
467       
468    def sendMessageToUser(self, admin, adminmail, user, subject, message) :
469        """Sends an email message to a user."""
470        message += _("\n\nPlease contact your system administrator :\n\n\t%s - <%s>\n") % (admin, adminmail)
471        usermail = user.Email or user.Name
472        if "@" not in usermail :
473            usermail = "%s@%s" % (usermail, self.maildomain or self.smtpserver or "localhost")
474        msg = MIMEText(message, _charset=self.charset)
475        msg["Subject"] = str(Header(subject, charset=self.charset))
476        msg["From"] = adminmail
477        msg["To"] = usermail
478        self.sendMessage(adminmail, usermail, msg.as_string())
479       
480    def sendMessageToAdmin(self, adminmail, subject, message) :
481        """Sends an email message to the Print Quota administrator."""
482        if "@" not in adminmail :
483            adminmail = "%s@%s" % (adminmail, self.maildomain or self.smtpserver or "localhost")
484        msg = MIMEText(message, _charset=self.charset)
485        msg["Subject"] = str(Header(subject, charset=self.charset))
486        msg["From"] = adminmail
487        msg["To"] = adminmail
488        self.sendMessage(adminmail, adminmail, msg.as_string())
489       
490    def _checkUserPQuota(self, userpquota) :           
491        """Checks the user quota on a printer and deny or accept the job."""
492        # then we check the user's own quota
493        # if we get there we are sure that policy is not EXTERNAL
494        user = userpquota.User
495        printer = userpquota.Printer
496        enforcement = self.config.getPrinterEnforcement(printer.Name)
497        self.logdebug("Checking user %s's quota on printer %s" % (user.Name, printer.Name))
498        (policy, dummy) = self.config.getPrinterPolicy(userpquota.Printer.Name)
499        if not userpquota.Exists :
500            # Unknown userquota
501            if policy == "ALLOW" :
502                action = "POLICY_ALLOW"
503            else :   
504                action = "POLICY_DENY"
505            self.printInfo(_("Unable to match user %s on printer %s, applying default policy (%s)") % (user.Name, printer.Name, action))
506        else :   
507            pagecounter = int(userpquota.PageCounter or 0)
508            if enforcement == "STRICT" :
509                pagecounter += self.softwareJobSize
510            if userpquota.SoftLimit is not None :
511                softlimit = int(userpquota.SoftLimit)
512                if pagecounter < softlimit :
513                    action = "ALLOW"
514                else :   
515                    if userpquota.HardLimit is None :
516                        # only a soft limit, this is equivalent to having only a hard limit
517                        action = "DENY"
518                    else :   
519                        hardlimit = int(userpquota.HardLimit)
520                        if softlimit <= pagecounter < hardlimit :   
521                            now = DateTime.now()
522                            if userpquota.DateLimit is not None :
523                                datelimit = DateTime.ISO.ParseDateTime(userpquota.DateLimit)
524                            else :
525                                datelimit = now + self.config.getGraceDelay(printer.Name)
526                                userpquota.setDateLimit(datelimit)
527                            if now < datelimit :
528                                action = "WARN"
529                            else :   
530                                action = "DENY"
531                        else :         
532                            action = "DENY"
533            else :       
534                if userpquota.HardLimit is not None :
535                    # no soft limit, only a hard one.
536                    hardlimit = int(userpquota.HardLimit)
537                    if pagecounter < hardlimit :
538                        action = "ALLOW"
539                    else :     
540                        action = "DENY"
541                else :
542                    # Both are unset, no quota, i.e. accounting only
543                    action = "ALLOW"
544        return action
545   
546    def checkGroupPQuota(self, grouppquota) :   
547        """Checks the group quota on a printer and deny or accept the job."""
548        group = grouppquota.Group
549        printer = grouppquota.Printer
550        enforcement = self.config.getPrinterEnforcement(printer.Name)
551        self.logdebug("Checking group %s's quota on printer %s" % (group.Name, printer.Name))
552        if group.LimitBy and (group.LimitBy.lower() == "balance") : 
553            val = group.AccountBalance or 0.0
554            if enforcement == "STRICT" : 
555                val -= self.softwareJobPrice # use precomputed size.
556            balancezero = self.config.getBalanceZero()
557            if val <= balancezero :
558                action = "DENY"
559            elif val <= self.config.getPoorMan() :   
560                action = "WARN"
561            else :   
562                action = "ALLOW"
563            if (enforcement == "STRICT") and (val == balancezero) :
564                action = "WARN" # we can still print until account is 0
565        else :
566            val = grouppquota.PageCounter or 0
567            if enforcement == "STRICT" :
568                val += self.softwareJobSize
569            if grouppquota.SoftLimit is not None :
570                softlimit = int(grouppquota.SoftLimit)
571                if val < softlimit :
572                    action = "ALLOW"
573                else :   
574                    if grouppquota.HardLimit is None :
575                        # only a soft limit, this is equivalent to having only a hard limit
576                        action = "DENY"
577                    else :   
578                        hardlimit = int(grouppquota.HardLimit)
579                        if softlimit <= val < hardlimit :   
580                            now = DateTime.now()
581                            if grouppquota.DateLimit is not None :
582                                datelimit = DateTime.ISO.ParseDateTime(grouppquota.DateLimit)
583                            else :
584                                datelimit = now + self.config.getGraceDelay(printer.Name)
585                                grouppquota.setDateLimit(datelimit)
586                            if now < datelimit :
587                                action = "WARN"
588                            else :   
589                                action = "DENY"
590                        else :         
591                            action = "DENY"
592            else :       
593                if grouppquota.HardLimit is not None :
594                    # no soft limit, only a hard one.
595                    hardlimit = int(grouppquota.HardLimit)
596                    if val < hardlimit :
597                        action = "ALLOW"
598                    else :     
599                        action = "DENY"
600                else :
601                    # Both are unset, no quota, i.e. accounting only
602                    action = "ALLOW"
603        return action
604   
605    def checkUserPQuota(self, userpquota) :
606        """Checks the user quota on a printer and all its parents and deny or accept the job."""
607        user = userpquota.User
608        printer = userpquota.Printer
609       
610        # indicates that a warning needs to be sent
611        warned = 0               
612       
613        # first we check any group the user is a member of
614        for group in self.storage.getUserGroups(user) :
615            # No need to check anything if the group is in noquota mode
616            if group.LimitBy != "noquota" :
617                grouppquota = self.storage.getGroupPQuota(group, printer)
618                # for the printer and all its parents
619                for gpquota in [ grouppquota ] + grouppquota.ParentPrintersGroupPQuota :
620                    if gpquota.Exists :
621                        action = self.checkGroupPQuota(gpquota)
622                        if action == "DENY" :
623                            return action
624                        elif action == "WARN" :   
625                            warned = 1
626                       
627        # Then we check the user's account balance
628        # if we get there we are sure that policy is not EXTERNAL
629        (policy, dummy) = self.config.getPrinterPolicy(printer.Name)
630        if user.LimitBy and (user.LimitBy.lower() == "balance") : 
631            self.logdebug("Checking account balance for user %s" % user.Name)
632            if user.AccountBalance is None :
633                if policy == "ALLOW" :
634                    action = "POLICY_ALLOW"
635                else :   
636                    action = "POLICY_DENY"
637                self.printInfo(_("Unable to find user %s's account balance, applying default policy (%s) for printer %s") % (user.Name, action, printer.Name))
638                return action       
639            else :   
640                if user.OverCharge == 0.0 :
641                    self.printInfo(_("User %s will not be charged for printing.") % user.Name)
642                    action = "ALLOW"
643                else :
644                    val = float(user.AccountBalance or 0.0)
645                    enforcement = self.config.getPrinterEnforcement(printer.Name)
646                    if enforcement == "STRICT" : 
647                        val -= self.softwareJobPrice # use precomputed size.
648                    balancezero = self.config.getBalanceZero()   
649                    if val <= balancezero :
650                        action = "DENY"
651                    elif val <= self.config.getPoorMan() :   
652                        action = "WARN"
653                    else :
654                        action = "ALLOW"
655                    if (enforcement == "STRICT") and (val == balancezero) :
656                        action = "WARN" # we can still print until account is 0
657                return action   
658        else :
659            # Then check the user quota on current printer and all its parents.               
660            policyallowed = 0
661            for upquota in [ userpquota ] + userpquota.ParentPrintersUserPQuota :               
662                action = self._checkUserPQuota(upquota)
663                if action in ("DENY", "POLICY_DENY") :
664                    return action
665                elif action == "WARN" :   
666                    warned = 1
667                elif action == "POLICY_ALLOW" :   
668                    policyallowed = 1
669            if warned :       
670                return "WARN"
671            elif policyallowed :   
672                return "POLICY_ALLOW" 
673            else :   
674                return "ALLOW"
675               
676    def externalMailTo(self, cmd, action, user, printer, message) :
677        """Warns the user with an external command."""
678        username = user.Name
679        printername = printer.Name
680        email = user.Email or user.Name
681        if "@" not in email :
682            email = "%s@%s" % (email, self.maildomain or self.smtpserver)
683        os.system(cmd % locals())
684   
685    def formatCommandLine(self, cmd, user, printer) :
686        """Executes an external command."""
687        username = user.Name
688        printername = printer.Name
689        return cmd % locals()
690       
691    def warnGroupPQuota(self, grouppquota) :
692        """Checks a group quota and send messages if quota is exceeded on current printer."""
693        group = grouppquota.Group
694        printer = grouppquota.Printer
695        admin = self.config.getAdmin(printer.Name)
696        adminmail = self.config.getAdminMail(printer.Name)
697        (mailto, arguments) = self.config.getMailTo(printer.Name)
698        if group.LimitBy in ("noquota", "nochange") :
699            action = "ALLOW"
700        else :   
701            action = self.checkGroupPQuota(grouppquota)
702            if action.startswith("POLICY_") :
703                action = action[7:]
704            if action == "DENY" :
705                adminmessage = _("Print Quota exceeded for group %s on printer %s") % (group.Name, printer.Name)
706                self.printInfo(adminmessage)
707                if mailto in [ "BOTH", "ADMIN" ] :
708                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
709                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
710                    for user in self.storage.getGroupMembers(group) :
711                        if mailto != "EXTERNAL" :
712                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printer.Name))
713                        else :   
714                            self.externalMailTo(arguments, action, user, printer, self.config.getHardWarn(printer.Name))
715            elif action == "WARN" :   
716                adminmessage = _("Print Quota low for group %s on printer %s") % (group.Name, printer.Name)
717                self.printInfo(adminmessage)
718                if mailto in [ "BOTH", "ADMIN" ] :
719                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
720                if group.LimitBy and (group.LimitBy.lower() == "balance") : 
721                    message = self.config.getPoorWarn()
722                else :     
723                    message = self.config.getSoftWarn(printer.Name)
724                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
725                    for user in self.storage.getGroupMembers(group) :
726                        if mailto != "EXTERNAL" :
727                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
728                        else :   
729                            self.externalMailTo(arguments, action, user, printer, message)
730        return action       
731       
732    def warnUserPQuota(self, userpquota) :
733        """Checks a user quota and send him a message if quota is exceeded on current printer."""
734        user = userpquota.User
735        printer = userpquota.Printer
736        admin = self.config.getAdmin(printer.Name)
737        adminmail = self.config.getAdminMail(printer.Name)
738        (mailto, arguments) = self.config.getMailTo(printer.Name)
739       
740        if user.LimitBy in ("noquota", "nochange") :
741            action = "ALLOW"
742        elif user.LimitBy == "noprint" :
743            action = "DENY"
744            message = _("User %s is not allowed to print at this time.") % user.Name
745            self.printInfo(message)
746            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
747                if mailto != "EXTERNAL" :
748                    self.sendMessageToUser(admin, adminmail, user, _("Printing denied."), message)
749                else :   
750                    self.externalMailTo(arguments, action, user, printer, message)
751            if mailto in [ "BOTH", "ADMIN" ] :
752                self.sendMessageToAdmin(adminmail, _("Print Quota"), message)
753        else :
754            action = self.checkUserPQuota(userpquota)
755            if action.startswith("POLICY_") :
756                action = action[7:]
757               
758            if action == "DENY" :
759                adminmessage = _("Print Quota exceeded for user %s on printer %s") % (user.Name, printer.Name)
760                self.printInfo(adminmessage)
761                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
762                    message = self.config.getHardWarn(printer.Name)
763                    if mailto != "EXTERNAL" :
764                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
765                    else :   
766                        self.externalMailTo(arguments, action, user, printer, message)
767                if mailto in [ "BOTH", "ADMIN" ] :
768                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
769            elif action == "WARN" :   
770                adminmessage = _("Print Quota low for user %s on printer %s") % (user.Name, printer.Name)
771                self.printInfo(adminmessage)
772                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
773                    if user.LimitBy and (user.LimitBy.lower() == "balance") : 
774                        message = self.config.getPoorWarn()
775                    else :     
776                        message = self.config.getSoftWarn(printer.Name)
777                    if mailto != "EXTERNAL" :   
778                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message)
779                    else :   
780                        self.externalMailTo(arguments, action, user, printer, message)
781                if mailto in [ "BOTH", "ADMIN" ] :
782                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
783        return action       
784       
Note: See TracBrowser for help on using the browser.