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

Revision 3311, 30.9 kB (checked in by jerome, 16 years ago)

TODO.

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