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