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

Revision 2692, 30.3 kB (checked in by jerome, 18 years ago)

Added the 'duplicatesdelay' and 'balancezero' directives.

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