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