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