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

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

Made the CGI scripts work again.
Moved even more functions to the utils module.
Removed the cgifuncs module, moved (and changed) content into utils.
If no output encoding defined, use UTF-8 : when wget is used to try
the CGI scripts, it doesn't set by default the accepted charset and
language headers.

  • 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.charset :
99            self.charset = "UTF-8"
100       
101        # pykota specific stuff
102        self.documentation = doc
103       
104        # Extract the effective username
105        uid = os.geteuid()
106        try :
107            self.effectiveUserName = pwd.getpwuid(uid)[0]
108        except (KeyError, IndexError), msg :   
109            self.printInfo(_("Strange problem with uid(%s) : %s") % (uid, msg), "warn")
110            self.effectiveUserName = os.getlogin()
111       
112    def deferredInit(self) :       
113        """Deferred initialization."""
114        confdir = os.environ.get("PYKOTA_HOME")
115        environHome = True
116        missingUser = False
117        if confdir is None :
118            environHome = False
119            # check for config files in the 'pykota' user's home directory.
120            try :
121                self.pykotauser = pwd.getpwnam("pykota")
122                confdir = self.pykotauser[5]
123            except KeyError :   
124                self.pykotauser = None
125                confdir = "/etc/pykota"
126                missingUser = True
127           
128        self.config = config.PyKotaConfig(confdir)
129        self.debug = self.config.getDebug()
130        self.smtpserver = self.config.getSMTPServer()
131        self.maildomain = self.config.getMailDomain()
132        self.logger = logger.openLogger(self.config.getLoggingBackend())
133           
134        # We NEED this here, even when not in an accounting filter/backend   
135        self.softwareJobSize = 0
136        self.softwareJobPrice = 0.0
137       
138        if environHome :
139            self.printInfo("PYKOTA_HOME environment variable is set. Configuration files were searched in %s" % confdir, "info")
140        else :
141            if missingUser :     
142                self.printInfo("The 'pykota' system account is missing. Configuration files were searched in %s instead." % confdir, "warn")
143       
144        self.logdebug("Language in use : %s" % self.language)
145        self.logdebug("Charset in use : %s" % self.charset)
146       
147        arguments = " ".join(['"%s"' % arg for arg in sys.argv])
148        self.logdebug("Command line arguments : %s" % arguments)
149       
150    def UTF8ToUserCharset(self, text) :
151        """Converts from UTF-8 to user's charset."""
152        if text is None :
153            return None
154        else :   
155            return text.decode("UTF-8", "replace").encode(self.charset, "replace") 
156       
157    def userCharsetToUTF8(self, text) :
158        """Converts from user's charset to UTF-8."""
159        if text is None :
160            return None
161        else :   
162            return text.decode(self.charset, "replace").encode("UTF-8", "replace")   
163       
164    def display(self, message) :
165        """Display a message but only if stdout is a tty."""
166        if sys.stdout.isatty() :
167            sys.stdout.write(message.encode(sys.stdout.encoding or "UTF-8", \
168                                            "replace"))
169            sys.stdout.flush()
170           
171    def logdebug(self, message) :   
172        """Logs something to debug output if debug is enabled."""
173        if self.debug :
174            self.logger.log_message(message.encode(sys.stdout.encoding \
175                                                       or "UTF-8", \
176                                                   "replace"), \
177                                    "debug")
178           
179    def printInfo(self, message, level="info") :       
180        """Sends a message to standard error."""
181        sys.stderr.write("%s: %s\n" % (level.upper(), \
182                                       message.encode(sys.stdout.encoding \
183                                                          or "UTF-8", \
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)
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.