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