Forgot to commit this:
[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?         "7":
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 static int 
503 cmd_delkeys (ASSUAN_CONTEXT ctx, char *line)
504 {
505   CTRL ctrl = assuan_get_pointer (ctx);
506   char *p;
507   STRLIST list, sl;
508   int rc;
509
510   /* break the line down into an STRLIST */
511   list = NULL;
512   for (p=line; *p; line = p)
513     {
514       while (*p && *p != ' ')
515         p++;
516       if (*p)
517         *p++ = 0;
518       if (*line)
519         {
520           sl = xtrymalloc (sizeof *sl + strlen (line));
521           if (!sl)
522             {
523               free_strlist (list);
524               return ASSUAN_Out_Of_Core;
525             }
526           sl->flags = 0;
527           strcpy_escaped_plus (sl->d, line);
528           sl->next = list;
529           list = sl;
530         }
531     }
532
533   rc = gpgsm_delete (ctrl, list);
534   free_strlist (list);
535
536   /* close and reset the fd */
537   close_message_fd (ctrl);
538   assuan_close_input_fd (ctx);
539   assuan_close_output_fd (ctx);
540
541   return map_to_assuan_status (rc);
542 }
543
544
545
546 /* MESSAGE FD=<n>
547
548    Set the file descriptor to read a message which is used with
549    detached signatures */
550 static int 
551 cmd_message (ASSUAN_CONTEXT ctx, char *line)
552 {
553   char *endp;
554   int fd;
555   CTRL ctrl = assuan_get_pointer (ctx);
556
557   if (strncmp (line, "FD=", 3))
558     return set_error (Syntax_Error, "FD=<n> expected");
559   line += 3;
560   if (!digitp (line))
561     return set_error (Syntax_Error, "number required");
562   fd = strtoul (line, &endp, 10);
563   if (*endp)
564     return set_error (Syntax_Error, "garbage found");
565   if (fd == -1)
566     return set_error (No_Input, NULL);
567
568   ctrl->server_local->message_fd = fd;
569   return 0;
570 }
571
572
573 static int 
574 do_listkeys (ASSUAN_CONTEXT ctx, char *line, int mode)
575 {
576   CTRL ctrl = assuan_get_pointer (ctx);
577   FILE *fp = assuan_get_data_fp (ctx);
578   char *p;
579   STRLIST list, sl;
580   unsigned int listmode;
581
582   if (!fp)
583     return set_error (General_Error, "no data stream");
584   
585   /* break the line down into an STRLIST */
586   list = NULL;
587   for (p=line; *p; line = p)
588     {
589       while (*p && *p != ' ')
590         p++;
591       if (*p)
592         *p++ = 0;
593       if (*line)
594         {
595           sl = xtrymalloc (sizeof *sl + strlen (line));
596           if (!sl)
597             {
598               free_strlist (list);
599               return ASSUAN_Out_Of_Core;
600             }
601           sl->flags = 0;
602           strcpy_escaped_plus (sl->d, line);
603           sl->next = list;
604           list = sl;
605         }
606     }
607
608   ctrl->with_colons = 1;
609   listmode = mode; 
610   if (ctrl->server_local->list_internal)
611     listmode |= (1<<6);
612   if (ctrl->server_local->list_external)
613     listmode |= (1<<7);
614   gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
615   free_strlist (list);
616   return 0;
617 }
618
619 static int 
620 cmd_listkeys (ASSUAN_CONTEXT ctx, char *line)
621 {
622   return do_listkeys (ctx, line, 3);
623 }
624
625 static int 
626 cmd_listsecretkeys (ASSUAN_CONTEXT ctx, char *line)
627 {
628   return do_listkeys (ctx, line, 2);
629 }
630
631 \f
632 /* GENKEY
633
634    Read the parameters in native format from the input fd and write a
635    certificate request to the output.
636  */
637 static int 
638 cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
639 {
640   CTRL ctrl = assuan_get_pointer (ctx);
641   int inp_fd, out_fd;
642   FILE *out_fp;
643   int rc;
644
645   inp_fd = assuan_get_input_fd (ctx);
646   if (inp_fd == -1)
647     return set_error (No_Input, NULL);
648   out_fd = assuan_get_output_fd (ctx);
649   if (out_fd == -1)
650     return set_error (No_Output, NULL);
651
652   out_fp = fdopen ( dup(out_fd), "w");
653   if (!out_fp)
654     return set_error (General_Error, "fdopen() failed");
655   rc = gpgsm_genkey (ctrl, inp_fd, out_fp);
656   fclose (out_fp);
657
658   /* close and reset the fds */
659   assuan_close_input_fd (ctx);
660   assuan_close_output_fd (ctx);
661
662   return map_to_assuan_status (rc);
663 }
664
665
666
667
668 \f
669 /* Tell the assuan library about our commands */
670 static int
671 register_commands (ASSUAN_CONTEXT ctx)
672 {
673   static struct {
674     const char *name;
675     int cmd_id;
676     int (*handler)(ASSUAN_CONTEXT, char *line);
677   } table[] = {
678     { "RECIPIENT",  0,  cmd_recipient },
679     { "ENCRYPT",    0,  cmd_encrypt },
680     { "DECRYPT",    0,  cmd_decrypt },
681     { "VERIFY",     0,  cmd_verify },
682     { "SIGN",       0,  cmd_sign },
683     { "IMPORT",     0,  cmd_import },
684     { "EXPORT",     0,  cmd_export },
685     { "",     ASSUAN_CMD_INPUT, NULL }, 
686     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
687     { "MESSAGE",    0,  cmd_message },
688     { "LISTKEYS",   0,  cmd_listkeys },
689     { "LISTSECRETKEYS",  0,  cmd_listsecretkeys },
690     { "GENKEY",     0,  cmd_genkey },
691     { "DELKEYS",    0,  cmd_delkeys },
692     { NULL }
693   };
694   int i, j, rc;
695
696   for (i=j=0; table[i].name; i++)
697     {
698       rc = assuan_register_command (ctx,
699                                     table[i].cmd_id? table[i].cmd_id
700                                                    : (ASSUAN_CMD_USER + j++),
701                                     table[i].name, table[i].handler);
702       if (rc)
703         return rc;
704     } 
705   return 0;
706 }
707
708 /* Startup the server */
709 void
710 gpgsm_server (void)
711 {
712   int rc;
713   int filedes[2];
714   ASSUAN_CONTEXT ctx;
715   struct server_control_s ctrl;
716
717   memset (&ctrl, 0, sizeof ctrl);
718   gpgsm_init_default_ctrl (&ctrl);
719
720   /* For now we use a simple pipe based server so that we can work
721      from scripts.  We will later add options to run as a daemon and
722      wait for requests on a Unix domain socket */
723   filedes[0] = 0;
724   filedes[1] = 1;
725   rc = assuan_init_pipe_server (&ctx, filedes);
726   if (rc)
727     {
728       log_error ("failed to initialize the server: %s\n",
729                  assuan_strerror(rc));
730       gpgsm_exit (2);
731     }
732   rc = register_commands (ctx);
733   if (rc)
734     {
735       log_error ("failed to the register commands with Assuan: %s\n",
736                  assuan_strerror(rc));
737       gpgsm_exit (2);
738     }
739   assuan_set_hello_line (ctx, "GNU Privacy Guard's S/M server ready");
740
741   assuan_register_reset_notify (ctx, reset_notify);
742   assuan_register_input_notify (ctx, input_notify);
743   assuan_register_output_notify (ctx, output_notify);
744   assuan_register_option_handler (ctx, option_handler);
745
746   assuan_set_pointer (ctx, &ctrl);
747   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
748   ctrl.server_local->assuan_ctx = ctx;
749   ctrl.server_local->message_fd = -1;
750   ctrl.server_local->list_internal = 1;
751   ctrl.server_local->list_external = 0;
752
753   if (DBG_ASSUAN)
754     assuan_set_log_stream (ctx, log_get_stream ());
755
756   for (;;)
757     {
758       rc = assuan_accept (ctx);
759       if (rc == -1)
760         {
761           break;
762         }
763       else if (rc)
764         {
765           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
766           break;
767         }
768       
769       rc = assuan_process (ctx);
770       if (rc)
771         {
772           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
773           continue;
774         }
775     }
776
777   gpgsm_release_certlist (ctrl.server_local->recplist);
778   ctrl.server_local->recplist = NULL;
779
780   assuan_deinit_server (ctx);
781 }
782
783
784 static const char *
785 get_status_string ( int no ) 
786 {
787   const char *s;
788
789   switch (no)
790     {
791     case STATUS_ENTER  : s = "ENTER"; break;
792     case STATUS_LEAVE  : s = "LEAVE"; break;
793     case STATUS_ABORT  : s = "ABORT"; break;
794     case STATUS_GOODSIG: s = "GOODSIG"; break;
795     case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
796     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
797     case STATUS_BADSIG : s = "BADSIG"; break;
798     case STATUS_ERRSIG : s = "ERRSIG"; break;
799     case STATUS_BADARMOR : s = "BADARMOR"; break;
800     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
801     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
802     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
803     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
804     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
805     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
806     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
807     case STATUS_GET_LINE         : s = "GET_LINE"; break;
808     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
809     case STATUS_GOT_IT   : s = "GOT_IT"; break;
810     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
811     case STATUS_SHM_GET  : s = "SHM_GET"; break;
812     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
813     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
814     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
815     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
816     case STATUS_SIG_ID   : s = "SIG_ID"; break;
817     case STATUS_ENC_TO   : s = "ENC_TO"; break;
818     case STATUS_NODATA   : s = "NODATA"; break;
819     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
820     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
821     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
822     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
823     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
824     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
825     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
826     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
827     case STATUS_GOODMDC  : s = "GOODMDC"; break;
828     case STATUS_BADMDC   : s = "BADMDC"; break;
829     case STATUS_ERRMDC   : s = "ERRMDC"; break;
830     case STATUS_IMPORTED         : s = "IMPORTED"; break;
831     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
832     case STATUS_FILE_START       : s = "FILE_START"; break;
833     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
834     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
835     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
836     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
837     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
838     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
839     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
840     case STATUS_PROGRESS         : s = "PROGRESS"; break;
841     case STATUS_SIG_CREATED      : s = "SIG_CREATED"; break;
842     case STATUS_SESSION_KEY      : s = "SESSION_KEY"; break;
843     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
844     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
845     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
846     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
847     case STATUS_END_STREAM     : s = "END_STREAM"; break;
848     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
849     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
850     case STATUS_INV_RECP       : s = "INV_RECP"; break;
851     case STATUS_NO_RECP        : s = "NO_RECP"; break;
852     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
853     case STATUS_EXPSIG         : s = "EXPSIG"; break;
854     case STATUS_EXPKEYSIG      : s = "EXPKEYSIG"; break;
855     case STATUS_TRUNCATED      : s = "TRUNCATED"; break;
856     case STATUS_ERROR          : s = "ERROR"; break;
857     default: s = "?"; break;
858     }
859   return s;
860 }
861
862
863 void
864 gpgsm_status2 (CTRL ctrl, int no, ...)
865 {
866   va_list arg_ptr;
867   const char *text;
868
869   va_start (arg_ptr, no);
870
871   if (ctrl->no_server)
872     {
873       if (ctrl->status_fd == -1)
874         return; /* no status wanted */
875       if (!statusfp)
876         {
877           if (ctrl->status_fd == 1)
878             statusfp = stdout;
879           else if (ctrl->status_fd == 2)
880             statusfp = stderr;
881           else
882             statusfp = fdopen (ctrl->status_fd, "w");
883       
884           if (!statusfp)
885             {
886               log_fatal ("can't open fd %d for status output: %s\n",
887                          ctrl->status_fd, strerror(errno));
888             }
889         }
890       
891       fputs ("[GNUPG:] ", statusfp);
892       fputs (get_status_string (no), statusfp);
893     
894       while ( (text = va_arg (arg_ptr, const char*) ))
895         {
896           putc ( ' ', statusfp );
897           for (; *text; text++) 
898             {
899               if (*text == '\n')
900                 fputs ( "\\n", statusfp );
901               else if (*text == '\r')
902                 fputs ( "\\r", statusfp );
903               else 
904                 putc ( *(const byte *)text,  statusfp );
905             }
906         }
907       putc ('\n', statusfp);
908       fflush (statusfp);
909     }
910   else 
911     {
912       ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
913       char buf[950], *p;
914       size_t n;
915
916       p = buf; 
917       n = 0;
918       while ( (text = va_arg (arg_ptr, const char *)) )
919         {
920           if (n)
921             {
922               *p++ = ' ';
923               n++;
924             }
925           for ( ; *text && n < DIM (buf)-2; n++)
926             *p++ = *text++;
927         }
928       *p = 0;
929       assuan_write_status (ctx, get_status_string (no), buf);
930     }
931
932   va_end (arg_ptr);
933 }
934
935 void
936 gpgsm_status (CTRL ctrl, int no, const char *text)
937 {
938   gpgsm_status2 (ctrl, no, text, NULL);
939 }
940
941
942
943 #if 0
944 /*
945  * Write a status line with a buffer using %XX escapes.  If WRAP is >
946  * 0 wrap the line after this length.  If STRING is not NULL it will
947  * be prepended to the buffer, no escaping is done for string.
948  * A wrap of -1 forces spaces not to be encoded as %20.
949  */
950 void
951 write_status_text_and_buffer ( int no, const char *string,
952                                const char *buffer, size_t len, int wrap )
953 {
954     const char *s, *text;
955     int esc, first;
956     int lower_limit = ' ';
957     size_t n, count, dowrap;
958
959     if( !statusfp )
960         return;  /* not enabled */
961     
962     if (wrap == -1) {
963         lower_limit--;
964         wrap = 0;
965     }
966
967     text = get_status_string (no);
968     count = dowrap = first = 1;
969     do {
970         if (dowrap) {
971             fprintf (statusfp, "[GNUPG:] %s ", text );
972             count = dowrap = 0;
973             if (first && string) {
974                 fputs (string, statusfp);
975                 count += strlen (string);
976             }
977             first = 0;
978         }
979         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
980             if ( *s == '%' || *(const byte*)s <= lower_limit 
981                            || *(const byte*)s == 127 ) 
982                 esc = 1;
983             if ( wrap && ++count > wrap ) {
984                 dowrap=1;
985                 break;
986             }
987         }
988         if (esc) {
989             s--; n++;
990         }
991         if (s != buffer) 
992             fwrite (buffer, s-buffer, 1, statusfp );
993         if ( esc ) {
994             fprintf (statusfp, "%%%02X", *(const byte*)s );
995             s++; n--;
996         }
997         buffer = s;
998         len = n;
999         if ( dowrap && len )
1000             putc ( '\n', statusfp );
1001     } while ( len );
1002
1003     putc ('\n',statusfp);
1004     fflush (statusfp);
1005 }
1006 #endif