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

Revision 2795, 33.1 kB (checked in by jerome, 18 years ago)

Now uses MIME to send mail, like it was alredy done in cupspykota.

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