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