2007-05-10 Marcus Brinkmann <marcus@g10code.de>
[pinentry.git] / pinentry / pinentry.c
1 /* pinentry.c - The PIN entry support library
2    Copyright (C) 2002, 2003 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, write to the Free Software
18    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19    02111-1307, USA  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <getopt.h>
29 #include <unistd.h>
30 #include <locale.h>
31 #ifdef HAVE_LANGINFO_H
32 #include <langinfo.h>
33 #endif
34 #include <limits.h>
35
36 #if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK
37 #include <iconv.h>
38 #endif
39
40 #include "assuan.h"
41 #include "memory.h"
42 #include "secmem-util.h"
43 #include "pinentry.h"
44
45
46 /* Keep the name of our program here. */
47 static char this_pgmname[50]; 
48
49
50 struct pinentry pinentry =
51   {
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     PINENTRY_COLOR_DEFAULT,
73     0,
74     PINENTRY_COLOR_DEFAULT,
75     PINENTRY_COLOR_DEFAULT,
76     0
77   };
78
79 \f
80
81 #if defined FALLBACK_CURSES || defined PINENTRY_CURSES || defined PINENTRY_GTK
82 char *
83 pinentry_utf8_to_local (char *lc_ctype, char *text)
84 {
85   iconv_t cd;
86   const char *input = text;
87   size_t input_len = strlen (text) + 1;
88   char *output;
89   size_t output_len;
90   char *output_buf;
91   size_t processed;
92   char *old_ctype;
93   char *target_encoding;
94
95   /* If no locale setting could be determined, simply copy the
96      string.  */
97   if (!lc_ctype)
98     {
99       fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
100                this_pgmname);
101       return strdup (text);
102     }
103
104   old_ctype = strdup (setlocale (LC_CTYPE, NULL));
105   if (!old_ctype)
106     return NULL;
107   setlocale (LC_CTYPE, lc_ctype);
108   target_encoding = nl_langinfo (CODESET);
109   if (!target_encoding)
110     target_encoding = "?";
111   setlocale (LC_CTYPE, old_ctype);
112   free (old_ctype);
113
114   /* This is overkill, but simplifies the iconv invocation greatly.  */
115   output_len = input_len * MB_LEN_MAX;
116   output_buf = output = malloc (output_len);
117   if (!output)
118     return NULL;
119
120   cd = iconv_open (target_encoding, "UTF-8");
121   if (cd == (iconv_t) -1)
122     {
123       fprintf (stderr, "%s: can't convert from UTF-8 to %s: %s\n",
124                this_pgmname, target_encoding, strerror (errno));
125       free (output_buf);
126       return NULL;
127     }
128   processed = iconv (cd, &input, &input_len, &output, &output_len);
129   iconv_close (cd);
130   if (processed == (size_t) -1 || input_len)
131     {
132       fprintf (stderr, "%s: error converting from UTF-8 to %s: %s\n",
133                this_pgmname, target_encoding, strerror (errno));
134       free (output_buf);
135       return NULL;
136     }
137   return output_buf;
138 }
139
140 /* Convert TEXT which is encoded according to LC_CTYPE to UTF-8.  With
141    SECURE set to true, use secure memory for the returned buffer.
142    Return NULL on error. */
143 char *
144 pinentry_local_to_utf8 (char *lc_ctype, char *text, int secure)
145 {
146   char *old_ctype;
147   char *source_encoding;
148   iconv_t cd;
149   const char *input = text;
150   size_t input_len = strlen (text) + 1;
151   char *output;
152   size_t output_len;
153   char *output_buf;
154   size_t processed;
155
156   /* If no locale setting could be determined, simply copy the
157      string.  */
158   if (!lc_ctype)
159     {
160       fprintf (stderr, "%s: no LC_CTYPE known - assuming UTF-8\n",
161                this_pgmname);
162       output_buf = secure? secmem_malloc (input_len) : malloc (input_len);
163       if (output_buf)
164         strcpy (output_buf, input);
165       return output_buf;
166     }
167
168   old_ctype = strdup (setlocale (LC_CTYPE, NULL));
169   if (!old_ctype)
170     return NULL;
171   setlocale (LC_CTYPE, lc_ctype);
172   source_encoding = nl_langinfo (CODESET);
173   setlocale (LC_CTYPE, old_ctype);
174   free (old_ctype);
175
176   /* This is overkill, but simplifies the iconv invocation greatly.  */
177   output_len = input_len * MB_LEN_MAX;
178   output_buf = output = secure? secmem_malloc (output_len):malloc (output_len);
179   if (!output)
180     return NULL;
181
182   cd = iconv_open ("UTF-8", source_encoding);
183   if (cd == (iconv_t) -1)
184     {
185       fprintf (stderr, "%s: can't convert from %s to UTF-8: %s\n",
186                this_pgmname, source_encoding? source_encoding : "?",
187                strerror (errno));
188       if (secure)
189         secmem_free (output_buf);
190       else
191         free (output_buf);
192       return NULL;
193     }
194   processed = iconv (cd, &input, &input_len, &output, &output_len);
195   iconv_close (cd);
196   if (processed == (size_t) -1 || input_len)
197     {
198       fprintf (stderr, "%s: error converting from %s to UTF-8: %s\n",
199                this_pgmname, source_encoding? source_encoding : "?",
200                strerror (errno));
201       if (secure)
202         secmem_free (output_buf);
203       else
204         free (output_buf);
205       return NULL;
206     }
207   return output_buf;
208 }
209 #endif
210
211 /* Try to make room for at least LEN bytes in the pinentry.  Returns
212    new buffer on success and 0 on failure or when the old buffer is
213    sufficient.  */
214 char *
215 pinentry_setbufferlen (pinentry_t pin, int len)
216 {
217   char *newp;
218   if (len < pinentry.pin_len)
219     return NULL;
220   newp = secmem_realloc (pin->pin, 2 * pin->pin_len);
221   if (newp)
222     {
223       pin->pin = newp;
224       pin->pin_len *= 2;
225     }
226   else
227     {
228       secmem_free (pin->pin);
229       pin->pin = 0;
230       pin->pin_len = 0;
231     }
232   return newp;
233 }
234
235
236 /* Initialize the secure memory subsystem, drop privileges and return.
237    Must be called early. */
238 void
239 pinentry_init (const char *pgmname)
240 {
241   /* Store away our name. */
242   if (strlen (pgmname) > sizeof this_pgmname - 2)
243     abort ();
244   strcpy (this_pgmname, pgmname);
245
246   /* Initialize secure memory.  1 is too small, so the default size
247      will be used.  */
248   secmem_init (1);
249   secmem_set_flags (SECMEM_WARN);
250   drop_privs ();
251
252   if (atexit (secmem_term))
253     /* FIXME: Could not register at-exit function, bail out.  */
254     ;
255
256   assuan_set_malloc_hooks (secmem_malloc, secmem_realloc, secmem_free);
257 }
258
259 /* Simple test to check whether DISPLAY is set or the option --display
260    was given.  Used to decide whether the GUI or curses should be
261    initialized.  */
262 int
263 pinentry_have_display (int argc, char **argv)
264 {
265   const char *s;
266
267   s = getenv ("DISPLAY");
268   if (s && *s)
269     return 1;
270   for (; argc; argc--, argv++)
271     if (!strcmp (*argv, "--display"))
272       return 1;
273   return 0;
274 }
275
276
277 \f
278 static void 
279 usage (void)
280 {
281   fprintf (stdout, "Usage: %s [OPTION]...\n"
282 "Ask securely for a secret and print it to stdout.\n"
283 "\n"
284 "      --display DISPLAY Set the X display\n"
285 "      --ttyname PATH    Set the tty terminal node name\n"
286 "      --ttytype NAME    Set the tty terminal type\n"
287 "      --lc-ctype        Set the tty LC_CTYPE value\n"
288 "      --lc-messages     Set the tty LC_MESSAGES value\n"
289 "  -e, --enhanced        Ask for timeout and insurance, too\n"
290 "  -g, --no-global-grab  Grab keyboard only while window is focused\n"
291 "      --parent-wid      Parent window ID (for positioning)\n"
292 "  -d, --debug           Turn on debugging output\n"
293 "  -h, --help            Display this help and exit\n"
294 "      --version         Output version information and exit\n", this_pgmname);
295 }
296
297
298 char *
299 parse_color (char *arg, pinentry_color_t *color_p, int *bright_p)
300 {
301   static struct
302   {
303     const char *name;
304     pinentry_color_t color;
305   } colors[] = { { "none", PINENTRY_COLOR_NONE },
306                  { "default", PINENTRY_COLOR_DEFAULT },
307                  { "black", PINENTRY_COLOR_BLACK },
308                  { "red", PINENTRY_COLOR_RED },
309                  { "green", PINENTRY_COLOR_GREEN },
310                  { "yellow", PINENTRY_COLOR_YELLOW },
311                  { "blue", PINENTRY_COLOR_BLUE },
312                  { "magenta", PINENTRY_COLOR_MAGENTA },
313                  { "cyan", PINENTRY_COLOR_CYAN },
314                  { "white", PINENTRY_COLOR_WHITE } };
315
316   int i;
317   char *new_arg;
318   pinentry_color_t color = PINENTRY_COLOR_DEFAULT;
319
320   if (!arg)
321     return NULL;
322
323   new_arg = strchr (arg, ',');
324   if (new_arg)
325     new_arg++;
326
327   if (bright_p)
328     {
329       const char *bname[] = { "bright-", "bright", "bold-", "bold" };
330
331       *bright_p = 0;
332       for (i = 0; i < sizeof (bname) / sizeof (bname[0]); i++)
333         if (!strncasecmp (arg, bname[i], strlen (bname[i])))
334           {
335             *bright_p = 1;
336             arg += strlen (bname[i]);
337           }
338     }
339
340   for (i = 0; i < sizeof (colors) / sizeof (colors[0]); i++)
341     if (!strncasecmp (arg, colors[i].name, strlen (colors[i].name)))
342       color = colors[i].color;
343
344   *color_p = color;
345   return new_arg;
346 }
347
348 /* Parse the command line options.  Returns 1 if user should print
349    version and exit.  Can exit the program if only help output is
350    requested.  */
351 int
352 pinentry_parse_opts (int argc, char *argv[])
353 {
354   int opt;
355   int opt_help = 0;
356   int opt_version = 0;
357   struct option opts[] =
358     {{ "debug", no_argument,             0, 'd' },
359      { "display", required_argument,     0, 'D' },
360      { "ttyname", required_argument,     0, 'T' },
361      { "ttytype", required_argument,     0, 'N' },
362      { "lc-ctype", required_argument,    0, 'C' },
363      { "lc-messages", required_argument, 0, 'M' },
364      { "enhanced", no_argument,          0, 'e' },
365      { "no-global-grab", no_argument,    0, 'g' },
366      { "parent-wid", required_argument,  0, 'W' },
367      { "colors", required_argument,      0, 'c' },
368      { "help", no_argument,              0, 'h' },
369      { "version", no_argument, &opt_version, 1 },
370      { NULL, 0, NULL, 0 }};
371   
372   while ((opt = getopt_long (argc, argv, "degh", opts, NULL)) != -1)
373     {
374       switch (opt)
375         {
376         case 0:
377         case '?':
378           break;
379         case 'd':
380           pinentry.debug = 1;
381           break;
382         case 'e':
383           pinentry.enhanced = 1;
384           break;
385         case 'g':
386           pinentry.grab = 0;
387           break;
388         case 'h':
389           opt_help = 1;
390           break;
391
392         case 'D':
393           /* Note, this is currently not used because the GUI engine
394              has already been initialized when parsing these options. */
395           pinentry.display = strdup (optarg);
396           if (!pinentry.display)
397             {
398               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
399               exit (EXIT_FAILURE);
400             }
401           break; 
402         case 'T':
403           pinentry.ttyname = strdup (optarg);
404           if (!pinentry.ttyname)
405             {
406               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
407               exit (EXIT_FAILURE);
408             }
409           break;
410         case 'N':
411           pinentry.ttytype = strdup (optarg);
412           if (!pinentry.ttytype)
413             {
414               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
415               exit (EXIT_FAILURE);
416             }
417           break;
418         case 'C':
419           pinentry.lc_ctype = strdup (optarg);
420           if (!pinentry.lc_ctype)
421             {
422               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
423               exit (EXIT_FAILURE);
424             }
425           break;
426         case 'M':
427           pinentry.lc_messages = strdup (optarg);
428           if (!pinentry.lc_messages)
429             {
430               fprintf (stderr, "%s: %s\n", this_pgmname, strerror (errno));
431               exit (EXIT_FAILURE);
432             }
433           break;
434         case 'W':
435           pinentry.parent_wid = atoi (optarg);
436           /* FIXME: Add some error handling.  Use strtol.  */
437           break;
438
439         case 'c':
440           optarg = parse_color (optarg, &pinentry.color_fg,
441                                 &pinentry.color_fg_bright);
442           optarg = parse_color (optarg, &pinentry.color_bg, NULL);
443           optarg = parse_color (optarg, &pinentry.color_so,
444                                 &pinentry.color_so_bright);
445           break;
446
447         default:
448           fprintf (stderr, "%s: oops: option not handled\n", this_pgmname);
449           break;
450         }
451     }
452   if (opt_version) 
453     return 1;
454   if (opt_help) 
455     {
456       usage ();
457       exit (EXIT_SUCCESS);
458     }
459   return 0;
460 }
461
462 \f
463 static int
464 option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
465 {
466   if (!strcmp (key, "no-grab") && !*value)
467     pinentry.grab = 0;
468   else if (!strcmp (key, "grab") && !*value)
469     pinentry.grab = 1;
470   else if (!strcmp (key, "debug-wait"))
471     {
472       fprintf (stderr, "%s: waiting for debugger - my pid is %u ...\n",
473                this_pgmname, (unsigned int) getpid());
474       sleep (*value?atoi (value):5);
475       fprintf (stderr, "%s: ... okay\n", this_pgmname);
476     }
477   else if (!strcmp (key, "display"))
478     {
479       if (pinentry.display)
480         free (pinentry.display);
481       pinentry.display = strdup (value);
482       if (!pinentry.display)
483         return ASSUAN_Out_Of_Core;
484     }
485   else if (!strcmp (key, "ttyname"))
486     {
487       if (pinentry.ttyname)
488         free (pinentry.ttyname);
489       pinentry.ttyname = strdup (value);
490       if (!pinentry.ttyname)
491         return ASSUAN_Out_Of_Core;
492     }
493   else if (!strcmp (key, "ttytype"))
494     {
495       if (pinentry.ttytype)
496         free (pinentry.ttytype);
497       pinentry.ttytype = strdup (value);
498       if (!pinentry.ttytype)
499         return ASSUAN_Out_Of_Core;
500     }
501   else if (!strcmp (key, "lc-ctype"))
502     {
503       if (pinentry.lc_ctype)
504         free (pinentry.lc_ctype);
505       pinentry.lc_ctype = strdup (value);
506       if (!pinentry.lc_ctype)
507         return ASSUAN_Out_Of_Core;
508     }
509   else if (!strcmp (key, "lc-messages"))
510     {
511       if (pinentry.lc_messages)
512         free (pinentry.lc_messages);
513       pinentry.lc_messages = strdup (value);
514       if (!pinentry.lc_messages)
515         return ASSUAN_Out_Of_Core;
516     }
517   else if (!strcmp (key, "parent-wid"))
518     {
519       pinentry.parent_wid = atoi (value);
520       /* FIXME: Use strtol and add some error handling.  */
521     }
522   else if (!strcmp (key, "touch-file"))
523     {
524       if (pinentry.touch_file)
525         free (pinentry.touch_file);
526       pinentry.touch_file = strdup (value);
527       if (!pinentry.touch_file)
528         return ASSUAN_Out_Of_Core;
529     }
530   else
531     return ASSUAN_Invalid_Option;
532   return 0;
533 }
534
535
536 /* note, that it is sufficient to allocate the target string D as
537    long as the source string S, i.e.: strlen(s)+1; */
538 static void
539 strcpy_escaped (char *d, const unsigned char *s)
540 {
541   while (*s)
542     {
543       if (*s == '%' && s[1] && s[2])
544         { 
545           s++;
546           *d++ = xtoi_2 ( s);
547           s += 2;
548         }
549       else
550         *d++ = *s++;
551     }
552   *d = 0; 
553 }
554
555
556 static int
557 cmd_setdesc (ASSUAN_CONTEXT ctx, char *line)
558 {
559   char *newd;
560   newd = malloc (strlen (line) + 1);
561
562   if (!newd)
563     return ASSUAN_Out_Of_Core;
564
565   strcpy_escaped (newd, line);
566   if (pinentry.description)
567     free (pinentry.description);
568   pinentry.description = newd;
569   return 0;
570 }
571
572
573 static int
574 cmd_setprompt (ASSUAN_CONTEXT ctx, char *line)
575 {
576   char *newp;
577   newp = malloc (strlen (line) + 1);
578
579   if (!newp)
580     return ASSUAN_Out_Of_Core;
581
582   strcpy_escaped (newp, line);
583   if (pinentry.prompt)
584     free (pinentry.prompt);
585   pinentry.prompt = newp;
586   return 0;
587 }
588
589
590 static int
591 cmd_seterror (ASSUAN_CONTEXT ctx, char *line)
592 {
593   char *newe;
594   newe = malloc (strlen (line) + 1);
595
596   if (!newe)
597     return ASSUAN_Out_Of_Core;
598
599   strcpy_escaped (newe, line);
600   if (pinentry.error)
601     free (pinentry.error);
602   pinentry.error = newe;
603   return 0;
604 }
605
606
607 static int
608 cmd_setok (ASSUAN_CONTEXT ctx, char *line)
609 {
610   char *newo;
611   newo = malloc (strlen (line) + 1);
612
613   if (!newo)
614     return ASSUAN_Out_Of_Core;
615
616   strcpy_escaped (newo, line);
617   if (pinentry.ok)
618     free (pinentry.ok);
619   pinentry.ok = newo;
620   return 0;
621 }
622
623
624 static int
625 cmd_setcancel (ASSUAN_CONTEXT ctx, char *line)
626 {
627   char *newc;
628   newc = malloc (strlen (line) + 1);
629
630   if (!newc)
631     return ASSUAN_Out_Of_Core;
632
633   strcpy_escaped (newc, line);
634   if (pinentry.cancel)
635     free (pinentry.cancel);
636   pinentry.cancel = newc;
637   return 0;
638 }
639
640
641 static int
642 cmd_getpin (ASSUAN_CONTEXT ctx, char *line)
643 {
644   int result;
645   int set_prompt = 0;
646
647   pinentry.pin = secmem_malloc (pinentry.pin_len);
648   if (!pinentry.pin)
649     return ASSUAN_Out_Of_Core;
650   if (!pinentry.prompt)
651     {
652       pinentry.prompt = "PIN:";
653       set_prompt = 1;
654     }
655   pinentry.locale_err = 0;
656   pinentry.one_button = 0;
657
658   result = (*pinentry_cmd_handler) (&pinentry);
659   if (pinentry.error)
660     {
661       free (pinentry.error);
662       pinentry.error = NULL;
663     }
664   if (set_prompt)
665     pinentry.prompt = NULL;
666
667   if (result < 0)
668     {
669       if (pinentry.pin)
670         {
671           secmem_free (pinentry.pin);
672           pinentry.pin = NULL;
673         }
674       return pinentry.locale_err? ASSUAN_Locale_Problem: ASSUAN_Canceled;
675     }
676
677   if (result)
678     {
679       result = assuan_send_data (ctx, pinentry.pin, result);
680       if (!result)
681         result = assuan_send_data (ctx, NULL, 0);
682     }
683
684   if (pinentry.pin)
685     {
686       secmem_free (pinentry.pin);
687       pinentry.pin = NULL;
688     }
689
690   return result;
691 }
692
693
694 /* Note that the option --one-button is hack to allow the use of old
695    pinentries while the caller is ignoring the result.  Given that
696    options have never been used or flagged as an error the new option
697    is an easy way to enable the messsage mode while not requiring to
698    update pinentry or to have the caller test for the message
699    command.  New applications which are free to require an updated
700    pinentry should use MESSAGE instead. */
701 static int
702 cmd_confirm (ASSUAN_CONTEXT ctx, char *line)
703 {
704   int result;
705
706   pinentry.one_button = !!strstr (line, "--one-button");
707   pinentry.locale_err = 0;
708   result = (*pinentry_cmd_handler) (&pinentry);
709   if (pinentry.error)
710     {
711       free (pinentry.error);
712       pinentry.error = NULL;
713     }
714
715   return result ? 0
716                 : (pinentry.locale_err? ASSUAN_Locale_Problem
717                                       : (pinentry.one_button 
718                                          ? 0
719                                          : ASSUAN_Not_Confirmed));
720 }
721
722
723 static int
724 cmd_message (ASSUAN_CONTEXT ctx, char *line)
725 {
726   int result;
727
728   pinentry.one_button = 1;
729   pinentry.locale_err = 0;
730   result = (*pinentry_cmd_handler) (&pinentry);
731   if (pinentry.error)
732     {
733       free (pinentry.error);
734       pinentry.error = NULL;
735     }
736
737   return result ? 0 
738                 : (pinentry.locale_err? ASSUAN_Locale_Problem
739                                       : 0);
740 }
741
742
743 /* Tell the assuan library about our commands.  */
744 static int
745 register_commands (ASSUAN_CONTEXT ctx)
746 {
747   static struct
748   {
749     const char *name;
750     int cmd_id;
751     int (*handler) (ASSUAN_CONTEXT, char *line);
752   } table[] =
753     {
754       { "SETDESC",    0,  cmd_setdesc },
755       { "SETPROMPT",  0,  cmd_setprompt },
756       { "SETERROR",   0,  cmd_seterror },
757       { "SETOK",      0,  cmd_setok },
758       { "SETCANCEL",  0,  cmd_setcancel },
759       { "GETPIN",     0,  cmd_getpin },
760       { "CONFIRM",    0,  cmd_confirm },
761       { "MESSAGE",    0,  cmd_message },
762       { NULL }
763     };
764   int i, j, rc;
765
766   for (i = j = 0; table[i].name; i++)
767     {
768       rc = assuan_register_command (ctx,
769                                     table[i].cmd_id ? table[i].cmd_id
770                                                    : (ASSUAN_CMD_USER + j++),
771                                     table[i].name, table[i].handler);
772       if (rc)
773         return rc;
774     } 
775   return 0;
776 }
777
778
779 /* Start the pinentry event loop.  The program will start to process
780    Assuan commands until it is finished or an error occurs.  If an
781    error occurs, -1 is returned.  Otherwise, 0 is returned.  */
782 int
783 pinentry_loop (void)
784 {
785   int rc;
786   int filedes[2];
787   ASSUAN_CONTEXT ctx;
788
789   /* Extra check to make sure we have dropped privs. */
790 #ifndef HAVE_DOSISH_SYSTEM
791   if (getuid() != geteuid())
792     abort ();
793 #endif
794
795   /* For now we use a simple pipe based server so that we can work
796      from scripts.  We will later add options to run as a daemon and
797      wait for requests on a Unix domain socket.  */
798   filedes[0] = 0;
799   filedes[1] = 1;
800   rc = assuan_init_pipe_server (&ctx, filedes);
801   if (rc)
802     {
803       fprintf (stderr, "%s: failed to initialize the server: %s\n",
804                this_pgmname, assuan_strerror(rc));
805       return -1;
806     }
807   rc = register_commands (ctx);
808   if (rc)
809     {
810       fprintf (stderr, "%s: failed to the register commands with Assuan: %s\n",
811                this_pgmname, assuan_strerror(rc));
812       return -1;
813     }
814
815   assuan_register_option_handler (ctx, option_handler);
816 #if 0
817   assuan_set_log_stream (ctx, stderr);
818 #endif
819   
820   for (;;)
821     {
822       rc = assuan_accept (ctx);
823       if (rc == -1)
824           break;
825       else if (rc)
826         {
827           fprintf (stderr, "%s: Assuan accept problem: %s\n",
828                    this_pgmname, assuan_strerror (rc));
829           break;
830         }
831       
832       rc = assuan_process (ctx);
833       if (rc)
834         {
835           fprintf (stderr, "%s: Assuan processing failed: %s\n",
836                    this_pgmname, assuan_strerror (rc));
837           continue;
838         }
839     }
840
841   assuan_deinit_server (ctx);
842   return 0;
843 }