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