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

Revision 2558, 28.9 kB (checked in by jerome, 19 years ago)

Small fix for uninitialized variable depending on the value of the 'mailto' directive
in pykota.conf
Severity : minor

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