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

Revision 3324, 30.5 kB (checked in by jerome, 16 years ago)

Moved some code elsewhere.
Minor changes to ensure that the job's ticket is correctly overwritten when needed.

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