2
Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at>
3
Copyright (C) 2001, 2002, 2007 g10 Code GmbH
4
Copyright (C) 2004 by Albrecht Dre� <albrecht.dress@arcor.de>
6
pinentry-gtk-2 is a pinentry application for the Gtk+-2 widget set.
7
It tries to follow the Gnome Human Interface Guide as close as
10
This program is free software; you can redistribute it and/or modify
11
it under the terms of the GNU General Public License as published by
12
the Free Software Foundation; either version 2 of the License, or
13
(at your option) any later version.
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
GNU General Public License for more details.
20
You should have received a copy of the GNU General Public License
21
along with this program; if not, write to the Free Software
22
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
38
#endif /* HAVE_GETOPT_H */
40
#include "gtksecentry.h"
43
#ifdef FALLBACK_CURSES
44
#include "pinentry-curses.h"
48
#define PGMNAME "pinentry-gtk2"
54
static pinentry_t pinentry;
55
static int passphrase_ok;
56
static int confirm_yes;
58
static GtkWidget *entry;
59
static GtkWidget *qualitybar;
60
static GtkWidget *insure;
61
static GtkWidget *time_out;
62
static GtkTooltips *tooltips;
64
/* Gnome hig small and large space in pixels. */
68
/* The text shown in the quality bar when no text is shown. This is not
69
* the empty string, becase because with an empty string the height of
70
* the quality bar is less than with a non-empty string. This results
71
* in ugly layout changes when the text changes from non-empty to empty
73
#define QUALITYBAR_EMPTY_TEXT " "
76
/* Constrain size of the window the window should not shrink beyond
77
the requisition, and should not grow vertically. */
79
constrain_size (GtkWidget *win, GtkRequisition *req, gpointer data)
81
static gint width, height;
84
if (req->width == width && req->height == height)
88
geo.min_width = width;
89
/* This limit is arbitrary, but INT_MAX breaks other things */
90
geo.max_width = 10000;
91
geo.min_height = geo.max_height = height;
92
gtk_window_set_geometry_hints (GTK_WINDOW (win), NULL, &geo,
93
GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
97
/* Grab the keyboard for maximum security */
99
grab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data)
104
if (gdk_keyboard_grab (win->window, FALSE, gdk_event_get_time (event)))
105
g_error ("could not grab keyboard");
110
ungrab_keyboard (GtkWidget *win, GdkEvent *event, gpointer data)
112
gdk_keyboard_ungrab (gdk_event_get_time (event));
117
delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
125
button_clicked (GtkWidget *widget, gpointer data)
131
/* Okay button or enter used in text field. */
133
if (pinentry->enhanced)
134
printf ("Options: %s\nTimeout: %d\n\n",
135
gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (insure))
137
gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (time_out)));
139
s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (entry));
143
pinentry_setbufferlen (pinentry, strlen (s) + 1);
145
strcpy (pinentry->pin, s);
152
enter_callback (GtkWidget *widget, GtkWidget *anentry)
154
button_clicked (widget, "ok");
159
confirm_button_clicked (GtkWidget *widget, gpointer data)
170
pinentry_utf8_validate (gchar *text)
177
if (g_utf8_validate (text, -1, NULL))
178
return g_strdup (text);
180
result = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
185
result = p = g_strdup (text);
186
while (!g_utf8_validate (p, -1, (const gchar **) &p))
193
/* Handler called for "changed". We use it to update the quality
196
changed_text_handler (GtkWidget *widget)
202
GdkColor color = { 0, 0, 0, 0};
204
if (!qualitybar || !pinentry->quality_bar)
207
s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (widget));
211
percent = length? pinentry_inq_quality (pinentry, s, length) : 0;
214
strcpy(textbuf, QUALITYBAR_EMPTY_TEXT);
217
else if (percent < 0)
219
snprintf (textbuf, sizeof textbuf, "(%d%%)", -percent);
225
snprintf (textbuf, sizeof textbuf, "%d%%", percent);
226
color.green = 0xffff;
228
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar),
229
(double)percent/100.0);
230
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar), textbuf);
231
gtk_widget_modify_bg (qualitybar, GTK_STATE_PRELIGHT, &color);
237
create_window (int confirm_mode)
240
GtkWidget *win, *box;
241
GtkWidget *wvbox, *chbox, *bbox;
245
tooltips = gtk_tooltips_new ();
247
/* FIXME: check the grabbing code against the one we used with the
249
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
250
acc = gtk_accel_group_new ();
252
g_signal_connect (G_OBJECT (win), "delete_event",
253
G_CALLBACK (delete_event), NULL);
256
g_signal_connect (G_OBJECT (win), "destroy", G_CALLBACK (gtk_main_quit),
259
g_signal_connect (G_OBJECT (win), "size-request",
260
G_CALLBACK (constrain_size), NULL);
263
//we need to grab the keyboard when its visible! not when its mapped (there is a difference)
264
g_object_set(G_OBJECT(win), "events", GDK_VISIBILITY_NOTIFY_MASK | GDK_STRUCTURE_MASK, NULL);
266
g_signal_connect (G_OBJECT (win),
267
pinentry->grab ? "visibility-notify-event" : "focus-in-event",
268
G_CALLBACK (grab_keyboard), NULL);
269
g_signal_connect (G_OBJECT (win),
270
pinentry->grab ? "unmap-event" : "focus-out-event",
271
G_CALLBACK (ungrab_keyboard), NULL);
273
gtk_window_add_accel_group (GTK_WINDOW (win), acc);
275
wvbox = gtk_vbox_new (FALSE, HIG_LARGE * 2);
276
gtk_container_add (GTK_CONTAINER (win), wvbox);
277
gtk_container_set_border_width (GTK_CONTAINER (wvbox), HIG_LARGE);
279
chbox = gtk_hbox_new (FALSE, HIG_LARGE);
280
gtk_box_pack_start (GTK_BOX (wvbox), chbox, FALSE, FALSE, 0);
282
w = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
283
GTK_ICON_SIZE_DIALOG);
284
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.0);
285
gtk_box_pack_start (GTK_BOX (chbox), w, FALSE, FALSE, 0);
287
box = gtk_vbox_new (FALSE, HIG_SMALL);
288
gtk_box_pack_start (GTK_BOX (chbox), box, TRUE, TRUE, 0);
290
if (pinentry->description)
292
msg = pinentry_utf8_validate (pinentry->description);
293
w = gtk_label_new (msg);
295
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
296
gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
297
gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
299
if (pinentry->error && !confirm_mode)
301
GdkColor color = { 0, 0xffff, 0, 0 };
303
msg = pinentry_utf8_validate (pinentry->error);
304
w = gtk_label_new (msg);
306
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
307
gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
308
gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
309
gtk_widget_modify_fg (w, GTK_STATE_NORMAL, &color);
316
GtkWidget* table = gtk_table_new (pinentry->quality_bar ? 2 : 1, 2,
318
gtk_box_pack_start (GTK_BOX (box), table, FALSE, FALSE, 0);
320
if (pinentry->prompt)
322
msg = pinentry_utf8_validate (pinentry->prompt);
323
w = gtk_label_new (msg);
325
gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
326
gtk_table_attach (GTK_TABLE (table), w, 0, 1, 0, 1,
327
GTK_FILL, GTK_FILL, 4, 0);
330
entry = gtk_secure_entry_new ();
331
gtk_widget_set_size_request (entry, 200, -1);
332
g_signal_connect (G_OBJECT (entry), "activate",
333
G_CALLBACK (enter_callback), entry);
334
g_signal_connect (G_OBJECT (entry), "changed",
335
G_CALLBACK (changed_text_handler), entry);
336
gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1,
337
GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
338
gtk_widget_grab_focus (entry);
339
gtk_widget_show (entry);
341
if (pinentry->quality_bar)
343
msg = pinentry_utf8_validate (pinentry->quality_bar);
344
w = gtk_label_new (msg);
346
gtk_misc_set_alignment (GTK_MISC (w), 1.0, 0.5);
347
gtk_table_attach (GTK_TABLE (table), w, 0, 1, 1, 2,
348
GTK_FILL, GTK_FILL, 4, 0);
349
qualitybar = gtk_progress_bar_new();
350
gtk_widget_add_events (qualitybar,
351
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
352
gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar),
353
QUALITYBAR_EMPTY_TEXT);
354
gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar), 0.0);
355
if (pinentry->quality_bar_tt)
356
gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips), qualitybar,
357
pinentry->quality_bar_tt, "");
358
gtk_table_attach (GTK_TABLE (table), qualitybar, 1, 2, 1, 2,
359
GTK_EXPAND|GTK_FILL, GTK_EXPAND|GTK_FILL, 0, 0);
362
if (pinentry->enhanced)
364
GtkWidget *sbox = gtk_hbox_new (FALSE, HIG_SMALL);
365
gtk_box_pack_start (GTK_BOX (box), sbox, FALSE, FALSE, 0);
367
w = gtk_label_new ("Forget secret after");
368
gtk_box_pack_start (GTK_BOX (sbox), w, FALSE, FALSE, 0);
371
time_out = gtk_spin_button_new
372
(GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, HUGE_VAL, 1, 60, 60)),
374
gtk_box_pack_start (GTK_BOX (sbox), time_out, FALSE, FALSE, 0);
375
gtk_widget_show (time_out);
377
w = gtk_label_new ("seconds");
378
gtk_box_pack_start (GTK_BOX (sbox), w, FALSE, FALSE, 0);
380
gtk_widget_show (sbox);
382
insure = gtk_check_button_new_with_label ("ask before giving out "
384
gtk_box_pack_start (GTK_BOX (box), insure, FALSE, FALSE, 0);
385
gtk_widget_show (insure);
389
bbox = gtk_hbutton_box_new ();
390
gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
391
gtk_box_set_spacing (GTK_BOX (bbox), 6);
392
gtk_box_pack_start (GTK_BOX (wvbox), bbox, TRUE, FALSE, 0);
394
if (!pinentry->one_button)
396
if (pinentry->cancel)
398
msg = pinentry_utf8_validate (pinentry->cancel);
399
w = gtk_button_new_with_label (msg);
403
w = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
404
gtk_container_add (GTK_CONTAINER (bbox), w);
405
g_signal_connect (G_OBJECT (w), "clicked",
406
G_CALLBACK (confirm_mode ? confirm_button_clicked
407
: button_clicked), NULL);
408
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
413
msg = pinentry_utf8_validate (pinentry->ok);
414
w = gtk_button_new_with_label (msg);
418
w = gtk_button_new_from_stock (GTK_STOCK_OK);
419
gtk_container_add (GTK_CONTAINER(bbox), w);
422
g_signal_connect (G_OBJECT (w), "clicked",
423
G_CALLBACK (button_clicked), "ok");
424
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
425
gtk_widget_grab_default (w);
426
g_signal_connect_object (G_OBJECT (entry), "focus_in_event",
427
G_CALLBACK (gtk_widget_grab_default),
432
g_signal_connect (G_OBJECT (w), "clicked",
433
G_CALLBACK(confirm_button_clicked), "ok");
434
GTK_WIDGET_SET_FLAGS (w, GTK_CAN_DEFAULT);
437
gtk_window_set_position (GTK_WINDOW (win), GTK_WIN_POS_CENTER);
439
gtk_widget_show_all(win);
446
gtk_cmd_handler (pinentry_t pe)
449
int want_pass = !!pe->pin;
454
w = create_window (want_pass ? 0 : 1);
456
gtk_widget_destroy (w);
457
while (gtk_events_pending ())
458
gtk_main_iteration ();
463
if (passphrase_ok && pe->pin)
464
return strlen (pe->pin);
473
pinentry_cmd_handler_t pinentry_cmd_handler = gtk_cmd_handler;
477
main (int argc, char *argv[])
479
pinentry_init (PGMNAME);
481
#ifdef FALLBACK_CURSES
482
if (pinentry_have_display (argc, argv))
483
gtk_init (&argc, &argv);
485
pinentry_cmd_handler = curses_cmd_handler;
487
gtk_init (&argc, &argv);
490
/* Consumes all arguments. */
491
if (pinentry_parse_opts (argc, argv))
493
printf(PGMNAME " " VERSION "\n");
497
if (pinentry_loop ())