7e53f00f5bca689ecc70f78793ba57b628522b96
[pinentry.git] / pinentry / pinentry.c
1 /* pinentry.c - The PIN entry support library
2    Copyright (C) 2002, 2003, 2007, 2008 g10 Code GmbH
3    
4    This file is part of PINENTRY.
5    
6    PINENTRY is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10  
11    PINENTRY is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15  
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, see <http://www.gnu.org/licenses/>.  
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <getopt.h>
28 #include <unistd.h>
29 #include <locale.h>
30 #ifdef HAVE_LANGINFO_H
31 #include <langinfo.h>
32 #endif
33 #include <limits.h>
34
35 #if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK
36 #include <iconv.h>
37 #endif
38
39 #include "assuan.h"
40 #include "memory.h"
41 #include "secmem-util.h"
42 #include "pinentry.h"
43
44
45 /* Keep the name of our program here. */
46 static char this_pgmname[50]; 
47
48
49 struct pinentry pinentry =
50   {
51     NULL,       /* Description.  */
52     NULL,       /* Error.  */
53     NULL,       /* Prompt.  */
54     NULL,       /* Ok button.  */
55     NULL,       /* Cancel button.  */
56     NULL,       /* PIN.  */
57     2048,       /* PIN length.  */
58     0,          /* Display.  */
59     0,          /* TTY name.  */
60     0,          /* TTY type.  */
61     0,          /* TTY LC_CTYPE.  */
62     0,          /* TTY LC_MESSAGES.  */
63     0,          /* Debug mode.  */
64     0,          /* Enhanced mode.  */
65     1,          /* Global grab.  */
66     0,          /* Parent Window ID.  */
67     NULL,       /* Touch file.  */
68     0,          /* Result.  */
69     0,          /* Locale error flag. */
70     0,          /* One-button flag.  */
71     NULL,       /* Quality-Bar flag and description.  */
72     NULL,       /* Quality-Bar tooltip.  */
73     PINENTRY_COLOR_DEFAULT,
74     0,
75     PINENTRY_COLOR_DEFAULT,
76     PINENTRY_COLOR_DEFAULT,
77     0,
78     NULL        /* Assuan context.  */
79   };
80
81 \f
82
83 #if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK
84 char *
85 pinentry_utf8_to_local (char *lc_ctype, char *text)
86 {
87   iconv_t cd;
88   const char *input = text;
89   size_t input_len = strlen (text) + 1;
90   char *output;
91   size_t output_len;
92   char *output_buf;
93   size_t processed;
94   char *old_ctype;
95   char *target_encoding;
96
97   /* If no locale setting could be determined, simply copy the
98      string.  */
99   if (!lc_ctype)
100     {
101       fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
102                this_pgmname);
103       return strdup (text);
104     }
105
106   old_ctype = strdup (setlocale (LC_CTYPE, NULL));
107   if (!old_ctype)
108     return NULL;
109   setlocale (LC_CTYPE, lc_ctype);
110   target_encoding = nl_langinfo (CODESET);
111   if (!target_encoding)
112     target_encoding = "?";
113   setlocale (LC_CTYPE, old_ctype);
114   free (old_ctype);
115
116   /* This is overkill, but simplifies the iconv invocation greatly.  */
117   output_len = input_len * MB_LEN_MAX;
118   output_buf = output = malloc (output_len);
119   if (!output)
120     return NULL;
121
122   cd = iconv_open (target_encoding, "UTF-8");
123   if (cd == (iconv_t) -1)
124     {
125       fprintf (stderr, "%s: can't convert from UTF-8 to %s: %s\n",
126                this_pgmname, target_encoding, strerror (errno));
127       free (output_buf);
128       return NULL;
129     }
130   processed = iconv (cd, &input, &input_len, &output, &output_len);
131   iconv_close (cd);
132   if (processed == (size_t) -1 || input_len)
133     {
134       fprintf (stderr, "%s: error converting from UTF-8 to %s: %s\n",
135                this_pgmname, target_encoding, strerror (errno));
136       free (output_buf);
137       return NULL;
138     }
139   return output_buf;
140 }
141
142 /* Convert TEXT which is encoded according to LC_CTYPE to UTF-8.  With
143    SECURE set to true, use secure memory for the returned buffer.
144    Return NULL on error. */
145 char *
146 pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure)
147 {
148   char *old_ctype;
149   char *source_encoding;
150   iconv_t cd;
151   const char *input = text;
152   size_t input_len = strlen (text) + 1;
153   char *output;
154   size_t output_len;
155   char *output_buf;
156   size_t processed;
157
158   /* If no locale setting could be determined, simply copy the
159      string.  */
160   if (!lc_ctype)
161     {
162       fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
163                this_pgmname);
164       output_buf = secure? secmem_malloc (input_len) : malloc (input_len);
165       if (output_buf)
166         strcpy (output_buf, input);
167       return output_buf;
168     }
169
170   old_ctype = strdup (setlocale (LC_CTYPE, NULL));
171   if (!old_ctype)
172     return NULL;
173   setlocale (LC_CTYPE, lc_ctype);
174   source_encoding = nl_langinfo (CODESET);
175   setlocale (LC_CTYPE, old_ctype);
176   free (old_ctype);
177
178   /* This is overkill, but simplifies the iconv invocation greatly.  */
179   output_len = input_len * MB_LEN_MAX;
180   output_buf = output = secure? secmem_malloc (output_len):malloc (output_len);
181   if (!output)
182     return NULL;
183
184   cd = iconv_open ("UTF-8", source_encoding);
185   if (cd == (iconv_t) -1)
186     {
187       fprintf (stderr, "%s: can't convert from %s to UTF-8: %s\n",
188                this_pgmname, source_encoding? source_encoding : "?",
189                strerror (errno));
190       if (secure)
191         secmem_free (output_buf);
192       else
193         free (output_buf);
194       return NULL;
195     }
196   processed = iconv (cd, &input, &input_len, &output, &output_len);
197   iconv_close (cd);
198   if (processed == (size_t) -1 || input_len)
199     {
200       fprintf (stderr, "%s: error converting from %s to UTF-8: %s\n",
201                this_pgmname, source_encoding? source_encoding : "?",
202                strerror (errno));
203       if (secure)
204         secmem_free (output_buf);
205       else
206         free (output_buf);
207       return NULL;
208     }
209   return output_buf;
210 }
211 #endif
212
213
214 /* Copy TEXT or TEXTLEN to BUFFER and escape as required.  Return a
215    pointer to the end of the new buffer.  Note that BUFFER must be
216    large enough to keep the entire text; allocataing it 3 times of
217    TEXTLEN is sufficient.  */
218 static char *
219 copy_and_escape (char *buffer, const void *text, size_t textlen)
220 {
221   int i;
222   const unsigned char *s = (unsigned char *)text;
223   char *p = buffer;
224   
225   for (i=0; i < textlen; i++)
226     {
227       if (s[i] < ' ' || s[i] == '+')
228         {
229           snprintf (p, 4, "%%%02X", s[i]);
230           p += 3;
231         }
232       else if (s[i] == ' ')
233         *p++ = '+';
234       else
235         *p++ = s[i];
236     }
237   return p;
238 }
239
240
241
242 /* Run a quality inquiry for PASSPHRASE of LENGTH.  (We need LENGTH
243    because not all backends might be able to return a proper
244    C-string.).  Returns: A value between -100 and 100 to give an
245    estimate of the passphrase's quality.  Negative values are use if
246    the caller won't even accept that passphrase.  Note that we expect
247    just one data line which should not be escaped in any represent a
248    numeric signed decimal value.  Extra data is currently ignored but
249    should not be send at all.  */
250 int
251 pinentry_inq_quality (pinentry_t pin, const char *passphrase, size_t length)
252 {
253   ASSUAN_CONTEXT ctx = pin->ctx_assuan;
254   const char prefix[] = "INQUIRE QUALITY ";
255   char *command;
256   char *line;
257   size_t linelen;
258   int gotvalue = 0;
259   int value = 0;
260   int rc;
261
262   if (!ctx)
263     return 0; /* Can't run the callback.  */
264
265   if (length > 300)
266     length = 300;  /* Limit so that it definitely fits into an Assuan
267                       line.  */
268
269   command = secmem_malloc (strlen (prefix) + 3*length + 1);
270   if (!command)
271     return 0;
272   strcpy (command, prefix);
273   copy_and_escape (command + strlen(command), passphrase, length);
274   rc = assuan_write_line (ctx, command);
275   secmem_free (command);
276   if (rc)
277     {
278       fprintf (stderr, "ASSUAN WRITE LINE failed: rc=%d\n", rc);
279       return 0;
280     }
281
282   for (;;)
283     {
284       do 
285         {
286           rc = assuan_read_line (ctx, &line, &linelen);
287           if (rc)
288             {
289               fprintf (stderr, "ASSUAN READ LINE failed: rc=%d\n", rc);
290               return 0;
291             }
292         }    
293       while (*line == '#' || !linelen);
294       if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
295           && (!line[3] || line[3] == ' '))
296         break; /* END command received*/
297       if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N'
298           && (!line[3] || line[3] == ' '))
299         break; /* CAN command received*/
300       if (line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
301           && (!line[3] || line[3] == ' '))
302         break; /* ERR command received*/
303       if (line[0] != 'D' || line[1] != ' ' || linelen < 3 || gotvalue)
304         continue;
305       gotvalue = 1;
306       value = atoi (line+2);
307     }
308   if (value < -100)
309     value = -100;
310   else if (value > 100)
311     value = 100;
312
313   return value;
314 }
315
316
317
318 /* Try to make room for at least LEN bytes in the pinentry.  Returns
319    new buffer on success and 0 on failure or when the old buffer is
320    sufficient.  */
321 char *
322 pinentry_setbufferlen (pinentry_t pin, int len)
323 {
324   char *newp;
325   if (len < pinentry.pin_len)
326     return NULL;
327   newp = secmem_realloc (pin->pin, 2 * pin->pin_len);
328   if (newp)
329     {
330       pin->pin = newp;
331       pin->pin_len *= 2;
332     }
333   else
334     {
335       secmem_free (pin->pin);
336       pin->pin = 0;
337       pin->pin_len = 0;
338     }
339   return newp;
340 }
341
342
343 /* Initialize the secure memory subsystem, drop privileges and return.
344    Must be called early. */
345 void
346 pinentry_init (const char *pgmname)
347 {
348   /* Store away our name. */
349   if (strlen (pgmname) > sizeof this_pgmname - 2)
350     abort ();
351   strcpy (this_pgmname, pgmname);
352
353   /* Initialize secure memory.  1 is too small, so the default size
354      will be used.  */
355   secmem_init (1);
356   secmem_set_flags (SECMEM_WARN);
357   drop_privs ();
358
359   if (atexit (secmem_term))
360     /* FIXME: Could not register at-exit function, bail out.  */
361     ;
362
363   assuan_set_malloc_hooks (secmem_malloc, secmem_realloc, secmem_free);
364 }
365
366 /* Simple test to check whether DISPLAY is set or the option --display
367    was given.  Used to decide whether the GUI or curses should be
368    initialized.  */
369 int
370 pinentry_have_display (int argc, char **argv)
371 {
372   const char *s;
373
374   s = getenv ("DISPLAY");
375   if (s && *s)
376     return 1;
377   for (; argc; argc--, argv++)
378     if (!strcmp (*argv, "--display"))
379       return 1;
380   return 0;
381 }
382
383
384 \f
385 static void 
386 usage (void)
387 {
388   fprintf (stdout, "Usage: %s [OPTION]...\n"
389 "Ask securely for a secret and print it to stdout.\n"
390 "\n"
391 "      --display DISPLAY Set the X display\n"
392 "      --ttyname PATH    Set the tty terminal node name\n"
393 "      --ttytype NAME    Set the tty terminal type\n"
394 "      --lc-ctype        Set the tty LC_CTYPE value\n"
395 "      --lc-messages     Set the tty LC_MESSAGES value\n"
396 "  -e, --enhanced        Ask for timeout and insurance, too\n"
397 "  -g, --no-global-grab  Grab keyboard only while window is focused\n"
398 "      --parent-wid      Parent window ID (for positioning)\n"
399 "  -d, --debug           Turn on debugging output\n"
400 "  -h, --help            Display this help and exit\n"
401 "      --version         Output version information and exit\n", this_pgmname);
402 }
403
404
405 char *
406 parse_color (char *arg, pinentry_color_t *color_p, int *bright_p)
407 {
408   static struct
409   {
410     const char *name;
411     pinentry_color_t color;
412   } colors[] = { { "none", PINENTRY_COLOR_NONE },
413                  { "default", PINENTRY_COLOR_DEFAULT },
414                  { "black", PINENTRY_COLOR_BLACK },
415                  { "red", PINENTRY_COLOR_RED },
416                  { "green", PINENTRY_COLOR_GREEN },
417                  { "yellow", PINENTRY_COLOR_YELLOW },
418                  { "blue", PINENTRY_COLOR_BLUE },
419                  { "magenta", PINENTRY_COLOR_MAGENTA },
420                  { "cyan", PINENTRY_COLOR_CYAN },
421                  { "white", PINENTRY_COLOR_WHITE } };
422
423   int i;
424   char *new_arg;
425   pinentry_color_t color = PINENTRY_COLOR_DEFAULT;
426
427   if (!arg)
428     return NULL;
429
430   new_arg = strchr (arg, ',');
431   if (new_arg)
432     new_arg++;
433
434   if (bright_p)
435     {
436       const char *bname[] = { "bright-", "bright", "bold-", "bold" };
437
438       *bright_p = 0;
439       for (i = 0; i < sizeof (bname) / sizeof (bname[0]); i++)
440         if (!strncasecmp (arg, bname[i], strlen (bname[i])))
441           {
442             *bright_p = 1;
443             arg += strlen (bname[i]);
444           }
445     }
446
447   for (i = 0; i < sizeof (colors) / sizeof (colors[0]); i++)
448     if (!strncasecmp (arg, colors[i].name, strlen (colors[i].name)))
449       color = colors[i].color;
450
451   *color_p = color;
452   return new_arg;
453 }
454
455 /* Parse the command line options.  Returns 1 if user should print
456    version and exit.  Can exit the program if only help output is
457    requested.  */
458 int
459 pinentry_parse_opts (int argc, char *argv[])
460 {
461   int opt;
462   int opt_help = 0;
463   int opt_version = 0;
464   struct option opts[] =
465     {{ "debug", no_argument,             0, 'd' },
466      { "display", required_argument,     0, 'D' },
467      { "ttyname", required_argument,     0, 'T' },
468      { "ttytype", required_argument,     0, 'N' },
469      { "lc-ctype", required_argument,    0, 'C' },
470      { "lc-messages", required_argument, 0, 'M' },
471      { "enhanced", no_argument,          0, 'e' },
472      { "no-global-grab", no_argument,    0, 'g' },
473      { "parent-wid", required_argument,  0, 'W' },
474      { "colors", required_argument,      0, 'c' },
475      { "help", no_argument,              0, 'h' },
476      { "version", no_argument, &opt_version, 1 },
477      { NULL, 0, NULL, 0 }};
478   
479   while ((opt = getopt_long (argc, argv, "degh", opts, NULL)) != -1)
480     {
481       switch (opt)
482         {
483         case 0:
484         case '?':
485           break;
486         case 'd':
487           pinentry.debug = 1;
488           break;
489         case 'e':
490           pinentry.enhanced = 1;
491           break;
492         case 'g':
493           pinentry.grab = 0;
494           break;
495         case 'h':
496           opt_help = 1;
497           break;
498
499         case 'D':
500           /* Note, this is currently not used because the GUI engine
501              has already been initialized when parsing these options. */
502           pinentry.display = strdup (optarg);
503           if (!pinentry.display)
504             {
505               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
506               exit (EXIT_FAILURE);
507             }
508           break; 
509         case 'T':
510           pinentry.ttyname = strdup (optarg);
511           if (!pinentry.ttyname)
512             {
513               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
514               exit (EXIT_FAILURE);
515             }
516           break;
517         case 'N':
518           pinentry.ttytype = strdup (optarg);
519           if (!pinentry.ttytype)
520             {
521               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
522               exit (EXIT_FAILURE);
523             }
524           break;
525         case 'C':
526           pinentry.lc_ctype = strdup (optarg);
527           if (!pinentry.lc_ctype)
528             {
529               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
530               exit (EXIT_FAILURE);
531             }
532           break;
533         case 'M':
534           pinentry.lc_messages = strdup (optarg);
535           if (!pinentry.lc_messages)
536             {
537               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
538               exit (EXIT_FAILURE);
539             }
540           break;
541         case 'W':
542           pinentry.parent_wid = atoi (optarg);
543           /* FIXME: Add some error handling.  Use strtol.  */
544           break;
545
546         case 'c':
547           optarg = parse_color (optarg, &pinentry.color_fg,
548                                 &pinentry.color_fg_bright);
549           optarg = parse_color (optarg, &pinentry.color_bg, NULL);
550           optarg = parse_color (optarg, &pinentry.color_so,
551                                 &pinentry.color_so_bright);
552           break;
553
554         default:
555           fprintf (stderr, "%s: oops: option not handled\n", this_pgmname);
556           break;
557         }
558     }
559   if (opt_version) 
560     return 1;
561   if (opt_help) 
562     {
563       usage ();
564       exit (EXIT_SUCCESS);
565     }
566   return 0;
567 }
568
569 \f
570 static int
571 option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
572 {
573   if (!strcmp (key, "no-grab") && !*value)
574     pinentry.grab = 0;
575   else if (!strcmp (key, "grab") && !*value)
576     pinentry.grab = 1;
577   else if (!strcmp (key, "debug-wait"))
578     {
579       fprintf (stderr, "%s: waiting for debugger - my pid is %u ...\n",
580                this_pgmname, (unsigned int) getpid());
581       sleep (*value?atoi (value):5);
582       fprintf (stderr, "%s: ... okay\n", this_pgmname);
583     }
584   else if (!strcmp (key, "display"))
585     {
586       if (pinentry.display)
587         free (pinentry.display);
588       pinentry.display = strdup (value);
589       if (!pinentry.display)
590         return ASSUAN_Out_Of_Core;
591     }
592   else if (!strcmp (key, "ttyname"))
593     {
594       if (pinentry.ttyname)
595         free (pinentry.ttyname);
596       pinentry.ttyname = strdup (value);
597       if (!pinentry.ttyname)
598         return ASSUAN_Out_Of_Core;
599     }
600   else if (!strcmp (key, "ttytype"))
601     {
602       if (pinentry.ttytype)
603         free (pinentry.ttytype);
604       pinentry.ttytype = strdup (value);
605       if (!pinentry.ttytype)
606         return ASSUAN_Out_Of_Core;
607     }
608   else if (!strcmp (key, "lc-ctype"))
609     {
610       if (pinentry.lc_ctype)
611         free (pinentry.lc_ctype);
612       pinentry.lc_ctype = strdup (value);
613       if (!pinentry.lc_ctype)
614         return ASSUAN_Out_Of_Core;
615     }
616   else if (!strcmp (key, "lc-messages"))
617     {
618       if (pinentry.lc_messages)
619         free (pinentry.lc_messages);
620       pinentry.lc_messages = strdup (value);
621       if (!pinentry.lc_messages)
622         return ASSUAN_Out_Of_Core;
623     }
624   else if (!strcmp (key, "parent-wid"))
625     {
626       pinentry.parent_wid = atoi (value);
627       /* FIXME: Use strtol and add some error handling.  */
628     }
629   else if (!strcmp (key, "touch-file"))
630     {
631       if (pinentry.touch_file)
632         free (pinentry.touch_file);
633       pinentry.touch_file = strdup (value);
634       if (!pinentry.touch_file)
635         return ASSUAN_Out_Of_Core;
636     }
637   else
638     return ASSUAN_Invalid_Option;
639   return 0;
640 }
641
642
643 /* Note, that it is sufficient to allocate the target string D as
644    long as the source string S, i.e.: strlen(s)+1; */
645 static void
646 strcpy_escaped (char *d, const unsigned char *s)
647 {
648   while (*s)
649     {
650       if (*s == '%' && s[1] && s[2])
651         { 
652           s++;
653           *d++ = xtoi_2 ( s);
654           s += 2;
655         }
656       else
657         *d++ = *s++;
658     }
659   *d = 0; 
660 }
661
662
663 static int
664 cmd_setdesc (ASSUAN_CONTEXT ctx, char *line)
665 {
666   char *newd;
667   newd = malloc (strlen (line) + 1);
668
669   if (!newd)
670     return ASSUAN_Out_Of_Core;
671
672   strcpy_escaped (newd, line);
673   if (pinentry.description)
674     free (pinentry.description);
675   pinentry.description = newd;
676   return 0;
677 }
678
679
680 static int
681 cmd_setprompt (ASSUAN_CONTEXT ctx, char *line)
682 {
683   char *newp;
684   newp = malloc (strlen (line) + 1);
685
686   if (!newp)
687     return ASSUAN_Out_Of_Core;
688
689   strcpy_escaped (newp, line);
690   if (pinentry.prompt)
691     free (pinentry.prompt);
692   pinentry.prompt = newp;
693   return 0;
694 }
695
696
697 static int
698 cmd_seterror (ASSUAN_CONTEXT ctx, char *line)
699 {
700   char *newe;
701   newe = malloc (strlen (line) + 1);
702
703   if (!newe)
704     return ASSUAN_Out_Of_Core;
705
706   strcpy_escaped (newe, line);
707   if (pinentry.error)
708     free (pinentry.error);
709   pinentry.error = newe;
710   return 0;
711 }
712
713
714 static int
715 cmd_setok (ASSUAN_CONTEXT ctx, char *line)
716 {
717   char *newo;
718   newo = malloc (strlen (line) + 1);
719
720   if (!newo)
721     return ASSUAN_Out_Of_Core;
722
723   strcpy_escaped (newo, line);
724   if (pinentry.ok)
725     free (pinentry.ok);
726   pinentry.ok = newo;
727   return 0;
728 }
729
730
731 static int
732 cmd_setcancel (ASSUAN_CONTEXT ctx, char *line)
733 {
734   char *newc;
735   newc = malloc (strlen (line) + 1);
736
737   if (!newc)
738     return ASSUAN_Out_Of_Core;
739
740   strcpy_escaped (newc, line);
741   if (pinentry.cancel)
742     free (pinentry.cancel);
743   pinentry.cancel = newc;
744   return 0;
745 }
746
747
748
749 static int
750 cmd_setqualitybar (ASSUAN_CONTEXT ctx, char *line)
751 {
752   char *newval;
753
754   if (!*line)
755     line = "Quality:";
756
757   newval = malloc (strlen (line) + 1);
758   if (!newval)
759     return ASSUAN_Out_Of_Core;
760
761   strcpy_escaped (newval, line);
762   if (pinentry.quality_bar)
763     free (pinentry.quality_bar);
764   pinentry.quality_bar = newval;
765   return 0;
766 }
767
768 /* Set the tooltip to be used for a quality bar.  */
769 static int
770 cmd_setqualitybar_tt (ASSUAN_CONTEXT ctx, char *line)
771 {
772   char *newval;
773
774   if (*line)
775     {
776       newval = malloc (strlen (line) + 1);
777       if (!newval)
778         return ASSUAN_Out_Of_Core;
779       
780       strcpy_escaped (newval, line);
781     }
782   else
783     newval = NULL;
784   if (pinentry.quality_bar_tt)
785     free (pinentry.quality_bar_tt);
786   pinentry.quality_bar_tt = newval;
787   return 0;
788 }
789
790
791 static int
792 cmd_getpin (ASSUAN_CONTEXT ctx, char *line)
793 {
794   int result;
795   int set_prompt = 0;
796
797   pinentry.pin = secmem_malloc (pinentry.pin_len);
798   if (!pinentry.pin)
799     return ASSUAN_Out_Of_Core;
800   if (!pinentry.prompt)
801     {
802       pinentry.prompt = "PIN:";
803       set_prompt = 1;
804     }
805   pinentry.locale_err = 0;
806   pinentry.one_button = 0;
807   pinentry.ctx_assuan = ctx;
808   result = (*pinentry_cmd_handler) (&pinentry);
809   pinentry.ctx_assuan = NULL;
810   if (pinentry.error)
811     {
812       free (pinentry.error);
813       pinentry.error = NULL;
814     }
815   if (set_prompt)
816     pinentry.prompt = NULL;
817
818   pinentry.quality_bar = 0;  /* Reset it after the command.  */
819
820   if (result < 0)
821     {
822       if (pinentry.pin)
823         {
824           secmem_free (pinentry.pin);
825           pinentry.pin = NULL;
826         }
827       return pinentry.locale_err? ASSUAN_Locale_Problem: ASSUAN_Canceled;
828     }
829
830   if (result)
831     {
832       result = assuan_send_data (ctx, pinentry.pin, result);
833       if (!result)
834         result = assuan_send_data (ctx, NULL, 0);
835     }
836
837   if (pinentry.pin)
838     {
839       secmem_free (pinentry.pin);
840       pinentry.pin = NULL;
841     }
842
843   return result;
844 }
845
846
847 /* Note that the option --one-button is hack to allow the use of old
848    pinentries while the caller is ignoring the result.  Given that
849    options have never been used or flagged as an error the new option
850    is an easy way to enable the messsage mode while not requiring to
851    update pinentry or to have the caller test for the message
852    command.  New applications which are free to require an updated
853    pinentry should use MESSAGE instead. */
854 static int
855 cmd_confirm (ASSUAN_CONTEXT ctx, char *line)
856 {
857   int result;
858
859   pinentry.one_button = !!strstr (line, "--one-button");
860   pinentry.quality_bar = 0;
861   pinentry.locale_err = 0;
862   result = (*pinentry_cmd_handler) (&pinentry);
863   if (pinentry.error)
864     {
865       free (pinentry.error);
866       pinentry.error = NULL;
867     }
868
869   return result ? 0
870                 : (pinentry.locale_err? ASSUAN_Locale_Problem
871                                       : (pinentry.one_button 
872                                          ? 0
873                                          : ASSUAN_Not_Confirmed));
874 }
875
876
877 static int
878 cmd_message (ASSUAN_CONTEXT ctx, char *line)
879 {
880   int result;
881
882   pinentry.one_button = 1;
883   pinentry.quality_bar = 0;
884   pinentry.locale_err = 0;
885   result = (*pinentry_cmd_handler) (&pinentry);
886   if (pinentry.error)
887     {
888       free (pinentry.error);
889       pinentry.error = NULL;
890     }
891
892   return result ? 0 
893                 : (pinentry.locale_err? ASSUAN_Locale_Problem
894                                       : 0);
895 }
896
897 /* GETINFO <what>
898
899    Multipurpose function to return a variety of information.
900    Supported values for WHAT are:
901
902      version     - Return the version of the program.
903      pid         - Return the process id of the server.
904  */
905 static int
906 cmd_getinfo (assuan_context_t ctx, char *line)
907 {
908   int rc;
909
910   if (!strcmp (line, "version"))
911     {
912       const char *s = VERSION;
913       rc = assuan_send_data (ctx, s, strlen (s));
914     }
915   else if (!strcmp (line, "pid"))
916     {
917       char numbuf[50];
918
919       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
920       rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
921     }
922   else
923     rc = ASSUAN_Parameter_Error;
924   return rc;
925 }
926
927
928 /* Tell the assuan library about our commands.  */
929 static int
930 register_commands (ASSUAN_CONTEXT ctx)
931 {
932   static struct
933   {
934     const char *name;
935     int cmd_id;
936     int (*handler) (ASSUAN_CONTEXT, char *line);
937   } table[] =
938     {
939       { "SETDESC",    0,  cmd_setdesc },
940       { "SETPROMPT",  0,  cmd_setprompt },
941       { "SETERROR",   0,  cmd_seterror },
942       { "SETOK",      0,  cmd_setok },
943       { "SETCANCEL",  0,  cmd_setcancel },
944       { "GETPIN",     0,  cmd_getpin },
945       { "CONFIRM",    0,  cmd_confirm },
946       { "MESSAGE",    0,  cmd_message },
947       { "SETQUALITYBAR", 0,  cmd_setqualitybar },
948       { "SETQUALITYBAR_TT", 0,  cmd_setqualitybar_tt },
949       { "GETINFO",    0,  cmd_getinfo },
950       { NULL }
951     };
952   int i, j, rc;
953
954   for (i = j = 0; table[i].name; i++)
955     {
956       rc = assuan_register_command (ctx,
957                                     table[i].cmd_id ? table[i].cmd_id
958                                                    : (ASSUAN_CMD_USER + j++),
959                                     table[i].name, table[i].handler);
960       if (rc)
961         return rc;
962     } 
963   return 0;
964 }
965
966
967 /* Start the pinentry event loop.  The program will start to process
968    Assuan commands until it is finished or an error occurs.  If an
969    error occurs, -1 is returned.  Otherwise, 0 is returned.  */
970 int
971 pinentry_loop (void)
972 {
973   int rc;
974   int filedes[2];
975   ASSUAN_CONTEXT ctx;
976
977   /* Extra check to make sure we have dropped privs. */
978 #ifndef HAVE_DOSISH_SYSTEM
979   if (getuid() != geteuid())
980     abort ();
981 #endif
982
983   /* For now we use a simple pipe based server so that we can work
984      from scripts.  We will later add options to run as a daemon and
985      wait for requests on a Unix domain socket.  */
986   filedes[0] = 0;
987   filedes[1] = 1;
988   rc = assuan_init_pipe_server (&ctx, filedes);
989   if (rc)
990     {
991       fprintf (stderr, "%s: failed to initialize the server: %s\n",
992                this_pgmname, assuan_strerror(rc));
993       return -1;
994     }
995   rc = register_commands (ctx);
996   if (rc)
997     {
998       fprintf (stderr, "%s: failed to the register commands with Assuan: %s\n",
999                this_pgmname, assuan_strerror(rc));
1000       return -1;
1001     }
1002
1003   assuan_register_option_handler (ctx, option_handler);
1004 #if 0
1005   assuan_set_log_stream (ctx, stderr);
1006 #endif
1007   
1008   for (;;)
1009     {
1010       rc = assuan_accept (ctx);
1011       if (rc == -1)
1012           break;
1013       else if (rc)
1014         {
1015           fprintf (stderr, "%s: Assuan accept problem: %s\n",
1016                    this_pgmname, assuan_strerror (rc));
1017           break;
1018         }
1019       
1020       rc = assuan_process (ctx);
1021       if (rc)
1022         {
1023           fprintf (stderr, "%s: Assuan processing failed: %s\n",
1024                    this_pgmname, assuan_strerror (rc));
1025           continue;
1026         }
1027     }
1028
1029   assuan_deinit_server (ctx);
1030   return 0;
1031 }