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

Revision 2650, 30.0 kB (checked in by jerome, 18 years ago)

A string matches if there's no pattern now.
Now uses the True and False constants instead of 0 and 1.

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