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