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