721f3faf064b07bb214cab3e579072739cb3f38c
[gnupg.git] / sm / server.c
1 /* server.c - Server mode and main entry point
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
3  *               2010 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <https://www.gnu.org/licenses/>.
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.h>
32 #include "../common/sysutils.h"
33 #include "../common/server-help.h"
34 #include "../common/asshelp.h"
35
36 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
37
38
39 /* The filepointer for status message used in non-server mode */
40 static FILE *statusfp;
41
42 /* Data used to assuciate an Assuan context with local server data */
43 struct server_local_s {
44   assuan_context_t assuan_ctx;
45   int message_fd;
46   int list_internal;
47   int list_external;
48   int list_to_output;           /* Write keylistings to the output fd. */
49   int enable_audit_log;         /* Use an audit log.  */
50   certlist_t recplist;
51   certlist_t signerlist;
52   certlist_t default_recplist; /* As set by main() - don't release. */
53   int allow_pinentry_notify;   /* Set if pinentry notifications should
54                                   be passed back to the client. */
55   int no_encrypt_to;           /* Local version of option.  */
56 };
57
58
59 /* Cookie definition for assuan data line output.  */
60 static gpgrt_ssize_t data_line_cookie_write (void *cookie,
61                                              const void *buffer, size_t size);
62 static int data_line_cookie_close (void *cookie);
63 static es_cookie_io_functions_t data_line_cookie_functions =
64   {
65     NULL,
66     data_line_cookie_write,
67     NULL,
68     data_line_cookie_close
69   };
70
71
72 \f
73 static int command_has_option (const char *cmd, const char *cmdopt);
74
75
76
77 \f
78 /* Note that it is sufficient to allocate the target string D as
79    long as the source string S, i.e.: strlen(s)+1; */
80 static void
81 strcpy_escaped_plus (char *d, const char *s)
82 {
83   while (*s)
84     {
85       if (*s == '%' && s[1] && s[2])
86         {
87           s++;
88           *d++ = xtoi_2 (s);
89           s += 2;
90         }
91       else if (*s == '+')
92         *d++ = ' ', s++;
93       else
94         *d++ = *s++;
95     }
96   *d = 0;
97 }
98
99
100 /* A write handler used by es_fopencookie to write assuan data
101    lines.  */
102 static gpgrt_ssize_t
103 data_line_cookie_write (void *cookie, const void *buffer, size_t size)
104 {
105   assuan_context_t ctx = cookie;
106
107   if (assuan_send_data (ctx, buffer, size))
108     {
109       gpg_err_set_errno (EIO);
110       return -1;
111     }
112
113   return (gpgrt_ssize_t)size;
114 }
115
116 static int
117 data_line_cookie_close (void *cookie)
118 {
119   assuan_context_t ctx = cookie;
120
121   if (assuan_send_data (ctx, NULL, 0))
122     {
123       gpg_err_set_errno (EIO);
124       return -1;
125     }
126
127   return 0;
128 }
129
130
131 static void
132 close_message_fd (ctrl_t ctrl)
133 {
134   if (ctrl->server_local->message_fd != -1)
135     {
136 #ifdef HAVE_W32CE_SYSTEM
137 #warning Is this correct for W32/W32CE?
138 #endif
139       close (ctrl->server_local->message_fd);
140       ctrl->server_local->message_fd = -1;
141     }
142 }
143
144
145 /* Start a new audit session if this has been enabled.  */
146 static gpg_error_t
147 start_audit_session (ctrl_t ctrl)
148 {
149   audit_release (ctrl->audit);
150   ctrl->audit = NULL;
151   if (ctrl->server_local->enable_audit_log && !(ctrl->audit = audit_new ()) )
152     return gpg_error_from_syserror ();
153
154   return 0;
155 }
156
157
158 static gpg_error_t
159 option_handler (assuan_context_t ctx, const char *key, const char *value)
160 {
161   ctrl_t ctrl = assuan_get_pointer (ctx);
162   gpg_error_t err = 0;
163
164   if (!strcmp (key, "putenv"))
165     {
166       /* Change the session's environment to be used for the
167          Pinentry.  Valid values are:
168           <NAME>            Delete envvar NAME
169           <KEY>=            Set envvar NAME to the empty string
170           <KEY>=<VALUE>     Set envvar NAME to VALUE
171       */
172       err = session_env_putenv (opt.session_env, value);
173     }
174   else if (!strcmp (key, "display"))
175     {
176       err = session_env_setenv (opt.session_env, "DISPLAY", value);
177     }
178   else if (!strcmp (key, "ttyname"))
179     {
180       err = session_env_setenv (opt.session_env, "GPG_TTY", value);
181     }
182   else if (!strcmp (key, "ttytype"))
183     {
184       err = session_env_setenv (opt.session_env, "TERM", value);
185     }
186   else if (!strcmp (key, "lc-ctype"))
187     {
188       xfree (opt.lc_ctype);
189       opt.lc_ctype = xtrystrdup (value);
190       if (!opt.lc_ctype)
191         err = gpg_error_from_syserror ();
192     }
193   else if (!strcmp (key, "lc-messages"))
194     {
195       xfree (opt.lc_messages);
196       opt.lc_messages = xtrystrdup (value);
197       if (!opt.lc_messages)
198         err = gpg_error_from_syserror ();
199     }
200   else if (!strcmp (key, "xauthority"))
201     {
202       err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
203     }
204   else if (!strcmp (key, "pinentry-user-data"))
205     {
206       err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
207     }
208   else if (!strcmp (key, "include-certs"))
209     {
210       int i = *value? atoi (value) : -1;
211       if (ctrl->include_certs < -2)
212         err = gpg_error (GPG_ERR_ASS_PARAMETER);
213       else
214         ctrl->include_certs = i;
215     }
216   else if (!strcmp (key, "list-mode"))
217     {
218       int i = *value? atoi (value) : 0;
219       if (!i || i == 1) /* default and mode 1 */
220         {
221           ctrl->server_local->list_internal = 1;
222           ctrl->server_local->list_external = 0;
223         }
224       else if (i == 2)
225         {
226           ctrl->server_local->list_internal = 0;
227           ctrl->server_local->list_external = 1;
228         }
229       else if (i == 3)
230         {
231           ctrl->server_local->list_internal = 1;
232           ctrl->server_local->list_external = 1;
233         }
234       else
235         err = gpg_error (GPG_ERR_ASS_PARAMETER);
236     }
237   else if (!strcmp (key, "list-to-output"))
238     {
239       int i = *value? atoi (value) : 0;
240       ctrl->server_local->list_to_output = i;
241     }
242   else if (!strcmp (key, "with-validation"))
243     {
244       int i = *value? atoi (value) : 0;
245       ctrl->with_validation = i;
246     }
247   else if (!strcmp (key, "with-secret"))
248     {
249       int i = *value? atoi (value) : 0;
250       ctrl->with_secret = i;
251     }
252   else if (!strcmp (key, "validation-model"))
253     {
254       int i = gpgsm_parse_validation_model (value);
255       if ( i >= 0 && i <= 2 )
256         ctrl->validation_model = i;
257       else
258         err = gpg_error (GPG_ERR_ASS_PARAMETER);
259     }
260   else if (!strcmp (key, "with-key-data"))
261     {
262       opt.with_key_data = 1;
263     }
264   else if (!strcmp (key, "enable-audit-log"))
265     {
266       int i = *value? atoi (value) : 0;
267       ctrl->server_local->enable_audit_log = i;
268     }
269   else if (!strcmp (key, "allow-pinentry-notify"))
270     {
271       ctrl->server_local->allow_pinentry_notify = 1;
272     }
273   else if (!strcmp (key, "with-ephemeral-keys"))
274     {
275       int i = *value? atoi (value) : 0;
276       ctrl->with_ephemeral_keys = i;
277     }
278   else if (!strcmp (key, "no-encrypt-to"))
279     {
280       ctrl->server_local->no_encrypt_to = 1;
281     }
282   else if (!strcmp (key, "offline"))
283     {
284       /* We ignore this option if gpgsm has been started with
285          --disable-dirmngr (which also sets offline).  */
286       if (!opt.disable_dirmngr)
287         {
288           int i = *value? !!atoi (value) : 1;
289           ctrl->offline = i;
290         }
291     }
292   else
293     err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
294
295   return err;
296 }
297
298
299 static gpg_error_t
300 reset_notify (assuan_context_t ctx, char *line)
301 {
302   ctrl_t ctrl = assuan_get_pointer (ctx);
303
304   (void) line;
305
306   gpgsm_release_certlist (ctrl->server_local->recplist);
307   gpgsm_release_certlist (ctrl->server_local->signerlist);
308   ctrl->server_local->recplist = NULL;
309   ctrl->server_local->signerlist = NULL;
310   close_message_fd (ctrl);
311   assuan_close_input_fd (ctx);
312   assuan_close_output_fd (ctx);
313   return 0;
314 }
315
316
317 static gpg_error_t
318 input_notify (assuan_context_t ctx, char *line)
319 {
320   ctrl_t ctrl = assuan_get_pointer (ctx);
321
322   ctrl->autodetect_encoding = 0;
323   ctrl->is_pem = 0;
324   ctrl->is_base64 = 0;
325   if (strstr (line, "--armor"))
326     ctrl->is_pem = 1;
327   else if (strstr (line, "--base64"))
328     ctrl->is_base64 = 1;
329   else if (strstr (line, "--binary"))
330     ;
331   else
332     ctrl->autodetect_encoding = 1;
333   return 0;
334 }
335
336 static gpg_error_t
337 output_notify (assuan_context_t ctx, char *line)
338 {
339   ctrl_t ctrl = assuan_get_pointer (ctx);
340
341   ctrl->create_pem = 0;
342   ctrl->create_base64 = 0;
343   if (strstr (line, "--armor"))
344     ctrl->create_pem = 1;
345   else if (strstr (line, "--base64"))
346     ctrl->create_base64 = 1; /* just the raw output */
347   return 0;
348 }
349
350
351 static const char hlp_recipient[] =
352   "RECIPIENT <userID>\n"
353   "\n"
354   "Set the recipient for the encryption.  USERID shall be the\n"
355   "internal representation of the key; the server may accept any other\n"
356   "way of specification [we will support this].  If this is a valid and\n"
357   "trusted recipient the server does respond with OK, otherwise the\n"
358   "return is an ERR with the reason why the recipient can't be used,\n"
359   "the encryption will then not be done for this recipient.  If the\n"
360   "policy is not to encrypt at all if not all recipients are valid, the\n"
361   "client has to take care of this.  All RECIPIENT commands are\n"
362   "cumulative until a RESET or an successful ENCRYPT command.";
363 static gpg_error_t
364 cmd_recipient (assuan_context_t ctx, char *line)
365 {
366   ctrl_t ctrl = assuan_get_pointer (ctx);
367   int rc;
368
369   if (!ctrl->audit)
370     rc = start_audit_session (ctrl);
371   else
372     rc = 0;
373
374   if (!rc)
375     rc = gpgsm_add_to_certlist (ctrl, line, 0,
376                                 &ctrl->server_local->recplist, 0);
377   if (rc)
378     {
379       gpgsm_status2 (ctrl, STATUS_INV_RECP,
380                      get_inv_recpsgnr_code (rc), line, NULL);
381     }
382
383   return rc;
384 }
385
386
387 static const char hlp_signer[] =
388   "SIGNER <userID>\n"
389   "\n"
390   "Set the signer's keys for the signature creation.  USERID should\n"
391   "be the internal representation of the key; the server may accept any\n"
392   "other way of specification [we will support this].  If this is a\n"
393   "valid and usable signing key the server does respond with OK,\n"
394   "otherwise it returns an ERR with the reason why the key can't be\n"
395   "used, the signing will then not be done for this key.  If the policy\n"
396   "is not to sign at all if not all signer keys are valid, the client\n"
397   "has to take care of this.  All SIGNER commands are cumulative until\n"
398   "a RESET but they are *not* reset by an SIGN command because it can\n"
399   "be expected that set of signers are used for more than one sign\n"
400   "operation.";
401 static gpg_error_t
402 cmd_signer (assuan_context_t ctx, char *line)
403 {
404   ctrl_t ctrl = assuan_get_pointer (ctx);
405   int rc;
406
407   rc = gpgsm_add_to_certlist (ctrl, line, 1,
408                               &ctrl->server_local->signerlist, 0);
409   if (rc)
410     {
411       gpgsm_status2 (ctrl, STATUS_INV_SGNR,
412                      get_inv_recpsgnr_code (rc), line, NULL);
413       /* For compatibility reasons we also issue the old code after the
414          new one.  */
415       gpgsm_status2 (ctrl, STATUS_INV_RECP,
416                      get_inv_recpsgnr_code (rc), line, NULL);
417     }
418   return rc;
419 }
420
421
422 static const char hlp_encrypt[] =
423   "ENCRYPT \n"
424   "\n"
425   "Do the actual encryption process. Takes the plaintext from the INPUT\n"
426   "command, writes to the ciphertext to the file descriptor set with\n"
427   "the OUTPUT command, take the recipients form all the recipients set\n"
428   "so far.  If this command fails the clients should try to delete all\n"
429   "output currently done or otherwise mark it as invalid.  GPGSM does\n"
430   "ensure that there won't be any security problem with leftover data\n"
431   "on the output in this case.\n"
432   "\n"
433   "This command should in general not fail, as all necessary checks\n"
434   "have been done while setting the recipients.  The input and output\n"
435   "pipes are closed.";
436 static gpg_error_t
437 cmd_encrypt (assuan_context_t ctx, char *line)
438 {
439   ctrl_t ctrl = assuan_get_pointer (ctx);
440   certlist_t cl;
441   int inp_fd, out_fd;
442   estream_t out_fp;
443   int rc;
444
445   (void)line;
446
447   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
448   if (inp_fd == -1)
449     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
450   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
451   if (out_fd == -1)
452     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
453
454   out_fp = es_fdopen_nc (out_fd, "w");
455   if (!out_fp)
456     return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
457
458   /* Now add all encrypt-to marked recipients from the default
459      list. */
460   rc = 0;
461   if (!opt.no_encrypt_to && !ctrl->server_local->no_encrypt_to)
462     {
463       for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next)
464         if (cl->is_encrypt_to)
465           rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert,
466                                            &ctrl->server_local->recplist, 1);
467     }
468   if (!rc)
469     rc = ctrl->audit? 0 : start_audit_session (ctrl);
470   if (!rc)
471     rc = gpgsm_encrypt (assuan_get_pointer (ctx),
472                         ctrl->server_local->recplist,
473                         inp_fd, out_fp);
474   es_fclose (out_fp);
475
476   gpgsm_release_certlist (ctrl->server_local->recplist);
477   ctrl->server_local->recplist = NULL;
478   /* Close and reset the fd */
479   close_message_fd (ctrl);
480   assuan_close_input_fd (ctx);
481   assuan_close_output_fd (ctx);
482   return rc;
483 }
484
485
486 static const char hlp_decrypt[] =
487   "DECRYPT\n"
488   "\n"
489   "This performs the decrypt operation after doing some check on the\n"
490   "internal state. (e.g. that only needed data has been set).  Because\n"
491   "it utilizes the GPG-Agent for the session key decryption, there is\n"
492   "no need to ask the client for a protecting passphrase - GPG-Agent\n"
493   "does take care of this by requesting this from the user.";
494 static gpg_error_t
495 cmd_decrypt (assuan_context_t ctx, char *line)
496 {
497   ctrl_t ctrl = assuan_get_pointer (ctx);
498   int inp_fd, out_fd;
499   estream_t out_fp;
500   int rc;
501
502   (void)line;
503
504   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
505   if (inp_fd == -1)
506     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
507   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
508   if (out_fd == -1)
509     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
510
511   out_fp = es_fdopen_nc (out_fd, "w");
512   if (!out_fp)
513     return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
514
515   rc = start_audit_session (ctrl);
516   if (!rc)
517     rc = gpgsm_decrypt (ctrl, inp_fd, out_fp);
518   es_fclose (out_fp);
519
520   /* Close and reset the fds. */
521   close_message_fd (ctrl);
522   assuan_close_input_fd (ctx);
523   assuan_close_output_fd (ctx);
524
525   return rc;
526 }
527
528
529 static const char hlp_verify[] =
530   "VERIFY\n"
531   "\n"
532   "This does a verify operation on the message send to the input FD.\n"
533   "The result is written out using status lines.  If an output FD was\n"
534   "given, the signed text will be written to that.\n"
535   "\n"
536   "If the signature is a detached one, the server will inquire about\n"
537   "the signed material and the client must provide it.";
538 static gpg_error_t
539 cmd_verify (assuan_context_t ctx, char *line)
540 {
541   int rc;
542   ctrl_t ctrl = assuan_get_pointer (ctx);
543   int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
544   int out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
545   estream_t out_fp = NULL;
546
547   (void)line;
548
549   if (fd == -1)
550     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
551
552   if (out_fd != -1)
553     {
554       out_fp = es_fdopen_nc (out_fd, "w");
555       if (!out_fp)
556         return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
557     }
558
559   rc = start_audit_session (ctrl);
560   if (!rc)
561     rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
562                        ctrl->server_local->message_fd, out_fp);
563   es_fclose (out_fp);
564
565   /* Close and reset the fd.  */
566   close_message_fd (ctrl);
567   assuan_close_input_fd (ctx);
568   assuan_close_output_fd (ctx);
569
570   return rc;
571 }
572
573
574 static const char hlp_sign[] =
575   "SIGN [--detached]\n"
576   "\n"
577   "Sign the data set with the INPUT command and write it to the sink\n"
578   "set by OUTPUT.  With \"--detached\", a detached signature is\n"
579   "created (surprise).";
580 static gpg_error_t
581 cmd_sign (assuan_context_t ctx, char *line)
582 {
583   ctrl_t ctrl = assuan_get_pointer (ctx);
584   int inp_fd, out_fd;
585   estream_t out_fp;
586   int detached;
587   int rc;
588
589   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
590   if (inp_fd == -1)
591     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
592   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
593   if (out_fd == -1)
594     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
595
596   detached = has_option (line, "--detached");
597
598   out_fp = es_fdopen_nc (out_fd, "w");
599   if (!out_fp)
600     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
601
602   rc = start_audit_session (ctrl);
603   if (!rc)
604     rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
605                      inp_fd, detached, out_fp);
606   es_fclose (out_fp);
607
608   /* close and reset the fd */
609   close_message_fd (ctrl);
610   assuan_close_input_fd (ctx);
611   assuan_close_output_fd (ctx);
612
613   return rc;
614 }
615
616
617 static const char hlp_import[] =
618   "IMPORT [--re-import]\n"
619   "\n"
620   "Import the certificates read form the input-fd, return status\n"
621   "message for each imported one.  The import checks the validity of\n"
622   "the certificate but not of the entire chain.  It is possible to\n"
623   "import expired certificates.\n"
624   "\n"
625   "With the option --re-import the input data is expected to a be a LF\n"
626   "separated list of fingerprints.  The command will re-import these\n"
627   "certificates, meaning that they are made permanent by removing\n"
628   "their ephemeral flag.";
629 static gpg_error_t
630 cmd_import (assuan_context_t ctx, char *line)
631 {
632   ctrl_t ctrl = assuan_get_pointer (ctx);
633   int rc;
634   int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
635   int reimport = has_option (line, "--re-import");
636
637   (void)line;
638
639   if (fd == -1)
640     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
641
642   rc = gpgsm_import (assuan_get_pointer (ctx), fd, reimport);
643
644   /* close and reset the fd */
645   close_message_fd (ctrl);
646   assuan_close_input_fd (ctx);
647   assuan_close_output_fd (ctx);
648
649   return rc;
650 }
651
652
653 static const char hlp_export[] =
654   "EXPORT [--data [--armor|--base64]] [--secret [--(raw|pkcs12)] [--] <pattern>\n"
655   "\n"
656   "Export the certificates selected by PATTERN.  With --data the output\n"
657   "is returned using Assuan D lines; the default is to use the sink given\n"
658   "by the last \"OUTPUT\" command.  The options --armor or --base64 encode \n"
659   "the output using the PEM respective a plain base-64 format; the default\n"
660   "is a binary format which is only suitable for a single certificate.\n"
661   "With --secret the secret key is exported using the PKCS#8 format,\n"
662   "with --raw using PKCS#1, and with --pkcs12 as full PKCS#12 container.";
663 static gpg_error_t
664 cmd_export (assuan_context_t ctx, char *line)
665 {
666   ctrl_t ctrl = assuan_get_pointer (ctx);
667   char *p;
668   strlist_t list, sl;
669   int use_data;
670   int opt_secret;
671   int opt_raw = 0;
672   int opt_pkcs12 = 0;
673
674   use_data = has_option (line, "--data");
675   if (use_data)
676     {
677       /* We need to override any possible setting done by an OUTPUT command. */
678       ctrl->create_pem = has_option (line, "--armor");
679       ctrl->create_base64 = has_option (line, "--base64");
680     }
681   opt_secret = has_option (line, "--secret");
682   if (opt_secret)
683     {
684       opt_raw = has_option (line, "--raw");
685       opt_pkcs12 = has_option (line, "--pkcs12");
686     }
687
688   line = skip_options (line);
689
690   /* Break the line down into an strlist_t. */
691   list = NULL;
692   for (p=line; *p; line = p)
693     {
694       while (*p && *p != ' ')
695         p++;
696       if (*p)
697         *p++ = 0;
698       if (*line)
699         {
700           sl = xtrymalloc (sizeof *sl + strlen (line));
701           if (!sl)
702             {
703               free_strlist (list);
704               return out_of_core ();
705             }
706           sl->flags = 0;
707           strcpy_escaped_plus (sl->d, line);
708           sl->next = list;
709           list = sl;
710         }
711     }
712
713   if (opt_secret)
714     {
715       if (!list || !*list->d)
716         return set_error (GPG_ERR_NO_DATA, "No key given");
717       if (list->next)
718         return set_error (GPG_ERR_TOO_MANY, "Only one key allowed");
719   }
720
721   if (use_data)
722     {
723       estream_t stream;
724
725       stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
726       if (!stream)
727         {
728           free_strlist (list);
729           return set_error (GPG_ERR_ASS_GENERAL,
730                             "error setting up a data stream");
731         }
732       if (opt_secret)
733         gpgsm_p12_export (ctrl, list->d, stream,
734                           opt_raw? 2 : opt_pkcs12 ? 0 : 1);
735       else
736         gpgsm_export (ctrl, list, stream);
737       es_fclose (stream);
738     }
739   else
740     {
741       int fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
742       estream_t out_fp;
743
744       if (fd == -1)
745         {
746           free_strlist (list);
747           return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
748         }
749       out_fp = es_fdopen_nc (fd, "w");
750       if (!out_fp)
751         {
752           free_strlist (list);
753           return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
754         }
755
756       if (opt_secret)
757         gpgsm_p12_export (ctrl, list->d, out_fp,
758                           opt_raw? 2 : opt_pkcs12 ? 0 : 1);
759       else
760         gpgsm_export (ctrl, list, out_fp);
761       es_fclose (out_fp);
762     }
763
764   free_strlist (list);
765   /* Close and reset the fds. */
766   close_message_fd (ctrl);
767   assuan_close_input_fd (ctx);
768   assuan_close_output_fd (ctx);
769   return 0;
770 }
771
772
773
774 static const char hlp_delkeys[] =
775   "DELKEYS <patterns>\n"
776   "\n"
777   "Delete the certificates specified by PATTERNS.  Each pattern shall be\n"
778   "a percent-plus escaped certificate specification.  Usually a\n"
779   "fingerprint will be used for this.";
780 static gpg_error_t
781 cmd_delkeys (assuan_context_t ctx, char *line)
782 {
783   ctrl_t ctrl = assuan_get_pointer (ctx);
784   char *p;
785   strlist_t list, sl;
786   int rc;
787
788   /* break the line down into an strlist_t */
789   list = NULL;
790   for (p=line; *p; line = p)
791     {
792       while (*p && *p != ' ')
793         p++;
794       if (*p)
795         *p++ = 0;
796       if (*line)
797         {
798           sl = xtrymalloc (sizeof *sl + strlen (line));
799           if (!sl)
800             {
801               free_strlist (list);
802               return out_of_core ();
803             }
804           sl->flags = 0;
805           strcpy_escaped_plus (sl->d, line);
806           sl->next = list;
807           list = sl;
808         }
809     }
810
811   rc = gpgsm_delete (ctrl, list);
812   free_strlist (list);
813
814   /* close and reset the fd */
815   close_message_fd (ctrl);
816   assuan_close_input_fd (ctx);
817   assuan_close_output_fd (ctx);
818
819   return rc;
820 }
821
822
823
824 static const char hlp_output[] =
825   "OUTPUT FD[=<n>]\n"
826   "\n"
827   "Set the file descriptor to write the output data to N.  If N is not\n"
828   "given and the operating system supports file descriptor passing, the\n"
829   "file descriptor currently in flight will be used.  See also the\n"
830   "\"INPUT\" and \"MESSAGE\" commands.";
831 static const char hlp_input[] =
832   "INPUT FD[=<n>]\n"
833   "\n"
834   "Set the file descriptor to read the input data to N.  If N is not\n"
835   "given and the operating system supports file descriptor passing, the\n"
836   "file descriptor currently in flight will be used.  See also the\n"
837   "\"MESSAGE\" and \"OUTPUT\" commands.";
838 static const char hlp_message[] =
839   "MESSAGE FD[=<n>]\n"
840   "\n"
841   "Set the file descriptor to read the message for a detached\n"
842   "signatures to N.  If N is not given and the operating system\n"
843   "supports file descriptor passing, the file descriptor currently in\n"
844   "flight will be used.  See also the \"INPUT\" and \"OUTPUT\" commands.";
845 static gpg_error_t
846 cmd_message (assuan_context_t ctx, char *line)
847 {
848   int rc;
849   gnupg_fd_t sysfd;
850   int fd;
851   ctrl_t ctrl = assuan_get_pointer (ctx);
852
853   rc = assuan_command_parse_fd (ctx, line, &sysfd);
854   if (rc)
855     return rc;
856
857 #ifdef HAVE_W32CE_SYSTEM
858   sysfd = _assuan_w32ce_finish_pipe ((int)sysfd, 0);
859   if (sysfd == INVALID_HANDLE_VALUE)
860     return set_error (gpg_err_code_from_syserror (),
861                       "rvid conversion failed");
862 #endif
863
864   fd = translate_sys2libc_fd (sysfd, 0);
865   if (fd == -1)
866     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
867   ctrl->server_local->message_fd = fd;
868   return 0;
869 }
870
871
872
873 static const char hlp_listkeys[] =
874   "LISTKEYS [<patterns>]\n"
875   "LISTSECRETKEYS [<patterns>]\n"
876   "DUMPKEYS [<patterns>]\n"
877   "DUMPSECRETKEYS [<patterns>]\n"
878   "\n"
879   "List all certificates or only those specified by PATTERNS.  Each\n"
880   "pattern shall be a percent-plus escaped certificate specification.\n"
881   "The \"SECRET\" versions of the command filter the output to include\n"
882   "only certificates where the secret key is available or a corresponding\n"
883   "smartcard has been registered.  The \"DUMP\" versions of the command\n"
884   "are only useful for debugging.  The output format is a percent escaped\n"
885   "colon delimited listing as described in the manual.\n"
886   "\n"
887   "These \"OPTION\" command keys effect the output::\n"
888   "\n"
889   "  \"list-mode\" set to 0: List only local certificates (default).\n"
890   "                     1: Ditto.\n"
891   "                     2: List only external certificates.\n"
892   "                     3: List local and external certificates.\n"
893   "\n"
894   "  \"with-validation\" set to true: Validate each certificate.\n"
895   "\n"
896   "  \"with-ephemeral-key\" set to true: Always include ephemeral\n"
897   "                                    certificates.\n"
898   "\n"
899   "  \"list-to-output\" set to true: Write output to the file descriptor\n"
900   "                                given by the last \"OUTPUT\" command.";
901 static int
902 do_listkeys (assuan_context_t ctx, char *line, int mode)
903 {
904   ctrl_t ctrl = assuan_get_pointer (ctx);
905   estream_t fp;
906   char *p;
907   strlist_t list, sl;
908   unsigned int listmode;
909   gpg_error_t err;
910
911   /* Break the line down into an strlist. */
912   list = NULL;
913   for (p=line; *p; line = p)
914     {
915       while (*p && *p != ' ')
916         p++;
917       if (*p)
918         *p++ = 0;
919       if (*line)
920         {
921           sl = xtrymalloc (sizeof *sl + strlen (line));
922           if (!sl)
923             {
924               free_strlist (list);
925               return out_of_core ();
926             }
927           sl->flags = 0;
928           strcpy_escaped_plus (sl->d, line);
929           sl->next = list;
930           list = sl;
931         }
932     }
933
934   if (ctrl->server_local->list_to_output)
935     {
936       int outfd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
937
938       if ( outfd == -1 )
939         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
940       fp = es_fdopen_nc (outfd, "w");
941       if (!fp)
942         return set_error (gpg_err_code_from_syserror (), "es_fdopen() failed");
943     }
944   else
945     {
946       fp = es_fopencookie (ctx, "w", data_line_cookie_functions);
947       if (!fp)
948         return set_error (GPG_ERR_ASS_GENERAL,
949                           "error setting up a data stream");
950     }
951
952   ctrl->with_colons = 1;
953   listmode = mode;
954   if (ctrl->server_local->list_internal)
955     listmode |= (1<<6);
956   if (ctrl->server_local->list_external)
957     listmode |= (1<<7);
958   err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
959   free_strlist (list);
960   es_fclose (fp);
961   if (ctrl->server_local->list_to_output)
962     assuan_close_output_fd (ctx);
963   return err;
964 }
965
966 static gpg_error_t
967 cmd_listkeys (assuan_context_t ctx, char *line)
968 {
969   return do_listkeys (ctx, line, 3);
970 }
971
972 static gpg_error_t
973 cmd_dumpkeys (assuan_context_t ctx, char *line)
974 {
975   return do_listkeys (ctx, line, 259);
976 }
977
978 static gpg_error_t
979 cmd_listsecretkeys (assuan_context_t ctx, char *line)
980 {
981   return do_listkeys (ctx, line, 2);
982 }
983
984 static gpg_error_t
985 cmd_dumpsecretkeys (assuan_context_t ctx, char *line)
986 {
987   return do_listkeys (ctx, line, 258);
988 }
989
990
991 \f
992 static const char hlp_genkey[] =
993   "GENKEY\n"
994   "\n"
995   "Read the parameters in native format from the input fd and write a\n"
996   "certificate request to the output.";
997 static gpg_error_t
998 cmd_genkey (assuan_context_t ctx, char *line)
999 {
1000   ctrl_t ctrl = assuan_get_pointer (ctx);
1001   int inp_fd, out_fd;
1002   estream_t in_stream, out_stream;
1003   int rc;
1004
1005   (void)line;
1006
1007   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
1008   if (inp_fd == -1)
1009     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
1010   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
1011   if (out_fd == -1)
1012     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
1013
1014   in_stream = es_fdopen_nc (inp_fd, "r");
1015   if (!in_stream)
1016     return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed");
1017
1018   out_stream = es_fdopen_nc (out_fd, "w");
1019   if (!out_stream)
1020     {
1021       es_fclose (in_stream);
1022       return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
1023     }
1024   rc = gpgsm_genkey (ctrl, in_stream, out_stream);
1025   es_fclose (out_stream);
1026   es_fclose (in_stream);
1027
1028   /* close and reset the fds */
1029   assuan_close_input_fd (ctx);
1030   assuan_close_output_fd (ctx);
1031
1032   return rc;
1033 }
1034
1035
1036 \f
1037 static const char hlp_getauditlog[] =
1038   "GETAUDITLOG [--data] [--html]\n"
1039   "\n"
1040   "If --data is used, the output is send using D-lines and not to the\n"
1041   "file descriptor given by an OUTPUT command.\n"
1042   "\n"
1043   "If --html is used the output is formatted as an XHTML block. This is\n"
1044   "designed to be incorporated into a HTML document.";
1045 static gpg_error_t
1046 cmd_getauditlog (assuan_context_t ctx, char *line)
1047 {
1048   ctrl_t ctrl = assuan_get_pointer (ctx);
1049   int  out_fd;
1050   estream_t out_stream;
1051   int opt_data, opt_html;
1052   int rc;
1053
1054   opt_data = has_option (line, "--data");
1055   opt_html = has_option (line, "--html");
1056   /* Not needed: line = skip_options (line); */
1057
1058   if (!ctrl->audit)
1059     return gpg_error (GPG_ERR_NO_DATA);
1060
1061   if (opt_data)
1062     {
1063       out_stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
1064       if (!out_stream)
1065         return set_error (GPG_ERR_ASS_GENERAL,
1066                           "error setting up a data stream");
1067     }
1068   else
1069     {
1070       out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
1071       if (out_fd == -1)
1072         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
1073
1074       out_stream = es_fdopen_nc (out_fd, "w");
1075       if (!out_stream)
1076         {
1077           return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
1078         }
1079     }
1080
1081   audit_print_result (ctrl->audit, out_stream, opt_html);
1082   rc = 0;
1083
1084   es_fclose (out_stream);
1085
1086   /* Close and reset the fd. */
1087   if (!opt_data)
1088     assuan_close_output_fd (ctx);
1089   return rc;
1090 }
1091
1092 static const char hlp_getinfo[] =
1093   "GETINFO <what>\n"
1094   "\n"
1095   "Multipurpose function to return a variety of information.\n"
1096   "Supported values for WHAT are:\n"
1097   "\n"
1098   "  version     - Return the version of the program.\n"
1099   "  pid         - Return the process id of the server.\n"
1100   "  agent-check - Return success if the agent is running.\n"
1101   "  cmd_has_option CMD OPT\n"
1102   "              - Returns OK if the command CMD implements the option OPT.\n"
1103   "  offline     - Returns OK if the connection is in offline mode.";
1104 static gpg_error_t
1105 cmd_getinfo (assuan_context_t ctx, char *line)
1106 {
1107   ctrl_t ctrl = assuan_get_pointer (ctx);
1108   int rc = 0;
1109
1110   if (!strcmp (line, "version"))
1111     {
1112       const char *s = VERSION;
1113       rc = assuan_send_data (ctx, s, strlen (s));
1114     }
1115   else if (!strcmp (line, "pid"))
1116     {
1117       char numbuf[50];
1118
1119       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
1120       rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
1121     }
1122   else if (!strcmp (line, "agent-check"))
1123     {
1124       rc = gpgsm_agent_send_nop (ctrl);
1125     }
1126   else if (!strncmp (line, "cmd_has_option", 14)
1127            && (line[14] == ' ' || line[14] == '\t' || !line[14]))
1128     {
1129       char *cmd, *cmdopt;
1130       line += 14;
1131       while (*line == ' ' || *line == '\t')
1132         line++;
1133       if (!*line)
1134         rc = gpg_error (GPG_ERR_MISSING_VALUE);
1135       else
1136         {
1137           cmd = line;
1138           while (*line && (*line != ' ' && *line != '\t'))
1139             line++;
1140           if (!*line)
1141             rc = gpg_error (GPG_ERR_MISSING_VALUE);
1142           else
1143             {
1144               *line++ = 0;
1145               while (*line == ' ' || *line == '\t')
1146                 line++;
1147               if (!*line)
1148                 rc = gpg_error (GPG_ERR_MISSING_VALUE);
1149               else
1150                 {
1151                   cmdopt = line;
1152                   if (!command_has_option (cmd, cmdopt))
1153                     rc = gpg_error (GPG_ERR_GENERAL);
1154                 }
1155             }
1156         }
1157     }
1158   else if (!strcmp (line, "offline"))
1159     {
1160       rc = ctrl->offline? 0 : gpg_error (GPG_ERR_GENERAL);
1161     }
1162   else
1163     rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
1164
1165   return rc;
1166 }
1167
1168
1169 static const char hlp_passwd[] =
1170   "PASSWD <userID>\n"
1171   "\n"
1172   "Change the passphrase of the secret key for USERID.";
1173 static gpg_error_t
1174 cmd_passwd (assuan_context_t ctx, char *line)
1175 {
1176   ctrl_t ctrl = assuan_get_pointer (ctx);
1177   gpg_error_t err;
1178   ksba_cert_t cert = NULL;
1179   char *grip = NULL;
1180
1181   line = skip_options (line);
1182
1183   err = gpgsm_find_cert (ctrl, line, NULL, &cert, 0);
1184   if (err)
1185     ;
1186   else if (!(grip = gpgsm_get_keygrip_hexstring (cert)))
1187     err = gpg_error (GPG_ERR_INTERNAL);
1188   else
1189     {
1190       char *desc = gpgsm_format_keydesc (cert);
1191       err = gpgsm_agent_passwd (ctrl, grip, desc);
1192       xfree (desc);
1193     }
1194
1195   xfree (grip);
1196   ksba_cert_release (cert);
1197
1198   return err;
1199 }
1200
1201
1202 \f
1203 /* Return true if the command CMD implements the option OPT.  */
1204 static int
1205 command_has_option (const char *cmd, const char *cmdopt)
1206 {
1207   if (!strcmp (cmd, "IMPORT"))
1208     {
1209       if (!strcmp (cmdopt, "re-import"))
1210         return 1;
1211     }
1212
1213   return 0;
1214 }
1215
1216
1217 /* Tell the assuan library about our commands */
1218 static int
1219 register_commands (assuan_context_t ctx)
1220 {
1221   static struct {
1222     const char *name;
1223     assuan_handler_t handler;
1224     const char * const help;
1225   } table[] = {
1226     { "RECIPIENT",     cmd_recipient, hlp_recipient },
1227     { "SIGNER",        cmd_signer,    hlp_signer },
1228     { "ENCRYPT",       cmd_encrypt,   hlp_encrypt },
1229     { "DECRYPT",       cmd_decrypt,   hlp_decrypt },
1230     { "VERIFY",        cmd_verify,    hlp_verify },
1231     { "SIGN",          cmd_sign,      hlp_sign },
1232     { "IMPORT",        cmd_import,    hlp_import },
1233     { "EXPORT",        cmd_export,    hlp_export },
1234     { "INPUT",         NULL,          hlp_input },
1235     { "OUTPUT",        NULL,          hlp_output },
1236     { "MESSAGE",       cmd_message,   hlp_message },
1237     { "LISTKEYS",      cmd_listkeys,  hlp_listkeys },
1238     { "DUMPKEYS",      cmd_dumpkeys,  hlp_listkeys },
1239     { "LISTSECRETKEYS",cmd_listsecretkeys, hlp_listkeys },
1240     { "DUMPSECRETKEYS",cmd_dumpsecretkeys, hlp_listkeys },
1241     { "GENKEY",        cmd_genkey,    hlp_genkey },
1242     { "DELKEYS",       cmd_delkeys,   hlp_delkeys },
1243     { "GETAUDITLOG",   cmd_getauditlog,    hlp_getauditlog },
1244     { "GETINFO",       cmd_getinfo,   hlp_getinfo },
1245     { "PASSWD",        cmd_passwd,    hlp_passwd },
1246     { NULL }
1247   };
1248   int i, rc;
1249
1250   for (i=0; table[i].name; i++)
1251     {
1252       rc = assuan_register_command (ctx, table[i].name, table[i].handler,
1253                                     table[i].help);
1254       if (rc)
1255         return rc;
1256     }
1257   return 0;
1258 }
1259
1260 /* Startup the server. DEFAULT_RECPLIST is the list of recipients as
1261    set from the command line or config file.  We only require those
1262    marked as encrypt-to. */
1263 void
1264 gpgsm_server (certlist_t default_recplist)
1265 {
1266   int rc;
1267   assuan_fd_t filedes[2];
1268   assuan_context_t ctx;
1269   struct server_control_s ctrl;
1270   static const char hello[] = ("GNU Privacy Guard's S/M server "
1271                                VERSION " ready");
1272
1273   memset (&ctrl, 0, sizeof ctrl);
1274   gpgsm_init_default_ctrl (&ctrl);
1275
1276   /* We use a pipe based server so that we can work from scripts.
1277      assuan_init_pipe_server will automagically detect when we are
1278      called with a socketpair and ignore FILEDES in this case. */
1279 #ifdef HAVE_W32CE_SYSTEM
1280   #define SERVER_STDIN es_fileno(es_stdin)
1281   #define SERVER_STDOUT es_fileno(es_stdout)
1282 #else
1283 #define SERVER_STDIN 0
1284 #define SERVER_STDOUT 1
1285 #endif
1286   filedes[0] = assuan_fdopen (SERVER_STDIN);
1287   filedes[1] = assuan_fdopen (SERVER_STDOUT);
1288   rc = assuan_new (&ctx);
1289   if (rc)
1290     {
1291       log_error ("failed to allocate assuan context: %s\n",
1292                  gpg_strerror (rc));
1293       gpgsm_exit (2);
1294     }
1295
1296   rc = assuan_init_pipe_server (ctx, filedes);
1297   if (rc)
1298     {
1299       log_error ("failed to initialize the server: %s\n",
1300                  gpg_strerror (rc));
1301       gpgsm_exit (2);
1302     }
1303   rc = register_commands (ctx);
1304   if (rc)
1305     {
1306       log_error ("failed to the register commands with Assuan: %s\n",
1307                  gpg_strerror(rc));
1308       gpgsm_exit (2);
1309     }
1310   if (opt.verbose || opt.debug)
1311     {
1312       char *tmp;
1313
1314       /* Fixme: Use the really used socket name.  */
1315       if (asprintf (&tmp,
1316                     "Home: %s\n"
1317                     "Config: %s\n"
1318                     "DirmngrInfo: %s\n"
1319                     "%s",
1320                     gnupg_homedir (),
1321                     opt.config_filename,
1322                     dirmngr_socket_name (),
1323                     hello) > 0)
1324         {
1325           assuan_set_hello_line (ctx, tmp);
1326           free (tmp);
1327         }
1328     }
1329   else
1330     assuan_set_hello_line (ctx, hello);
1331
1332   assuan_register_reset_notify (ctx, reset_notify);
1333   assuan_register_input_notify (ctx, input_notify);
1334   assuan_register_output_notify (ctx, output_notify);
1335   assuan_register_option_handler (ctx, option_handler);
1336
1337   assuan_set_pointer (ctx, &ctrl);
1338   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
1339   ctrl.server_local->assuan_ctx = ctx;
1340   ctrl.server_local->message_fd = -1;
1341   ctrl.server_local->list_internal = 1;
1342   ctrl.server_local->list_external = 0;
1343   ctrl.server_local->default_recplist = default_recplist;
1344
1345   for (;;)
1346     {
1347       rc = assuan_accept (ctx);
1348       if (rc == -1)
1349         {
1350           break;
1351         }
1352       else if (rc)
1353         {
1354           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
1355           break;
1356         }
1357
1358       rc = assuan_process (ctx);
1359       if (rc)
1360         {
1361           log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
1362           continue;
1363         }
1364     }
1365
1366   gpgsm_release_certlist (ctrl.server_local->recplist);
1367   ctrl.server_local->recplist = NULL;
1368   gpgsm_release_certlist (ctrl.server_local->signerlist);
1369   ctrl.server_local->signerlist = NULL;
1370   xfree (ctrl.server_local);
1371
1372   audit_release (ctrl.audit);
1373   ctrl.audit = NULL;
1374
1375   assuan_release (ctx);
1376 }
1377
1378
1379
1380 gpg_error_t
1381 gpgsm_status2 (ctrl_t ctrl, int no, ...)
1382 {
1383   gpg_error_t err = 0;
1384   va_list arg_ptr;
1385   const char *text;
1386
1387   va_start (arg_ptr, no);
1388
1389   if (ctrl->no_server && ctrl->status_fd == -1)
1390     ; /* No status wanted. */
1391   else if (ctrl->no_server)
1392     {
1393       if (!statusfp)
1394         {
1395           if (ctrl->status_fd == 1)
1396             statusfp = stdout;
1397           else if (ctrl->status_fd == 2)
1398             statusfp = stderr;
1399           else
1400             statusfp = fdopen (ctrl->status_fd, "w");
1401
1402           if (!statusfp)
1403             {
1404               log_fatal ("can't open fd %d for status output: %s\n",
1405                          ctrl->status_fd, strerror(errno));
1406             }
1407         }
1408
1409       fputs ("[GNUPG:] ", statusfp);
1410       fputs (get_status_string (no), statusfp);
1411
1412       while ( (text = va_arg (arg_ptr, const char*) ))
1413         {
1414           putc ( ' ', statusfp );
1415           for (; *text; text++)
1416             {
1417               if (*text == '\n')
1418                 fputs ( "\\n", statusfp );
1419               else if (*text == '\r')
1420                 fputs ( "\\r", statusfp );
1421               else
1422                 putc ( *(const byte *)text,  statusfp );
1423             }
1424         }
1425       putc ('\n', statusfp);
1426       fflush (statusfp);
1427     }
1428   else
1429     {
1430       err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
1431                                           get_status_string (no), arg_ptr);
1432     }
1433
1434   va_end (arg_ptr);
1435   return err;
1436 }
1437
1438 gpg_error_t
1439 gpgsm_status (ctrl_t ctrl, int no, const char *text)
1440 {
1441   return gpgsm_status2 (ctrl, no, text, NULL);
1442 }
1443
1444 gpg_error_t
1445 gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
1446                             gpg_err_code_t ec)
1447 {
1448   char buf[30];
1449
1450   sprintf (buf, "%u", (unsigned int)ec);
1451   if (text)
1452     return gpgsm_status2 (ctrl, no, text, buf, NULL);
1453   else
1454     return gpgsm_status2 (ctrl, no, buf, NULL);
1455 }
1456
1457 gpg_error_t
1458 gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text,
1459                          gpg_error_t err)
1460 {
1461   char buf[30];
1462
1463   snprintf (buf, sizeof buf, "%u", err);
1464   if (text)
1465     return gpgsm_status2 (ctrl, no, text, buf, NULL);
1466   else
1467     return gpgsm_status2 (ctrl, no, buf, NULL);
1468 }
1469
1470
1471 /* Helper to notify the client about Pinentry events.  Because that
1472    might disturb some older clients, this is only done when enabled
1473    via an option.  Returns an gpg error code. */
1474 gpg_error_t
1475 gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
1476 {
1477   if (!ctrl || !ctrl->server_local
1478       || !ctrl->server_local->allow_pinentry_notify)
1479     return 0;
1480   return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
1481 }