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

Revision 2782, 32.0 kB (checked in by jerome, 18 years ago)

Code cleaning.
Topped to 10000 the number of times the percent will be displayed by not displaying it if there's no change.
This is very useful when adding 25000 users on 300 printers through an ssh connection...

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