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

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

Now outputs the average speed, so no need to do this in filldb anymore.

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