root / pykota / trunk / bin / pknotify @ 2786

Revision 2786, 11.7 kB (checked in by jerome, 19 years ago)

Combining cupspykota, pknotify and PyKotIcon? now work fine to check user's credentials.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# A notifier for PyKota
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22#
23# $Id$
24#
25#
26
27import sys
28import os
29import popen2
30import socket
31import xmlrpclib
32
33try :
34    import PAM
35except ImportError :   
36    hasPAM = 0
37else :   
38    hasPAM = 1
39
40from pykota.tool import Tool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
41
42__doc__ = N_("""pknotify v%(__version__)s (c) %(__years__)s %(__author__)s
43
44Notifies or ask questions to end users who launched the PyKotIcon application.
45
46command line usage :
47
48  pknotify  [options]  [arguments]
49
50options :
51
52  -v | --version             Prints pkbanner's version number then exits.
53  -h | --help                Prints this message then exits.
54 
55  -d | --destination h[:p]   Sets the destination hostname and optional
56                             port onto which contact the remote PyKotIcon
57                             application. This option is mandatory.
58                             When not specified, the port defaults to 7654.
59                             
60  -a | --ask                 Tells pknotify to ask something to the end
61                             user. Then pknotify will output the result.
62                       
63  -C | --checkauth           When --ask is used and both an 'username' and a
64                             'password' are asked to the end user, then
65                             pknotify will try to authenticate the user
66                             through PAM. If authentified, this program
67                             will print "AUTH=YES", else "AUTH=NO".
68                             If a field is missing, "AUTH=IMPOSSIBLE" will
69                             be printed. If the user is authenticated, then
70                             "USERNAME=xxxx" will be printed as well.
71                             
72  -c | --confirm             Tells pknotify to ask for either a confirmation                       
73                             or abortion.
74                             
75  -n | --notify              Tells pknotify to send an informational message
76                             message to the end user.
77                             
78  -q | --quit                Tells pknotify to send a message asking the
79                             PyKotIcon application to exit. This option can
80                             be combined with the other ones to make PyKotIcon
81                             exit after having sent the answer from the dialog.
82                             
83  You MUST specify either --ask, --confirm, --notify or --quit.
84
85  arguments :             
86 
87    -a | --ask : Several arguments are accepted, or the form
88                 "label:varname:defaultvalue". The result will
89                 be printed to stdout in the following format :
90                 VAR1NAME=VAR1VALUE
91                 VAR2NAME=VAR2VALUE
92                 ...
93                 If the dialog was cancelled, nothing will be
94                 printed. If one of the varname is 'password'
95                 then this field is asked as a password (you won't
96                 see what you type in), and is NOT printed. Although
97                 it is not printed, it will be used to check if
98                 authentication is valid if you specify --checkauth.
99                 
100    -c | --confirm : A single argument is expected, representing the
101                     message to display. If the dialog is confirmed
102                     then pknotify will print OK, else CANCEL.
103                     
104    -n | --notify : A single argument is expected, representing the                 
105                    message to display. In this case pknotify will
106                    always print OK.
107                   
108examples :                   
109
110  pknotify -d client:7654 --confirm "This job costs :\n10 credits !"
111 
112  Would display the cost of a print job and asks for confirmation.
113 
114  pknotify --destination $PYKOTAJOBORIGINATINGHOSTNAME:7654 \\
115           --checkauth --ask "Your name:username:" "Your password:password:"
116           
117  Asks an username and password, and checks if they are valid.         
118  NB : The PYKOTAJOBORIGINATINGHOSTNAME environment variable is
119  only set if you launch pknotify from cupspykota through a directive
120  in ~pykota/pykota.conf
121""")
122       
123class PyKotaNotify(Tool) :       
124    """A class for pknotify."""
125    def sanitizeMessage(self, msg) :
126        """Replaces \\n and returns a messagee in xmlrpclib Binary format."""
127        return xmlrpclib.Binary(msg.replace("\\n", "\n"))
128       
129    def convPAM(self, auth, queries, userdata) :
130        """Prepares PAM datas."""
131        response = []
132        for (query, qtype) in queries :
133            if qtype == PAM.PAM_PROMPT_ECHO_OFF :
134                response.append((self.password, 0))
135            elif qtype in (PAM.PAM_PROMPT_ECHO_ON, PAM.PAM_ERROR_MSG, PAM.PAM_TEXT_INFO) :
136                self.printInfo("Unexpected PAM query : %s (%s)" % (query, qtype), "warn")
137                response.append(('', 0))
138            else:
139                return None
140        return response
141
142    def checkAuth(self, username, password) :   
143        """Checks if we could authenticate an username with a password."""
144        if not hasPAM :   
145            raise PyKotaCommandLineError, _("You MUST install PyPAM for this functionnality to work !")
146        else :   
147            retcode = False
148            self.password = password
149            self.regainPriv()
150            auth = PAM.pam()
151            auth.start("passwd")
152            auth.set_item(PAM.PAM_USER, username)
153            auth.set_item(PAM.PAM_CONV, self.convPAM)
154            try :
155                auth.authenticate()
156                auth.acct_mgmt()
157            except PAM.error, resp :
158                self.printInfo(_("Authentication error for user %s : %s") % (username, resp), "warn")
159            except :
160                self.printInfo(_("Internal error : can't authenticate user %s") % username, "error")
161            else :
162                self.logdebug(_("Password correct for user %s") % username)
163                retcode = True
164            self.dropPriv()   
165            return retcode
166           
167    def main(self, arguments, options) :
168        """Notifies or asks questions to end users through PyKotIcon."""
169        try :
170            (destination, port) = options["destination"].split(":")
171        except ValueError :
172            destination = options["destination"]
173            port = 7654
174        try :   
175            server = xmlrpclib.ServerProxy("http://%s:%s" % (destination, port))
176            if options["ask"] :
177                labels = []
178                varnames = []
179                varvalues = {}
180                for arg in arguments :
181                    try :
182                        (label, varname, varvalue) = arg.split(":", 2)
183                    except ValueError :   
184                        raise PyKotaCommandLineError, "argument '%s' is invalid !" % arg
185                    labels.append(self.sanitizeMessage(label))
186                    varname = varname.lower()
187                    varnames.append(varname)
188                    varvalues[varname] = self.sanitizeMessage(varvalue)
189                result = server.askDatas(labels, varnames, varvalues)   
190                if result["isValid"] :
191                    authok = None
192                    if options["checkauth"] :
193                        if ("username" in varnames) and ("password" in varnames) :
194                            if self.checkAuth(result["username"].data, result["password"].data) :
195                                authok = "AUTH=YES"
196                            else :   
197                                authok = "AUTH=NO"
198                        else :       
199                            authok = "AUTH=IMPOSSIBLE"       
200                    for varname in varnames :
201                        if (varname != "password") \
202                           and ((varname != "username") or (authok == "AUTH=YES")) :
203                            print "%s=%s" % (varname.upper(), result[varname].data)
204                    if authok is not None :       
205                        print authok       
206            elif options["confirm"] :
207                print server.showDialog(self.sanitizeMessage(arguments[0]), True)
208            elif options["notify"] :
209                print server.showDialog(self.sanitizeMessage(arguments[0]), False)
210               
211            if options["quit"] :   
212                server.quitApplication()
213        except (socket.error, socket.gaierror), msg :
214            raise PyKotaCommandLineError, "%s : %s" % (_("Connection error"), str(msg))
215       
216if __name__ == "__main__" :
217    retcode = 0
218    try :
219        defaults = { \
220                   }
221        short_options = "vhd:acnqC"
222        long_options = ["help", "version", "destination=", \
223                        "ask", "checkauth", "confirm", "notify", "quit" ]
224       
225        # Initializes the command line tool
226        notifier = PyKotaNotify(doc=__doc__)
227        notifier.deferredInit()
228       
229        # parse and checks the command line
230        (options, args) = notifier.parseCommandline(sys.argv[1:], short_options, long_options)
231       
232        # sets long options
233        options["help"] = options["h"] or options["help"]
234        options["version"] = options["v"] or options["version"]
235        options["destination"] = options["d"] or options["destination"]
236        options["ask"] = options["a"] or options["ask"]
237        options["confirm"] = options["c"] or options["confirm"]
238        options["notify"] = options["n"] or options["notify"]
239        options["quit"] = options["q"] or options["quit"]
240        options["checkauth"] = options["C"] or options["checkauth"]
241       
242        if options["help"] :
243            notifier.display_usage_and_quit()
244        elif options["version"] :
245            notifier.display_version_and_quit()
246        elif (options["ask"] and (options["confirm"] or options["notify"])) \
247             or (options["confirm"] and (options["ask"] or options["notify"])) \
248             or (options["checkauth"] and not options["ask"]) \
249             or (options["notify"] and (options["ask"] or options["confirm"])) :
250            raise PyKotaCommandLineError, _("incompatible options, see help.")
251        elif (not options["destination"]) \
252             or not (options["quit"] or options["ask"] or options["confirm"] or options["notify"]) :
253            raise PyKotaCommandLineError, _("some options are mandatory, see help.")
254        elif (not args) and (not options["quit"]) :
255            raise PyKotaCommandLineError, _("some options require arguments, see help.")
256        else :
257            retcode = notifier.main(args, options)
258    except KeyboardInterrupt :       
259        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
260        retcode = -3
261    except PyKotaCommandLineError, msg :   
262        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
263        retcode = -2
264    except SystemExit :       
265        pass
266    except :
267        try :
268            notifier.crashed("%s failed" % sys.argv[0])
269        except :   
270            crashed("%s failed" % sys.argv[0])
271        retcode = -1
272       
273    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.