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

Revision 3317, 31.0 kB (checked in by jerome, 16 years ago)

Fixed another character encoding problem in the sending of email messages
in the automated bug reporting code.

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