Add quality bar to the GTK-2 version.
authorWerner Koch <wk@gnupg.org>
Tue, 18 Sep 2007 11:38:21 +0000 (11:38 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 18 Sep 2007 11:38:21 +0000 (11:38 +0000)
Doesn't look very pretty yet.

ChangeLog
doc/pinentry.texi
gtk+-2/pinentry-gtk-2.c
pinentry/pinentry.c
pinentry/pinentry.h

index 0317297..5216a96 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2007-09-18  Werner Koch  <wk@g10code.com>
+
+       * pinentry/pinentry.h (struct pinentry): Add member QUALITY_BAR
+       and CTX_ASSUAN.
+       * pinentry/pinentry.c (cmd_setqualitybar): New.
+       (copy_and_escape): New.
+       (pinentry_inq_quality): New.
+
 2007-07-09  Werner Koch  <wk@g10code.com>
 
        * doc/pinentry.texi: Fixed direntry syntax.
index e697f36..afa05be 100644 (file)
@@ -276,6 +276,18 @@ a GETPIN or CONFIRM, and is only displayed when asking for a PIN.
   S: OK
 @end example
 
+@item Enable a passphrase quality indicator
+Adds a quality indicator to the GETPIN window.  This indicator is
+updated as the passphrase is typed.  The clients needs to implement an
+inquiry named "QUALITY" which gets passed the current passpharse
+(percent-plus escaped) and should send back a string with a single
+numerical vauelue between -100 and 100.  Negative values will be
+displayed in red.
+@example
+  C: SETQUALITYBAR
+  S: OK
+@end example
+
 @item Ask for a PIN
 The meat of this tool is to ask for a passphrase of PIN, it is done with
 this command:
index 69b6e3e..ebb1411 100644 (file)
@@ -1,6 +1,6 @@
 /* pinentry-gtk-2.c
    Copyright (C) 1999 Robert Bihlmeyer <robbe@orcus.priv.at>
-   Copyright (C) 2001, 2002 g10 Code GmbH
+   Copyright (C) 2001, 2002, 2007 g10 Code GmbH
    Copyright (C) 2004 by Albrecht DreƟ <albrecht.dress@arcor.de>
 
    pinentry-gtk-2 is a pinentry application for the Gtk+-2 widget set.
@@ -56,6 +56,7 @@ static int passphrase_ok;
 static int confirm_yes;
 
 static GtkWidget *entry;
+static GtkWidget *qualitybar;
 static GtkWidget *insure;
 static GtkWidget *time_out;
 
@@ -181,6 +182,49 @@ pinentry_utf8_validate (gchar *text)
 }
 
 
+/* Handler called for "changed".   We use it to update the quality
+   indicator.  */
+static void
+changed_text_handler (GtkWidget *widget)
+{
+  char textbuf[50];
+  const char *s;
+  int length;
+  int percent;
+  GdkColor color = { 0, 0, 0, 0};
+
+  if (!qualitybar || !pinentry->quality_bar)
+    return;
+
+  s = gtk_secure_entry_get_text (GTK_SECURE_ENTRY (widget));
+  if (!s)
+    s = "";
+  length = strlen (s);
+  percent = length? pinentry_inq_quality (pinentry, s, length) : 0;
+  if (!length)
+    {
+      *textbuf = 0;
+      color.red = 0xffff;
+    }
+  else if (percent < 0)
+    {
+      snprintf (textbuf, sizeof textbuf, "(%d%%)", -percent);
+      color.red = 0xffff;
+      percent = -percent;
+    }
+  else
+    {
+      snprintf (textbuf, sizeof textbuf, "%d%%", percent);
+      color.green = 0xffff;
+    }
+  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar), 
+                                 (double)percent/100.0);
+  gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar), textbuf);
+  gtk_widget_modify_bg (qualitybar, GTK_STATE_PRELIGHT, &color);
+}
+
+
+
 static GtkWidget *
 create_window (int confirm_mode)
 {
@@ -237,7 +281,7 @@ create_window (int confirm_mode)
       g_free (msg);
       gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
       gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
-       gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
+      gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
     }
   if (pinentry->error && !confirm_mode)
     {
@@ -251,7 +295,17 @@ create_window (int confirm_mode)
       gtk_box_pack_start (GTK_BOX (box), w, TRUE, FALSE, 0);
       gtk_widget_modify_fg (w, GTK_STATE_NORMAL, &color);
     }
-
+  
+  if (pinentry->quality_bar)
+    {
+      qualitybar = gtk_progress_bar_new();
+      gtk_progress_bar_set_text (GTK_PROGRESS_BAR (qualitybar), "<small>foo</small>");
+      gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (qualitybar), 0.0);
+      gtk_box_pack_start (GTK_BOX (box), qualitybar, TRUE, FALSE, 0);
+    }
+  else
+    qualitybar = NULL;
+  
   ebox = gtk_hbox_new (FALSE, HIG_SMALL);
   gtk_box_pack_start (GTK_BOX(box), ebox, FALSE, FALSE, 0);
 
@@ -264,10 +318,13 @@ create_window (int confirm_mode)
          g_free (msg);
          gtk_box_pack_start (GTK_BOX (ebox), w, FALSE, FALSE, 0);
        }
+
       entry = gtk_secure_entry_new ();
       gtk_widget_set_size_request (entry, 200, -1);
       g_signal_connect (G_OBJECT (entry), "activate",
                        G_CALLBACK (enter_callback), entry);
+      g_signal_connect (G_OBJECT (entry), "changed",
+                        G_CALLBACK (changed_text_handler), entry);
       gtk_box_pack_start (GTK_BOX (ebox), entry, TRUE, TRUE, 0);
       gtk_widget_grab_focus (entry);
       gtk_widget_show (entry);
index a993a27..0def6ff 100644 (file)
@@ -1,5 +1,5 @@
 /* pinentry.c - The PIN entry support library
-   Copyright (C) 2002, 2003 g10 Code GmbH
+   Copyright (C) 2002, 2003, 2007 g10 Code GmbH
    
    This file is part of PINENTRY.
    
@@ -69,11 +69,13 @@ struct pinentry pinentry =
     0,         /* Result.  */
     0,          /* Locale error flag. */
     0,          /* One-button flag.  */
+    0,          /* Quality-Bar flag.  */
     PINENTRY_COLOR_DEFAULT,
     0,
     PINENTRY_COLOR_DEFAULT,
     PINENTRY_COLOR_DEFAULT,
-    0
+    0,
+    NULL        /* Assuan context.  */
   };
 
 \f
@@ -208,6 +210,111 @@ pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure)
 }
 #endif
 
+
+/* Copy TEXT or TEXTLEN to BUFFER and escape as required.  Return a
+   pointer to the end of the new buffer.  Note that BUFFER must be
+   large enough to keep the entire text; allocataing it 3 times of
+   TEXTLEN is sufficient.  */
+static char *
+copy_and_escape (char *buffer, const void *text, size_t textlen)
+{
+  int i;
+  const unsigned char *s = (unsigned char *)text;
+  char *p = buffer;
+  
+  for (i=0; i < textlen; i++)
+    {
+      if (s[i] < ' ' || s[i] == '+')
+        {
+          snprintf (p, 4, "%%%02X", s[i]);
+          p += 3;
+        }
+      else if (s[i] == ' ')
+        *p++ = '+';
+      else
+        *p++ = s[i];
+    }
+  return p;
+}
+
+
+
+/* Run a quality inquiry for PASSPHRASE of LENGTH.  (We need LENGTH
+   because not all backends might be able to return a proper
+   C-string.).  Returns: A value between -100 and 100 to give an
+   estimate of the passphrase's quality.  Negative values are use if
+   the caller won't even accept that passphrase.  Note that we expect
+   just one data line which should not be escaped in any represent a
+   numeric signed decimal value.  Extra data is currently ignored but
+   should not be send at all.  */
+int
+pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length)
+{
+  ASSUAN_CONTEXT ctx = pin->ctx_assuan;
+  const char prefix[] = "INQUIRE QUALITY ";
+  char *command;
+  char *line;
+  size_t linelen;
+  int gotvalue = 0;
+  int value = 0;
+  int rc;
+
+  if (!ctx)
+    return 0; /* Can't run the callback.  */
+
+  if (length > 300)
+    length = 300;  /* Limit so that it definitely fits into an Assuan
+                      line.  */
+
+  command = secmem_malloc (strlen (prefix) + 3*length + 1);
+  if (!command)
+    return 0;
+  strcpy (command, prefix);
+  copy_and_escape (command + strlen(command), passphrase, length);
+  rc = assuan_write_line (ctx, command);
+  secmem_free (command);
+  if (rc)
+    {
+      fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
+      return 0;
+    }
+
+  for (;;)
+    {
+      do 
+        {
+          rc = assuan_read_line (ctx, &line, &linelen);
+          if (rc)
+            {
+              fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
+              return 0;
+            }
+        }    
+      while (*line == '#' || !linelen);
+      if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+          && (!line[3] || line[3] == ' '))
+        break; /* END command received*/
+      if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
+          && (!line[3] || line[3] == ' '))
+        break; /* CAN command received*/
+      if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+          && (!line[3] || line[3] == ' '))
+        break; /* ERR command received*/
+      if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
+        continue;
+      gotvalue = 1;
+      value = atoi (line+2);
+    }
+  if (value < -100)
+    value = -100;
+  else if (value > 100)
+    value = 100;
+
+  return value;
+}
+
+
+
 /* Try to make room for at least LEN bytes in the pinentry.  Returns
    new buffer on success and 0 on failure or when the old buffer is
    sufficient.  */
@@ -638,6 +745,15 @@ cmd_setcancel (ASSUAN_CONTEXT ctx, char *line)
 }
 
 
+
+static int
+cmd_setqualitybar (ASSUAN_CONTEXT ctx, char *line)
+{
+  pinentry.quality_bar = 1;
+  return 0;
+}
+
+
 static int
 cmd_getpin (ASSUAN_CONTEXT ctx, char *line)
 {
@@ -654,8 +770,9 @@ cmd_getpin (ASSUAN_CONTEXT ctx, char *line)
     }
   pinentry.locale_err = 0;
   pinentry.one_button = 0;
-
+  pinentry.ctx_assuan = ctx;
   result = (*pinentry_cmd_handler) (&pinentry);
+  pinentry.ctx_assuan = NULL;
   if (pinentry.error)
     {
       free (pinentry.error);
@@ -664,6 +781,8 @@ cmd_getpin (ASSUAN_CONTEXT ctx, char *line)
   if (set_prompt)
     pinentry.prompt = NULL;
 
+  pinentry.quality_bar = 0;  /* Reset it after the command.  */
+
   if (result < 0)
     {
       if (pinentry.pin)
@@ -704,6 +823,7 @@ cmd_confirm (ASSUAN_CONTEXT ctx, char *line)
   int result;
 
   pinentry.one_button = !!strstr (line, "--one-button");
+  pinentry.quality_bar = 0;
   pinentry.locale_err = 0;
   result = (*pinentry_cmd_handler) (&pinentry);
   if (pinentry.error)
@@ -726,6 +846,7 @@ cmd_message (ASSUAN_CONTEXT ctx, char *line)
   int result;
 
   pinentry.one_button = 1;
+  pinentry.quality_bar = 0;
   pinentry.locale_err = 0;
   result = (*pinentry_cmd_handler) (&pinentry);
   if (pinentry.error)
@@ -759,6 +880,7 @@ register_commands (ASSUAN_CONTEXT ctx)
       { "GETPIN",     0,  cmd_getpin },
       { "CONFIRM",    0,  cmd_confirm },
       { "MESSAGE",    0,  cmd_message },
+      { "SETQUALITYBAR", 0,  cmd_setqualitybar },
       { NULL }
     };
   int i, j, rc;
index fec9b4b..81e8872 100644 (file)
@@ -91,12 +91,21 @@ struct pinentry
      dismiss button is required. */
   int one_button;
 
+  /* If this is set, a passphrase quality indicator is shown.  There
+     will also be an inquiry back to the caller to get an indication
+     of the quality for the passphrase entered so far.  */
+  int quality_bar;
+
   /* For the curses pinentry, the color of error messages.  */
   pinentry_color_t color_fg;
   int color_fg_bright;
   pinentry_color_t color_bg;
   pinentry_color_t color_so;
   int color_so_bright;
+
+  /* Fo the quality indicator we need to do an inquiry.  Thus we need
+     to save the assuan ctx.  */
+  void *ctx_assuan;
 };
 typedef struct pinentry *pinentry_t;
 
@@ -124,6 +133,11 @@ char *pinentry_utf8_to_local (char *lc_ctype, char *text);
    Return NULL on error. */
 char *pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure);
 
+
+/* Run a quality inquiry for PASSPHRASE of LENGTH. */
+int pinentry_inq_quality (pinentry_t pin, 
+                          const char *passphrase, size_t length);
+
 /* Try to make room for at least LEN bytes for the pin in the pinentry
    PIN.  Returns new buffer on success and 0 on failure.  */
 char *pinentry_setbufferlen (pinentry_t pin, int len);