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

Revision 2657, 30.2 kB (checked in by jerome, 18 years ago)

Huge speed improvements when using the --delete command line option for pkprinters, pkbcodes and edpykota.

  • 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            if val <= 0.0 :
461                action = "DENY"
462            elif val <= self.config.getPoorMan() :   
463                action = "WARN"
464            else :   
465                action = "ALLOW"
466            if (enforcement == "STRICT") and (val == 0.0) :
467                action = "WARN" # we can still print until account is 0
468        else :
469            val = grouppquota.PageCounter or 0
470            if enforcement == "STRICT" :
471                val += self.softwareJobSize
472            if grouppquota.SoftLimit is not None :
473                softlimit = int(grouppquota.SoftLimit)
474                if val < softlimit :
475                    action = "ALLOW"
476                else :   
477                    if grouppquota.HardLimit is None :
478                        # only a soft limit, this is equivalent to having only a hard limit
479                        action = "DENY"
480                    else :   
481                        hardlimit = int(grouppquota.HardLimit)
482                        if softlimit <= val < hardlimit :   
483                            now = DateTime.now()
484                            if grouppquota.DateLimit is not None :
485                                datelimit = DateTime.ISO.ParseDateTime(grouppquota.DateLimit)
486                            else :
487                                datelimit = now + self.config.getGraceDelay(printer.Name)
488                                grouppquota.setDateLimit(datelimit)
489                            if now < datelimit :
490                                action = "WARN"
491                            else :   
492                                action = "DENY"
493                        else :         
494                            action = "DENY"
495            else :       
496                if grouppquota.HardLimit is not None :
497                    # no soft limit, only a hard one.
498                    hardlimit = int(grouppquota.HardLimit)
499                    if val < hardlimit :
500                        action = "ALLOW"
501                    else :     
502                        action = "DENY"
503                else :
504                    # Both are unset, no quota, i.e. accounting only
505                    action = "ALLOW"
506        return action
507   
508    def checkUserPQuota(self, userpquota) :
509        """Checks the user quota on a printer and all its parents and deny or accept the job."""
510        user = userpquota.User
511        printer = userpquota.Printer
512       
513        # indicates that a warning needs to be sent
514        warned = 0               
515       
516        # first we check any group the user is a member of
517        for group in self.storage.getUserGroups(user) :
518            # No need to check anything if the group is in noquota mode
519            if group.LimitBy != "noquota" :
520                grouppquota = self.storage.getGroupPQuota(group, printer)
521                # for the printer and all its parents
522                for gpquota in [ grouppquota ] + grouppquota.ParentPrintersGroupPQuota :
523                    if gpquota.Exists :
524                        action = self.checkGroupPQuota(gpquota)
525                        if action == "DENY" :
526                            return action
527                        elif action == "WARN" :   
528                            warned = 1
529                       
530        # Then we check the user's account balance
531        # if we get there we are sure that policy is not EXTERNAL
532        (policy, dummy) = self.config.getPrinterPolicy(printer.Name)
533        if user.LimitBy and (user.LimitBy.lower() == "balance") : 
534            self.logdebug("Checking account balance for user %s" % user.Name)
535            if user.AccountBalance is None :
536                if policy == "ALLOW" :
537                    action = "POLICY_ALLOW"
538                else :   
539                    action = "POLICY_DENY"
540                self.printInfo(_("Unable to find user %s's account balance, applying default policy (%s) for printer %s") % (user.Name, action, printer.Name))
541                return action       
542            else :   
543                if user.OverCharge == 0.0 :
544                    self.printInfo(_("User %s will not be charged for printing.") % user.Name)
545                    action = "ALLOW"
546                else :
547                    val = float(user.AccountBalance or 0.0)
548                    enforcement = self.config.getPrinterEnforcement(printer.Name)
549                    if enforcement == "STRICT" : 
550                        val -= self.softwareJobPrice # use precomputed size.
551                    if val <= 0.0 :
552                        action = "DENY"
553                    elif val <= self.config.getPoorMan() :   
554                        action = "WARN"
555                    else :
556                        action = "ALLOW"
557                    if (enforcement == "STRICT") and (val == 0.0) :
558                        action = "WARN" # we can still print until account is 0
559                return action   
560        else :
561            # Then check the user quota on current printer and all its parents.               
562            policyallowed = 0
563            for upquota in [ userpquota ] + userpquota.ParentPrintersUserPQuota :               
564                action = self._checkUserPQuota(upquota)
565                if action in ("DENY", "POLICY_DENY") :
566                    return action
567                elif action == "WARN" :   
568                    warned = 1
569                elif action == "POLICY_ALLOW" :   
570                    policyallowed = 1
571            if warned :       
572                return "WARN"
573            elif policyallowed :   
574                return "POLICY_ALLOW" 
575            else :   
576                return "ALLOW"
577               
578    def externalMailTo(self, cmd, action, user, printer, message) :
579        """Warns the user with an external command."""
580        username = user.Name
581        printername = printer.Name
582        email = user.Email or user.Name
583        if "@" not in email :
584            email = "%s@%s" % (email, self.maildomain or self.smtpserver)
585        os.system(cmd % locals())
586   
587    def formatCommandLine(self, cmd, user, printer) :
588        """Executes an external command."""
589        username = user.Name
590        printername = printer.Name
591        return cmd % locals()
592       
593    def warnGroupPQuota(self, grouppquota) :
594        """Checks a group quota and send messages if quota is exceeded on current printer."""
595        group = grouppquota.Group
596        printer = grouppquota.Printer
597        admin = self.config.getAdmin(printer.Name)
598        adminmail = self.config.getAdminMail(printer.Name)
599        (mailto, arguments) = self.config.getMailTo(printer.Name)
600        if group.LimitBy in ("noquota", "nochange") :
601            action = "ALLOW"
602        else :   
603            action = self.checkGroupPQuota(grouppquota)
604            if action.startswith("POLICY_") :
605                action = action[7:]
606            if action == "DENY" :
607                adminmessage = _("Print Quota exceeded 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 mailto in [ "BOTH", "USER", "EXTERNAL" ] :
612                    for user in self.storage.getGroupMembers(group) :
613                        if mailto != "EXTERNAL" :
614                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printer.Name))
615                        else :   
616                            self.externalMailTo(arguments, action, user, printer, self.config.getHardWarn(printer.Name))
617            elif action == "WARN" :   
618                adminmessage = _("Print Quota low for group %s on printer %s") % (group.Name, printer.Name)
619                self.printInfo(adminmessage)
620                if mailto in [ "BOTH", "ADMIN" ] :
621                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
622                if group.LimitBy and (group.LimitBy.lower() == "balance") : 
623                    message = self.config.getPoorWarn()
624                else :     
625                    message = self.config.getSoftWarn(printer.Name)
626                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
627                    for user in self.storage.getGroupMembers(group) :
628                        if mailto != "EXTERNAL" :
629                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
630                        else :   
631                            self.externalMailTo(arguments, action, user, printer, message)
632        return action       
633       
634    def warnUserPQuota(self, userpquota) :
635        """Checks a user quota and send him a message if quota is exceeded on current printer."""
636        user = userpquota.User
637        printer = userpquota.Printer
638        admin = self.config.getAdmin(printer.Name)
639        adminmail = self.config.getAdminMail(printer.Name)
640        (mailto, arguments) = self.config.getMailTo(printer.Name)
641       
642        if user.LimitBy in ("noquota", "nochange") :
643            action = "ALLOW"
644        elif user.LimitBy == "noprint" :
645            action = "DENY"
646            message = _("User %s is not allowed to print at this time.") % user.Name
647            self.printInfo(message)
648            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
649                if mailto != "EXTERNAL" :
650                    self.sendMessageToUser(admin, adminmail, user, _("Printing denied."), message)
651                else :   
652                    self.externalMailTo(arguments, action, user, printer, message)
653            if mailto in [ "BOTH", "ADMIN" ] :
654                self.sendMessageToAdmin(adminmail, _("Print Quota"), message)
655        else :
656            action = self.checkUserPQuota(userpquota)
657            if action.startswith("POLICY_") :
658                action = action[7:]
659               
660            if action == "DENY" :
661                adminmessage = _("Print Quota exceeded for user %s on printer %s") % (user.Name, printer.Name)
662                self.printInfo(adminmessage)
663                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
664                    message = self.config.getHardWarn(printer.Name)
665                    if mailto != "EXTERNAL" :
666                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
667                    else :   
668                        self.externalMailTo(arguments, action, user, printer, message)
669                if mailto in [ "BOTH", "ADMIN" ] :
670                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
671            elif action == "WARN" :   
672                adminmessage = _("Print Quota low for user %s on printer %s") % (user.Name, printer.Name)
673                self.printInfo(adminmessage)
674                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
675                    if user.LimitBy and (user.LimitBy.lower() == "balance") : 
676                        message = self.config.getPoorWarn()
677                    else :     
678                        message = self.config.getSoftWarn(printer.Name)
679                    if mailto != "EXTERNAL" :   
680                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message)
681                    else :   
682                        self.externalMailTo(arguments, action, user, printer, message)
683                if mailto in [ "BOTH", "ADMIN" ] :
684                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
685        return action       
686       
Note: See TracBrowser for help on using the browser.