summaryrefslogtreecommitdiffstats
path: root/kdmlib/kgreeterplugin.h
blob: c4591e14b926d59697ef5dc0d4508e3b9c487a3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
/*

    Authentication method specific conversation plugin for KDE's greeter widgets

    Copyright (C) 2003 Oswald Buddenhagen <[email protected]>
    Copyright (C) 2003 Fabian Kaiser <[email protected]>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.
*/

#ifndef KGREETERPLUGIN_H
#define KGREETERPLUGIN_H

#include <tqvariant.h>
#include <tqmessagebox.h>
#include <kdemacros.h>

class KdmThemer;

class TQWidget;
class TQLayoutItem;

class KGreeterPluginHandler {
public:
    /* keep in sync with V_IS_* */
    enum { IsSecret = 1, IsUser = 2, IsPassword = 4, IsOldPassword = 8,
           IsNewPassword = 16 };
    /**
     * Reply to textPrompt().
     * @param text text to return to core; null to abort auth cycle
     * @param tag zero or one of Is*
     */
    virtual void gplugReturnText( const char *text, int tag ) = 0;
    /**
     * Reply to binaryPrompt().
     * @param data data in pam_client format to return to the core;
     *  null to abort auth cycle
     */
    virtual void gplugReturnBinary( const char *data ) = 0;
    /**
     * Tell the greeter who is logging in.
     * Call this preferably before gplugStart, as otherwise the .dmrc
     * load will be delayed. Don't call at all if your plugin doesn't
     * have the Local flag set. Call only for internally generated
     * user changes.
     * @param user the user logging in
     */
    virtual void gplugSetUser( const TQString &user ) = 0;
    /**
     * Start processing.
     */
    virtual void gplugStart() = 0;
    /**
     * Plugins that expect user input from a different device than the mouse or
     * keyboard must call this when user activity is detected to prevent the
     * greeter from resetting/going away. Events should be compressed to no
     * more than ten per second; one every five seconds is actually enough.
     * Events should be actual changes to the input fields, not random motion.
     */
    virtual void gplugActivity() = 0;
    /**
     * Show a message box on behalf of the talker.
     * @param type message severity
     * @param text message text
     */
    virtual void gplugMsgBox( TQMessageBox::Icon type, const TQString &text ) = 0;
};

/**
 * Abstract base class for conversation plugins ("talkers") to be used with
 * KDM, kdesktop_lock, etc.
 * The authentication method used by a particular instance of a plugin
 * may be configurable, but the instance must handle exactly one method,
 * i.e., info->method must be determined at the latest at init() time.
 */
class KGreeterPlugin {
public:
    KGreeterPlugin( KGreeterPluginHandler *h ) : handler( h ) {}
    virtual ~KGreeterPlugin() {}

    /**
     * Variations of the talker:
     * - Authenticate: authentication
     * - AuthChAuthTok: authentication and password change
     * - ChAuthTok: password change
     */
    enum Function { Authenticate, AuthChAuthTok, ChAuthTok };

    /**
     * Contexts the talker can be used in:
     * - Login: kdm login dialog
     * - Shutdown: kdm shutdown dialog
     * - Unlock: kdm unlock dialog (TODO)
     * - ChangeTok: kdm password change dialog (TODO)
     * - ExUnlock: kdesktop_lock unlock dialog
     * - ExChangeTok: kdepasswd password change dialog (TODO)
     *
     * The Ex* contexts exist within a running session; the talker must know
     * how to obtain the currently logged in user (+ domain/realm, etc.)
     * itself (i.e., fixedEntity will be null). The non-Ex variants will have
     * a fixedEntity passed in.
     */
    enum Context { Login, Shutdown, Unlock, ChangeTok,
                   ExUnlock, ExChangeTok };

    /**
     * Provide the talker with a list of selectable users. This can be used
     * for autocompletion, etc.
     * Will be called only when not running.
     * @param users the users to load.
     */
    virtual void loadUsers( const TQStringList &users ) = 0;

    /**
     * Preload the talker with an (opaque to the greeter) entity.
     * Will be called only when not running.
     * @param entity the entity to preload the talker with. That
     *  will usually be something like "user" or "user@domain".
     * @param field the sub-widget (probably line edit) to put the cursor into.
     *  If -1, preselect the user for timed login. This means pre-filling
     *  the password field with anything, disabling it, and placing the
     *  cursor in the user name field.
     */
    virtual void presetEntity( const TQString &entity, int field ) = 0;

    /**
     * Obtain the actually logged in entity.
     * Will be called only after succeeded() was called.
     */
    virtual TQString getEntity() const = 0;

    /**
     * "Push" a user into the talker. That can be a click into the user list
     * or successful authentication without the talker calling gplugSetUser.
     * Will be called only when running.
     * @param user the user to set. Note that this is a UNIX login, not a
     *  canonical entity
     */
    virtual void setUser( const TQString &user ) = 0;

    /**
     * En-/disable any widgets contained in the talker.
     * Will be called only when not running.
     * @param on the state to set
     */
    virtual void setEnabled( bool on ) = 0;

    /**
     * Called when a message from the authentication backend arrives.
     * @param message the message received from the backend
     * @param error if true, @p message is an error message, otherwise it's
     *  an informational message
     * @return true means that the talker already handled the message, false
     *  that the greeter should display it in a message box
     *
     * FIXME: Filtering a message usually means that the backend issued a
     * prompt and obtains the authentication data itself. However, in that
     * state the backend is unresponsive, e.g., no shutdown is possible.
     * The frontend could send the backend a signal, but the "escape path"
     * within the backend is unclear (PAM won't like simply longjmp()ing
     * out of it).
     */
    virtual bool textMessage( const char *message, bool error ) = 0;

    /**
     * Prompt the user for data. Reply by calling handler->gplugReturnText().
     * @param propmt the prompt to display. It may be null, in which case
     *  "Username"/"Password" should be shown and the replies should be tagged
     *  with the respective Is* flag.
     * @param echo if true, a normal input widget can be used, otherwise one that
     *  visually obscures the user's input.
     * @param nonBlocking if true, report whatever is already available,
     *  otherwise wait for user input.
     */
    virtual void textPrompt( const char *prompt, bool echo, bool nonBlocking ) = 0;

    /**
     * Request binary authentication data from the talker. Reply by calling
     * handler->gplugReturnBinary().
     * @param prompt prompt in pam_client format
     * @param nonBlocking if true, report whatever is already available,
     *  otherwise wait for user input.
     * @return always true for now
     *
     * TODO:
     * @return if true, the prompt was handled by the talker, otherwise the
     *  handler has to use libpam_client to obtain the authentication data.
     *  In that state the talker still can abort the data fetch by
     *  gplugReturn()ing a null array. When the data was obtained, another
     *  binaryPrompt with a null prompt will be issued.
     */
    virtual bool binaryPrompt( const char *prompt, bool nonBlocking ) = 0;

    /**
     * This can either
     *  - Start a processing cycle. Will be called only when not running.
     *  - Restart authTok cycle - will be called while running and implies
     *    revive(). PAM is a bit too clever, so we need this.
     * In any case the talker is running afterwards.
     */
    virtual void start() = 0;

    /**
     * Request to suspend the auth. Make sure that a second talker of any
     * type will be able to operate while this one is suspended (no busy
     * device nodes, etc.).
     * Will be called only if running within Login context. (Actually it
     * won't be called at all, but be prepared.)
     */
    virtual void suspend() = 0;

    /**
     * Request to resume the auth from the point it was suspended at.
     * Will be called only when suspended.
     */
    virtual void resume() = 0;

    /**
     * The "login" button was pressed in the greeter.
     * This might call gplugReturn* or gplugStart.
     * Will be called only when running.
     */
    virtual void next() = 0;

    /**
     * Abort auth cycle. Note that this should _not_ clear out already
     * entered auth tokens if they are still on the screen.
     * Will be called only when running and stops it.
     */
    virtual void abort() = 0;

    /**
     * Indicate successful end of the current phase.
     * This is more or less a request to disable editable widgets
     * responsible for the that phase.
     * There will be no further attempt to enter that phase until the
     * widget is destroyed.
     * Will be called only when running and stops it.
     */
    virtual void succeeded() = 0;

    /**
     * Indicate unsuccessful end of the current phase.
     * This is mostly a request to disable all editable widgets.
     * The widget will be treated as dead until revive() is called.
     * Will be called only when running and stops it.
     */
    virtual void failed() = 0;

    /**
     * Prepare retrying the previously failed phase.
     * This is mostly a request to re-enable all editable widgets failed()
     * disabled previously, clear the probably incorrect authentication tokens
     * and to set the input focus appropriately.
     * Will be called only after failed() (possibly with clear() in between),
     * or after presetEntity() with field -1.
     */
    virtual void revive() = 0;

    /**
     * Clear any edit widgets, particularily anything set by setUser.
     * Will be called only when not running.
     */
    virtual void clear() = 0;

    /**
     * Obtain the TQLayoutItem containg the widget(s) to actually handle the
     * conversation. See TQLayout and TQWidgetItem for possible implementations.
     */
    TQLayoutItem *getLayoutItem() const { return tqlayoutItem; }

protected:
    KGreeterPluginHandler *handler;
    TQLayoutItem *tqlayoutItem;
};

struct KDE_EXPORT kgreeterplugin_info {
    /**
     * Human readable name of this plugin (should be a little more
     * informative than just the libary name). Must be I18N_NOOP()ed.
     */
    const char *name;

    /**
     * The authentication method to use - the meaning is up to the backend,
     * but will usually be related to the PAM service.
     */
    const char *method;

    /**
     * Capabilities.
     */
    enum {
        /**
         * All users exist on the local system permanently (will be listed
         * by getpwent()); an entity corresponds to a UNIX user.
         */
        Local = 1,
        /**
         * The entities consist of multiple fields.
         * PluginOptions/<plugin>.FocusField is used instead of FocusPasswd.
         */
        Fielded = 2,
        /**
         * An entity can be preset, the talker has a widget where a user can
         * be selected explicitly. If the method is "classic", timed login
         * is possible, too.
         * This also means that setUser/gplugSetUser can be used and a
         * userlist can be shown at all - provided Local is set as well.
         */
        Presettable = 4
    };

    /*
     * Capability flags.
     */
    int flags;

    /**
     * Call after loading the plugin.
     *
     * @param method if non-empty and the plugin is unable to handle that
     *  method, return false. If the plugin has a constant method defined
     *  above, it can ignore this parameter.
     * @param getConf can be used to obtain configuration items from the
     *  greeter; you have to pass it the @p ctx pointer.
     *   The only predefined key (in KDM) is "EchoMode", which is an int
     *   (in fact, KPasswordEdit::EchoModes).
     *   Other keys are obtained from the PluginOptions option; see kdmrc
     *   for details.
     *   If the key is unknown, dflt is returned.
     * @param ctx context pointer for @p getConf
     * @return if false, unload the plugin again (don't call done() first)
     */
    bool (*init)( const TQString &method,
                  TQVariant (*getConf)( void *ctx, const char *key,
                                       const TQVariant &dflt ),
                  void *ctx );

    /**
     * Call before unloading the plugin.
     * This pointer can be null.
     */
    void (*done)( void );

    /**
     * Factory method to create an instance of the plugin.
     * Note that multiple instances can exist at one time, but only
     * one of them is active at any moment (the others would be suspended
     * or not running at all).
     * @param handler the object offering the necessary callbacks
     * @param parent parent widget
     * @param predecessor the focus widget before the conversation widget
     * @param fixedEntity see below
     * @param func see below
     * @param ctx see below
     * @return an instance of this conversation plugin
     *
     * Valid combinations of Function and Context:
     * - Authenticate:Login - init
     * - Authenticate:Shutdown - init, for now "root" is passed as fixedEntitiy
     *  and it is not supposed to be displayed. Plugins with Local not set
     *  might have to conjure something up to make getEntity() return a
     *  canonical entitiy. FIXME: don't restrict shutdown to root.
     * - AuthChAuthTok:Login, AuthChAuthTok:Shutdown - cont/cont,
     *  only relevant for classic method (as it is relevant only for password-
     *  less logins, which always use classic). The login should not be shown -
     *  it is known to the user already; the backend won't ask for it, either.
     * - ChAuthTok:Login & ChAuthTok:Shutdown - cont
     * - Authenticate:Unlock & Authenticate:ExUnlock - init,
     *   AuthChAuthTok:ChangeTok & AuthChAuthTok:ExChangeTok - init/cont,
     *  display fixedEntity as labels. The backend does not ask for the UNIX
     *  login, as it already knows it - but it will ask for all components of
     *  the entity if it is no UNIX login.
     *
     * "init" means that the plugin is supposed to call gplugStart, "cont"
     * that the backend is already in a cycle of the method the plugin was
     * initialized with.
     */
    KGreeterPlugin *(*create)( KGreeterPluginHandler *handler,
                               KdmThemer *themer,
                               TQWidget *parent, TQWidget *predecessor,
                               const TQString &fixedEntity,
                               KGreeterPlugin::Function func,
                               KGreeterPlugin::Context ctx );
};

#endif