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