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

Revision 2793, 32.5 kB (checked in by jerome, 18 years ago)

Fixed a potential division by zero error.

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