Statistics
| Revision:

root / src / passphrase.c

History | View | Annotate | Download (9.9 kB)

1
/* passphrase.c - GTK+ based passphrase callback
2
 *      Copyright (C) 2001 Werner Koch (dd9jn)
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 2 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
 */
18
19
#ifdef HAVE_CONFIG_H
20
#  include <config.h>
21
#endif
22
23
#if USE_GPGME
24
25
#include <glib.h>
26
#include <glib/gi18n.h>
27
#include <gdk/gdktypes.h>
28
#include <gdk/gdkkeysyms.h>
29
#include <gdk/gdkdisplay.h>
30
#ifdef GDK_WINDOWING_X11
31
#  include <gdk/gdkx.h>
32
#endif /* GDK_WINDOWING_X11 */
33
#include <gtk/gtkmain.h>
34
#include <gtk/gtkwidget.h>
35
#include <gtk/gtkwindow.h>
36
#include <gtk/gtkvbox.h>
37
#include <gtk/gtktable.h>
38
#include <gtk/gtklabel.h>
39
#include <gtk/gtkentry.h>
40
#include <gtk/gtkhbbox.h>
41
#include <gtk/gtkbutton.h>
42
#include <gtk/gtksignal.h>
43
#include <gtk/gtkstock.h>
44
#include <string.h>
45
#include <sys/types.h>
46
#if HAVE_SYS_MMAN_H
47
#  include <sys/mman.h>
48
#endif
49
50
#ifdef G_OS_WIN32
51
#  include <windows.h>
52
#endif
53
54
#include "passphrase.h"
55
#include "prefs_common.h"
56
#include "manage_window.h"
57
#include "utils.h"
58
#include "gtkutils.h"
59
60
static gboolean grab_all = FALSE;
61
62
static gboolean pass_ack;
63
static gchar *last_pass = NULL;
64
65
static void passphrase_ok_cb(GtkWidget *widget, gpointer data);
66
static void passphrase_cancel_cb(GtkWidget *widget, gpointer data);
67
static gint passphrase_deleted(GtkWidget *widget, GdkEventAny *event,
68
                               gpointer data);
69
static gboolean passphrase_key_pressed(GtkWidget *widget, GdkEventKey *event,
70
                                       gpointer data);
71
static gchar* passphrase_mbox(const gchar *uid_hint, const gchar *pass_hint,
72
                              gint prev_bad);
73
74
static GtkWidget *create_description(const gchar *uid_hint,
75
                                     const gchar *pass_hint, gint prev_bad);
76
77
void
78
gpgmegtk_set_passphrase_grab(gint yes)
79
{
80
    grab_all = yes;
81
}
82
83
static gchar*
84
passphrase_mbox(const gchar *uid_hint, const gchar *pass_hint, gint prev_bad)
85
{
86
    gchar *the_passphrase = NULL;
87
    GtkWidget *vbox;
88
    GtkWidget *confirm_box;
89
    GtkWidget *window;
90
    GtkWidget *pass_entry;
91
    GtkWidget *ok_button;
92
    GtkWidget *cancel_button;
93
94
    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
95
    gtk_window_set_title(GTK_WINDOW(window), _("Passphrase"));
96
    gtk_widget_set_size_request(window, 450, -1);
97
    gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
98
    gtk_window_set_modal(GTK_WINDOW(window), TRUE);
99
    gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, FALSE);
100
    g_signal_connect(G_OBJECT(window), "delete_event",
101
                     G_CALLBACK(passphrase_deleted), NULL);
102
    g_signal_connect(G_OBJECT(window), "key_press_event",
103
                     G_CALLBACK(passphrase_key_pressed), NULL);
104
    MANAGE_WINDOW_SIGNALS_CONNECT(window);
105
    manage_window_set_transient(GTK_WINDOW(window));
106
107
    vbox = gtk_vbox_new(FALSE, 8);
108
    gtk_container_add(GTK_CONTAINER(window), vbox);
109
    gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
110
111
    if (uid_hint || pass_hint) {
112
        GtkWidget *label;
113
        label = create_description (uid_hint, pass_hint, prev_bad);
114
        gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
115
    }
116
117
    pass_entry = gtk_entry_new();
118
    gtk_box_pack_start(GTK_BOX(vbox), pass_entry, FALSE, FALSE, 0);
119
    gtk_entry_set_visibility(GTK_ENTRY(pass_entry), FALSE);
120
    gtk_widget_grab_focus(pass_entry);
121
122
    gtkut_stock_button_set_create(&confirm_box, &ok_button, GTK_STOCK_OK,
123
                                  &cancel_button, GTK_STOCK_CANCEL,
124
                                  NULL, NULL);
125
    gtk_box_pack_end(GTK_BOX(vbox), confirm_box, FALSE, FALSE, 0);
126
    gtk_widget_grab_default(ok_button);
127
128
    g_signal_connect(G_OBJECT(ok_button), "clicked",
129
                     G_CALLBACK(passphrase_ok_cb), NULL);
130
    g_signal_connect(G_OBJECT(pass_entry), "activate",
131
                     G_CALLBACK(passphrase_ok_cb), NULL);
132
    g_signal_connect(G_OBJECT(cancel_button), "clicked",
133
                     G_CALLBACK(passphrase_cancel_cb), NULL);
134
135
    gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER);
136
    if (grab_all)   
137
        gtk_window_set_policy (GTK_WINDOW(window), FALSE, FALSE, TRUE);
138
    
139
    gtk_widget_show_all(window);
140
141
    if (grab_all) {
142
        /* make sure that window is viewable
143
         * FIXME: this is still not enough */
144
        gtk_widget_show_now(window);
145
        gdk_flush();
146
#ifdef GDK_WINDOWING_X11
147
        gdk_x11_display_grab(gdk_display_get_default());
148
#endif /* GDK_WINDOWING_X11 */
149
        if (gdk_pointer_grab(window->window, TRUE, 0,
150
                             window->window, NULL, GDK_CURRENT_TIME)) {
151
#ifdef GDK_WINDOWING_X11
152
            gdk_x11_display_ungrab(gdk_display_get_default());
153
#endif /* GDK_WINDOWING_X11 */
154
            g_warning("OOPS: Could not grab mouse\n");
155
            gtk_widget_destroy(window);
156
            return NULL;
157
        }
158
        if (gdk_keyboard_grab(window->window, FALSE, GDK_CURRENT_TIME)) {
159
            gdk_display_pointer_ungrab(gdk_display_get_default(),
160
                                        GDK_CURRENT_TIME);
161
#ifdef GDK_WINDOWING_X11
162
            gdk_x11_display_ungrab(gdk_display_get_default());
163
#endif /* GDK_WINDOWING_X11 */
164
            g_warning("OOPS: Could not grab keyboard\n");
165
            gtk_widget_destroy(window);
166
            return NULL;
167
        }
168
    }
169
170
    gtk_main();
171
172
    if (grab_all) {
173
        gdk_display_keyboard_ungrab(gdk_display_get_default(),
174
                                    GDK_CURRENT_TIME);
175
        gdk_display_pointer_ungrab(gdk_display_get_default(), GDK_CURRENT_TIME);
176
#ifdef GDK_WINDOWING_X11
177
        gdk_x11_display_ungrab(gdk_display_get_default());
178
#endif /* GDK_WINDOWING_X11 */
179
        gdk_flush();
180
    }
181
182
    manage_window_focus_out(window, NULL, NULL);
183
184
    if (pass_ack) {
185
        const gchar *entry_text;
186
        entry_text = gtk_entry_get_text(GTK_ENTRY(pass_entry));
187
        the_passphrase = g_locale_from_utf8(entry_text, -1, NULL, NULL, NULL);
188
        if (!the_passphrase)
189
            the_passphrase = g_strdup(entry_text);
190
    }
191
    gtk_widget_destroy(window);
192
193
    return the_passphrase;
194
}
195
196
197
static void 
198
passphrase_ok_cb(GtkWidget *widget, gpointer data)
199
{
200
    pass_ack = TRUE;
201
    gtk_main_quit();
202
}
203
204
static void 
205
passphrase_cancel_cb(GtkWidget *widget, gpointer data)
206
{
207
    pass_ack = FALSE;
208
    gtk_main_quit();
209
}
210
211
212
static gint
213
passphrase_deleted(GtkWidget *widget, GdkEventAny *event, gpointer data)
214
{
215
    passphrase_cancel_cb(NULL, NULL);
216
    return TRUE;
217
}
218
219
220
static gboolean
221
passphrase_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
222
{
223
    if (event && event->keyval == GDK_Escape)
224
        passphrase_cancel_cb(NULL, NULL);
225
    return FALSE;
226
}
227
228
static gint 
229
linelen (const gchar *s)
230
{
231
    gint i;
232
233
    for (i = 0; *s && *s != '\n'; s++, i++)
234
        ;
235
236
    return i;
237
}
238
239
static GtkWidget *
240
create_description(const gchar *uid_hint, const gchar *pass_hint, gint prev_bad)
241
{
242
    const gchar *uid = NULL, *info = NULL;
243
    gchar *buf;
244
    GtkWidget *label;
245
246
    if (!uid_hint)
247
        uid = _("[no user id]");
248
    else
249
        uid = uid_hint;
250
    if (!pass_hint)
251
        info = "";
252
    else
253
        info = pass_hint;
254
255
    buf = g_strdup_printf (_("%sPlease enter the passphrase for:\n\n"
256
                           "  %.*s  \n"
257
                           "(%.*s)\n"),
258
                           prev_bad ?
259
                           _("Bad passphrase! Try again...\n\n") : "",
260
                           linelen (uid), uid, linelen (info), info);
261
262
    label = gtk_label_new (buf);
263
    gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
264
    g_free (buf);
265
266
    return label;
267
}
268
269
static int free_passphrase(gpointer _unused)
270
{
271
    if (last_pass != NULL) {
272
#if HAVE_MLOCK
273
        munlock(last_pass, strlen(last_pass));
274
#endif
275
        g_free(last_pass);
276
        last_pass = NULL;
277
        debug_print("%% passphrase removed\n");
278
    }
279
    
280
    return FALSE;
281
}
282
283
gpgme_error_t
284
gpgmegtk_passphrase_cb(void *opaque, const char *uid_hint,
285
        const char *passphrase_hint, int prev_bad, int fd)
286
{
287
    const char *pass;
288
#ifdef G_OS_WIN32
289
    HANDLE hd = (HANDLE)fd;
290
    DWORD n;
291
#endif
292
293
    if (prefs_common.store_passphrase && last_pass != NULL && !prev_bad) {
294
#ifdef G_OS_WIN32
295
        WriteFile(hd, last_pass, strlen(last_pass), &n, NULL);
296
        WriteFile(hd, "\n", 1, &n, NULL);
297
#else
298
        write(fd, last_pass, strlen(last_pass));
299
        write(fd, "\n", 1);
300
#endif
301
        return GPG_ERR_NO_ERROR;
302
    }
303
    gpgmegtk_set_passphrase_grab (prefs_common.passphrase_grab);
304
    debug_print ("%% requesting passphrase for `%s':\n", uid_hint);
305
    pass = passphrase_mbox (uid_hint, passphrase_hint, prev_bad);
306
    gpgmegtk_free_passphrase();
307
    if (!pass) {
308
        debug_print ("%% cancel passphrase entry\n");
309
#ifdef G_OS_WIN32
310
        WriteFile(hd, "\n", 1, &n, NULL);
311
        CloseHandle(hd); /* somehow it will block without this */
312
#else
313
        write(fd, "\n", 1);
314
#endif
315
        return GPG_ERR_CANCELED;
316
    }
317
    else {
318
        if (prefs_common.store_passphrase) {
319
            last_pass = g_strdup(pass);
320
#if HAVE_MLOCK
321
            if (mlock(last_pass, strlen(last_pass)) == -1)
322
                debug_print("%% locking passphrase failed\n");
323
#endif
324
325
            if (prefs_common.store_passphrase_timeout > 0) {
326
                g_timeout_add_full(G_PRIORITY_LOW, prefs_common.store_passphrase_timeout * 60 * 1000, free_passphrase, NULL, NULL);
327
            }
328
        }
329
        debug_print ("%% sending passphrase\n");
330
    }
331
#ifdef G_OS_WIN32
332
    WriteFile(hd, pass, strlen(pass), &n, NULL);
333
    WriteFile(hd, "\n", 1, &n, NULL);
334
#else
335
    write(fd, pass, strlen(pass));
336
    write(fd, "\n", 1);
337
#endif
338
    return GPG_ERR_NO_ERROR;
339
}
340
341
void gpgmegtk_free_passphrase()
342
{
343
    (void)free_passphrase(NULL); // could be inline
344
}
345
346
#endif /* USE_GPGME */