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