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