Imported Upstream version 1.14.0
[platform/upstream/gpgme.git] / src / engine-gpgsm.c
1 /* engine-gpgsm.c - GpgSM engine.
2  * Copyright (C) 2000 Werner Koch (dd9jn)
3  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009,
4  *               2010 g10 Code GmbH
5  *
6  * This file is part of GPGME.
7  *
8  * GPGME is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as
10  * published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * GPGME is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this program; if not, see <https://gnu.org/licenses/>.
20  * SPDX-License-Identifier: LGPL-2.1-or-later
21  */
22
23 #if HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef HAVE_SYS_TYPES_H
30 # include <sys/types.h>
31 #endif
32 #include <assert.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #ifdef HAVE_LOCALE_H
37 #include <locale.h>
38 #endif
39 #include <fcntl.h> /* FIXME */
40
41 #include "gpgme.h"
42 #include "util.h"
43 #include "ops.h"
44 #include "wait.h"
45 #include "priv-io.h"
46 #include "sema.h"
47 #include "data.h"
48
49 #include "assuan.h"
50 #include "debug.h"
51
52 #include "engine-backend.h"
53
54 \f
55 typedef struct
56 {
57   int fd;       /* FD we talk about.  */
58   int server_fd;/* Server FD for this connection.  */
59   int dir;      /* Inbound/Outbound, maybe given implicit?  */
60   void *data;   /* Handler-specific data.  */
61   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
62   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
63                              need this because _gpgme_io_fd2str can't
64                              be used on a closed descriptor.  */
65 } iocb_data_t;
66
67
68 struct engine_gpgsm
69 {
70   assuan_context_t assuan_ctx;
71
72   int lc_ctype_set;
73   int lc_messages_set;
74
75   iocb_data_t status_cb;
76
77   /* Input, output etc are from the servers perspective.  */
78   iocb_data_t input_cb;
79   gpgme_data_t input_helper_data;  /* Input helper data object.  */
80   void *input_helper_memory;       /* Input helper memory block.  */
81
82   iocb_data_t output_cb;
83
84   iocb_data_t message_cb;
85   iocb_data_t diag_cb;
86
87   struct
88   {
89     engine_status_handler_t fnc;
90     void *fnc_value;
91     gpgme_status_cb_t mon_cb;
92     void *mon_cb_value;
93   } status;
94
95   struct
96   {
97     engine_colon_line_handler_t fnc;
98     void *fnc_value;
99     struct
100     {
101       char *line;
102       int linesize;
103       int linelen;
104     } attic;
105     int any; /* any data line seen */
106   } colon;
107
108   gpgme_data_t inline_data;  /* Used to collect D lines.  */
109
110   char request_origin[10];
111
112   struct gpgme_io_cbs io_cbs;
113
114   /* Memory data containing diagnostics (--logger-fd) of gpgsm */
115   gpgme_data_t diagnostics;
116 };
117
118 typedef struct engine_gpgsm *engine_gpgsm_t;
119
120
121 static void gpgsm_io_event (void *engine,
122                             gpgme_event_io_t type, void *type_data);
123
124
125 \f
126 static char *
127 gpgsm_get_version (const char *file_name)
128 {
129   return _gpgme_get_program_version (file_name ? file_name
130                                      : _gpgme_get_default_gpgsm_name ());
131 }
132
133
134 static const char *
135 gpgsm_get_req_version (void)
136 {
137   return "2.0.4";
138 }
139
140 \f
141 static void
142 close_notify_handler (int fd, void *opaque)
143 {
144   engine_gpgsm_t gpgsm = opaque;
145
146   assert (fd != -1);
147   if (gpgsm->status_cb.fd == fd)
148     {
149       if (gpgsm->status_cb.tag)
150         (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
151       gpgsm->status_cb.fd = -1;
152       gpgsm->status_cb.tag = NULL;
153       /* Because the server keeps on running as long as the
154        * gpgme_ctx_t is valid the diag fd will not receive a close and
155        * thus the operation gets stuck trying to read the diag fd.
156        * The status fd however is closed right after it received the
157        * "OK" from the command.  So we use this event to also close
158        * the diag fd.  */
159       _gpgme_io_close (gpgsm->diag_cb.fd);
160     }
161   else if (gpgsm->input_cb.fd == fd)
162     {
163       if (gpgsm->input_cb.tag)
164         (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
165       gpgsm->input_cb.fd = -1;
166       gpgsm->input_cb.tag = NULL;
167       if (gpgsm->input_helper_data)
168         {
169           gpgme_data_release (gpgsm->input_helper_data);
170           gpgsm->input_helper_data = NULL;
171         }
172       if (gpgsm->input_helper_memory)
173         {
174           free (gpgsm->input_helper_memory);
175           gpgsm->input_helper_memory = NULL;
176         }
177     }
178   else if (gpgsm->output_cb.fd == fd)
179     {
180       if (gpgsm->output_cb.tag)
181         (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
182       gpgsm->output_cb.fd = -1;
183       gpgsm->output_cb.tag = NULL;
184     }
185   else if (gpgsm->message_cb.fd == fd)
186     {
187       if (gpgsm->message_cb.tag)
188         (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
189       gpgsm->message_cb.fd = -1;
190       gpgsm->message_cb.tag = NULL;
191     }
192   else if (gpgsm->diag_cb.fd == fd)
193     {
194       if (gpgsm->diag_cb.tag)
195         (*gpgsm->io_cbs.remove) (gpgsm->diag_cb.tag);
196       gpgsm->diag_cb.fd = -1;
197       gpgsm->diag_cb.tag = NULL;
198     }
199 }
200
201
202 /* This is the default inquiry callback.  We use it to handle the
203    Pinentry notifications.  */
204 static gpgme_error_t
205 default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
206 {
207   (void)gpgsm;
208
209   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
210     {
211       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
212     }
213
214   return 0;
215 }
216
217
218 static gpgme_error_t
219 gpgsm_cancel (void *engine)
220 {
221   engine_gpgsm_t gpgsm = engine;
222
223   if (!gpgsm)
224     return gpg_error (GPG_ERR_INV_VALUE);
225
226   if (gpgsm->status_cb.fd != -1)
227     _gpgme_io_close (gpgsm->status_cb.fd);
228   if (gpgsm->input_cb.fd != -1)
229     _gpgme_io_close (gpgsm->input_cb.fd);
230   if (gpgsm->output_cb.fd != -1)
231     _gpgme_io_close (gpgsm->output_cb.fd);
232   if (gpgsm->message_cb.fd != -1)
233     _gpgme_io_close (gpgsm->message_cb.fd);
234   if (gpgsm->diag_cb.fd != -1)
235     _gpgme_io_close (gpgsm->diag_cb.fd);
236
237   if (gpgsm->assuan_ctx)
238     {
239       assuan_release (gpgsm->assuan_ctx);
240       gpgsm->assuan_ctx = NULL;
241     }
242
243   return 0;
244 }
245
246
247 static void
248 gpgsm_release (void *engine)
249 {
250   engine_gpgsm_t gpgsm = engine;
251
252   if (!gpgsm)
253     return;
254
255   gpgsm_cancel (engine);
256
257   gpgme_data_release (gpgsm->diagnostics);
258
259   free (gpgsm->colon.attic.line);
260   free (gpgsm);
261 }
262
263
264 static gpgme_error_t
265 gpgsm_new (void **engine, const char *file_name, const char *home_dir,
266            const char *version)
267 {
268   gpgme_error_t err = 0;
269   engine_gpgsm_t gpgsm;
270   const char *pgmname;
271   const char *argv[7];
272   char *diag_fd_str = NULL;
273   int argc;
274   int fds[2];
275   int child_fds[5];
276   int nchild_fds;
277   char *dft_display = NULL;
278   char dft_ttyname[64];
279   char *env_tty = NULL;
280   char *dft_ttytype = NULL;
281   char *optstr;
282   unsigned int connect_flags;
283
284   (void)version; /* Not yet used.  */
285
286   gpgsm = calloc (1, sizeof *gpgsm);
287   if (!gpgsm)
288     return gpg_error_from_syserror ();
289
290   gpgsm->status_cb.fd = -1;
291   gpgsm->status_cb.dir = 1;
292   gpgsm->status_cb.tag = 0;
293   gpgsm->status_cb.data = gpgsm;
294
295   gpgsm->input_cb.fd = -1;
296   gpgsm->input_cb.dir = 0;
297   gpgsm->input_cb.tag = 0;
298   gpgsm->input_cb.server_fd = -1;
299   *gpgsm->input_cb.server_fd_str = 0;
300   gpgsm->output_cb.fd = -1;
301   gpgsm->output_cb.dir = 1;
302   gpgsm->output_cb.tag = 0;
303   gpgsm->output_cb.server_fd = -1;
304   *gpgsm->output_cb.server_fd_str = 0;
305   gpgsm->message_cb.fd = -1;
306   gpgsm->message_cb.dir = 0;
307   gpgsm->message_cb.tag = 0;
308   gpgsm->message_cb.server_fd = -1;
309   *gpgsm->message_cb.server_fd_str = 0;
310   gpgsm->diag_cb.fd = -1;
311   gpgsm->diag_cb.dir = 1;
312   gpgsm->diag_cb.tag = 0;
313   gpgsm->diag_cb.server_fd = -1;
314   *gpgsm->diag_cb.server_fd_str = 0;
315
316   gpgsm->status.fnc = 0;
317   gpgsm->colon.fnc = 0;
318   gpgsm->colon.attic.line = 0;
319   gpgsm->colon.attic.linesize = 0;
320   gpgsm->colon.attic.linelen = 0;
321   gpgsm->colon.any = 0;
322
323   gpgsm->inline_data = NULL;
324
325   gpgsm->io_cbs.add = NULL;
326   gpgsm->io_cbs.add_priv = NULL;
327   gpgsm->io_cbs.remove = NULL;
328   gpgsm->io_cbs.event = NULL;
329   gpgsm->io_cbs.event_priv = NULL;
330
331   if (_gpgme_io_pipe (fds, 1) < 0)
332     {
333       err = gpg_error_from_syserror ();
334       goto leave;
335     }
336   gpgsm->diag_cb.fd = fds[0];
337   gpgsm->diag_cb.server_fd = fds[1];
338
339 #if USE_DESCRIPTOR_PASSING
340   child_fds[0] = gpgsm->diag_cb.server_fd;
341   child_fds[1] = -1;
342   nchild_fds = 2;
343   connect_flags = ASSUAN_PIPE_CONNECT_FDPASSING;
344 #else /*!USE_DESCRIPTOR_PASSING*/
345   if (_gpgme_io_pipe (fds, 0) < 0)
346     {
347       err = gpg_error_from_syserror ();
348       goto leave;
349     }
350   gpgsm->input_cb.fd = fds[1];
351   gpgsm->input_cb.server_fd = fds[0];
352
353   if (_gpgme_io_pipe (fds, 1) < 0)
354     {
355       err = gpg_error_from_syserror ();
356       goto leave;
357     }
358   gpgsm->output_cb.fd = fds[0];
359   gpgsm->output_cb.server_fd = fds[1];
360
361   if (_gpgme_io_pipe (fds, 0) < 0)
362     {
363       err = gpg_error_from_syserror ();
364       goto leave;
365     }
366   gpgsm->message_cb.fd = fds[1];
367   gpgsm->message_cb.server_fd = fds[0];
368
369   child_fds[0] = gpgsm->input_cb.server_fd;
370   child_fds[1] = gpgsm->output_cb.server_fd;
371   child_fds[2] = gpgsm->message_cb.server_fd;
372   child_fds[3] = gpgsm->diag_cb.server_fd;
373   child_fds[4] = -1;
374   nchild_fds = 5;
375   connect_flags = 0;
376 #endif  /*!USE_DESCRIPTOR_PASSING*/
377
378   pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
379
380   argc = 0;
381   argv[argc++] = _gpgme_get_basename (pgmname);
382   if (home_dir)
383     {
384       argv[argc++] = "--homedir";
385       argv[argc++] = home_dir;
386     }
387   /* Set up diagnostics */
388   err = gpgme_data_new (&gpgsm->diagnostics);
389   if (err)
390     goto leave;
391   gpgsm->diag_cb.data = gpgsm->diagnostics;
392   argv[argc++] = "--logger-fd";
393   if (gpgrt_asprintf (&diag_fd_str, "%i", gpgsm->diag_cb.server_fd) == -1)
394     {
395       err = gpg_error_from_syserror ();
396       goto leave;
397     }
398   argv[argc++] = diag_fd_str;
399   argv[argc++] = "--server";
400   argv[argc++] = NULL;
401
402   err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
403                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
404                         NULL);
405   if (err)
406     goto leave;
407   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
408
409   {
410     assuan_fd_t achild_fds[5];
411     int i;
412
413     /* For now... */
414     for (i = 0; i < nchild_fds; i++)
415       achild_fds[i] = (assuan_fd_t) child_fds[i];
416
417     err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
418                                achild_fds, NULL, NULL, connect_flags);
419
420     /* FIXME: Check whether our Windows code still updates the list.*/
421     for (i = 0; i < nchild_fds; i++)
422       child_fds[i] = (int) achild_fds[i];
423   }
424
425
426 #if !USE_DESCRIPTOR_PASSING
427   /* On Windows, handles are inserted in the spawned process with
428      DuplicateHandle, and child_fds contains the server-local names
429      for the inserted handles when assuan_pipe_connect returns.  */
430   if (!err)
431     {
432       /* Note: We don't use _gpgme_io_fd2str here.  On W32 the
433          returned handles are real W32 system handles, not whatever
434          GPGME uses internally (which may be a system handle, a C
435          library handle or a GLib/Qt channel.  Confusing, yes, but
436          remember these are server-local names, so they are not part
437          of GPGME at all.  */
438       snprintf (gpgsm->input_cb.server_fd_str,
439                 sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
440       snprintf (gpgsm->output_cb.server_fd_str,
441                 sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
442       snprintf (gpgsm->message_cb.server_fd_str,
443                 sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
444       snprintf (gpgsm->diag_cb.server_fd_str,
445                 sizeof gpgsm->diag_cb.server_fd_str, "%d", child_fds[3]);
446     }
447 #endif
448   if (err)
449     goto leave;
450
451   err = _gpgme_getenv ("DISPLAY", &dft_display);
452   if (err)
453     goto leave;
454   if (dft_display)
455     {
456       if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
457         {
458           free (dft_display);
459           err = gpg_error_from_syserror ();
460           goto leave;
461         }
462       free (dft_display);
463
464       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
465                              NULL, NULL, NULL);
466       gpgrt_free (optstr);
467       if (err)
468         goto leave;
469     }
470
471   err = _gpgme_getenv ("GPG_TTY", &env_tty);
472   if (isatty (1) || env_tty || err)
473     {
474       int rc = 0;
475
476       if (err)
477         goto leave;
478       else if (env_tty)
479         {
480           snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
481           free (env_tty);
482         }
483       else
484         rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
485
486       /* Even though isatty() returns 1, ttyname_r() may fail in many
487          ways, e.g., when /dev/pts is not accessible under chroot.  */
488       if (!rc)
489         {
490           if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
491             {
492               err = gpg_error_from_syserror ();
493               goto leave;
494             }
495           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
496                                  NULL, NULL, NULL);
497           gpgrt_free (optstr);
498           if (err)
499             goto leave;
500
501           err = _gpgme_getenv ("TERM", &dft_ttytype);
502           if (err)
503             goto leave;
504           if (dft_ttytype)
505             {
506               if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
507                 {
508                   free (dft_ttytype);
509                   err = gpg_error_from_syserror ();
510                   goto leave;
511                 }
512               free (dft_ttytype);
513
514               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
515                                      NULL, NULL, NULL, NULL);
516               gpgrt_free (optstr);
517               if (err)
518                 goto leave;
519             }
520         }
521     }
522
523   /* Ask gpgsm to enable the audit log support.  */
524   if (!err)
525     {
526       err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
527                              NULL, NULL, NULL, NULL, NULL, NULL);
528       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
529         err = 0; /* This is an optional feature of gpgsm.  */
530     }
531
532
533 #ifdef HAVE_W32_SYSTEM
534   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
535      gpgsm to tell us when it needs it.  */
536   if (!err)
537     {
538       err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
539                              NULL, NULL, NULL, NULL, NULL, NULL);
540       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
541         err = 0; /* This is a new feature of gpgsm.  */
542     }
543 #endif /*HAVE_W32_SYSTEM*/
544
545 #if !USE_DESCRIPTOR_PASSING
546   if (!err
547       && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
548                                       close_notify_handler, gpgsm)
549           || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
550                                          close_notify_handler, gpgsm)
551           || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
552                                          close_notify_handler, gpgsm)))
553     {
554       err = gpg_error (GPG_ERR_GENERAL);
555       goto leave;
556     }
557 #endif
558   if (!err && _gpgme_io_set_close_notify (gpgsm->diag_cb.fd,
559                                           close_notify_handler, gpgsm))
560     {
561       err = gpg_error (GPG_ERR_GENERAL);
562       goto leave;
563     }
564
565  leave:
566   /* Close the server ends of the pipes (because of this, we must use
567      the stored server_fd_str in the function start).  Our ends are
568      closed in gpgsm_release().  */
569 #if !USE_DESCRIPTOR_PASSING
570   if (gpgsm->input_cb.server_fd != -1)
571     _gpgme_io_close (gpgsm->input_cb.server_fd);
572   if (gpgsm->output_cb.server_fd != -1)
573     _gpgme_io_close (gpgsm->output_cb.server_fd);
574   if (gpgsm->message_cb.server_fd != -1)
575     _gpgme_io_close (gpgsm->message_cb.server_fd);
576   if (gpgsm->diag_cb.server_fd != -1)
577     _gpgme_io_close (gpgsm->diag_cb.server_fd);
578 #endif
579
580   if (err)
581     gpgsm_release (gpgsm);
582   else
583     *engine = gpgsm;
584   free (diag_fd_str);
585   return err;
586 }
587
588
589 /* Copy flags from CTX into the engine object.  */
590 static void
591 gpgsm_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
592 {
593   engine_gpgsm_t gpgsm = engine;
594
595   if (ctx->request_origin)
596     {
597       if (strlen (ctx->request_origin) + 1 > sizeof gpgsm->request_origin)
598         strcpy (gpgsm->request_origin, "xxx"); /* Too long  - force error */
599       else
600         strcpy (gpgsm->request_origin, ctx->request_origin);
601     }
602   else
603     *gpgsm->request_origin = 0;
604 }
605
606
607 static gpgme_error_t
608 gpgsm_set_locale (void *engine, int category, const char *value)
609 {
610   engine_gpgsm_t gpgsm = engine;
611   gpgme_error_t err;
612   char *optstr;
613   const char *catstr;
614
615   /* FIXME: If value is NULL, we need to reset the option to default.
616      But we can't do this.  So we error out here.  GPGSM needs support
617      for this.  */
618   if (0)
619     ;
620 #ifdef LC_CTYPE
621   else if (category == LC_CTYPE)
622     {
623       catstr = "lc-ctype";
624       if (!value && gpgsm->lc_ctype_set)
625         return gpg_error (GPG_ERR_INV_VALUE);
626       if (value)
627         gpgsm->lc_ctype_set = 1;
628     }
629 #endif
630 #ifdef LC_MESSAGES
631   else if (category == LC_MESSAGES)
632     {
633       catstr = "lc-messages";
634       if (!value && gpgsm->lc_messages_set)
635         return gpg_error (GPG_ERR_INV_VALUE);
636       if (value)
637         gpgsm->lc_messages_set = 1;
638     }
639 #endif /* LC_MESSAGES */
640   else
641     return gpg_error (GPG_ERR_INV_VALUE);
642
643   /* FIXME: Reset value to default.  */
644   if (!value)
645     return 0;
646
647   if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
648     err = gpg_error_from_syserror ();
649   else
650     {
651       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
652                              NULL, NULL, NULL, NULL);
653       gpgrt_free (optstr);
654     }
655
656   return err;
657 }
658
659
660 static gpgme_error_t
661 gpgsm_assuan_simple_command (engine_gpgsm_t gpgsm, const char *cmd,
662                              engine_status_handler_t status_fnc,
663                              void *status_fnc_value)
664 {
665   assuan_context_t ctx = gpgsm->assuan_ctx;
666   gpg_error_t err, cb_err;
667   char *line;
668   size_t linelen;
669
670   err = assuan_write_line (ctx, cmd);
671   if (err)
672     return err;
673
674   cb_err = 0;
675   do
676     {
677       err = assuan_read_line (ctx, &line, &linelen);
678       if (err)
679         break;
680
681       if (*line == '#' || !linelen)
682         continue;
683
684       if (linelen >= 2
685           && line[0] == 'O' && line[1] == 'K'
686           && (line[2] == '\0' || line[2] == ' '))
687         break;
688       else if (linelen >= 4
689           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
690           && line[3] == ' ')
691         {
692           /* We prefer a callback generated error because that one is
693              more related to gpgme and thus probably more important
694              than the error returned by the engine.  */
695           err = cb_err? cb_err : atoi (&line[4]);
696           cb_err = 0;
697         }
698       else if (linelen >= 2
699                && line[0] == 'S' && line[1] == ' ')
700         {
701           /* After an error from a status callback we skip all further
702              status lines.  */
703           if (!cb_err)
704             {
705               char *rest;
706               gpgme_status_code_t r;
707
708               rest = strchr (line + 2, ' ');
709               if (!rest)
710                 rest = line + linelen; /* set to an empty string */
711               else
712                 *(rest++) = 0;
713
714               r = _gpgme_parse_status (line + 2);
715               if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
716                 {
717                   /* Note that we call the monitor even if we do
718                    * not know the status code (r < 0).  */
719                   cb_err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
720                                                  line + 2, rest);
721                 }
722
723               if (r >= 0 && status_fnc && !cb_err)
724                 cb_err = status_fnc (status_fnc_value, r, rest);
725             }
726         }
727       else
728         {
729           /* Invalid line or INQUIRY.  We can't do anything else than
730              to stop.  As with ERR we prefer a status callback
731              generated error code, though.  */
732           err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
733           cb_err = 0;
734         }
735     }
736   while (!err);
737
738   /* We only want the first error from the status handler, thus we
739    * take the one saved in CB_ERR. */
740   if (!err && cb_err)
741     err = cb_err;
742
743   return err;
744 }
745
746
747 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
748
749 static void
750 gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
751 {
752 #if !USE_DESCRIPTOR_PASSING
753   switch (fd_type)
754     {
755     case INPUT_FD:
756       _gpgme_io_close (gpgsm->input_cb.fd);
757       break;
758     case OUTPUT_FD:
759       _gpgme_io_close (gpgsm->output_cb.fd);
760       break;
761     case MESSAGE_FD:
762       _gpgme_io_close (gpgsm->message_cb.fd);
763       break;
764     }
765 #else
766   (void)gpgsm;
767   (void)fd_type;
768 #endif
769 }
770
771 #define COMMANDLINELEN 40
772 static gpgme_error_t
773 gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
774 {
775   gpg_error_t err = 0;
776   char line[COMMANDLINELEN];
777   const char *which;
778   iocb_data_t *iocb_data;
779 #if USE_DESCRIPTOR_PASSING
780   int dir;
781 #endif
782
783   switch (fd_type)
784     {
785     case INPUT_FD:
786       which = "INPUT";
787       iocb_data = &gpgsm->input_cb;
788       break;
789
790     case OUTPUT_FD:
791       which = "OUTPUT";
792       iocb_data = &gpgsm->output_cb;
793       break;
794
795     case MESSAGE_FD:
796       which = "MESSAGE";
797       iocb_data = &gpgsm->message_cb;
798       break;
799
800     default:
801       return gpg_error (GPG_ERR_INV_VALUE);
802     }
803
804 #if USE_DESCRIPTOR_PASSING
805   dir = iocb_data->dir;
806   /* We try to short-cut the communication by giving GPGSM direct
807      access to the file descriptor, rather than using a pipe.  */
808   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
809   if (iocb_data->server_fd < 0)
810     {
811       int fds[2];
812
813       if (_gpgme_io_pipe (fds, dir) < 0)
814         return gpg_error_from_syserror ();
815
816       iocb_data->fd = dir ? fds[0] : fds[1];
817       iocb_data->server_fd = dir ? fds[1] : fds[0];
818
819       if (_gpgme_io_set_close_notify (iocb_data->fd,
820                                       close_notify_handler, gpgsm))
821         {
822           err = gpg_error (GPG_ERR_GENERAL);
823           goto leave_set_fd;
824         }
825     }
826
827   err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
828   if (err)
829     goto leave_set_fd;
830
831   _gpgme_io_close (iocb_data->server_fd);
832   iocb_data->server_fd = -1;
833
834   if (opt)
835     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
836   else
837     snprintf (line, COMMANDLINELEN, "%s FD", which);
838 #else
839   if (opt)
840     snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
841               which, iocb_data->server_fd_str, opt);
842   else
843     snprintf (line, COMMANDLINELEN, "%s FD=%s",
844               which, iocb_data->server_fd_str);
845 #endif
846
847   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
848
849 #if USE_DESCRIPTOR_PASSING
850  leave_set_fd:
851   if (err)
852     {
853       _gpgme_io_close (iocb_data->fd);
854       iocb_data->fd = -1;
855       if (iocb_data->server_fd != -1)
856         {
857           _gpgme_io_close (iocb_data->server_fd);
858           iocb_data->server_fd = -1;
859         }
860     }
861 #endif
862
863   return err;
864 }
865
866
867 static const char *
868 map_data_enc (gpgme_data_t d)
869 {
870   switch (gpgme_data_get_encoding (d))
871     {
872     case GPGME_DATA_ENCODING_NONE:
873       break;
874     case GPGME_DATA_ENCODING_BINARY:
875       return "--binary";
876     case GPGME_DATA_ENCODING_BASE64:
877       return "--base64";
878     case GPGME_DATA_ENCODING_ARMOR:
879       return "--armor";
880     default:
881       break;
882     }
883   return NULL;
884 }
885
886
887 static gpgme_error_t
888 status_handler (void *opaque, int fd)
889 {
890   struct io_cb_data *data = (struct io_cb_data *) opaque;
891   engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
892   gpgme_error_t err = 0;
893   char *line;
894   size_t linelen;
895
896   do
897     {
898       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
899       if (err)
900         {
901           /* Try our best to terminate the connection friendly.  */
902           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
903           TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
904                   "fd 0x%x: error from assuan (%d) getting status line : %s",
905                   fd, err, gpg_strerror (err));
906         }
907       else if (linelen >= 3
908                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
909                && (line[3] == '\0' || line[3] == ' '))
910         {
911           if (line[3] == ' ')
912             err = atoi (&line[4]);
913           if (! err)
914             err = gpg_error (GPG_ERR_GENERAL);
915           TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
916                   "fd 0x%x: ERR line - mapped to: %s",
917                   fd, err ? gpg_strerror (err) : "ok");
918           /* Try our best to terminate the connection friendly.  */
919           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
920         }
921       else if (linelen >= 2
922                && line[0] == 'O' && line[1] == 'K'
923                && (line[2] == '\0' || line[2] == ' '))
924         {
925           if (gpgsm->status.fnc)
926             {
927               char emptystring[1] = {0};
928               err = gpgsm->status.fnc (gpgsm->status.fnc_value,
929                                        GPGME_STATUS_EOF, emptystring);
930               if (gpg_err_code (err) == GPG_ERR_FALSE)
931                 err = 0; /* Drop special error code.  */
932             }
933
934           if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
935             {
936               /* We must tell a colon function about the EOF. We do
937                  this only when we have seen any data lines.  Note
938                  that this inlined use of colon data lines will
939                  eventually be changed into using a regular data
940                  channel. */
941               gpgsm->colon.any = 0;
942               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
943             }
944           TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
945                   "fd 0x%x: OK line - final status: %s",
946                   fd, err ? gpg_strerror (err) : "ok");
947           _gpgme_io_close (gpgsm->status_cb.fd);
948           return err;
949         }
950       else if (linelen > 2
951                && line[0] == 'D' && line[1] == ' '
952                && gpgsm->colon.fnc)
953         {
954           /* We are using the colon handler even for plain inline data
955              - strange name for that function but for historic reasons
956              we keep it.  */
957           /* FIXME We can't use this for binary data because we
958              assume this is a string.  For the current usage of colon
959              output it is correct.  */
960           char *src = line + 2;
961           char *end = line + linelen;
962           char *dst;
963           char **aline = &gpgsm->colon.attic.line;
964           int *alinelen = &gpgsm->colon.attic.linelen;
965
966           if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
967             {
968               char *newline = realloc (*aline, *alinelen + linelen + 1);
969               if (!newline)
970                 err = gpg_error_from_syserror ();
971               else
972                 {
973                   *aline = newline;
974                   gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
975                 }
976             }
977           if (!err)
978             {
979               dst = *aline + *alinelen;
980
981               while (!err && src < end)
982                 {
983                   if (*src == '%' && src + 2 < end)
984                     {
985                       /* Handle escaped characters.  */
986                       ++src;
987                       *dst = _gpgme_hextobyte (src);
988                       (*alinelen)++;
989                       src += 2;
990                     }
991                   else
992                     {
993                       *dst = *src++;
994                       (*alinelen)++;
995                     }
996
997                   if (*dst == '\n')
998                     {
999                       /* Terminate the pending line, pass it to the colon
1000                          handler and reset it.  */
1001
1002                       gpgsm->colon.any = 1;
1003                       if (*alinelen > 1 && *(dst - 1) == '\r')
1004                         dst--;
1005                       *dst = '\0';
1006
1007                       /* FIXME How should we handle the return code?  */
1008                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
1009                       if (!err)
1010                         {
1011                           dst = *aline;
1012                           *alinelen = 0;
1013                         }
1014                     }
1015                   else
1016                     dst++;
1017                 }
1018             }
1019           TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
1020                   "fd 0x%x: D line; final status: %s",
1021                   fd, err? gpg_strerror (err):"ok");
1022         }
1023       else if (linelen > 2
1024                && line[0] == 'D' && line[1] == ' '
1025                && gpgsm->inline_data)
1026         {
1027           char *src = line + 2;
1028           char *end = line + linelen;
1029           char *dst = src;
1030           gpgme_ssize_t nwritten;
1031
1032           linelen = 0;
1033           while (src < end)
1034             {
1035               if (*src == '%' && src + 2 < end)
1036                 {
1037                   /* Handle escaped characters.  */
1038                   ++src;
1039                   *dst++ = _gpgme_hextobyte (src);
1040                   src += 2;
1041                 }
1042               else
1043                 *dst++ = *src++;
1044
1045               linelen++;
1046             }
1047
1048           src = line + 2;
1049           while (linelen > 0)
1050             {
1051               nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
1052               if (nwritten <= 0 || nwritten > linelen)
1053                 {
1054                   err = gpg_error_from_syserror ();
1055                   break;
1056                 }
1057               src += nwritten;
1058               linelen -= nwritten;
1059             }
1060
1061           TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
1062                   "fd 0x%x: D inlinedata; final status: %s",
1063                   fd, err? gpg_strerror (err):"ok");
1064         }
1065       else if (linelen > 2
1066                && line[0] == 'S' && line[1] == ' ')
1067         {
1068           char *rest;
1069           gpgme_status_code_t r;
1070
1071           rest = strchr (line + 2, ' ');
1072           if (!rest)
1073             rest = line + linelen; /* set to an empty string */
1074           else
1075             *(rest++) = 0;
1076
1077           r = _gpgme_parse_status (line + 2);
1078           if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
1079             {
1080               /* Note that we call the monitor even if we do
1081                * not know the status code (r < 0).  */
1082               err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
1083                                           line + 2, rest);
1084             }
1085           else
1086             err = 0;
1087
1088           if (r >= 0 && !err)
1089             {
1090               if (gpgsm->status.fnc)
1091                 {
1092                   err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
1093                   if (gpg_err_code (err) == GPG_ERR_FALSE)
1094                     err = 0; /* Drop special error code.  */
1095                 }
1096             }
1097           else
1098             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
1099           TRACE (DEBUG_CTX, "gpgme:status_handler", gpgsm,
1100                   "fd 0x%x: S line (%s) - final status: %s",
1101                   fd, line+2, err? gpg_strerror (err):"ok");
1102         }
1103       else if (linelen >= 7
1104                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
1105                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
1106                && line[6] == 'E'
1107                && (line[7] == '\0' || line[7] == ' '))
1108         {
1109           char *keyword = line+7;
1110
1111           while (*keyword == ' ')
1112             keyword++;;
1113           default_inq_cb (gpgsm, keyword);
1114           assuan_write_line (gpgsm->assuan_ctx, "END");
1115         }
1116
1117     }
1118   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
1119
1120   return err;
1121 }
1122
1123
1124 static gpgme_error_t
1125 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
1126 {
1127   gpgme_error_t err;
1128
1129   TRACE_BEG  (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
1130               "fd=%d, dir %d", iocbd->fd, iocbd->dir);
1131   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
1132                               iocbd->fd, iocbd->dir,
1133                               handler, iocbd->data, &iocbd->tag);
1134   if (err)
1135     return TRACE_ERR (err);
1136   if (!iocbd->dir)
1137     /* FIXME Kludge around poll() problem.  */
1138     err = _gpgme_io_set_nonblocking (iocbd->fd);
1139   return TRACE_ERR (err);
1140 }
1141
1142
1143 static gpgme_error_t
1144 start (engine_gpgsm_t gpgsm, const char *command)
1145 {
1146   gpgme_error_t err;
1147   assuan_fd_t afdlist[5];
1148   int fdlist[5];
1149   int nfds;
1150   int i;
1151
1152   if (*gpgsm->request_origin)
1153     {
1154       char *cmd;
1155
1156       cmd = _gpgme_strconcat ("OPTION request-origin=",
1157                               gpgsm->request_origin, NULL);
1158       if (!cmd)
1159         return gpg_error_from_syserror ();
1160       err = gpgsm_assuan_simple_command (gpgsm, cmd, NULL, NULL);
1161       free (cmd);
1162       if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION)
1163         return err;
1164     }
1165
1166   /* We need to know the fd used by assuan for reads.  We do this by
1167      using the assumption that the first returned fd from
1168      assuan_get_active_fds() is always this one.  */
1169   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
1170                                 afdlist, DIM (afdlist));
1171   if (nfds < 1)
1172     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1173   /* For now... */
1174   for (i = 0; i < nfds; i++)
1175     fdlist[i] = (int) afdlist[i];
1176
1177   /* We "duplicate" the file descriptor, so we can close it here (we
1178      can't close fdlist[0], as that is closed by libassuan, and
1179      closing it here might cause libassuan to close some unrelated FD
1180      later).  Alternatively, we could special case status_fd and
1181      register/unregister it manually as needed, but this increases
1182      code duplication and is more complicated as we can not use the
1183      close notifications etc.  A third alternative would be to let
1184      Assuan know that we closed the FD, but that complicates the
1185      Assuan interface.  */
1186
1187   gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
1188   if (gpgsm->status_cb.fd < 0)
1189     return gpg_error_from_syserror ();
1190
1191   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
1192                                   close_notify_handler, gpgsm))
1193     {
1194       _gpgme_io_close (gpgsm->status_cb.fd);
1195       gpgsm->status_cb.fd = -1;
1196       return gpg_error (GPG_ERR_GENERAL);
1197     }
1198
1199   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1200   if (!err && gpgsm->input_cb.fd != -1)
1201     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1202   if (!err && gpgsm->output_cb.fd != -1)
1203     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1204   if (!err && gpgsm->message_cb.fd != -1)
1205     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1206   if (!err && gpgsm->diag_cb.fd != -1)
1207     err = add_io_cb (gpgsm, &gpgsm->diag_cb, _gpgme_data_inbound_handler);
1208
1209   if (!err)
1210     err = assuan_write_line (gpgsm->assuan_ctx, command);
1211
1212   if (!err)
1213     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
1214
1215   return err;
1216 }
1217
1218
1219 #if USE_DESCRIPTOR_PASSING
1220 static gpgme_error_t
1221 gpgsm_reset (void *engine)
1222 {
1223   engine_gpgsm_t gpgsm = engine;
1224
1225   /* IF we have an active connection we must send a reset because we
1226      need to reset the list of signers.  Note that RESET does not
1227      reset OPTION commands. */
1228   return (gpgsm->assuan_ctx
1229           ? gpgsm_assuan_simple_command (gpgsm, "RESET", NULL, NULL)
1230           : 0);
1231 }
1232 #endif
1233
1234
1235
1236 static gpgme_error_t
1237 gpgsm_decrypt (void *engine,
1238                gpgme_decrypt_flags_t flags,
1239                gpgme_data_t ciph, gpgme_data_t plain,
1240                int export_session_key, const char *override_session_key,
1241                int auto_key_retrieve)
1242 {
1243   engine_gpgsm_t gpgsm = engine;
1244   gpgme_error_t err;
1245
1246   (void)flags;
1247
1248   /* gpgsm is not capable of exporting session keys right now, so we
1249    * will ignore this if requested. */
1250   (void)export_session_key;
1251   (void)override_session_key;
1252
1253   /* --auto-key-retrieve is also not supported.  */
1254   (void)auto_key_retrieve;
1255
1256   if (!gpgsm)
1257     return gpg_error (GPG_ERR_INV_VALUE);
1258
1259   gpgsm->input_cb.data = ciph;
1260   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1261   if (err)
1262     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1263   gpgsm->output_cb.data = plain;
1264   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1265   if (err)
1266     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1267   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1268   gpgsm->inline_data = NULL;
1269
1270   err = start (engine, "DECRYPT");
1271   return err;
1272 }
1273
1274
1275 static gpgme_error_t
1276 gpgsm_delete (void *engine, gpgme_key_t key, unsigned int flags)
1277 {
1278   engine_gpgsm_t gpgsm = engine;
1279   gpgme_error_t err;
1280   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1281   char *linep = fpr;
1282   char *line;
1283   int length = 8;       /* "DELKEYS " */
1284
1285   (void)flags;
1286
1287   if (!fpr)
1288     return gpg_error (GPG_ERR_INV_VALUE);
1289
1290   while (*linep)
1291     {
1292       length++;
1293       if (*linep == '%' || *linep == ' ' || *linep == '+')
1294         length += 2;
1295       linep++;
1296     }
1297   length++;
1298
1299   line = malloc (length);
1300   if (!line)
1301     return gpg_error_from_syserror ();
1302
1303   strcpy (line, "DELKEYS ");
1304   linep = &line[8];
1305
1306   while (*fpr)
1307     {
1308       switch (*fpr)
1309         {
1310         case '%':
1311           *(linep++) = '%';
1312           *(linep++) = '2';
1313           *(linep++) = '5';
1314           break;
1315         case ' ':
1316           *(linep++) = '%';
1317           *(linep++) = '2';
1318           *(linep++) = '0';
1319           break;
1320         case '+':
1321           *(linep++) = '%';
1322           *(linep++) = '2';
1323           *(linep++) = 'B';
1324           break;
1325         default:
1326           *(linep++) = *fpr;
1327           break;
1328         }
1329       fpr++;
1330     }
1331   *linep = '\0';
1332
1333   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1334   gpgsm_clear_fd (gpgsm, INPUT_FD);
1335   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1336   gpgsm->inline_data = NULL;
1337
1338   err = start (gpgsm, line);
1339   free (line);
1340
1341   return err;
1342 }
1343
1344
1345 static gpgme_error_t
1346 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1347 {
1348   gpgme_error_t err = 0;
1349   char *line;
1350   int linelen;
1351   int invalid_recipients = 0;
1352   int i;
1353
1354   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1355   line = malloc (10 + 40 + 1);
1356   if (!line)
1357     return gpg_error_from_syserror ();
1358   strcpy (line, "RECIPIENT ");
1359   for (i =0; !err && recp[i]; i++)
1360     {
1361       char *fpr;
1362       int newlen;
1363
1364       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1365         {
1366           invalid_recipients++;
1367           continue;
1368         }
1369       fpr = recp[i]->subkeys->fpr;
1370
1371       newlen = 11 + strlen (fpr);
1372       if (linelen < newlen)
1373         {
1374           char *newline = realloc (line, newlen);
1375           if (! newline)
1376             {
1377               int saved_err = gpg_error_from_syserror ();
1378               free (line);
1379               return saved_err;
1380             }
1381           line = newline;
1382           linelen = newlen;
1383         }
1384       strcpy (&line[10], fpr);
1385
1386       err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
1387                                          gpgsm->status.fnc_value);
1388       /* FIXME: This requires more work.  */
1389       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1390         invalid_recipients++;
1391       else if (err)
1392         {
1393           free (line);
1394           return err;
1395         }
1396     }
1397   free (line);
1398   return gpg_error (invalid_recipients
1399                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1400 }
1401
1402
1403 /* Take recipients from the LF delimited STRING and send RECIPIENT
1404  * commands to gpgsm.  */
1405 static gpgme_error_t
1406 set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string)
1407 {
1408   gpg_error_t err = 0;
1409   char *line = NULL;
1410   int ignore = 0;
1411   int any = 0;
1412   const char *s;
1413   int n;
1414
1415   do
1416     {
1417       while (*string == ' ' || *string == '\t')
1418         string++;
1419       if (!*string)
1420         break;
1421
1422       s = strchr (string, '\n');
1423       if (s)
1424         n = s - string;
1425       else
1426         n = strlen (string);
1427       while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
1428         n--;
1429
1430       if (!ignore && n == 2 && !memcmp (string, "--", 2))
1431         ignore = 1;
1432       else if (!ignore && n > 2 && !memcmp (string, "--", 2))
1433         err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
1434       else if (n) /* Not empty - use it.  */
1435         {
1436           gpgrt_free (line);
1437           if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
1438             err = gpg_error_from_syserror ();
1439           else
1440             {
1441               err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
1442                                                  gpgsm->status.fnc_value);
1443               if (!err)
1444                 any = 1;
1445             }
1446         }
1447
1448       string += n + !!s;
1449     }
1450   while (!err);
1451
1452   if (!err && !any)
1453     err = gpg_error (GPG_ERR_MISSING_KEY);
1454   gpgrt_free (line);
1455   return err;
1456 }
1457
1458
1459 static gpgme_error_t
1460 gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
1461                gpgme_encrypt_flags_t flags,
1462                gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1463 {
1464   engine_gpgsm_t gpgsm = engine;
1465   gpgme_error_t err;
1466
1467   if (!gpgsm)
1468     return gpg_error (GPG_ERR_INV_VALUE);
1469   if (!recp && !recpstring) /* Symmetric only */
1470     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1471
1472   if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
1473     {
1474       err = gpgsm_assuan_simple_command (gpgsm,
1475                                          "OPTION no-encrypt-to", NULL, NULL);
1476       if (err)
1477         return err;
1478     }
1479
1480   gpgsm->input_cb.data = plain;
1481   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1482   if (err)
1483     return err;
1484   gpgsm->output_cb.data = ciph;
1485   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1486                       : map_data_enc (gpgsm->output_cb.data));
1487   if (err)
1488     return err;
1489   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1490   gpgsm->inline_data = NULL;
1491
1492   if (!recp && recpstring)
1493     err = set_recipients_from_string (gpgsm, recpstring);
1494   else
1495     err = set_recipients (gpgsm, recp);
1496
1497   if (!err)
1498     err = start (gpgsm, "ENCRYPT");
1499
1500   return err;
1501 }
1502
1503
1504 static gpgme_error_t
1505 gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
1506               gpgme_data_t keydata, int use_armor)
1507 {
1508   engine_gpgsm_t gpgsm = engine;
1509   gpgme_error_t err = 0;
1510   char *cmd;
1511
1512   if (!gpgsm)
1513     return gpg_error (GPG_ERR_INV_VALUE);
1514
1515   if (!pattern)
1516     pattern = "";
1517
1518   cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
1519   if (!cmd)
1520     return gpg_error_from_syserror ();
1521
1522   strcpy (cmd, "EXPORT ");
1523   if ((mode & GPGME_EXPORT_MODE_SECRET))
1524     {
1525       strcat (cmd, "--secret ");
1526       if ((mode & GPGME_EXPORT_MODE_RAW))
1527         strcat (cmd, "--raw ");
1528       else if ((mode & GPGME_EXPORT_MODE_PKCS12))
1529         strcat (cmd, "--pkcs12 ");
1530     }
1531   strcat (cmd, pattern);
1532
1533   gpgsm->output_cb.data = keydata;
1534   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1535                       : map_data_enc (gpgsm->output_cb.data));
1536   if (err)
1537     return err;
1538   gpgsm_clear_fd (gpgsm, INPUT_FD);
1539   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1540   gpgsm->inline_data = NULL;
1541
1542   err = start (gpgsm, cmd);
1543   free (cmd);
1544   return err;
1545 }
1546
1547
1548 static gpgme_error_t
1549 gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
1550                   gpgme_data_t keydata, int use_armor)
1551 {
1552   engine_gpgsm_t gpgsm = engine;
1553   gpgme_error_t err = 0;
1554   char *line;
1555   /* Length is "EXPORT " + "--secret " + "--pkcs12 " + p + '\0'.  */
1556   int length = 7 + 9 + 9 + 1;
1557   char *linep;
1558
1559   if (!gpgsm)
1560     return gpg_error (GPG_ERR_INV_VALUE);
1561
1562   if (pattern && *pattern)
1563     {
1564       const char **pat = pattern;
1565
1566       while (*pat)
1567         {
1568           const char *patlet = *pat;
1569
1570           while (*patlet)
1571             {
1572               length++;
1573               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1574                 length += 2;
1575               patlet++;
1576             }
1577           pat++;
1578           length++;
1579         }
1580     }
1581   line = malloc (length);
1582   if (!line)
1583     return gpg_error_from_syserror ();
1584
1585   strcpy (line, "EXPORT ");
1586   if ((mode & GPGME_EXPORT_MODE_SECRET))
1587     {
1588       strcat (line, "--secret ");
1589       if ((mode & GPGME_EXPORT_MODE_RAW))
1590         strcat (line, "--raw ");
1591       else if ((mode & GPGME_EXPORT_MODE_PKCS12))
1592         strcat (line, "--pkcs12 ");
1593     }
1594   linep = &line[strlen (line)];
1595
1596   if (pattern && *pattern)
1597     {
1598       while (*pattern)
1599         {
1600           const char *patlet = *pattern;
1601
1602           while (*patlet)
1603             {
1604               switch (*patlet)
1605                 {
1606                 case '%':
1607                   *(linep++) = '%';
1608                   *(linep++) = '2';
1609                   *(linep++) = '5';
1610                   break;
1611                 case ' ':
1612                   *(linep++) = '%';
1613                   *(linep++) = '2';
1614                   *(linep++) = '0';
1615                   break;
1616                 case '+':
1617                   *(linep++) = '%';
1618                   *(linep++) = '2';
1619                   *(linep++) = 'B';
1620                   break;
1621                 default:
1622                   *(linep++) = *patlet;
1623                   break;
1624                 }
1625               patlet++;
1626             }
1627           pattern++;
1628           if (*pattern)
1629             *linep++ = ' ';
1630         }
1631     }
1632   *linep = '\0';
1633
1634   gpgsm->output_cb.data = keydata;
1635   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1636                       : map_data_enc (gpgsm->output_cb.data));
1637   if (err)
1638     return err;
1639   gpgsm_clear_fd (gpgsm, INPUT_FD);
1640   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1641   gpgsm->inline_data = NULL;
1642
1643   err = start (gpgsm, line);
1644   free (line);
1645   return err;
1646 }
1647
1648
1649 static gpgme_error_t
1650 gpgsm_genkey (void *engine,
1651               const char *userid, const char *algo,
1652               unsigned long reserved, unsigned long expires,
1653               gpgme_key_t key, unsigned int flags,
1654               gpgme_data_t help_data, unsigned int extraflags,
1655               gpgme_data_t pubkey, gpgme_data_t seckey)
1656 {
1657   engine_gpgsm_t gpgsm = engine;
1658   gpgme_error_t err;
1659
1660   (void)reserved;
1661
1662   if (!gpgsm)
1663     return gpg_error (GPG_ERR_INV_VALUE);
1664
1665   if (help_data)
1666     {
1667       if (!pubkey || seckey)
1668         return gpg_error (GPG_ERR_INV_VALUE);
1669
1670       gpgsm->input_cb.data = help_data;
1671       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1672       if (err)
1673         return err;
1674       gpgsm->output_cb.data = pubkey;
1675       err = gpgsm_set_fd (gpgsm, OUTPUT_FD,
1676                           (extraflags & GENKEY_EXTRAFLAG_ARMOR)? "--armor"
1677                           : map_data_enc (gpgsm->output_cb.data));
1678       if (err)
1679         return err;
1680       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1681       gpgsm->inline_data = NULL;
1682
1683       err = start (gpgsm, "GENKEY");
1684       return err;
1685     }
1686
1687   (void)userid;
1688   (void)algo;
1689   (void)expires;
1690   (void)key;
1691   (void)flags;
1692
1693   /* The new interface has not yet been implemented,  */
1694   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1695 }
1696
1697
1698 static gpgme_error_t
1699 gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
1700 {
1701   engine_gpgsm_t gpgsm = engine;
1702   gpgme_error_t err;
1703   gpgme_data_encoding_t dataenc;
1704   int idx;
1705
1706   if (!gpgsm)
1707     return gpg_error (GPG_ERR_INV_VALUE);
1708
1709   if (keydata && keyarray)
1710     return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
1711
1712   dataenc = gpgme_data_get_encoding (keydata);
1713
1714   if (keyarray)
1715     {
1716       size_t buflen;
1717       char *buffer, *p;
1718
1719       /* Fist check whether the engine already features the
1720          --re-import option.  */
1721       err = gpgsm_assuan_simple_command
1722         (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
1723       if (err)
1724         return gpg_error (GPG_ERR_NOT_SUPPORTED);
1725
1726       /* Create an internal data object with a list of all
1727          fingerprints.  The data object and its memory (to avoid an
1728          extra copy by gpgme_data_new_from_mem) are stored in two
1729          variables which are released by the close_notify_handler.  */
1730       for (idx=0, buflen=0; keyarray[idx]; idx++)
1731         {
1732           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1733               && keyarray[idx]->subkeys
1734               && keyarray[idx]->subkeys->fpr
1735               && *keyarray[idx]->subkeys->fpr)
1736             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
1737         }
1738       /* Allocate a buffer with extra space for the trailing Nul
1739          introduced by the use of stpcpy.  */
1740       buffer = malloc (buflen+1);
1741       if (!buffer)
1742         return gpg_error_from_syserror ();
1743       for (idx=0, p = buffer; keyarray[idx]; idx++)
1744         {
1745           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1746               && keyarray[idx]->subkeys
1747               && keyarray[idx]->subkeys->fpr
1748               && *keyarray[idx]->subkeys->fpr)
1749             p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
1750         }
1751
1752       err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
1753                                      buffer, buflen, 0);
1754       if (err)
1755         {
1756           free (buffer);
1757           return err;
1758         }
1759       gpgsm->input_helper_memory = buffer;
1760
1761       gpgsm->input_cb.data = gpgsm->input_helper_data;
1762       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1763       if (err)
1764         {
1765           gpgme_data_release (gpgsm->input_helper_data);
1766           gpgsm->input_helper_data = NULL;
1767           free (gpgsm->input_helper_memory);
1768           gpgsm->input_helper_memory = NULL;
1769           return err;
1770         }
1771       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1772       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1773       gpgsm->inline_data = NULL;
1774
1775       return start (gpgsm, "IMPORT --re-import");
1776     }
1777   else if (dataenc == GPGME_DATA_ENCODING_URL
1778            || dataenc == GPGME_DATA_ENCODING_URL0
1779            || dataenc == GPGME_DATA_ENCODING_URLESC)
1780     {
1781       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1782     }
1783   else
1784     {
1785       gpgsm->input_cb.data = keydata;
1786       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1787       if (err)
1788         return err;
1789       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1790       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1791       gpgsm->inline_data = NULL;
1792
1793       return start (gpgsm, "IMPORT");
1794     }
1795 }
1796
1797
1798 static gpgme_error_t
1799 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1800                gpgme_keylist_mode_t mode, int engine_flags)
1801 {
1802   engine_gpgsm_t gpgsm = engine;
1803   char *line;
1804   gpgme_error_t err;
1805   int list_mode = 0;
1806
1807   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1808     list_mode |= 1;
1809   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1810     list_mode |= 2;
1811
1812   if (!pattern)
1813     pattern = "";
1814
1815   /* Hack to make sure that the agent is started.  Only if the agent
1816      has been started an application may connect to the agent via
1817      GPGME_PROTOCOL_ASSUAN - for example to look for smartcards.  We
1818      do this only if a secret key listing has been requested.  In
1819      general this is not needed because a secret key listing starts
1820      the agent.  However on a fresh installation no public keys are
1821      available and thus there is no need for gpgsm to ask the agent
1822      whether a secret key exists for the public key.  */
1823   if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
1824     gpgsm_assuan_simple_command (gpgsm, "GETINFO agent-check", NULL, NULL);
1825
1826   /* Always send list-mode option because RESET does not reset it.  */
1827   if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1828     return gpg_error_from_syserror ();
1829   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
1830   gpgrt_free (line);
1831   if (err)
1832     return err;
1833
1834
1835   /* Always send key validation because RESET does not reset it.  */
1836
1837   /* Use the validation mode if requested.  We don't check for an error
1838      yet because this is a pretty fresh gpgsm features. */
1839   gpgsm_assuan_simple_command (gpgsm,
1840                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1841                                "OPTION with-validation=1":
1842                                "OPTION with-validation=0" ,
1843                                NULL, NULL);
1844   /* Include the ephemeral keys if requested.  We don't check for an error
1845      yet because this is a pretty fresh gpgsm features. */
1846   gpgsm_assuan_simple_command (gpgsm,
1847                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
1848                                "OPTION with-ephemeral-keys=1":
1849                                "OPTION with-ephemeral-keys=0" ,
1850                                NULL, NULL);
1851   gpgsm_assuan_simple_command (gpgsm,
1852                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1853                                "OPTION with-secret=1":
1854                                "OPTION with-secret=0" ,
1855                                NULL, NULL);
1856   gpgsm_assuan_simple_command (gpgsm,
1857                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1858                                "OPTION offline=1":
1859                                "OPTION offline=0" ,
1860                                NULL, NULL);
1861
1862
1863   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1864   line = malloc (15 + strlen (pattern) + 1);
1865   if (!line)
1866     return gpg_error_from_syserror ();
1867   if (secret_only)
1868     {
1869       strcpy (line, "LISTSECRETKEYS ");
1870       strcpy (&line[15], pattern);
1871     }
1872   else
1873     {
1874       strcpy (line, "LISTKEYS ");
1875       strcpy (&line[9], pattern);
1876     }
1877
1878   gpgsm_clear_fd (gpgsm, INPUT_FD);
1879   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1880   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1881   gpgsm->inline_data = NULL;
1882
1883   err = start (gpgsm, line);
1884   free (line);
1885   return err;
1886 }
1887
1888
1889 static gpgme_error_t
1890 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1891                    int reserved, gpgme_keylist_mode_t mode, int engine_flags)
1892 {
1893   engine_gpgsm_t gpgsm = engine;
1894   char *line;
1895   gpgme_error_t err;
1896   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1897   int length = 15 + 1;
1898   char *linep;
1899   int any_pattern = 0;
1900   int list_mode = 0;
1901
1902   if (reserved)
1903     return gpg_error (GPG_ERR_INV_VALUE);
1904
1905   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1906     list_mode |= 1;
1907   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1908     list_mode |= 2;
1909
1910   /* Always send list-mode option because RESET does not reset it.  */
1911   if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1912     return gpg_error_from_syserror ();
1913   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
1914   gpgrt_free (line);
1915   if (err)
1916     return err;
1917
1918   /* Always send key validation because RESET does not reset it.  */
1919   /* Use the validation mode if required.  We don't check for an error
1920      yet because this is a pretty fresh gpgsm features. */
1921   gpgsm_assuan_simple_command (gpgsm,
1922                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1923                                "OPTION with-validation=1":
1924                                "OPTION with-validation=0" ,
1925                                NULL, NULL);
1926   gpgsm_assuan_simple_command (gpgsm,
1927                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1928                                "OPTION with-secret=1":
1929                                "OPTION with-secret=0" ,
1930                                NULL, NULL);
1931   gpgsm_assuan_simple_command (gpgsm,
1932                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1933                                "OPTION offline=1":
1934                                "OPTION offline=0" ,
1935                                NULL, NULL);
1936
1937   if (pattern && *pattern)
1938     {
1939       const char **pat = pattern;
1940
1941       while (*pat)
1942         {
1943           const char *patlet = *pat;
1944
1945           while (*patlet)
1946             {
1947               length++;
1948               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1949                 length += 2;
1950               patlet++;
1951             }
1952           pat++;
1953           length++;
1954         }
1955     }
1956   line = malloc (length);
1957   if (!line)
1958     return gpg_error_from_syserror ();
1959   if (secret_only)
1960     {
1961       strcpy (line, "LISTSECRETKEYS ");
1962       linep = &line[15];
1963     }
1964   else
1965     {
1966       strcpy (line, "LISTKEYS ");
1967       linep = &line[9];
1968     }
1969
1970   if (pattern && *pattern)
1971     {
1972       while (*pattern)
1973         {
1974           const char *patlet = *pattern;
1975
1976           while (*patlet)
1977             {
1978               switch (*patlet)
1979                 {
1980                 case '%':
1981                   *(linep++) = '%';
1982                   *(linep++) = '2';
1983                   *(linep++) = '5';
1984                   break;
1985                 case ' ':
1986                   *(linep++) = '%';
1987                   *(linep++) = '2';
1988                   *(linep++) = '0';
1989                   break;
1990                 case '+':
1991                   *(linep++) = '%';
1992                   *(linep++) = '2';
1993                   *(linep++) = 'B';
1994                   break;
1995                 default:
1996                   *(linep++) = *patlet;
1997                   break;
1998                 }
1999               patlet++;
2000             }
2001           any_pattern = 1;
2002           *linep++ = ' ';
2003           pattern++;
2004         }
2005     }
2006   if (any_pattern)
2007     linep--;
2008   *linep = '\0';
2009
2010   gpgsm_clear_fd (gpgsm, INPUT_FD);
2011   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2012   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2013   gpgsm->inline_data = NULL;
2014
2015   err = start (gpgsm, line);
2016   free (line);
2017   return err;
2018 }
2019
2020
2021 static gpgme_error_t
2022 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
2023             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
2024             int include_certs, gpgme_ctx_t ctx /* FIXME */)
2025 {
2026   engine_gpgsm_t gpgsm = engine;
2027   gpgme_error_t err;
2028   char *assuan_cmd;
2029   int i;
2030   gpgme_key_t key;
2031
2032   (void)use_textmode;
2033
2034   if (!gpgsm)
2035     return gpg_error (GPG_ERR_INV_VALUE);
2036
2037   /* FIXME: This does not work as RESET does not reset it so we can't
2038      revert back to default.  */
2039   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
2040     {
2041       /* FIXME: Make sure that if we run multiple operations, that we
2042          can reset any previously set value in case the default is
2043          requested.  */
2044
2045       if (gpgrt_asprintf (&assuan_cmd,
2046                           "OPTION include-certs %i", include_certs) < 0)
2047         return gpg_error_from_syserror ();
2048       err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
2049       gpgrt_free (assuan_cmd);
2050       if (err)
2051         return err;
2052     }
2053
2054   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
2055     {
2056       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
2057       if (s && strlen (s) < 80)
2058         {
2059           char buf[100];
2060
2061           strcpy (stpcpy (buf, "SIGNER "), s);
2062           err = gpgsm_assuan_simple_command (gpgsm, buf,
2063                                              gpgsm->status.fnc,
2064                                              gpgsm->status.fnc_value);
2065         }
2066       else
2067         err = gpg_error (GPG_ERR_INV_VALUE);
2068       gpgme_key_unref (key);
2069       if (err)
2070         return err;
2071     }
2072
2073   gpgsm->input_cb.data = in;
2074   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
2075   if (err)
2076     return err;
2077   gpgsm->output_cb.data = out;
2078   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
2079                       : map_data_enc (gpgsm->output_cb.data));
2080   if (err)
2081     return err;
2082   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2083   gpgsm->inline_data = NULL;
2084
2085   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
2086                ? "SIGN --detached" : "SIGN");
2087   return err;
2088 }
2089
2090
2091 static gpgme_error_t
2092 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
2093               gpgme_data_t plaintext, gpgme_ctx_t ctx)
2094 {
2095   engine_gpgsm_t gpgsm = engine;
2096   gpgme_error_t err;
2097
2098   (void)ctx;
2099
2100   if (!gpgsm)
2101     return gpg_error (GPG_ERR_INV_VALUE);
2102
2103   gpgsm->input_cb.data = sig;
2104   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
2105   if (err)
2106     return err;
2107   if (!signed_text)
2108     {
2109       /* Normal or cleartext signature.  */
2110       if (plaintext)
2111         {
2112           gpgsm->output_cb.data = plaintext;
2113           err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
2114         }
2115       else
2116         {
2117           /* No output requested.  */
2118           gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2119         }
2120       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2121     }
2122   else
2123     {
2124       /* Detached signature.  */
2125       gpgsm->message_cb.data = signed_text;
2126       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
2127       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2128     }
2129   gpgsm->inline_data = NULL;
2130
2131   if (!err)
2132     err = start (gpgsm, "VERIFY");
2133
2134   return err;
2135 }
2136
2137
2138 /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
2139    object.  */
2140 static gpgme_error_t
2141 gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
2142 {
2143   engine_gpgsm_t gpgsm = engine;
2144   gpgme_error_t err = 0;
2145
2146
2147   if (!gpgsm || !output)
2148     return gpg_error (GPG_ERR_INV_VALUE);
2149
2150   if ((flags & GPGME_AUDITLOG_DIAG) && (flags & GPGME_AUDITLOG_HTML))
2151     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
2152
2153   if ((flags & GPGME_AUDITLOG_DIAG))
2154     {
2155       char buf[BUFFER_SIZE];
2156       int nread;
2157       int any_written = 0;
2158       gpgme_data_rewind (gpgsm->diagnostics);
2159
2160       while ((nread = gpgme_data_read (gpgsm->diagnostics,
2161                                        buf, BUFFER_SIZE)) > 0)
2162         {
2163           any_written = 1;
2164           if (gpgme_data_write (output, buf, nread) == -1)
2165             return gpg_error_from_syserror ();
2166         }
2167       if (!any_written)
2168         return gpg_error (GPG_ERR_NO_DATA);
2169
2170       if (nread == -1)
2171         return gpg_error_from_syserror ();
2172
2173       gpgme_data_rewind (output);
2174       return 0;
2175     }
2176
2177   if (!gpgsm->assuan_ctx)
2178     return gpg_error (GPG_ERR_INV_VALUE);
2179
2180 #if USE_DESCRIPTOR_PASSING
2181   gpgsm->output_cb.data = output;
2182   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
2183   if (err)
2184     return err;
2185
2186   gpgsm_clear_fd (gpgsm, INPUT_FD);
2187   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2188   gpgsm->inline_data = NULL;
2189 # define CMD  "GETAUDITLOG"
2190 #else
2191   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2192   gpgsm_clear_fd (gpgsm, INPUT_FD);
2193   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2194   gpgsm->inline_data = output;
2195 # define CMD  "GETAUDITLOG --data"
2196 #endif
2197
2198   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
2199
2200   return err;
2201 }
2202
2203
2204 /* This sets a status callback for monitoring status lines before they
2205  * are passed to a caller set handler.  */
2206 static void
2207 gpgsm_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
2208 {
2209   engine_gpgsm_t gpgsm = engine;
2210
2211   gpgsm->status.mon_cb = cb;
2212   gpgsm->status.mon_cb_value = cb_value;
2213 }
2214
2215
2216 static void
2217 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
2218                           void *fnc_value)
2219 {
2220   engine_gpgsm_t gpgsm = engine;
2221
2222   gpgsm->status.fnc = fnc;
2223   gpgsm->status.fnc_value = fnc_value;
2224 }
2225
2226
2227 static gpgme_error_t
2228 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
2229                               void *fnc_value)
2230 {
2231   engine_gpgsm_t gpgsm = engine;
2232
2233   gpgsm->colon.fnc = fnc;
2234   gpgsm->colon.fnc_value = fnc_value;
2235   gpgsm->colon.any = 0;
2236   return 0;
2237 }
2238
2239
2240 static void
2241 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
2242 {
2243   engine_gpgsm_t gpgsm = engine;
2244   gpgsm->io_cbs = *io_cbs;
2245 }
2246
2247
2248 static void
2249 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
2250 {
2251   engine_gpgsm_t gpgsm = engine;
2252
2253   TRACE (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
2254           "event %p, type %d, type_data %p",
2255           gpgsm->io_cbs.event, type, type_data);
2256   if (gpgsm->io_cbs.event)
2257     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
2258 }
2259
2260
2261 static gpgme_error_t
2262 gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
2263 {
2264   engine_gpgsm_t gpgsm = engine;
2265   gpgme_error_t err;
2266   char *line;
2267
2268   (void)flags;
2269
2270   if (!key || !key->subkeys || !key->subkeys->fpr)
2271     return gpg_error (GPG_ERR_INV_CERT_OBJ);
2272
2273   if (gpgrt_asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
2274     return gpg_error_from_syserror ();
2275
2276   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2277   gpgsm_clear_fd (gpgsm, INPUT_FD);
2278   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2279   gpgsm->inline_data = NULL;
2280
2281   err = start (gpgsm, line);
2282   gpgrt_free (line);
2283
2284   return err;
2285 }
2286
2287
2288
2289 struct engine_ops _gpgme_engine_ops_gpgsm =
2290   {
2291     /* Static functions.  */
2292     _gpgme_get_default_gpgsm_name,
2293     NULL,
2294     gpgsm_get_version,
2295     gpgsm_get_req_version,
2296     gpgsm_new,
2297
2298     /* Member functions.  */
2299     gpgsm_release,
2300 #if USE_DESCRIPTOR_PASSING
2301     gpgsm_reset,
2302 #else
2303     NULL,                       /* reset */
2304 #endif
2305     gpgsm_set_status_cb,
2306     gpgsm_set_status_handler,
2307     NULL,               /* set_command_handler */
2308     gpgsm_set_colon_line_handler,
2309     gpgsm_set_locale,
2310     NULL,               /* set_protocol */
2311     gpgsm_set_engine_flags,
2312     gpgsm_decrypt,
2313     gpgsm_delete,       /* decrypt_verify */
2314     NULL,               /* edit */
2315     gpgsm_encrypt,
2316     NULL,               /* encrypt_sign */
2317     gpgsm_export,
2318     gpgsm_export_ext,
2319     gpgsm_genkey,
2320     gpgsm_import,
2321     gpgsm_keylist,
2322     gpgsm_keylist_ext,
2323     NULL,               /* keylist_data */
2324     NULL,               /* keysign */
2325     NULL,               /* tofu_policy */
2326     gpgsm_sign,
2327     gpgsm_verify,
2328     gpgsm_getauditlog,
2329     NULL,               /* opassuan_transact */
2330     NULL,               /* conf_load */
2331     NULL,               /* conf_save */
2332     NULL,               /* conf_dir */
2333     NULL,               /* query_swdb */
2334     gpgsm_set_io_cbs,
2335     gpgsm_io_event,
2336     gpgsm_cancel,
2337     NULL,               /* cancel_op */
2338     gpgsm_passwd,
2339     NULL,               /* set_pinentry_mode */
2340     NULL                /* opspawn */
2341   };