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

Revision 2605, 29.8 kB (checked in by jerome, 18 years ago)

Now all command line tools accept the -A | --arguments command line
option to specify the name of a file containing one argument per line.
NB : all other arguments are ignored, only the ones read from the file
are used for now.
This allows people to bypass the maximal command line's size, to
add several thousand users with a single command for example.

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