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

Revision 2762, 31.1 kB (checked in by jerome, 18 years ago)

Make pkusers --list behave like edpykota --list : allowed to normal users,
but restricted in what can be seen.

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