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