1 /* passphrase.c - GTK based passphrase callback
2 * Copyright (C) 2001-2022 Werner Koch (dd9jn) and the Claws Mail team
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 3 of the License, or
7 * (at your option) any later version.
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.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include <glib/gi18n.h>
27 #include <gdk/gdkkeysyms.h>
28 #ifdef GDK_WINDOWING_X11
29 # include <gdk/gdkx.h>
30 #endif /* GDK_WINDOWING_X11 */
33 #include <sys/types.h>
40 #include "passphrase.h"
41 #include "prefs_common.h"
42 #include "prefs_gpg.h"
43 #include "manage_window.h"
45 #include "mainwindow.h"
46 #include "summaryview.h"
48 static gboolean grab_all
= FALSE
;
50 static gboolean pass_ack
;
51 static gchar
*last_pass
= NULL
;
53 static void passphrase_ok_cb(GtkWidget
*widget
, gpointer data
);
54 static void passphrase_cancel_cb(GtkWidget
*widget
, gpointer data
);
55 static gint
passphrase_deleted(GtkWidget
*widget
, GdkEventAny
*event
,
57 static gboolean
passphrase_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
60 static GtkWidget
*create_description(const gchar
*uid_hint
, gint prev_bad
, gint new_key
);
63 gpgmegtk_set_passphrase_grab(gint yes
)
69 passphrase_mbox(const gchar
*uid_hint
, const gchar
*pass_hint
, gint prev_bad
, gint new_key
)
71 gchar
*the_passphrase
= NULL
;
72 GtkWidget
*vbox
, *hbox
;
73 GtkWidget
*confirm_box
;
75 GtkWidget
*pass_entry
;
77 GtkWidget
*cancel_button
;
80 SummaryView
*summaryview
= mainwindow_get_mainwindow()->summaryview
;
82 gtk_menu_popdown(GTK_MENU(summaryview
->popupmenu
));
84 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "passphrase");
85 gtk_window_set_title(GTK_WINDOW(window
), _("Passphrase"));
86 gtk_window_set_default_size(GTK_WINDOW(window
), 375, 100);
87 gtk_window_set_resizable(GTK_WINDOW(window
), TRUE
);
88 gtk_window_set_position(GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
89 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
90 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
91 g_signal_connect(G_OBJECT(window
), "delete_event",
92 G_CALLBACK(passphrase_deleted
), NULL
);
93 g_signal_connect(G_OBJECT(window
), "key_press_event",
94 G_CALLBACK(passphrase_key_pressed
), NULL
);
95 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
96 manage_window_set_transient(GTK_WINDOW(window
));
98 vbox
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 8);
99 gtk_container_add(GTK_CONTAINER(window
), vbox
);
100 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 8);
102 if (uid_hint
|| pass_hint
) {
103 GtkWidget
*label
, *icon
;
104 label
= create_description (uid_hint
, prev_bad
, new_key
);
105 icon
= gtk_image_new_from_icon_name("dialog-password",
106 GTK_ICON_SIZE_DIALOG
);
108 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 12);
109 gtk_container_set_border_width (GTK_CONTAINER (hbox
), 5);
110 gtk_widget_show (hbox
);
111 gtk_box_pack_start (GTK_BOX(hbox
), icon
, FALSE
, FALSE
, 0);
112 gtk_box_pack_start (GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
113 gtk_box_pack_start (GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
116 pass_entry
= gtk_entry_new();
117 gtk_box_pack_start(GTK_BOX(vbox
), pass_entry
, FALSE
, FALSE
, 0);
118 gtk_entry_set_visibility(GTK_ENTRY(pass_entry
), FALSE
);
119 gtk_widget_grab_focus(pass_entry
);
121 gtkut_stock_button_set_create(&confirm_box
,
122 &cancel_button
, NULL
, _("_Cancel"),
123 &ok_button
, NULL
, _("_OK"),
126 gtk_box_pack_end(GTK_BOX(vbox
), confirm_box
, FALSE
, FALSE
, 0);
127 gtk_widget_grab_default(ok_button
);
129 g_signal_connect(G_OBJECT(ok_button
), "clicked",
130 G_CALLBACK(passphrase_ok_cb
), NULL
);
131 g_signal_connect(G_OBJECT(pass_entry
), "activate",
132 G_CALLBACK(passphrase_ok_cb
), NULL
);
133 g_signal_connect(G_OBJECT(cancel_button
), "clicked",
134 G_CALLBACK(passphrase_cancel_cb
), NULL
);
136 gtk_window_set_position (GTK_WINDOW(window
), GTK_WIN_POS_CENTER
);
138 gtk_window_set_resizable(GTK_WINDOW(window
), FALSE
);
140 gtk_widget_show_all(window
);
143 int err
= 0, cnt
= 0;
144 /* make sure that window is viewable */
145 gtk_widget_show_now(window
);
146 gdkwin
= gtk_widget_get_window(window
);
148 while(gtk_events_pending()) {
149 gtk_main_iteration();
152 if ((err
= gdk_pointer_grab(gdkwin
, TRUE
, 0,
153 gdkwin
, NULL
, GDK_CURRENT_TIME
))) {
154 if (err
== GDK_GRAB_NOT_VIEWABLE
&& cnt
< 10) {
156 g_warning("trying to grab mouse again");
157 gtk_main_iteration();
160 g_warning("OOPS: Could not grab mouse");
161 gtk_widget_destroy(window
);
165 if (gdk_keyboard_grab(gdkwin
, FALSE
, GDK_CURRENT_TIME
)) {
166 gdk_display_pointer_ungrab(gdk_display_get_default(),
168 g_warning("OOPS: Could not grab keyboard");
169 gtk_widget_destroy(window
);
177 gdk_display_keyboard_ungrab(gdk_display_get_default(),
179 gdk_display_pointer_ungrab(gdk_display_get_default(), GDK_CURRENT_TIME
);
183 manage_window_focus_out(window
, NULL
, NULL
);
186 const gchar
*entry_text
;
187 entry_text
= gtk_entry_get_text(GTK_ENTRY(pass_entry
));
188 the_passphrase
= g_locale_from_utf8(entry_text
, -1, NULL
, NULL
, NULL
);
189 if (the_passphrase
== NULL
)
190 the_passphrase
= g_strdup (entry_text
);
192 gtk_widget_destroy (window
);
194 return the_passphrase
;
199 passphrase_ok_cb(GtkWidget
*widget
, gpointer data
)
206 passphrase_cancel_cb(GtkWidget
*widget
, gpointer data
)
214 passphrase_deleted(GtkWidget
*widget
, GdkEventAny
*event
, gpointer data
)
216 passphrase_cancel_cb(NULL
, NULL
);
222 passphrase_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
224 if (event
&& event
->keyval
== GDK_KEY_Escape
)
225 passphrase_cancel_cb(NULL
, NULL
);
230 linelen (const gchar
*s
)
234 for (i
= 0; *s
&& *s
!= '\n'; s
++, i
++)
241 create_description(const gchar
*uid_hint
, gint prev_bad
, gint new_key
)
243 const gchar
*uid
= NULL
;
246 gchar
*my_uid
= NULL
;
248 uid
= _("[no user id]");
252 my_uid
= g_strdup(uid
);
253 while (strchr(my_uid
, '<'))
254 *(strchr(my_uid
, '<')) = '(';
255 while (strchr(my_uid
, '>'))
256 *(strchr(my_uid
, '>')) = ')';
259 buf
= g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s%s</span>\n\n%.*s\n",
260 prev_bad
? _("Passphrases did not match.\n") : "",
261 _("Please enter the passphrase for the new key:"),
262 linelen (my_uid
), my_uid
);
263 } else if (new_key
== 2) {
264 buf
= g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%.*s\n",
265 _("Please re-enter the passphrase for the new key:"),
266 linelen (my_uid
), my_uid
);
268 buf
= g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s%s</span>\n\n%.*s\n",
269 prev_bad
? _("Bad passphrase.\n") : "",
270 _("Please enter the passphrase for:"),
271 linelen (my_uid
), my_uid
);
274 label
= gtk_label_new (buf
);
275 gtk_label_set_use_markup(GTK_LABEL (label
), TRUE
);
276 gtk_label_set_justify (GTK_LABEL (label
), GTK_JUSTIFY_LEFT
);
277 gtk_label_set_line_wrap(GTK_LABEL (label
), TRUE
);
283 static int free_passphrase(gpointer _unused
)
285 if (last_pass
!= NULL
) {
286 #ifndef G_PLATFORM_WIN32
287 munlock(last_pass
, strlen(last_pass
));
291 debug_print("%% passphrase removed\n");
298 gpgmegtk_passphrase_cb(void *opaque
, const char *uid_hint
,
299 const char *passphrase_hint
, int prev_bad
, int fd
)
303 if (prefs_gpg_get_config()->store_passphrase
&& last_pass
&& !prev_bad
)
304 pass
= g_strdup(last_pass
);
306 gpgmegtk_set_passphrase_grab (prefs_gpg_get_config()->passphrase_grab
);
307 debug_print ("%% requesting passphrase for '%s'\n", uid_hint
);
308 pass
= passphrase_mbox (uid_hint
, passphrase_hint
, prev_bad
, FALSE
);
309 gpgmegtk_free_passphrase();
311 debug_print ("%% cancel passphrase entry\n");
312 if (write(fd
, "\n", 1) != 1)
313 debug_print("short write\n");
315 return GPG_ERR_CANCELED
;
318 if (prefs_gpg_get_config()->store_passphrase
) {
319 last_pass
= g_strdup(pass
);
320 #ifndef G_PLATFORM_WIN32
321 if (mlock(last_pass
, strlen(last_pass
)) == -1)
322 debug_print("%% locking passphrase failed\n");
324 if (prefs_gpg_get_config()->store_passphrase_timeout
> 0) {
325 g_timeout_add(prefs_gpg_get_config()
326 ->store_passphrase_timeout
*60*1000,
327 free_passphrase
, NULL
);
330 debug_print ("%% sending passphrase\n");
336 /* Under Windows FD is actually a System handle. */
338 WriteFile ((HANDLE
)fd
, pass
, strlen (pass
), &nwritten
, NULL
);
339 WriteFile ((HANDLE
)fd
, "\n", 1, &nwritten
, NULL
);
342 if (write(fd
, pass
, strlen(pass
)) != strlen(pass
))
343 debug_print("short write\n");
345 if (write(fd
, "\n", 1) != 1)
346 debug_print("short write\n");
350 return GPG_ERR_NO_ERROR
;
353 void gpgmegtk_free_passphrase()
355 (void)free_passphrase(NULL
); /* could be inline */
358 #endif /* USE_GPGME */