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

Revision 3288, 32.9 kB (checked in by jerome, 16 years ago)

Moved all exceptions definitions to a dedicated module.

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