root / pykota / trunk / bin / warnpykota @ 3464

Revision 3464, 12.7 kB (checked in by jerome, 15 years ago)

Added proper encoding to fix #35.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: utf-8 -*-*-
3#
4# PyKota : Print Quotas for CUPS
5#
6# (c) 2003, 2004, 2005, 2006, 2007, 2008 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 3 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, see <http://www.gnu.org/licenses/>.
19#
20# $Id$
21#
22#
23
24"""This command line tool can automatically send periodic email
25notifications to users or groups who have reached the limit of their
26printing quota."""
27
28import sys
29import os
30import pwd
31import socket
32import smtplib
33from email.MIMEText import MIMEText
34from email.Header import Header
35import email.Utils
36
37import pykota.appinit
38from pykota.utils import run
39from pykota.commandline import PyKotaOptionParser
40from pykota.errors import PyKotaCommandLineError
41from pykota.tool import PyKotaTool
42
43class WarnPyKota(PyKotaTool) :
44    """A class for warnpykota."""
45    def sendMessage(self, adminmail, touser, fullmessage) :
46        """Sends an email message containing headers to some user."""
47        smtpserver = self.smtpserver
48        try :
49            server = smtplib.SMTP(smtpserver)
50        except socket.error, msg :
51            self.printInfo(_("Impossible to connect to SMTP server : %(smtpserver)s") \
52                                                % locals(), \
53                           "error")
54        else :
55            try :
56                server.sendmail(adminmail, [touser], fullmessage)
57            except smtplib.SMTPException, answer :
58                for (k, v) in answer.recipients.items() :
59                    errormsg = v[0]
60                    errorvalue = v[1]
61                    self.printInfo(_("Impossible to send mail to %(touser)s, error %(errormsg)s : %(errorvalue)s") \
62                                        % locals(), \
63                                   "error")
64            server.quit()
65
66    def sendMessageToUser(self, admin, adminmail, user, subject, message) :
67        """Sends an email message to a user."""
68        message += _("\n\nPlease contact your system administrator :\n\n\t%s - <%s>\n") % (admin, adminmail)
69        usermail = user.Email or user.Name
70        if "@" not in usermail :
71            usermail = "%s@%s" % (usermail, self.maildomain or self.smtpserver or "localhost")
72        msg = MIMEText(message.encode(self.charset, "replace"), _charset=self.charset)
73        msg["Subject"] = Header(subject.encode(self.charset, "replace"), charset=self.charset)
74        msg["From"] = adminmail
75        msg["To"] = usermail
76        msg["Date"] = email.Utils.formatdate(localtime=True)
77        self.sendMessage(adminmail, usermail, msg.as_string())
78
79    def sendMessageToAdmin(self, adminmail, subject, message) :
80        """Sends an email message to the Print Quota administrator."""
81        if "@" not in adminmail :
82            adminmail = "%s@%s" % (adminmail, self.maildomain or self.smtpserver or "localhost")
83        msg = MIMEText(message.encode(self.charset, "replace"), _charset=self.charset)
84        msg["Subject"] = Header(subject.encode(self.charset, "replace"), charset=self.charset)
85        msg["From"] = adminmail
86        msg["To"] = adminmail
87        self.sendMessage(adminmail, adminmail, msg.as_string())
88
89    def warnGroupPQuota(self, grouppquota) :
90        """Checks a group quota and send messages if quota is exceeded on current printer."""
91        group = grouppquota.Group
92        groupname = group.Name
93        printer = grouppquota.Printer
94        printername = printer.Name
95        admin = self.config.getAdmin(printername)
96        adminmail = self.config.getAdminMail(printername)
97        (mailto, arguments) = self.config.getMailTo(printername)
98        if group.LimitBy in ("noquota", "nochange") :
99            action = "ALLOW"
100        else :
101            action = self.checkGroupPQuota(grouppquota)
102            if action.startswith("POLICY_") :
103                action = action[7:]
104            if action == "DENY" :
105                adminmessage = _("Print Quota exceeded for group %(groupname)s on printer %(printername)s") % locals()
106                self.printInfo(adminmessage)
107                if mailto in [ "BOTH", "ADMIN" ] :
108                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
109                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
110                    for user in self.storage.getGroupMembers(group) :
111                        if mailto != "EXTERNAL" :
112                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), self.config.getHardWarn(printername))
113                        else :
114                            self.externalMailTo(arguments, action, user, printer, self.config.getHardWarn(printername))
115            elif action == "WARN" :
116                adminmessage = _("Print Quota low for group %(groupname)s on printer %(printername)s") % locals()
117                self.printInfo(adminmessage)
118                if mailto in [ "BOTH", "ADMIN" ] :
119                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
120                if group.LimitBy and (group.LimitBy.lower() == "balance") :
121                    message = self.config.getPoorWarn()
122                else :
123                    message = self.config.getSoftWarn(printername)
124                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
125                    for user in self.storage.getGroupMembers(group) :
126                        if mailto != "EXTERNAL" :
127                            self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
128                        else :
129                            self.externalMailTo(arguments, action, user, printer, message)
130        return action
131
132    def warnUserPQuota(self, userpquota) :
133        """Checks a user quota and send him a message if quota is exceeded on current printer."""
134        user = userpquota.User
135        username = user.Name
136        printer = userpquota.Printer
137        printername = printer.Name
138        admin = self.config.getAdmin(printername)
139        adminmail = self.config.getAdminMail(printername)
140        (mailto, arguments) = self.config.getMailTo(printername)
141
142        if user.LimitBy in ("noquota", "nochange") :
143            action = "ALLOW"
144        elif user.LimitBy == "noprint" :
145            action = "DENY"
146            message = _("User %(username)s is not allowed to print at this time.") % locals()
147            self.printInfo(message)
148            if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
149                if mailto != "EXTERNAL" :
150                    self.sendMessageToUser(admin, adminmail, user, _("Printing denied."), message)
151                else :
152                    self.externalMailTo(arguments, action, user, printer, message)
153            if mailto in [ "BOTH", "ADMIN" ] :
154                self.sendMessageToAdmin(adminmail, _("Print Quota"), message)
155        else :
156            action = self.checkUserPQuota(userpquota)
157            if action.startswith("POLICY_") :
158                action = action[7:]
159
160            if action == "DENY" :
161                adminmessage = _("Print Quota exceeded for user %(username)s on printer %(printername)s") % locals()
162                self.printInfo(adminmessage)
163                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
164                    message = self.config.getHardWarn(printername)
165                    if mailto != "EXTERNAL" :
166                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Exceeded"), message)
167                    else :
168                        self.externalMailTo(arguments, action, user, printer, message)
169                if mailto in [ "BOTH", "ADMIN" ] :
170                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
171            elif action == "WARN" :
172                adminmessage = _("Print Quota low for user %(username)s on printer %(printername)s") % locals()
173                self.printInfo(adminmessage)
174                if mailto in [ "BOTH", "USER", "EXTERNAL" ] :
175                    if user.LimitBy and (user.LimitBy.lower() == "balance") :
176                        message = self.config.getPoorWarn()
177                    else :
178                        message = self.config.getSoftWarn(printername)
179                    if mailto != "EXTERNAL" :
180                        self.sendMessageToUser(admin, adminmail, user, _("Print Quota Low"), message)
181                    else :
182                        self.externalMailTo(arguments, action, user, printer, message)
183                if mailto in [ "BOTH", "ADMIN" ] :
184                    self.sendMessageToAdmin(adminmail, _("Print Quota"), adminmessage)
185        return action
186
187    def main(self, ugnames, options) :
188        """Warn users or groups over print quota."""
189        if self.config.isAdmin :
190            # PyKota administrator
191            if not ugnames :
192                # no username, means all usernames
193                ugnames = [ "*" ]
194        else :
195            # not a PyKota administrator
196            # warns only the current user
197            # the utility of this is discutable, but at least it
198            # protects other users from mail bombing if they are
199            # over quota.
200            username = pwd.getpwuid(os.geteuid())[0].decode("ANSI_X3.4-1968", "replace")
201            if options.groups :
202                user = self.storage.getUser(username)
203                if user.Exists :
204                    ugnames = [ g.Name for g in self.storage.getUserGroups(user) ]
205                else :
206                    ugnames = [ ]
207            else :
208                ugnames = [ username ]
209
210        printername = options.printer
211        printers = self.storage.getMatchingPrinters(printername)
212        if not printers :
213            raise PyKotaCommandLineError, _("There's no printer matching %(printername)s") \
214                                                        % locals()
215        alreadydone = {}
216        for printer in printers :
217            if options.groups :
218                for (group, grouppquota) in self.storage.getPrinterGroupsAndQuotas(printer, ugnames) :
219                    self.warnGroupPQuota(grouppquota)
220            else :
221                for (user, userpquota) in self.storage.getPrinterUsersAndQuotas(printer, ugnames) :
222                    # we only want to warn users who have ever printed something
223                    # and don't want to warn users who have never printed
224                    if ((user.AccountBalance > self.config.getBalanceZero()) and \
225                       (user.AccountBalance != user.LifeTimePaid)) or \
226                       userpquota.PageCounter or userpquota.LifePageCounter or \
227                       self.storage.getUserNbJobsFromHistory(user) :
228                        done = alreadydone.get(user.Name)
229                        if (user.LimitBy == 'quota') or not done :
230                            action = self.warnUserPQuota(userpquota)
231                            if not done :
232                                alreadydone[user.Name] = (action in ('WARN', 'DENY'))
233
234if __name__ == "__main__" :
235    parser = PyKotaOptionParser(description=_("A tool to warn users and groups who have reached the limit of their printing quota."),
236                                usage="warnpykota [options] [usernames|groupnames]")
237    parser.add_option("-g", "--groups",
238                            action="store_true",
239                            dest="groups",
240                            help=_("Notify all members for all the named groups which have reached the limit of their printing quota. Without this option, individual users are notified instead of users groups."))
241    parser.add_option("-P", "--printer",
242                            dest="printer",
243                            default="*",
244                            help=_("Acts on this printer only. You can specify several printer names by separating them with commas. The default value is '%default', which means all printers."))
245
246    parser.add_example('',
247                       _("This would notify all users who have reached the limit of their printing quota on any printer."))
248    parser.add_example('--printer HP2100',
249                       _("This would notify all users who have reached the limit of their printing quota on printer 'HP2100'."))
250    parser.add_example('--groups --printer "HP*,XER*" "dev*"',
251                       _("This would notify all users of the groups whose name begins with 'dev' and for which the printing quota limit is reached on any printer whose name begins with 'HP' or 'XER'."))
252
253    run(parser, WarnPyKota)
Note: See TracBrowser for help on using the browser.