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