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

Revision 2511, 27.7 kB (checked in by jerome, 19 years ago)

Fixed debug message

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