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