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