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

Revision 2765, 31.2 kB (checked in by jerome, 18 years ago)

Optimized pkbcodes like edpykota.

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