1/*
2
3 Authentication method specific conversation plugin for KDE's greeter widgets
4
5 Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org>
6 Copyright (C) 2003 Fabian Kaiser <xfk@softpro.de>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22*/
23
24#ifndef KGREETERPLUGIN_H
25#define KGREETERPLUGIN_H
26
27#include <QtCore/QVariant>
28#include <QtGui/QMessageBox>
29#include <kdemacros.h>
30
31class QWidget;
32
33class KGreeterPluginHandler {
34public:
35 virtual ~KGreeterPluginHandler() {}
36 /* keep in sync with V_IS_* */
37 enum { IsSecret = 1, IsUser = 2, IsPassword = 4, IsOldPassword = 8,
38 IsNewPassword = 16 };
39 /**
40 * Reply to textPrompt().
41 * @param text text to return to core; null to abort auth cycle
42 * @param tag zero or one of Is*
43 */
44 virtual void gplugReturnText(const char *text, int tag) = 0;
45 /**
46 * Reply to binaryPrompt().
47 * @param data data in pam_client format to return to the core;
48 * null to abort auth cycle
49 */
50 virtual void gplugReturnBinary(const char *data) = 0;
51 /**
52 * Tell the greeter who is logging in.
53 * Call this preferably before gplugStart, as otherwise the .dmrc
54 * load will be delayed. Don't call at all if your plugin doesn't
55 * have the Local flag set. Call only for internally generated
56 * user changes.
57 * @param user the user logging in
58 */
59 virtual void gplugSetUser(const QString &user) = 0;
60 /**
61 * Start processing.
62 */
63 virtual void gplugStart() = 0;
64 /**
65 * This should be called each time the talker changes in any way from the
66 * pristine state after an authentication cycle starts, so the greeter
67 * knows it must reset the fields after some time of inactivity.
68 */
69 virtual void gplugChanged() = 0;
70 /**
71 * Plugins that expect user input from a different device than the mouse or
72 * keyboard must call this when user activity is detected to prevent the
73 * greeter from resetting/going away. Events should be compressed to no
74 * more than ten per second; one every five seconds is actually enough.
75 * Events should be actual changes to the input fields, not random motion.
76 */
77 virtual void gplugActivity() = 0;
78 /**
79 * Show a message box on behalf of the talker.
80 * @param type message severity
81 * @param text message text
82 */
83 virtual void gplugMsgBox(QMessageBox::Icon type, const QString &text) = 0;
84 /**
85 * Determine if the named widget is welcomed.
86 * @param id the widget name
87 */
88 virtual bool gplugHasNode(const QString &id) = 0;
89};
90
91/**
92 * Abstract base class for conversation plugins ("talkers") to be used with
93 * KDM, kdesktop_lock, etc.
94 * The authentication method used by a particular instance of a plugin
95 * may be configurable, but the instance must handle exactly one method,
96 * i.e., info->method must be determined at the latest at init() time.
97 */
98class KGreeterPlugin {
99public:
100 KGreeterPlugin(KGreeterPluginHandler *h) : handler(h) {}
101 virtual ~KGreeterPlugin() {}
102
103 /**
104 * Variations of the talker:
105 * - Authenticate: authentication
106 * - AuthChAuthTok: authentication and password change
107 * - ChAuthTok: password change
108 */
109 enum Function { Authenticate, AuthChAuthTok, ChAuthTok };
110
111 /**
112 * Contexts the talker can be used in:
113 * - Login: kdm login dialog
114 * - Shutdown: kdm shutdown dialog
115 * - Unlock: kdm unlock dialog (TODO)
116 * - ChangeTok: kdm password change dialog (TODO)
117 * - ExUnlock: kdesktop_lock unlock dialog
118 * - ExChangeTok: kdepasswd password change dialog (TODO)
119 *
120 * The Ex* contexts exist within a running session; the talker must know
121 * how to obtain the currently logged in user (+ domain/realm, etc.)
122 * itself (i.e., fixedEntity will be null). The non-Ex variants will have
123 * a fixedEntity passed in.
124 */
125 enum Context { Login, Shutdown, Unlock, ChangeTok,
126 ExUnlock, ExChangeTok };
127
128 /**
129 * Provide the talker with a list of selectable users. This can be used
130 * for autocompletion, etc.
131 * Will be called only when not running.
132 * @param users the users to load.
133 */
134 virtual void loadUsers(const QStringList &users) = 0;
135
136 /**
137 * Preload the talker with an (opaque to the greeter) entity.
138 * Will be called only when not running.
139 * @param entity the entity to preload the talker with. That
140 * will usually be something like "user" or "user@domain".
141 * @param field the sub-widget (probably line edit) to put the cursor into.
142 * If -1, preselect the user for timed login. This means pre-filling
143 * the password field with anything, disabling it, and placing the
144 * cursor in the user name field.
145 */
146 virtual void presetEntity(const QString &entity, int field) = 0;
147
148 /**
149 * Obtain the actually logged in entity.
150 * Will be called only after succeeded() was called.
151 */
152 virtual QString getEntity() const = 0;
153
154 /**
155 * "Push" a user into the talker. That can be a click into the user list
156 * or successful authentication without the talker calling gplugSetUser.
157 * Will be called only when running.
158 * @param user the user to set. Note that this is a UNIX login, not a
159 * canonical entity
160 */
161 virtual void setUser(const QString &user) = 0;
162
163 /**
164 * En-/disable any widgets contained in the talker.
165 * Will be called only when not running.
166 * @param on the state to set
167 */
168 virtual void setEnabled(bool on) = 0;
169
170 /**
171 * Called when a message from the authentication backend arrives.
172 * @param message the message received from the backend
173 * @param error if true, @p message is an error message, otherwise it's
174 * an informational message
175 * @return true means that the talker already handled the message, false
176 * that the greeter should display it in a message box
177 *
178 * FIXME: Filtering a message usually means that the backend issued a
179 * prompt and obtains the authentication data itself. However, in that
180 * state the backend is unresponsive, e.g., no shutdown is possible.
181 * The frontend could send the backend a signal, but the "escape path"
182 * within the backend is unclear (PAM won't like simply longjmp()ing
183 * out of it).
184 */
185 virtual bool textMessage(const char *message, bool error) = 0;
186
187 /**
188 * Prompt the user for data. Reply by calling handler->gplugReturnText().
189 * @param propmt the prompt to display. It may be null, in which case
190 * "Username"/"Password" should be shown and the replies should be tagged
191 * with the respective Is* flag.
192 * @param echo if true, a normal input widget can be used, otherwise one that
193 * visually obscures the user's input.
194 * @param nonBlocking if true, report whatever is already available,
195 * otherwise wait for user input.
196 */
197 virtual void textPrompt(const char *prompt, bool echo, bool nonBlocking) = 0;
198
199 /**
200 * Request binary authentication data from the talker. Reply by calling
201 * handler->gplugReturnBinary().
202 * @param prompt prompt in pam_client format
203 * @param nonBlocking if true, report whatever is already available,
204 * otherwise wait for user input.
205 * @return always true for now
206 *
207 * TODO:
208 * @return if true, the prompt was handled by the talker, otherwise the
209 * handler has to use libpam_client to obtain the authentication data.
210 * In that state the talker still can abort the data fetch by
211 * gplugReturn()ing a null array. When the data was obtained, another
212 * binaryPrompt with a null prompt will be issued.
213 */
214 virtual bool binaryPrompt(const char *prompt, bool nonBlocking) = 0;
215
216 /**
217 * This can either
218 * - Start a processing cycle. Will be called only when not running.
219 * - Restart authTok cycle - will be called while running and implies
220 * revive(). PAM is a bit too clever, so we need this.
221 * In any case the talker is running afterwards.
222 */
223 virtual void start() = 0;
224
225 /**
226 * Request to suspend the auth. Make sure that a second talker of any
227 * type will be able to operate while this one is suspended (no busy
228 * device nodes, etc.).
229 * Will be called only if running within Login context. (Actually it
230 * won't be called at all, but be prepared.)
231 */
232 virtual void suspend() = 0;
233
234 /**
235 * Request to resume the auth from the point it was suspended at.
236 * Will be called only when suspended.
237 */
238 virtual void resume() = 0;
239
240 /**
241 * The "login" button was pressed in the greeter.
242 * This might call gplugReturn* or gplugStart.
243 * Will be called only when running.
244 */
245 virtual void next() = 0;
246
247 /**
248 * Abort auth cycle. Note that this should _not_ clear out already
249 * entered auth tokens if they are still on the screen.
250 * Will be called only when running and stops it.
251 */
252 virtual void abort() = 0;
253
254 /**
255 * Indicate successful end of the current phase.
256 * This is more or less a request to disable editable widgets
257 * responsible for the that phase.
258 * There will be no further attempt to enter that phase until the
259 * widget is destroyed or revived.
260 * Will be called only when running and stops it.
261 */
262 virtual void succeeded() = 0;
263
264 /**
265 * Indicate unsuccessful end of the current phase.
266 * This is mostly a request to disable all editable widgets.
267 * The widget will be treated as dead until revive() is called.
268 * Will be called only when running and stops it.
269 */
270 virtual void failed() = 0;
271
272 /**
273 * Prepare retrying the previously failed phase.
274 * This is mostly a request to re-enable all editable widgets failed()
275 * disabled previously, clear the probably incorrect authentication tokens
276 * and to set the input focus appropriately.
277 * Will be called only after failed() (possibly with clear() in between),
278 * after succeeded() (after an aborted forced authentication token change),
279 * or after presetEntity() with field -1.
280 */
281 virtual void revive() = 0;
282
283 /**
284 * Clear any edit widgets, particularly anything set by setUser.
285 * Will be called only when not running.
286 */
287 virtual void clear() = 0;
288
289 typedef QList<QWidget *> WidgetList;
290
291 /**
292 * Obtain the QWidget to actually handle the conversation.
293 */
294 const WidgetList &getWidgets() const { return widgetList; }
295
296protected:
297 KGreeterPluginHandler *handler;
298 WidgetList widgetList;
299};
300
301struct KDE_EXPORT KGreeterPluginInfo {
302 /**
303 * Human readable name of this plugin (should be a little more
304 * informative than just the libary name). Must be I18N_NOOP()ed.
305 */
306 const char *name;
307
308 /**
309 * The authentication method to use - the meaning is up to the backend,
310 * but will usually be related to the PAM service.
311 */
312 const char *method;
313
314 /**
315 * Capabilities.
316 */
317 enum {
318 /**
319 * All users exist on the local system permanently (will be listed
320 * by getpwent()); an entity corresponds to a UNIX user.
321 */
322 Local = 1,
323 /**
324 * The entities consist of multiple fields.
325 * PluginOptions/<plugin>.FocusField is used instead of FocusPasswd.
326 */
327 Fielded = 2,
328 /**
329 * An entity can be preset, the talker has a widget where a user can
330 * be selected explicitly. If the method is "classic", timed login
331 * is possible, too.
332 * This also means that setUser/gplugSetUser can be used and a
333 * userlist can be shown at all - provided Local is set as well.
334 */
335 Presettable = 4
336 };
337
338 /*
339 * Capability flags.
340 */
341 int flags;
342
343 /**
344 * Call after loading the plugin.
345 *
346 * @param method if non-empty and the plugin is unable to handle that
347 * method, return false. If the plugin has a constant method defined
348 * above, it can ignore this parameter.
349 * @param getConf can be used to obtain configuration items from the
350 * greeter; you have to pass it the @p ctx pointer.
351 * The only predefined key (in KDM) is "EchoMode", which is an int
352 * (in fact, QLineEdit::EchoModes).
353 * Other keys are obtained from the PluginOptions option; see kdmrc
354 * for details.
355 * If the key is unknown, dflt is returned.
356 * @param ctx context pointer for @p getConf
357 * @return if false, unload the plugin again (don't call done() first)
358 */
359 bool (*init)(const QString &method,
360 QVariant(*getConf)(void *ctx, const char *key,
361 const QVariant &dflt),
362 void *ctx);
363
364 /**
365 * Call before unloading the plugin.
366 * This pointer can be null.
367 */
368 void (*done)(void);
369
370 /**
371 * Factory method to create an instance of the plugin.
372 * Note that multiple instances can exist at one time, but only
373 * one of them is active at any moment (the others would be suspended
374 * or not running at all).
375 * @param handler the object offering the necessary callbacks
376 * @param parent parent widget
377 * @param predecessor the focus widget before the conversation widget
378 * @param fixedEntity see below
379 * @param func see below
380 * @param ctx see below
381 * @return an instance of this conversation plugin
382 *
383 * Valid combinations of Function and Context:
384 * - Authenticate:Login - init
385 * - Authenticate:Shutdown - init, for now "root" is passed as fixedEntitiy
386 * and it is not supposed to be displayed. Plugins with Local not set
387 * might have to conjure something up to make getEntity() return a
388 * canonical entitiy. FIXME: don't restrict shutdown to root.
389 * - AuthChAuthTok:Login, AuthChAuthTok:Shutdown - cont/cont,
390 * only relevant for classic method (as it is relevant only for password-
391 * less logins, which always use classic). The login should not be shown -
392 * it is known to the user already; the backend won't ask for it, either.
393 * - ChAuthTok:Login & ChAuthTok:Shutdown - cont
394 * - Authenticate:Unlock & Authenticate:ExUnlock - init,
395 * AuthChAuthTok:ChangeTok & AuthChAuthTok:ExChangeTok - init/cont,
396 * display fixedEntity as labels. The backend does not ask for the UNIX
397 * login, as it already knows it - but it will ask for all components of
398 * the entity if it is no UNIX login.
399 *
400 * "init" means that the plugin is supposed to call gplugStart, "cont"
401 * that the backend is already in a cycle of the method the plugin was
402 * initialized with (it does not hurt if gplugStart is still called).
403 */
404 KGreeterPlugin *(*create)(KGreeterPluginHandler *handler,
405 QWidget *parent,
406 const QString &fixedEntity,
407 KGreeterPlugin::Function func,
408 KGreeterPlugin::Context ctx);
409};
410
411#endif
412