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