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

Revision 2610, 29.8 kB (checked in by jerome, 18 years ago)

Fixed corner case of bad command line options handling

  • 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        short = "%sA:" % short
257        long.append("arguments=")
258        withoutarg = []
259        witharg = []
260        lgs = len(short)
261        i = 0
262        while i < lgs :
263            ii = i + 1
264            if (ii < lgs) and (short[ii] == ':') :
265                # needs an argument
266                witharg.append(short[i])
267                ii = ii + 1 # skip the ':'
268            else :
269                # doesn't need an argument
270                withoutarg.append(short[i])
271            i = ii
272               
273        for option in long :
274            if option[-1] == '=' :
275                # needs an argument
276                witharg.append(option[:-1])
277            else :
278                # doesn't need an argument
279                withoutarg.append(option)
280       
281        # then we parse the command line
282        done = 0
283        while not done :
284            # we begin with all possible options unset
285            parsed = {}
286            for option in withoutarg + witharg :
287                parsed[option] = None
288            args = []       # to not break if something unexpected happened
289            try :
290                options, args = getopt.getopt(argv, short, long)
291                if options :
292                    for (o, v) in options :
293                        # we skip the '-' chars
294                        lgo = len(o)
295                        i = 0
296                        while (i < lgo) and (o[i] == '-') :
297                            i = i + 1
298                        o = o[i:]
299                        if o in witharg :
300                            # needs an argument : set it
301                            parsed[o] = v
302                        elif o in withoutarg :
303                            # doesn't need an argument : boolean
304                            parsed[o] = 1
305                        else :
306                            # should never occur
307                            raise PyKotaCommandLineError, "Unexpected problem when parsing command line"
308                elif (not args) and (not allownothing) and sys.stdin.isatty() : # no option and no argument, we display help if we are a tty
309                    self.display_usage_and_quit()
310            except getopt.error, msg :
311                raise PyKotaCommandLineError, str(msg)
312            else :   
313                if parsed["arguments"] or parsed["A"] :
314                    # arguments are in a file, we ignore all other arguments
315                    # and reset the list of arguments to the lines read from
316                    # the file.
317                    argsfile = open(parsed["arguments"] or parsed["A"], "r")
318                    argv = [ l.strip() for l in argsfile.readlines() ]
319                    argsfile.close()
320                    for i in range(len(argv)) :
321                        argi = argv[i]
322                        if argi.startswith('"') and argi.endswith('"') :
323                            argv[i] = argi[1:-1]
324                else :   
325                    done = 1
326        return (parsed, args)
327   
328class PyKotaTool(Tool) :   
329    """Base class for all PyKota command line tools."""
330    def __init__(self, lang="", charset=None, doc="PyKota v%(__version__)s (c) %(__years__)s %(__author__)s") :
331        """Initializes the command line tool and opens the database."""
332        Tool.__init__(self, lang, charset, doc)
333       
334    def deferredInit(self) :   
335        """Deferred initialization."""
336        Tool.deferredInit(self)
337        self.storage = storage.openConnection(self)
338        if self.config.isAdmin : # TODO : We don't know this before, fix this !
339            self.logdebug("Beware : running as a PyKota administrator !")
340        else :   
341            self.logdebug("Don't Panic : running as a mere mortal !")
342       
343    def clean(self) :   
344        """Ensures that the database is closed."""
345        try :
346            self.storage.close()
347        except (TypeError, NameError, AttributeError) :   
348            pass
349           
350    def isValidName(self, name) :
351        """Checks if a user or printer name is valid."""
352        invalidchars = "/@?*,;&|"
353        for c in list(invalidchars) :
354            if c in name :
355                return 0
356        return 1       
357       
358    def sendMessage(self, adminmail, touser, fullmessage) :
359        """Sends an email message containing headers to some user."""
360        if "@" not in touser :
361            touser = "%s@%s" % (touser, self.maildomain or self.smtpserver)
362        try :   
363            server = smtplib.SMTP(self.smtpserver)
364        except socket.error, msg :   
365            self.printInfo(_("Impossible to connect to SMTP server : %s") % msg, "error")
366        else :
367            try :
368                server.sendmail(adminmail, [touser], "From: %s\nTo: %s\n%s" % (adminmail, touser, fullmessage))
369            except smtplib.SMTPException, answer :   
370                for (k, v) in answer.recipients.items() :
371                    self.printInfo(_("Impossible to send mail to %s, error %s : %s") % (k, v[0], v[1]), "error")
372            server.quit()
373       
374    def sendMessageToUser(self, admin, adminmail, user, subject, message) :
375        """Sends an email message to a user."""
376        message += _("\n\nPlease contact your system administrator :\n\n\t%s - <%s>\n") % (admin, adminmail)
377        self.sendMessage(adminmail, user.Email or user.Name, "Subject: %s\n\n%s" % (subject, message))
378       
379    def sendMessageToAdmin(self, adminmail, subject, message) :
380        """Sends an email message to the Print Quota administrator."""
381        self.sendMessage(adminmail, adminmail, "Subject: %s\n\n%s" % (subject, message))
382       
383    def _checkUserPQuota(self, userpquota) :           
384        """Checks the user quota on a printer and deny or accept the job."""
385        # then we check the user's own quota
386        # if we get there we are sure that policy is not EXTERNAL
387        user = userpquota.User
388        printer = userpquota.Printer
389        enforcement = self.config.getPrinterEnforcement(printer.Name)
390        self.logdebug("Checking user %s's quota on printer %s" % (user.Name, printer.Name))
391        (policy, dummy) = self.config.getPrinterPolicy(userpquota.Printer.Name)
392        if not userpquota.Exists :
393            # Unknown userquota
394            if policy == "ALLOW" :
395                action = "POLICY_ALLOW"
396            else :   
397                action = "POLICY_DENY"
398            self.printInfo(_("Unable to match user %s on printer %s, applying default policy (%s)") % (user.Name, printer.Name, action))
399        else :   
400            pagecounter = int(userpquota.PageCounter or 0)
401            if enforcement == "STRICT" :
402                pagecounter += self.softwareJobSize
403            if userpquota.SoftLimit is not None :
404                softlimit = int(userpquota.SoftLimit)
405                if pagecounter < softlimit :
406                    action = "ALLOW"
407                else :   
408                    if userpquota.HardLimit is None :
409                        # only a soft limit, this is equivalent to having only a hard limit
410                        action = "DENY"
411                    else :   
412                        hardlimit = int(userpquota.HardLimit)
413                        if softlimit <= pagecounter < hardlimit :   
414                            now = DateTime.now()
415                            if userpquota.DateLimit is not None :
416                                datelimit = DateTime.ISO.ParseDateTime(userpquota.DateLimit)
417                            else :
418                                datelimit = now + self.config.getGraceDelay(printer.Name)
419                                userpquota.setDateLimit(datelimit)
420                            if now < datelimit :
421                                action = "WARN"
422                            else :   
423                                action = "DENY"
424                        else :         
425                            action = "DENY"
426            else :       
427                if userpquota.HardLimit is not None :
428                    # no soft limit, only a hard one.
429                    hardlimit = int(userpquota.HardLimit)
430                    if pagecounter < hardlimit :
431                        action = "ALLOW"
432                    else :     
433                        action = "DENY"
434                else :
435                    # Both are unset, no quota, i.e. accounting only
436                    action = "ALLOW"
437        return action
438   
439    def checkGroupPQuota(self, grouppquota) :   
440        """Checks the group quota on a printer and deny or accept the job."""
441        group = grouppquota.Group
442        printer = grouppquota.Printer
443        enforcement = self.config.getPrinterEnforcement(printer.Name)
444        self.logdebug("Checking group %s's quota on printer %s" % (group.Name, printer.Name))
445        if group.LimitBy and (group.LimitBy.lower() == "balance") : 
446            val = group.AccountBalance or 0.0
447            if enforcement == "STRICT" : 
448                val -= self.softwareJobPrice # use precomputed size.
449            if val <= 0.0 :
450                action = "DENY"
451            elif val <= self.config.getPoorMan() :   
452                action = "WARN"
453            else :   
454                action = "ALLOW"
455            if (enforcement == "STRICT") and (val == 0.0) :
456                action = "WARN" # we can still print until account is 0
457        else :
458            val = grouppquota.PageCounter or 0
459            if enforcement == "STRICT" :
460                val += self.softwareJobSize
461            if grouppquota.SoftLimit is not None :
462                softlimit = int(grouppquota.SoftLimit)
463                if val < softlimit :
464                    action = "ALLOW"
465                else :   
466                    if grouppquota.HardLimit is None :
467                        # only a soft limit, this is equivalent to having only a hard limit
468                        action = "DENY"
469                    else :   
470                        hardlimit = int(grouppquota.HardLimit)
471                        if softlimit <= val < hardlimit :   
472                            now = DateTime.now()
473                            if grouppquota.DateLimit is not None :
474                                datelimit = DateTime.ISO.ParseDateTime(grouppquota.DateLimit)
475                            else :
476                                datelimit = now + self.config.getGraceDelay(printer.Name)
477                                grouppquota.setDateLimit(datelimit)
478                            if now < datelimit :
479                                action = "WARN"
480                            else :   
481                                action = "DENY"
482                        else :         
483                            action = "DENY"
484            else :       
485                if grouppquota.HardLimit is not None :
486                    # no soft limit, only a hard one.
487                    hardlimit = int(grouppquota.HardLimit)
488                    if val < hardlimit :
489                        action = "ALLOW"
490                    else :     
491                        action = "DENY"
492                else :
493                    # Both are unset, no quota, i.e. accounting only
494                    action = "ALLOW"
495        return action
496   
497    def checkUserPQuota(self, userpquota) :
498        """Checks the user quota on a printer and all its parents and deny or accept the job."""
499        user = userpquota.User
500        printer = userpquota.Printer
501       
502        # indicates that a warning needs to be sent
503        warned = 0               
504       
505        # first we check any group the user is a member of
506        for group in self.storage.getUserGroups(user) :
507            # No need to check anything if the group is in noquota mode
508            if group.LimitBy != "noquota" :
509                grouppquota = self.storage.getGroupPQuota(group, printer)
510                # for the printer and all its parents
511                for gpquota in [ grouppquota ] + grouppquota.ParentPrintersGroupPQuota :
512                    if gpquota.Exists :
513                        action = self.checkGroupPQuota(gpquota)
514                        if action == "DENY" :
515                            return action
516                        elif action == "WARN" :   
517                            warned = 1
518                       
519        # Then we check the user's account balance
520        # if we get there we are sure that policy is not EXTERNAL
521        (policy, dummy) = self.config.getPrinterPolicy(printer.Name)
522        if user.LimitBy and (user.LimitBy.lower() == "balance") : 
523            self.logdebug("Checking account balance for user %s" % user.Name)
524            if user.AccountBalance is None :
525                if policy == "ALLOW" :
526                    action = "POLICY_ALLOW"
527                else :   
528                    action = "POLICY_DENY"
529                self.printInfo(_("Unable to find user %s's account balance, applying default policy (%s) for printer %s") % (user.Name, action, printer.Name))
530                return action       
531            else :   
532                if user.OverCharge == 0.0 :
533                    self.printInfo(_("User %s will not be charged for printing.") % user.Name)
534                    action = "ALLOW"
535                else :
536                    val = float(user.AccountBalance or 0.0)
537                    enforcement = self.config.getPrinterEnforcement(printer.Name)
538                    if enforcement == "STRICT" : 
539                        val -= self.softwareJobPrice # use precomputed size.
540                    if val <= 0.0 :
541                        action = "DENY"
542                    elif val <= self.config.getPoorMan() :   
543                        action = "WARN"
544                    else :
545                        action = "ALLOW"
546                    if (enforcement == "STRICT") and (val == 0.0) :
547                        action = "WARN" # we can still print until account is 0
548                return action   
549        else :
550            # Then check the user quota on current printer and all its parents.               
551            policyallowed = 0
552            for upquota in [ userpquota ] + userpquota.ParentPrintersUserPQuota :               
553                action = self._checkUserPQuota(upquota)
554                if action in ("DENY", "POLICY_DENY") :
555                    return action
556                elif action == "WARN" :   
557                    warned = 1
558                elif action == "POLICY_ALLOW" :   
559                    policyallowed = 1
560            if warned :       
561                return "WARN"
562            elif policyallowed :   
563                return "POLICY_ALLOW" 
564            else :   
565                return "ALLOW"
566               
567    def externalMailTo(self, cmd, action, user, printer, message) :
568        """Warns the user with an external command."""
569        username = user.Name
570        printername = printer.Name
571        email = user.Email or user.Name
572        if "@" not in email :
573            email = "%s@%s" % (email, self.maildomain or self.smtpserver)
574        os.system(cmd % locals())
575   
576    def formatCommandLine(self, cmd, user, printer) :
577        """Executes an external command."""
578        username = user.Name
579        printername = printer.Name
580        return cmd % locals()
581       
582    def warnGroupPQuota(self, grouppquota) :
583        """Checks a group quota and send messages if quota is exceeded on current printer."""
584        group = grouppquota.Group
585        printer = grouppquota.Printer
586        admin = self.config.getAdmin(printer.Name)
587        adminmail = self.config.getAdminMail(printer.Name)
588        (mailto, arguments) = self.config.getMailTo(printer.Name)
589        if group.LimitBy in ("noquota", "nochange") :
590            action = "ALLOW"
591        else :   
592            action = self.checkGroupPQuota(grouppquota)
593            if action.startswith("POLICY_") :
594                action = action[7:]
595            if action == "DENY" :
596                adminmessage = _("Print Quota exceeded for group %s on printer %s") % (group.Name, printer.Name)
597                self.printInfo(adminmessage)
598                if mailto in [ "BOTH", "ADMIN" ] :
599                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
600                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
601                    for user in self.storage.getGroupMembers(group) :
602                        if mailto != "EXTERNAL" :
603                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printer.Name))
604                        else :   
605                            self.externalMailTo(arguments, action, user, printer, self.config.getHardWarn(printer.Name))
606            elif action == "WARN" :   
607                adminmessage = _("Print Quota low for group %s on printer %s") % (group.Name, printer.Name)
608                self.printInfo(adminmessage)
609                if mailto in [ "BOTH", "ADMIN" ] :
610                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
611                if group.LimitBy and (group.LimitBy.lower() == "balance") : 
612                    message = self.config.getPoorWarn()
613                else :     
614                    message = self.config.getSoftWarn(printer.Name)
615                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
616                    for user in self.storage.getGroupMembers(group) :
617                        if mailto != "EXTERNAL" :
618                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
619                        else :   
620                            self.externalMailTo(arguments, action, user, printer, message)
621        return action       
622       
623    def warnUserPQuota(self, userpquota) :
624        """Checks a user quota and send him a message if quota is exceeded on current printer."""
625        user = userpquota.User
626        printer = userpquota.Printer
627        admin = self.config.getAdmin(printer.Name)
628        adminmail = self.config.getAdminMail(printer.Name)
629        (mailto, arguments) = self.config.getMailTo(printer.Name)
630       
631        if user.LimitBy in ("noquota", "nochange") :
632            action = "ALLOW"
633        elif user.LimitBy == "noprint" :
634            action = "DENY"
635            message = _("User %s is not allowed to print at this time.") % user.Name
636            self.printInfo(message)
637            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
638                if mailto != "EXTERNAL" :
639                    self.sendMessageToUser(admin, adminmail, user, _("Printing denied."), message)
640                else :   
641                    self.externalMailTo(arguments, action, user, printer, message)
642            if mailto in [ "BOTH", "ADMIN" ] :
643                self.sendMessageToAdmin(adminmail, _("Print Quota"), message)
644        else :
645            action = self.checkUserPQuota(userpquota)
646            if action.startswith("POLICY_") :
647                action = action[7:]
648               
649            if action == "DENY" :
650                adminmessage = _("Print Quota exceeded for user %s on printer %s") % (user.Name, printer.Name)
651                self.printInfo(adminmessage)
652                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
653                    message = self.config.getHardWarn(printer.Name)
654                    if mailto != "EXTERNAL" :
655                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
656                    else :   
657                        self.externalMailTo(arguments, action, user, printer, message)
658                if mailto in [ "BOTH", "ADMIN" ] :
659                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
660            elif action == "WARN" :   
661                adminmessage = _("Print Quota low for user %s on printer %s") % (user.Name, printer.Name)
662                self.printInfo(adminmessage)
663                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
664                    if user.LimitBy and (user.LimitBy.lower() == "balance") : 
665                        message = self.config.getPoorWarn()
666                    else :     
667                        message = self.config.getSoftWarn(printer.Name)
668                    if mailto != "EXTERNAL" :   
669                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message)
670                    else :   
671                        self.externalMailTo(arguments, action, user, printer, message)
672                if mailto in [ "BOTH", "ADMIN" ] :
673                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
674        return action       
675       
Note: See TracBrowser for help on using the browser.