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

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

Added import statements for email, the rest of the work has
to be done though...

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