Made it compile.
[gnupg.git] / sm / server.c
1 /* server.c - Server mode and main entry point 
2  *      Copyright (C) 2001, 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it 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  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU 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 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <ctype.h>
28 #include <unistd.h>
29
30 #include "gpgsm.h"
31 #include "../assuan/assuan.h"
32
33 #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
34
35
36 /* The filepointer for status message used in non-server mode */
37 static FILE *statusfp;
38
39 /* Data used to assuciate an Assuan context with local server data */
40 struct server_local_s {
41   ASSUAN_CONTEXT assuan_ctx;
42   int message_fd;
43   int list_internal;
44   int list_external;
45   CERTLIST recplist;
46   CERTLIST signerlist;
47 };
48
49
50
51 /* note, that it is sufficient to allocate the target string D as
52    long as the source string S, i.e.: strlen(s)+1; */
53 static void
54 strcpy_escaped_plus (char *d, const unsigned char *s)
55 {
56   while (*s)
57     {
58       if (*s == '%' && s[1] && s[2])
59         { 
60           s++;
61           *d++ = xtoi_2 ( s);
62           s += 2;
63         }
64       else if (*s == '+')
65         *d++ = ' ', s++;
66       else
67         *d++ = *s++;
68     }
69   *d = 0; 
70 }
71
72
73
74
75 /* Check whether the option NAME appears in LINE */
76 static int
77 has_option (const char *line, const char *name)
78 {
79   const char *s;
80   int n = strlen (name);
81
82   s = strstr (line, name);
83   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
84 }
85
86
87 static void 
88 close_message_fd (CTRL ctrl)
89 {
90   if (ctrl->server_local->message_fd != -1)
91     {
92       close (ctrl->server_local->message_fd);
93       ctrl->server_local->message_fd = -1;
94     }
95 }
96
97
98 static int
99 option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
100 {
101   CTRL ctrl = assuan_get_pointer (ctx);
102
103   if (!strcmp (key, "include-certs"))
104     {
105       int i = *value? atoi (value) : -1;
106       if (ctrl->include_certs < -2)
107         return ASSUAN_Parameter_Error;
108       ctrl->include_certs = i;
109     }
110   else   if (!strcmp (key, "display"))
111     {
112       if (opt.display)
113         free (opt.display);
114       opt.display = strdup (value);
115       if (!opt.display)
116         return ASSUAN_Out_Of_Core;
117     }
118   else if (!strcmp (key, "ttyname"))
119     {
120       if (opt.ttyname)
121         free (opt.ttyname);
122       opt.ttyname = strdup (value);
123       if (!opt.ttyname)
124         return ASSUAN_Out_Of_Core;
125     }
126   else if (!strcmp (key, "ttytype"))
127     {
128       if (opt.ttytype)
129         free (opt.ttytype);
130       opt.ttytype = strdup (value);
131       if (!opt.ttytype)
132         return ASSUAN_Out_Of_Core;
133     }
134   else if (!strcmp (key, "lc-ctype"))
135     {
136       if (opt.lc_ctype)
137         free (opt.lc_ctype);
138       opt.lc_ctype = strdup (value);
139       if (!opt.lc_ctype)
140         return ASSUAN_Out_Of_Core;
141     }
142   else if (!strcmp (key, "lc-messages"))
143     {
144       if (opt.lc_messages)
145         free (opt.lc_messages);
146       opt.lc_messages = strdup (value);
147       if (!opt.lc_messages)
148         return ASSUAN_Out_Of_Core;
149     }
150   else if (!strcmp (key, "list-mode"))
151     {
152       int i = *value? atoi (value) : 0;
153       if (!i || i == 1) /* default and mode 1 */
154         {
155           ctrl->server_local->list_internal = 1;
156           ctrl->server_local->list_external = 0;
157         }
158       else if (i == 2)
159         {
160           ctrl->server_local->list_internal = 0;
161           ctrl->server_local->list_external = 1;
162         }
163       else if (i == 3)
164         {
165           ctrl->server_local->list_internal = 1;
166           ctrl->server_local->list_external = 1;
167         }
168       else
169         return ASSUAN_Parameter_Error;
170     }
171   else
172     return ASSUAN_Invalid_Option;
173
174   return 0;
175 }
176
177
178
179
180 static void
181 reset_notify (ASSUAN_CONTEXT ctx)
182 {
183   CTRL ctrl = assuan_get_pointer (ctx);
184
185   gpgsm_release_certlist (ctrl->server_local->recplist);
186   gpgsm_release_certlist (ctrl->server_local->signerlist);
187   ctrl->server_local->recplist = NULL;
188   ctrl->server_local->signerlist = NULL;
189   close_message_fd (ctrl);
190   assuan_close_input_fd (ctx);
191   assuan_close_output_fd (ctx);
192 }
193
194
195 static void
196 input_notify (ASSUAN_CONTEXT ctx, const char *line)
197 {
198   CTRL ctrl = assuan_get_pointer (ctx);
199
200   ctrl->autodetect_encoding = 0;
201   ctrl->is_pem = 0;
202   ctrl->is_base64 = 0;
203   if (strstr (line, "--armor"))
204     ctrl->is_pem = 1;  
205   else if (strstr (line, "--base64"))
206     ctrl->is_base64 = 1; 
207   else if (strstr (line, "--binary"))
208     ;
209   else
210     ctrl->autodetect_encoding = 1;
211 }
212
213 static void
214 output_notify (ASSUAN_CONTEXT ctx, const char *line)
215 {
216   CTRL ctrl = assuan_get_pointer (ctx);
217
218   ctrl->create_pem = 0;
219   ctrl->create_base64 = 0;
220   if (strstr (line, "--armor"))
221     ctrl->create_pem = 1;  
222   else if (strstr (line, "--base64"))
223     ctrl->create_base64 = 1; /* just the raw output */
224 }
225
226
227
228 /*  RECIPIENT <userID>
229
230   Set the recipient for the encryption.  <userID> should be the
231   internal representation of the key; the server may accept any other
232   way of specification [we will support this].  If this is a valid and
233   trusted recipient the server does respond with OK, otherwise the
234   return is an ERR with the reason why the recipient can't be used,
235   the encryption will then not be done for this recipient.  IF the
236   policy is not to encrypt at all if not all recipients are valid, the
237   client has to take care of this.  All RECIPIENT commands are
238   cumulative until a RESET or an successful ENCRYPT command.  */
239 static int 
240 cmd_recipient (ASSUAN_CONTEXT ctx, char *line)
241 {
242   CTRL ctrl = assuan_get_pointer (ctx);
243   int rc;
244
245   rc = gpgsm_add_to_certlist (ctrl, line, 0, &ctrl->server_local->recplist);
246   if (rc)
247     gpgsm_status2 (ctrl, STATUS_INV_RECP,
248                    rc == -1? "1":
249                    rc == GNUPG_No_Public_Key?       "1":
250                    rc == GNUPG_Ambiguous_Name?      "2":
251                    rc == GNUPG_Wrong_Key_Usage?     "3":
252                    rc == GNUPG_Certificate_Revoked? "4":
253                    rc == GNUPG_Certificate_Expired? "5":
254                    rc == GNUPG_No_CRL_Known?        "6":
255                    rc == GNUPG_CRL_Too_Old?         "7":
256                    rc == GNUPG_No_Policy_Match?     "8":
257                    "0",
258                    line, NULL);
259
260   return map_to_assuan_status (rc);
261 }
262
263 /*  SIGNER <userID>
264
265   Set the signer's keys for the signature creation.  <userID> should
266   be the internal representation of the key; the server may accept any
267   other way of specification [we will support this].  If this is a
268   valid and usable signing key the server does respond with OK,
269   otherwise it returns an ERR with the reason why the key can't be
270   used, the signing will then not be done for this key.  If the policy
271   is not to sign at all if not all signer keys are valid, the client
272   has to take care of this.  All SIGNER commands are cumulative until
273   a RESET but they are *not* reset by an SIGN command becuase it can
274   be expected that set of signers are used for more than one sign
275   operation.  
276
277   Note that this command returns an INV_RECP status which is a bit
278   strange, but they are very similar.  */
279 static int 
280 cmd_signer (ASSUAN_CONTEXT ctx, char *line)
281 {
282   CTRL ctrl = assuan_get_pointer (ctx);
283   int rc;
284
285   rc = gpgsm_add_to_certlist (ctrl, line, 1, &ctrl->server_local->signerlist);
286   if (rc)
287     gpgsm_status2 (ctrl, STATUS_INV_RECP,
288                    rc == -1? "1":
289                    rc == GNUPG_No_Public_Key?       "1":
290                    rc == GNUPG_Ambiguous_Name?      "2":
291                    rc == GNUPG_Wrong_Key_Usage?     "3":
292                    rc == GNUPG_Certificate_Revoked? "4":
293                    rc == GNUPG_Certificate_Expired? "5":
294                    rc == GNUPG_No_CRL_Known?        "6":
295                    rc == GNUPG_CRL_Too_Old?         "7":
296                    rc == GNUPG_No_Policy_Match?     "8":
297                    rc == GNUPG_No_Secret_Key?       "9":
298                    "0",
299                    line, NULL);
300
301   return map_to_assuan_status (rc);
302 }
303
304
305 /* ENCRYPT 
306
307   Do the actual encryption process. Takes the plaintext from the INPUT
308   command, writes to the ciphertext to the file descriptor set with
309   the OUTPUT command, take the recipients form all the recipients set
310   so far.  If this command fails the clients should try to delete all
311   output currently done or otherwise mark it as invalid.  GPGSM does
312   ensure that there won't be any security problem with leftover data
313   on the output in this case.
314
315   This command should in general not fail, as all necessary checks
316   have been done while setting the recipients.  The input and output
317   pipes are closed. */
318 static int 
319 cmd_encrypt (ASSUAN_CONTEXT ctx, char *line)
320 {
321   CTRL ctrl = assuan_get_pointer (ctx);
322   int inp_fd, out_fd;
323   FILE *out_fp;
324   int rc;
325
326   inp_fd = assuan_get_input_fd (ctx);
327   if (inp_fd == -1)
328     return set_error (No_Input, NULL);
329   out_fd = assuan_get_output_fd (ctx);
330   if (out_fd == -1)
331     return set_error (No_Output, NULL);
332
333   out_fp = fdopen ( dup(out_fd), "w");
334   if (!out_fp)
335     return set_error (General_Error, "fdopen() failed");
336   rc = gpgsm_encrypt (assuan_get_pointer (ctx),
337                       ctrl->server_local->recplist,
338                       inp_fd, out_fp);
339   fclose (out_fp);
340
341   gpgsm_release_certlist (ctrl->server_local->recplist);
342   ctrl->server_local->recplist = NULL;
343   /* close and reset the fd */
344   close_message_fd (ctrl);
345   assuan_close_input_fd (ctx);
346   assuan_close_output_fd (ctx);
347   return map_to_assuan_status (rc);
348 }
349
350 /* DECRYPT
351
352   This performs the decrypt operation after doing some check on the
353   internal state. (e.g. that only needed data has been set).  Because
354   it utilizes the GPG-Agent for the session key decryption, there is
355   no need to ask the client for a protecting passphrase - GpgAgent
356   does take care of this by requesting this from the user. */
357 static int 
358 cmd_decrypt (ASSUAN_CONTEXT ctx, char *line)
359 {
360   CTRL ctrl = assuan_get_pointer (ctx);
361   int inp_fd, out_fd;
362   FILE *out_fp;
363   int rc;
364
365   inp_fd = assuan_get_input_fd (ctx);
366   if (inp_fd == -1)
367     return set_error (No_Input, NULL);
368   out_fd = assuan_get_output_fd (ctx);
369   if (out_fd == -1)
370     return set_error (No_Output, NULL);
371
372   out_fp = fdopen ( dup(out_fd), "w");
373   if (!out_fp)
374     return set_error (General_Error, "fdopen() failed");
375   rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); 
376   fclose (out_fp);
377
378   /* close and reset the fd */
379   close_message_fd (ctrl);
380   assuan_close_input_fd (ctx);
381   assuan_close_output_fd (ctx);
382
383   return map_to_assuan_status (rc);
384 }
385
386
387 /* VERIFY
388
389   This does a verify operation on the message send to the input-FD.
390   The result is written out using status lines.  If an output FD was
391   given, the signed text will be written to that.
392   
393   If the signature is a detached one, the server will inquire about
394   the signed material and the client must provide it.
395   */
396 static int 
397 cmd_verify (ASSUAN_CONTEXT ctx, char *line)
398 {
399   int rc;
400   CTRL ctrl = assuan_get_pointer (ctx);
401   int fd = assuan_get_input_fd (ctx);
402   int out_fd = assuan_get_output_fd (ctx);
403   FILE *out_fp = NULL;
404
405   if (fd == -1)
406     return set_error (No_Input, NULL);
407
408   if (out_fd != -1)
409     {
410       out_fp = fdopen ( dup(out_fd), "w");
411       if (!out_fp)
412         return set_error (General_Error, "fdopen() failed");
413     }
414
415   rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
416                      ctrl->server_local->message_fd, out_fp);
417   if (out_fp)
418     fclose (out_fp);
419
420   /* close and reset the fd */
421   close_message_fd (ctrl);
422   assuan_close_input_fd (ctx);
423   assuan_close_output_fd (ctx);
424
425   return map_to_assuan_status (rc);
426 }
427
428
429 /* SIGN [--detached]
430
431   Sign the data set with the INPUT command and write it to the sink
432   set by OUTPUT.  With "--detached" specified, a detached signature is
433   created (surprise).  */
434 static int 
435 cmd_sign (ASSUAN_CONTEXT ctx, char *line)
436 {
437   CTRL ctrl = assuan_get_pointer (ctx);
438   int inp_fd, out_fd;
439   FILE *out_fp;
440   int detached;
441   int rc;
442
443   inp_fd = assuan_get_input_fd (ctx);
444   if (inp_fd == -1)
445     return set_error (No_Input, NULL);
446   out_fd = assuan_get_output_fd (ctx);
447   if (out_fd == -1)
448     return set_error (No_Output, NULL);
449
450   detached = has_option (line, "--detached"); 
451
452   out_fp = fdopen ( dup(out_fd), "w");
453   if (!out_fp)
454     return set_error (General_Error, "fdopen() failed");
455
456   rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
457                    inp_fd, detached, out_fp);
458   fclose (out_fp);
459
460   /* close and reset the fd */
461   close_message_fd (ctrl);
462   assuan_close_input_fd (ctx);
463   assuan_close_output_fd (ctx);
464
465   return map_to_assuan_status (rc);
466 }
467
468
469 /* IMPORT
470
471   Import the certificates read form the input-fd, return status
472   message for each imported one.  The import checks the validity of
473   the certificate but not of the entire chain.  It is possible to
474   import expired certificates.  */
475 static int 
476 cmd_import (ASSUAN_CONTEXT ctx, char *line)
477 {
478   CTRL ctrl = assuan_get_pointer (ctx);
479   int rc;
480   int fd = assuan_get_input_fd (ctx);
481
482   if (fd == -1)
483     return set_error (No_Input, NULL);
484
485   rc = gpgsm_import (assuan_get_pointer (ctx), fd);
486
487   /* close and reset the fd */
488   close_message_fd (ctrl);
489   assuan_close_input_fd (ctx);
490   assuan_close_output_fd (ctx);
491
492   return map_to_assuan_status (rc);
493 }
494
495
496 static int 
497 cmd_export (ASSUAN_CONTEXT ctx, char *line)
498 {
499   CTRL ctrl = assuan_get_pointer (ctx);
500   int fd = assuan_get_output_fd (ctx);
501   FILE *out_fp;
502   char *p;
503   STRLIST list, sl;
504
505   if (fd == -1)
506     return set_error (No_Output, NULL);
507   
508   /* break the line down into an STRLIST */
509   list = NULL;
510   for (p=line; *p; line = p)
511     {
512       while (*p && *p != ' ')
513         p++;
514       if (*p)
515         *p++ = 0;
516       if (*line)
517         {
518           sl = xtrymalloc (sizeof *sl + strlen (line));
519           if (!sl)
520             {
521               free_strlist (list);
522               return ASSUAN_Out_Of_Core;
523             }
524           sl->flags = 0;
525           strcpy_escaped_plus (sl->d, line);
526           sl->next = list;
527           list = sl;
528         }
529     }
530
531   out_fp = fdopen ( dup(fd), "w");
532   if (!out_fp)
533     {
534       free_strlist (list);
535       return set_error (General_Error, "fdopen() failed");
536     }
537
538   gpgsm_export (ctrl, list, out_fp);
539   fclose (out_fp);
540   free_strlist (list);
541   /* close and reset the fd */
542   close_message_fd (ctrl);
543   assuan_close_input_fd (ctx);
544   assuan_close_output_fd (ctx);
545   return 0;
546 }
547
548
549 static int 
550 cmd_delkeys (ASSUAN_CONTEXT ctx, char *line)
551 {
552   CTRL ctrl = assuan_get_pointer (ctx);
553   char *p;
554   STRLIST list, sl;
555   int rc;
556
557   /* break the line down into an STRLIST */
558   list = NULL;
559   for (p=line; *p; line = p)
560     {
561       while (*p && *p != ' ')
562         p++;
563       if (*p)
564         *p++ = 0;
565       if (*line)
566         {
567           sl = xtrymalloc (sizeof *sl + strlen (line));
568           if (!sl)
569             {
570               free_strlist (list);
571               return ASSUAN_Out_Of_Core;
572             }
573           sl->flags = 0;
574           strcpy_escaped_plus (sl->d, line);
575           sl->next = list;
576           list = sl;
577         }
578     }
579
580   rc = gpgsm_delete (ctrl, list);
581   free_strlist (list);
582
583   /* close and reset the fd */
584   close_message_fd (ctrl);
585   assuan_close_input_fd (ctx);
586   assuan_close_output_fd (ctx);
587
588   return map_to_assuan_status (rc);
589 }
590
591
592
593 /* MESSAGE FD=<n>
594
595    Set the file descriptor to read a message which is used with
596    detached signatures */
597 static int 
598 cmd_message (ASSUAN_CONTEXT ctx, char *line)
599 {
600   char *endp;
601   int fd;
602   CTRL ctrl = assuan_get_pointer (ctx);
603
604   if (strncmp (line, "FD=", 3))
605     return set_error (Syntax_Error, "FD=<n> expected");
606   line += 3;
607   if (!digitp (line))
608     return set_error (Syntax_Error, "number required");
609   fd = strtoul (line, &endp, 10);
610   if (*endp)
611     return set_error (Syntax_Error, "garbage found");
612   if (fd == -1)
613     return set_error (No_Input, NULL);
614
615   ctrl->server_local->message_fd = fd;
616   return 0;
617 }
618
619
620 static int 
621 do_listkeys (ASSUAN_CONTEXT ctx, char *line, int mode)
622 {
623   CTRL ctrl = assuan_get_pointer (ctx);
624   FILE *fp = assuan_get_data_fp (ctx);
625   char *p;
626   STRLIST list, sl;
627   unsigned int listmode;
628
629   if (!fp)
630     return set_error (General_Error, "no data stream");
631   
632   /* break the line down into an STRLIST */
633   list = NULL;
634   for (p=line; *p; line = p)
635     {
636       while (*p && *p != ' ')
637         p++;
638       if (*p)
639         *p++ = 0;
640       if (*line)
641         {
642           sl = xtrymalloc (sizeof *sl + strlen (line));
643           if (!sl)
644             {
645               free_strlist (list);
646               return ASSUAN_Out_Of_Core;
647             }
648           sl->flags = 0;
649           strcpy_escaped_plus (sl->d, line);
650           sl->next = list;
651           list = sl;
652         }
653     }
654
655   ctrl->with_colons = 1;
656   listmode = mode; 
657   if (ctrl->server_local->list_internal)
658     listmode |= (1<<6);
659   if (ctrl->server_local->list_external)
660     listmode |= (1<<7);
661   gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
662   free_strlist (list);
663   return 0;
664 }
665
666 static int 
667 cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
668 {
669   return do_listkeys (ctx, line, 3);
670 }
671
672 static int 
673 cmd_listsecretkeys (ASSUAN_CONTEXT ctx, char *line)
674 {
675   return do_listkeys (ctx, line, 2);
676 }
677
678 \f
679 /* GENKEY
680
681    Read the parameters in native format from the input fd and write a
682    certificate request to the output.
683  */
684 static int 
685 cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
686 {
687   CTRL ctrl = assuan_get_pointer (ctx);
688   int inp_fd, out_fd;
689   FILE *out_fp;
690   int rc;
691
692   inp_fd = assuan_get_input_fd (ctx);
693   if (inp_fd == -1)
694     return set_error (No_Input, NULL);
695   out_fd = assuan_get_output_fd (ctx);
696   if (out_fd == -1)
697     return set_error (No_Output, NULL);
698
699   out_fp = fdopen ( dup(out_fd), "w");
700   if (!out_fp)
701     return set_error (General_Error, "fdopen() failed");
702   rc = gpgsm_genkey (ctrl, inp_fd, out_fp);
703   fclose (out_fp);
704
705   /* close and reset the fds */
706   assuan_close_input_fd (ctx);
707   assuan_close_output_fd (ctx);
708
709   return map_to_assuan_status (rc);
710 }
711
712
713
714
715 \f
716 /* Tell the assuan library about our commands */
717 static int
718 register_commands (ASSUAN_CONTEXT ctx)
719 {
720   static struct {
721     const char *name;
722     int cmd_id;
723     int (*handler)(ASSUAN_CONTEXT, char *line);
724   } table[] = {
725     { "RECIPIENT",  0,  cmd_recipient },
726     { "SIGNER",     0,  cmd_signer },
727     { "ENCRYPT",    0,  cmd_encrypt },
728     { "DECRYPT",    0,  cmd_decrypt },
729     { "VERIFY",     0,  cmd_verify },
730     { "SIGN",       0,  cmd_sign },
731     { "IMPORT",     0,  cmd_import },
732     { "EXPORT",     0,  cmd_export },
733     { "",     ASSUAN_CMD_INPUT, NULL }, 
734     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
735     { "MESSAGE",    0,  cmd_message },
736     { "LISTKEYS",   0,  cmd_listkeys },
737     { "LISTSECRETKEYS",  0,  cmd_listsecretkeys },
738     { "GENKEY",     0,  cmd_genkey },
739     { "DELKEYS",    0,  cmd_delkeys },
740     { NULL }
741   };
742   int i, j, rc;
743
744   for (i=j=0; table[i].name; i++)
745     {
746       rc = assuan_register_command (ctx,
747                                     table[i].cmd_id? table[i].cmd_id
748                                                    : (ASSUAN_CMD_USER + j++),
749                                     table[i].name, table[i].handler);
750       if (rc)
751         return rc;
752     } 
753   return 0;
754 }
755
756 /* Startup the server */
757 void
758 gpgsm_server (void)
759 {
760   int rc;
761   int filedes[2];
762   ASSUAN_CONTEXT ctx;
763   struct server_control_s ctrl;
764
765   memset (&ctrl, 0, sizeof ctrl);
766   gpgsm_init_default_ctrl (&ctrl);
767
768   /* For now we use a simple pipe based server so that we can work
769      from scripts.  We will later add options to run as a daemon and
770      wait for requests on a Unix domain socket */
771   filedes[0] = 0;
772   filedes[1] = 1;
773   rc = assuan_init_pipe_server (&ctx, filedes);
774   if (rc)
775     {
776       log_error ("failed to initialize the server: %s\n",
777                  assuan_strerror(rc));
778       gpgsm_exit (2);
779     }
780   rc = register_commands (ctx);
781   if (rc)
782     {
783       log_error ("failed to the register commands with Assuan: %s\n",
784                  assuan_strerror(rc));
785       gpgsm_exit (2);
786     }
787   assuan_set_hello_line (ctx, "GNU Privacy Guard's S/M server ready");
788
789   assuan_register_reset_notify (ctx, reset_notify);
790   assuan_register_input_notify (ctx, input_notify);
791   assuan_register_output_notify (ctx, output_notify);
792   assuan_register_option_handler (ctx, option_handler);
793
794   assuan_set_pointer (ctx, &ctrl);
795   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
796   ctrl.server_local->assuan_ctx = ctx;
797   ctrl.server_local->message_fd = -1;
798   ctrl.server_local->list_internal = 1;
799   ctrl.server_local->list_external = 0;
800
801   if (DBG_ASSUAN)
802     assuan_set_log_stream (ctx, log_get_stream ());
803
804   for (;;)
805     {
806       rc = assuan_accept (ctx);
807       if (rc == -1)
808         {
809           break;
810         }
811       else if (rc)
812         {
813           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
814           break;
815         }
816       
817       rc = assuan_process (ctx);
818       if (rc)
819         {
820           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
821           continue;
822         }
823     }
824
825   gpgsm_release_certlist (ctrl.server_local->recplist);
826   ctrl.server_local->recplist = NULL;
827   gpgsm_release_certlist (ctrl.server_local->signerlist);
828   ctrl.server_local->signerlist = NULL;
829
830   assuan_deinit_server (ctx);
831 }
832
833
834 static const char *
835 get_status_string ( int no ) 
836 {
837   const char *s;
838
839   switch (no)
840     {
841     case STATUS_ENTER  : s = "ENTER"; break;
842     case STATUS_LEAVE  : s = "LEAVE"; break;
843     case STATUS_ABORT  : s = "ABORT"; break;
844     case STATUS_GOODSIG: s = "GOODSIG"; break;
845     case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
846     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
847     case STATUS_BADSIG : s = "BADSIG"; break;
848     case STATUS_ERRSIG : s = "ERRSIG"; break;
849     case STATUS_BADARMOR : s = "BADARMOR"; break;
850     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
851     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
852     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
853     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
854     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
855     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
856     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
857     case STATUS_GET_LINE         : s = "GET_LINE"; break;
858     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
859     case STATUS_GOT_IT   : s = "GOT_IT"; break;
860     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
861     case STATUS_SHM_GET  : s = "SHM_GET"; break;
862     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
863     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
864     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
865     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
866     case STATUS_SIG_ID   : s = "SIG_ID"; break;
867     case STATUS_ENC_TO   : s = "ENC_TO"; break;
868     case STATUS_NODATA   : s = "NODATA"; break;
869     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
870     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
871     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
872     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
873     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
874     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
875     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
876     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
877     case STATUS_GOODMDC  : s = "GOODMDC"; break;
878     case STATUS_BADMDC   : s = "BADMDC"; break;
879     case STATUS_ERRMDC   : s = "ERRMDC"; break;
880     case STATUS_IMPORTED         : s = "IMPORTED"; break;
881     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
882     case STATUS_FILE_START       : s = "FILE_START"; break;
883     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
884     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
885     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
886     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
887     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
888     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
889     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
890     case STATUS_PROGRESS         : s = "PROGRESS"; break;
891     case STATUS_SIG_CREATED      : s = "SIG_CREATED"; break;
892     case STATUS_SESSION_KEY      : s = "SESSION_KEY"; break;
893     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
894     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
895     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
896     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
897     case STATUS_END_STREAM     : s = "END_STREAM"; break;
898     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
899     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
900     case STATUS_INV_RECP       : s = "INV_RECP"; break;
901     case STATUS_NO_RECP        : s = "NO_RECP"; break;
902     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
903     case STATUS_EXPSIG         : s = "EXPSIG"; break;
904     case STATUS_EXPKEYSIG      : s = "EXPKEYSIG"; break;
905     case STATUS_TRUNCATED      : s = "TRUNCATED"; break;
906     case STATUS_ERROR          : s = "ERROR"; break;
907     default: s = "?"; break;
908     }
909   return s;
910 }
911
912
913 void
914 gpgsm_status2 (CTRL ctrl, int no, ...)
915 {
916   va_list arg_ptr;
917   const char *text;
918
919   va_start (arg_ptr, no);
920
921   if (ctrl->no_server)
922     {
923       if (ctrl->status_fd == -1)
924         return; /* no status wanted */
925       if (!statusfp)
926         {
927           if (ctrl->status_fd == 1)
928             statusfp = stdout;
929           else if (ctrl->status_fd == 2)
930             statusfp = stderr;
931           else
932             statusfp = fdopen (ctrl->status_fd, "w");
933       
934           if (!statusfp)
935             {
936               log_fatal ("can't open fd %d for status output: %s\n",
937                          ctrl->status_fd, strerror(errno));
938             }
939         }
940       
941       fputs ("[GNUPG:] ", statusfp);
942       fputs (get_status_string (no), statusfp);
943     
944       while ( (text = va_arg (arg_ptr, const char*) ))
945         {
946           putc ( ' ', statusfp );
947           for (; *text; text++) 
948             {
949               if (*text == '\n')
950                 fputs ( "\\n", statusfp );
951               else if (*text == '\r')
952                 fputs ( "\\r", statusfp );
953               else 
954                 putc ( *(const byte *)text,  statusfp );
955             }
956         }
957       putc ('\n', statusfp);
958       fflush (statusfp);
959     }
960   else 
961     {
962       ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
963       char buf[950], *p;
964       size_t n;
965
966       p = buf; 
967       n = 0;
968       while ( (text = va_arg (arg_ptr, const char *)) )
969         {
970           if (n)
971             {
972               *p++ = ' ';
973               n++;
974             }
975           for ( ; *text && n < DIM (buf)-2; n++)
976             *p++ = *text++;
977         }
978       *p = 0;
979       assuan_write_status (ctx, get_status_string (no), buf);
980     }
981
982   va_end (arg_ptr);
983 }
984
985 void
986 gpgsm_status (CTRL ctrl, int no, const char *text)
987 {
988   gpgsm_status2 (ctrl, no, text, NULL);
989 }
990
991
992
993 #if 0
994 /*
995  * Write a status line with a buffer using %XX escapes.  If WRAP is >
996  * 0 wrap the line after this length.  If STRING is not NULL it will
997  * be prepended to the buffer, no escaping is done for string.
998  * A wrap of -1 forces spaces not to be encoded as %20.
999  */
1000 void
1001 write_status_text_and_buffer ( int no, const char *string,
1002                                const char *buffer, size_t len, int wrap )
1003 {
1004     const char *s, *text;
1005     int esc, first;
1006     int lower_limit = ' ';
1007     size_t n, count, dowrap;
1008
1009     if( !statusfp )
1010         return;  /* not enabled */
1011     
1012     if (wrap == -1) {
1013         lower_limit--;
1014         wrap = 0;
1015     }
1016
1017     text = get_status_string (no);
1018     count = dowrap = first = 1;
1019     do {
1020         if (dowrap) {
1021             fprintf (statusfp, "[GNUPG:] %s ", text );
1022             count = dowrap = 0;
1023             if (first && string) {
1024                 fputs (string, statusfp);
1025                 count += strlen (string);
1026             }
1027             first = 0;
1028         }
1029         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
1030             if ( *s == '%' || *(const byte*)s <= lower_limit 
1031                            || *(const byte*)s == 127 ) 
1032                 esc = 1;
1033             if ( wrap && ++count > wrap ) {
1034                 dowrap=1;
1035                 break;
1036             }
1037         }
1038         if (esc) {
1039             s--; n++;
1040         }
1041         if (s != buffer) 
1042             fwrite (buffer, s-buffer, 1, statusfp );
1043         if ( esc ) {
1044             fprintf (statusfp, "%%%02X", *(const byte*)s );
1045             s++; n--;
1046         }
1047         buffer = s;
1048         len = n;
1049         if ( dowrap && len )
1050             putc ( '\n', statusfp );
1051     } while ( len );
1052
1053     putc ('\n',statusfp);
1054     fflush (statusfp);
1055 }
1056 #endif