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