* gpgsm.c (main): Disable core dumps.
[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));
382
383   return 0;
384 }
385
386 \f
387 /* GENKEY
388
389    Read the parameters in native format from the input fd and write a
390    certificate request to the output.
391  */
392 static int 
393 cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
394 {
395   CTRL ctrl = assuan_get_pointer (ctx);
396   int inp_fd, out_fd;
397   FILE *out_fp;
398   int rc;
399
400   inp_fd = assuan_get_input_fd (ctx);
401   if (inp_fd == -1)
402     return set_error (No_Input, NULL);
403   out_fd = assuan_get_output_fd (ctx);
404   if (out_fd == -1)
405     return set_error (No_Output, NULL);
406
407   out_fp = fdopen ( dup(out_fd), "w");
408   if (!out_fp)
409     return set_error (General_Error, "fdopen() failed");
410   rc = gpgsm_genkey (ctrl, inp_fd, out_fp);
411   fclose (out_fp);
412
413   if (!rc)
414     {
415       /* close and reset the fds */
416       assuan_close_input_fd (ctx);
417       assuan_close_output_fd (ctx);
418     }
419   return map_to_assuan_status (rc);
420 }
421
422
423
424
425 \f
426 /* Tell the assuan library about our commands */
427 static int
428 register_commands (ASSUAN_CONTEXT ctx)
429 {
430   static struct {
431     const char *name;
432     int cmd_id;
433     int (*handler)(ASSUAN_CONTEXT, char *line);
434   } table[] = {
435     { "RECIPIENT",  0,  cmd_recipient },
436     { "ENCRYPT",    0,  cmd_encrypt },
437     { "DECRYPT",    0,  cmd_decrypt },
438     { "VERIFY",     0,  cmd_verify },
439     { "SIGN",       0,  cmd_sign },
440     { "IMPORT",     0,  cmd_import },
441     { "",     ASSUAN_CMD_INPUT, NULL }, 
442     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
443     { "MESSAGE",    0,  cmd_message },
444     { "LISTKEYS",   0,  cmd_listkeys },
445     { "GENKEY",     0,  cmd_genkey },
446     { NULL }
447   };
448   int i, j, rc;
449
450   for (i=j=0; table[i].name; i++)
451     {
452       rc = assuan_register_command (ctx,
453                                     table[i].cmd_id? table[i].cmd_id
454                                                    : (ASSUAN_CMD_USER + j++),
455                                     table[i].name, table[i].handler);
456       if (rc)
457         return rc;
458     } 
459   return 0;
460 }
461
462 /* Startup the server */
463 void
464 gpgsm_server (void)
465 {
466   int rc;
467   int filedes[2];
468   ASSUAN_CONTEXT ctx;
469   struct server_control_s ctrl;
470
471   memset (&ctrl, 0, sizeof ctrl);
472   gpgsm_init_default_ctrl (&ctrl);
473
474   /* For now we use a simple pipe based server so that we can work
475      from scripts.  We will later add options to run as a daemon and
476      wait for requests on a Unix domain socket */
477   filedes[0] = 0;
478   filedes[1] = 1;
479   rc = assuan_init_pipe_server (&ctx, filedes);
480   if (rc)
481     {
482       log_error ("failed to initialize the server: %s\n",
483                  assuan_strerror(rc));
484       gpgsm_exit (2);
485     }
486   rc = register_commands (ctx);
487   if (rc)
488     {
489       log_error ("failed to the register commands with Assuan: %s\n",
490                  assuan_strerror(rc));
491       gpgsm_exit (2);
492     }
493   assuan_set_hello_line (ctx, "GNU Privacy Guard's S/M server ready");
494
495   assuan_register_reset_notify (ctx, reset_notify);
496   assuan_register_input_notify (ctx, input_notify);
497   assuan_register_output_notify (ctx, output_notify);
498   assuan_register_option_handler (ctx, option_handler);
499
500   assuan_set_pointer (ctx, &ctrl);
501   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
502   ctrl.server_local->assuan_ctx = ctx;
503   ctrl.server_local->message_fd = -1;
504
505   if (DBG_AGENT)
506     assuan_set_log_stream (ctx, log_get_stream ());
507
508   for (;;)
509     {
510       rc = assuan_accept (ctx);
511       if (rc == -1)
512         {
513           break;
514         }
515       else if (rc)
516         {
517           log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
518           break;
519         }
520       
521       rc = assuan_process (ctx);
522       if (rc)
523         {
524           log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
525           continue;
526         }
527     }
528
529   gpgsm_release_certlist (ctrl.server_local->recplist);
530   ctrl.server_local->recplist = NULL;
531
532   assuan_deinit_server (ctx);
533 }
534
535
536 static const char *
537 get_status_string ( int no ) 
538 {
539   const char *s;
540
541   switch (no)
542     {
543     case STATUS_ENTER  : s = "ENTER"; break;
544     case STATUS_LEAVE  : s = "LEAVE"; break;
545     case STATUS_ABORT  : s = "ABORT"; break;
546     case STATUS_GOODSIG: s = "GOODSIG"; break;
547     case STATUS_SIGEXPIRED: s = "SIGEXPIRED"; break;
548     case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
549     case STATUS_BADSIG : s = "BADSIG"; break;
550     case STATUS_ERRSIG : s = "ERRSIG"; break;
551     case STATUS_BADARMOR : s = "BADARMOR"; break;
552     case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
553     case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
554     case STATUS_TRUST_NEVER      : s = "TRUST_NEVER"; break;
555     case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
556     case STATUS_TRUST_FULLY      : s = "TRUST_FULLY"; break;
557     case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
558     case STATUS_GET_BOOL         : s = "GET_BOOL"; break;
559     case STATUS_GET_LINE         : s = "GET_LINE"; break;
560     case STATUS_GET_HIDDEN       : s = "GET_HIDDEN"; break;
561     case STATUS_GOT_IT   : s = "GOT_IT"; break;
562     case STATUS_SHM_INFO         : s = "SHM_INFO"; break;
563     case STATUS_SHM_GET  : s = "SHM_GET"; break;
564     case STATUS_SHM_GET_BOOL     : s = "SHM_GET_BOOL"; break;
565     case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
566     case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
567     case STATUS_VALIDSIG         : s = "VALIDSIG"; break;
568     case STATUS_SIG_ID   : s = "SIG_ID"; break;
569     case STATUS_ENC_TO   : s = "ENC_TO"; break;
570     case STATUS_NODATA   : s = "NODATA"; break;
571     case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
572     case STATUS_NO_PUBKEY        : s = "NO_PUBKEY"; break;
573     case STATUS_NO_SECKEY        : s = "NO_SECKEY"; break;
574     case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
575     case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
576     case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
577     case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
578     case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
579     case STATUS_GOODMDC  : s = "GOODMDC"; break;
580     case STATUS_BADMDC   : s = "BADMDC"; break;
581     case STATUS_ERRMDC   : s = "ERRMDC"; break;
582     case STATUS_IMPORTED         : s = "IMPORTED"; break;
583     case STATUS_IMPORT_RES       : s = "IMPORT_RES"; break;
584     case STATUS_FILE_START       : s = "FILE_START"; break;
585     case STATUS_FILE_DONE        : s = "FILE_DONE"; break;
586     case STATUS_FILE_ERROR       : s = "FILE_ERROR"; break;
587     case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
588     case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
589     case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
590     case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
591     case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
592     case STATUS_PROGRESS         : s = "PROGRESS"; break;
593     case STATUS_SIG_CREATED      : s = "SIG_CREATED"; break;
594     case STATUS_SESSION_KEY      : s = "SESSION_KEY"; break;
595     case STATUS_NOTATION_NAME  : s = "NOTATION_NAME" ; break;
596     case STATUS_NOTATION_DATA  : s = "NOTATION_DATA" ; break;
597     case STATUS_POLICY_URL     : s = "POLICY_URL" ; break;
598     case STATUS_BEGIN_STREAM   : s = "BEGIN_STREAM"; break;
599     case STATUS_END_STREAM     : s = "END_STREAM"; break;
600     case STATUS_KEY_CREATED    : s = "KEY_CREATED"; break;
601     case STATUS_UNEXPECTED     : s = "UNEXPECTED"; break;
602     case STATUS_INV_RECP       : s = "INV_RECP"; break;
603     case STATUS_NO_RECP        : s = "NO_RECP"; break;
604     case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
605     default: s = "?"; break;
606     }
607   return s;
608 }
609
610
611
612 void
613 gpgsm_status (CTRL ctrl, int no, const char *text)
614 {
615   if (ctrl->no_server)
616     {
617       if (ctrl->status_fd == -1)
618         return; /* no status wanted */
619       if (!statusfp)
620         {
621           if (ctrl->status_fd == 1)
622             statusfp = stdout;
623           else if (ctrl->status_fd == 2)
624             statusfp = stderr;
625           else
626             statusfp = fdopen (ctrl->status_fd, "w");
627       
628           if (!statusfp)
629             {
630               log_fatal ("can't open fd %d for status output: %s\n",
631                          ctrl->status_fd, strerror(errno));
632             }
633         }
634       
635       fputs ("[GNUPG:] ", statusfp);
636       fputs (get_status_string (no), statusfp);
637     
638       if (text)
639         {
640           putc ( ' ', statusfp );
641           for (; *text; text++) 
642             {
643               if (*text == '\n')
644                 fputs ( "\\n", statusfp );
645               else if (*text == '\r')
646                 fputs ( "\\r", statusfp );
647               else 
648                 putc ( *(const byte *)text,  statusfp );
649             }
650         }
651       putc ('\n', statusfp);
652       fflush (statusfp);
653     }
654   else 
655     {
656       ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
657
658       assuan_write_status (ctx, get_status_string (no), text);
659     }
660 }
661
662
663 #if 0
664 /*
665  * Write a status line with a buffer using %XX escapes.  If WRAP is >
666  * 0 wrap the line after this length.  If STRING is not NULL it will
667  * be prepended to the buffer, no escaping is done for string.
668  * A wrap of -1 forces spaces not to be encoded as %20.
669  */
670 void
671 write_status_text_and_buffer ( int no, const char *string,
672                                const char *buffer, size_t len, int wrap )
673 {
674     const char *s, *text;
675     int esc, first;
676     int lower_limit = ' ';
677     size_t n, count, dowrap;
678
679     if( !statusfp )
680         return;  /* not enabled */
681     
682     if (wrap == -1) {
683         lower_limit--;
684         wrap = 0;
685     }
686
687     text = get_status_string (no);
688     count = dowrap = first = 1;
689     do {
690         if (dowrap) {
691             fprintf (statusfp, "[GNUPG:] %s ", text );
692             count = dowrap = 0;
693             if (first && string) {
694                 fputs (string, statusfp);
695                 count += strlen (string);
696             }
697             first = 0;
698         }
699         for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
700             if ( *s == '%' || *(const byte*)s <= lower_limit 
701                            || *(const byte*)s == 127 ) 
702                 esc = 1;
703             if ( wrap && ++count > wrap ) {
704                 dowrap=1;
705                 break;
706             }
707         }
708         if (esc) {
709             s--; n++;
710         }
711         if (s != buffer) 
712             fwrite (buffer, s-buffer, 1, statusfp );
713         if ( esc ) {
714             fprintf (statusfp, "%%%02X", *(const byte*)s );
715             s++; n--;
716         }
717         buffer = s;
718         len = n;
719         if ( dowrap && len )
720             putc ( '\n', statusfp );
721     } while ( len );
722
723     putc ('\n',statusfp);
724     fflush (statusfp);
725 }
726 #endif
727
728
729
730
731
732
733