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