commit 650ce60587c5c0a6800fbd5ddd1a9c8a1977e77a
parent 36565e2ef4894c996a29d18f49aae2cfc6fd0c61
Author: Jaromil <jaromil@dyne.org>
Date:   Sun, 16 Jan 2011 23:44:13 +0100
our own askpass gui
Diffstat:
| A | src/tomb-askpass.c |  |  | 332 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
1 file changed, 332 insertions(+), 0 deletions(-)
diff --git a/src/tomb-askpass.c b/src/tomb-askpass.c
@@ -0,0 +1,332 @@
+/* Tomb askpass
+   
+   Derived from gtk-led-askpass.c version 0.9
+   by Dafydd Harries <daf@muse.19inch.net>, 2003 2004
+   (An ssh-askpass alike software)
+   
+   Based on ideas from ssh-askpass-gnome, by Damien Miller and Nalin Dahyabhai,
+   and on Jim Knoble's x11-ssh-askpass.
+   
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+   
+   This program 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 General Public License for more details.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation, Inc.,
+   59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+   
+   See also:
+   
+   http://www.cgabriel.org/sw/gtk2-ssh-askpass/
+   -- Jim Knoble's x11-ssh-askpass
+   http://www.cgabriel.org/sw/gtk2-ssh-askpass/
+   -- Christopher Gabriel's gtk2-ssh-askpass
+   
+   Todo:
+
+   - Internationalise. Probably entails autotoolising.
+   - Add more eye candy.
+   - Implement optional mouse/server grabbing.
+   - Alow overriding the title on the command line.
+   - Make the LED box a proper GTK+ widget.
+   
+*/
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+/* The Tomb icon is an artwork by Jordi aka MonMort
+   a nomadic graffiti artist from Barcelona */
+#include <monmort.xpm>
+
+/* Title for the dialog. */
+#define TITLE "Unlocking tomb"
+
+/* Width of each LED. */
+#define LED_WIDTH 8
+/* Height of each LED. */
+#define LED_HEIGHT 16
+/* Space around and between LEDs. */
+#define LED_MARGIN 5
+/* Number of LEDs to have. */
+#define LED_COUNT 12
+
+/* How many times to attempt to grab the keyboard before giving up. */
+#define GRAB_TRIES_MAX 10
+/* How long to sleep, in microseconds, in between keyboard grab attempts. */
+#define GRAB_SLEEP 100000
+/* Sleep length, in milliseconds, after Control-U press. */
+#define CLEAR_SLEEP 800
+
+enum {
+	LED_STATE_OFF,
+	LED_STATE_GREEN,
+	LED_STATE_RED,
+	LED_STATES
+};
+
+GdkPixbuf *pb_monmort;
+
+GdkColor colours[LED_STATES] = {
+	/* LED_STATE_OFF */
+	{ 0, 0x3333, 0x6666, 0x3333 },
+	/* LED_STATE_GREEN */
+	{ 0, 0x6666, 0xFFFF, 0x6666 },
+	/* LED_STATE_RED */
+	{ 0, 0xDDDD, 0x3333, 0x3333 }
+};
+
+
+void draw_led(GtkWidget *widget, gint state, guint offset)
+{
+	GdkGC *gc = gdk_gc_new(widget->window);
+
+	/* Draw the border. */
+	gdk_draw_rectangle(widget->window,
+		widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+		FALSE,
+		LED_MARGIN + offset * (LED_WIDTH + LED_MARGIN),
+		LED_MARGIN,
+		LED_WIDTH,
+		LED_HEIGHT);
+
+	gdk_gc_set_rgb_fg_color(gc, &(colours[state]));
+
+	/* Draw the inside rectangle. */
+	gdk_draw_rectangle(widget->window,
+		gc,
+		TRUE,
+		LED_MARGIN + offset * (LED_WIDTH + LED_MARGIN) + 1,
+		LED_MARGIN + 1,
+		LED_WIDTH - 1,
+		LED_HEIGHT - 1);
+}
+
+gboolean led_area_expose_handler(GtkWidget *led_area, GdkEventExpose *event,
+	GString *passphrase)
+{
+	gint i, width, height;
+	gint length = passphrase->len;
+
+	gdk_drawable_get_size(GDK_DRAWABLE(led_area->window), &width, &height);
+
+	/* Draw a focus indicator if appropriate. */
+	if (GTK_WIDGET_HAS_FOCUS(led_area))
+		gtk_paint_focus(led_area->style, led_area->window,
+			GTK_WIDGET_STATE(led_area), &(event->area), led_area,
+			"", 0, 0, width, height);
+
+	/* Draw each LED. */
+	for (i = 0; i < LED_COUNT; i++) {
+		/* This is the complicated bit. */
+		gboolean on = ((length / LED_COUNT) % 2 == 0) ?
+				(length % LED_COUNT >  i) :
+				(length % LED_COUNT <= i);
+
+		draw_led(led_area, on ? LED_STATE_GREEN : LED_STATE_OFF, i);
+	}
+
+	/* TRUE means not to propagate the event. */
+	return TRUE;
+}
+
+gboolean timeout_handler(GtkWidget *led_area)
+{
+	gtk_widget_queue_draw(led_area);
+
+	return FALSE;
+}
+
+void clear(GString *passphrase, GtkWidget *led_area)
+{
+	gint i;
+
+	/*
+	Delete bit by bit to ensure erasure. g_string_erase() will overwrite
+	the last character with 0, so we shouldn't need to worry about leaving
+	sensitive data in memory. Note that the string may be empty. This is
+	so that the interface responds consistently.
+	*/
+	while (passphrase->len > 0)
+		g_string_erase(passphrase, passphrase->len - 1, 1);
+
+	for (i = 0; i < LED_COUNT; i++)
+		draw_led(led_area, LED_STATE_RED, i);
+
+	/*
+	Remove the redness after a delay. If an exposure is triggered, such as
+	by a key getting pressed, then the redraw will simply happen early.
+	*/
+	gtk_timeout_add(CLEAR_SLEEP, (GtkFunction)timeout_handler, led_area);
+}
+
+gboolean led_area_key_press_handler(GtkWidget *led_area, GdkEventKey *event,
+				    GString *passphrase) {
+  /* Obtain Unicode representation of key released. */
+  gunichar c = gdk_keyval_to_unicode(event->keyval);
+  /* Determine whether the key released was printable. */
+  gint isprint = g_unichar_isprint(c);
+  /* Obtain default modifier mask. */
+  guint modifiers = gtk_accelerator_get_default_mod_mask();
+  
+  if (event->keyval == GDK_Delete) {
+    clear(passphrase, led_area);
+    return TRUE;
+  }
+
+  if ((event->state & modifiers) == GDK_CONTROL_MASK) {
+
+    if (event->keyval == GDK_u) {
+      /* C-u -- delete everything. */
+      clear(passphrase, led_area);
+      /* Return early in order to avoid the redraw. */
+      return TRUE;
+
+    } else {
+
+      /* Unrecognised keypress. */
+      return FALSE;
+    }
+    
+  } else if (event->keyval == GDK_BackSpace && passphrase->len > 0) {
+    /*
+      Backspace -- remove last character. See the comment above
+      about g_string_erase.
+    */
+    g_string_erase(passphrase, passphrase->len - 1, 1);
+	} else if (isprint) {
+		/* Printable character. */
+		g_string_append_unichar(passphrase, c);
+	} else {
+		/* Unrecognized keypress, propagate. */
+		return FALSE;
+	}
+
+	/* Trigger a redraw of the LED area. */
+	gtk_widget_queue_draw(led_area);
+
+	/* TRUE means not to propagate the event. */
+	return TRUE;
+}
+
+gboolean led_area_button_press_handler(GtkWidget *led_area,
+	GdkEventButton *event, gpointer data)
+{
+	gtk_widget_grab_focus(led_area);
+
+	return TRUE;
+}
+
+int main(int argc, char *argv[])
+{
+	gint response, grab_tries, i;
+	char keyname[256];
+	GString *passphrase = g_string_new("");
+	GtkWidget *dialog, *alignment, *led_area;
+	GList tmplist;
+
+	gtk_set_locale();
+	gtk_init(&argc, &argv);
+
+	if (argc > 1) {
+	  snprintf(keyname,255,"%s",argv[1]);
+	} else {
+	  sprintf(keyname,"unknown");
+	}
+	/*
+	dialog
+	`- vbox (implicit)
+	   `- aligment
+	      `- led_area
+	*/
+
+	/* Question dialog with no parent; OK and Cancel buttons. */
+	dialog = gtk_message_dialog_new_with_markup
+	  (NULL, 0, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK,
+	   "Enter the password to unlock\n"
+	   "<span font=\"Times 24\">%s</span>", keyname);
+	gtk_window_set_title(GTK_WINDOW(dialog), TITLE);
+
+	// set and show the image icon
+	pb_monmort = gdk_pixbuf_new_from_xpm_data(monmort);
+	tmplist.data = (gpointer*)pb_monmort;
+	tmplist.prev = tmplist.next = NULL;
+	gtk_window_set_icon_list(GTK_WINDOW(dialog), &tmplist);
+	gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog), 
+				     gtk_image_new_from_pixbuf(pb_monmort));
+
+	/* Place the dialog in the middle of the screen. */
+	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
+	/* OK is the default action. */
+	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+	
+	/* Add some spacing within the dialog's vbox. */
+	gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog)->vbox), 3);
+
+	/* The alignment widget containing the drawing area. */
+	alignment = gtk_alignment_new(0.5, 0.5, 0, 0);
+	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), alignment);
+
+	/* The drawing area for the LEDs. */
+	led_area = gtk_drawing_area_new();
+	gtk_container_add(GTK_CONTAINER(alignment), led_area);
+	/* Make the LED widget focusable. */
+	GTK_WIDGET_SET_FLAGS(led_area, GTK_CAN_FOCUS);
+	/* Make the LED widget focused. */
+	gtk_widget_grab_focus(led_area);
+	/* Make the LED widget receive key press and button press events. */
+	gtk_widget_add_events(led_area,
+		GDK_KEY_PRESS_MASK | GDK_BUTTON_PRESS_MASK);
+	/* Set a size request. */
+	gtk_widget_set_size_request(led_area,
+		LED_MARGIN + (LED_WIDTH + LED_MARGIN) * LED_COUNT,
+		LED_HEIGHT + LED_MARGIN * 2);
+	/* Set up handler for key releases. */
+	g_signal_connect(G_OBJECT(led_area), "key_press_event",
+		G_CALLBACK(led_area_key_press_handler), passphrase);
+	/* Set up handler for button releases. */
+	g_signal_connect(G_OBJECT(led_area), "button_press_event",
+		G_CALLBACK(led_area_button_press_handler), NULL);
+	/* Set up handler for expose events. */
+	g_signal_connect(G_OBJECT(led_area), "expose_event",
+		G_CALLBACK(led_area_expose_handler), passphrase);
+
+	
+	/* Show all the widgets. */
+	gtk_widget_show_all(dialog);
+	/* Put the dialog on the screen now for the grab to work. */
+	gtk_widget_show_now(dialog);
+
+	/* Grab the keyboard */
+	gdk_keyboard_grab(GTK_WIDGET(dialog)->window, FALSE, GDK_CURRENT_TIME);
+	
+	/* Make the dialog stay on top. */
+	gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE);
+
+	/* Run the dialog. */
+	response = gtk_dialog_run(GTK_DIALOG(dialog));
+
+	/* Ungrab the keyboard. */
+	gdk_keyboard_ungrab(GDK_CURRENT_TIME);
+
+	/* If the OK button was pressed, print the passphrase. */
+	if (response == GTK_RESPONSE_OK)
+		g_print("%s\n", passphrase->str);
+
+	/* Scrub the passphrase, if any. */
+	for (i = 0; i < passphrase->len; i++)
+		passphrase->str[i] = '\0';
+
+	if (response == GTK_RESPONSE_OK)
+		return 0;
+
+	return 1;
+}
+