Fix last change.
[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
42 #include "assuan.h"
43 #include "status-table.h"
44 #include "debug.h"
45
46 #include "engine-backend.h"
47
48 \f
49 typedef struct
50 {
51   int fd;       /* FD we talk about.  */
52   int server_fd;/* Server FD for this connection.  */
53   int dir;      /* Inbound/Outbound, maybe given implicit?  */
54   void *data;   /* Handler-specific data.  */
55   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
56   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
57                              need this because _gpgme_io_fd2str can't
58                              be used on a closed descriptor.  */
59 } iocb_data_t;
60
61
62 struct engine_gpgsm
63 {
64   assuan_context_t assuan_ctx;
65
66   int lc_ctype_set;
67   int lc_messages_set;
68
69   iocb_data_t status_cb;
70
71   /* Input, output etc are from the servers perspective.  */
72   iocb_data_t input_cb;
73   gpgme_data_t input_helper_data;  /* Input helper data object.  */
74   void *input_helper_memory;       /* Input helper memory block.  */
75
76   iocb_data_t output_cb;
77
78   iocb_data_t message_cb;
79
80   struct
81   {
82     engine_status_handler_t fnc;
83     void *fnc_value;
84   } status;
85
86   struct
87   {
88     engine_colon_line_handler_t fnc;
89     void *fnc_value;
90     struct
91     {
92       char *line;
93       int linesize;
94       int linelen;
95     } attic;
96     int any; /* any data line seen */
97   } colon; 
98
99   gpgme_data_t inline_data;  /* Used to collect D lines.  */
100
101   struct gpgme_io_cbs io_cbs;
102 };
103
104 typedef struct engine_gpgsm *engine_gpgsm_t;
105
106
107 static void gpgsm_io_event (void *engine, 
108                             gpgme_event_io_t type, void *type_data);
109
110
111 \f
112 static char *
113 gpgsm_get_version (const char *file_name)
114 {
115   return _gpgme_get_program_version (file_name ? file_name
116                                      : _gpgme_get_gpgsm_path ());
117 }
118
119
120 static const char *
121 gpgsm_get_req_version (void)
122 {
123   return NEED_GPGSM_VERSION;
124 }
125
126 \f
127 static void
128 close_notify_handler (int fd, void *opaque)
129 {
130   engine_gpgsm_t gpgsm = opaque;
131
132   assert (fd != -1);
133   if (gpgsm->status_cb.fd == fd)
134     {
135       if (gpgsm->status_cb.tag)
136         (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
137       gpgsm->status_cb.fd = -1;
138       gpgsm->status_cb.tag = NULL;
139     }
140   else if (gpgsm->input_cb.fd == fd)
141     {
142       if (gpgsm->input_cb.tag)
143         (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
144       gpgsm->input_cb.fd = -1;
145       gpgsm->input_cb.tag = NULL;
146       if (gpgsm->input_helper_data)
147         {
148           gpgme_data_release (gpgsm->input_helper_data);
149           gpgsm->input_helper_data = NULL;
150         }
151       if (gpgsm->input_helper_memory)
152         {
153           free (gpgsm->input_helper_memory);
154           gpgsm->input_helper_memory = NULL;
155         }
156     }
157   else if (gpgsm->output_cb.fd == fd)
158     {
159       if (gpgsm->output_cb.tag)
160         (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
161       gpgsm->output_cb.fd = -1;
162       gpgsm->output_cb.tag = NULL;
163     }
164   else if (gpgsm->message_cb.fd == fd)
165     {
166       if (gpgsm->message_cb.tag)
167         (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
168       gpgsm->message_cb.fd = -1;
169       gpgsm->message_cb.tag = NULL;
170     }
171 }
172
173
174 /* This is the default inquiry callback.  We use it to handle the
175    Pinentry notifications.  */
176 static gpgme_error_t
177 default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
178 {
179   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
180     {
181       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
182     }
183
184   return 0;
185 }
186
187
188 static gpgme_error_t
189 gpgsm_cancel (void *engine)
190 {
191   engine_gpgsm_t gpgsm = engine;
192
193   if (!gpgsm)
194     return gpg_error (GPG_ERR_INV_VALUE);
195
196   if (gpgsm->status_cb.fd != -1)
197     _gpgme_io_close (gpgsm->status_cb.fd);
198   if (gpgsm->input_cb.fd != -1)
199     _gpgme_io_close (gpgsm->input_cb.fd);
200   if (gpgsm->output_cb.fd != -1)
201     _gpgme_io_close (gpgsm->output_cb.fd);
202   if (gpgsm->message_cb.fd != -1)
203     _gpgme_io_close (gpgsm->message_cb.fd);
204
205   if (gpgsm->assuan_ctx)
206     {
207       assuan_release (gpgsm->assuan_ctx);
208       gpgsm->assuan_ctx = NULL;
209     }
210
211   return 0;
212 }
213
214
215 static void
216 gpgsm_release (void *engine)
217 {
218   engine_gpgsm_t gpgsm = engine;
219
220   if (!gpgsm)
221     return;
222
223   gpgsm_cancel (engine);
224
225   free (gpgsm->colon.attic.line);
226   free (gpgsm);
227 }
228
229
230 static gpgme_error_t
231 gpgsm_new (void **engine, const char *file_name, const char *home_dir)
232 {
233   gpgme_error_t err = 0;
234   engine_gpgsm_t gpgsm;
235   const char *argv[5];
236   int argc;
237 #if !USE_DESCRIPTOR_PASSING
238   int fds[2];
239   int child_fds[4];
240 #endif
241   char *dft_display = NULL;
242   char dft_ttyname[64];
243   char *dft_ttytype = NULL;
244   char *optstr;
245
246   gpgsm = calloc (1, sizeof *gpgsm);
247   if (!gpgsm)
248     return gpg_error_from_errno (errno);
249
250   gpgsm->status_cb.fd = -1;
251   gpgsm->status_cb.dir = 1;
252   gpgsm->status_cb.tag = 0;
253   gpgsm->status_cb.data = gpgsm;
254
255   gpgsm->input_cb.fd = -1;
256   gpgsm->input_cb.dir = 0;
257   gpgsm->input_cb.tag = 0;
258   gpgsm->input_cb.server_fd = -1;
259   *gpgsm->input_cb.server_fd_str = 0;
260   gpgsm->output_cb.fd = -1;
261   gpgsm->output_cb.dir = 1;
262   gpgsm->output_cb.tag = 0;
263   gpgsm->output_cb.server_fd = -1;
264   *gpgsm->output_cb.server_fd_str = 0;
265   gpgsm->message_cb.fd = -1;
266   gpgsm->message_cb.dir = 0;
267   gpgsm->message_cb.tag = 0;
268   gpgsm->message_cb.server_fd = -1;
269   *gpgsm->message_cb.server_fd_str = 0;
270
271   gpgsm->status.fnc = 0;
272   gpgsm->colon.fnc = 0;
273   gpgsm->colon.attic.line = 0;
274   gpgsm->colon.attic.linesize = 0;
275   gpgsm->colon.attic.linelen = 0;
276   gpgsm->colon.any = 0;
277
278   gpgsm->inline_data = NULL;
279
280   gpgsm->io_cbs.add = NULL;
281   gpgsm->io_cbs.add_priv = NULL;
282   gpgsm->io_cbs.remove = NULL;
283   gpgsm->io_cbs.event = NULL;
284   gpgsm->io_cbs.event_priv = NULL;
285
286 #if !USE_DESCRIPTOR_PASSING
287   if (_gpgme_io_pipe (fds, 0) < 0)
288     {
289       err = gpg_error_from_errno (errno);
290       goto leave;
291     }
292   gpgsm->input_cb.fd = fds[1];
293   gpgsm->input_cb.server_fd = fds[0];
294
295   if (_gpgme_io_pipe (fds, 1) < 0)
296     {
297       err = gpg_error_from_errno (errno);
298       goto leave;
299     }
300   gpgsm->output_cb.fd = fds[0];
301   gpgsm->output_cb.server_fd = fds[1];
302
303   if (_gpgme_io_pipe (fds, 0) < 0)
304     {
305       err = gpg_error_from_errno (errno);
306       goto leave;
307     }
308   gpgsm->message_cb.fd = fds[1];
309   gpgsm->message_cb.server_fd = fds[0];
310
311   child_fds[0] = gpgsm->input_cb.server_fd;
312   child_fds[1] = gpgsm->output_cb.server_fd;
313   child_fds[2] = gpgsm->message_cb.server_fd;
314   child_fds[3] = -1;
315 #endif
316
317   argc = 0;
318   argv[argc++] = "gpgsm";
319   if (home_dir)
320     {
321       argv[argc++] = "--homedir";
322       argv[argc++] = home_dir;
323     }
324   argv[argc++] = "--server";
325   argv[argc++] = NULL;
326
327   err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
328                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
329                         NULL);
330   if (err)
331     goto leave;
332   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
333
334 #if USE_DESCRIPTOR_PASSING
335   err = assuan_pipe_connect
336     (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
337      argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
338 #else
339   err = assuan_pipe_connect
340     (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
341      argv, child_fds, NULL, NULL, 0);
342
343   /* On Windows, handles are inserted in the spawned process with
344      DuplicateHandle, and child_fds contains the server-local names
345      for the inserted handles when assuan_pipe_connect returns.  */
346   if (!err)
347     {
348       /* Note: We don't use _gpgme_io_fd2str here.  On W32 the
349          returned handles are real W32 system handles, not whatever
350          GPGME uses internally (which may be a system handle, a C
351          library handle or a GLib/Qt channel.  Confusing, yes, but
352          remember these are server-local names, so they are not part
353          of GPGME at all.  */
354       snprintf (gpgsm->input_cb.server_fd_str,
355                 sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
356       snprintf (gpgsm->output_cb.server_fd_str,
357                 sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
358       snprintf (gpgsm->message_cb.server_fd_str,
359                 sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
360     }
361 #endif
362   if (err)
363     goto leave;
364
365   err = _gpgme_getenv ("DISPLAY", &dft_display);
366   if (err)
367     goto leave;
368   if (dft_display)
369     {
370       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
371         {
372           free (dft_display);
373           err = gpg_error_from_errno (errno);
374           goto leave;
375         }
376       free (dft_display);
377
378       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
379                              NULL, NULL, NULL);
380       free (optstr);
381       if (err)
382         goto leave;
383     }
384
385   if (isatty (1))
386     {
387       int rc;
388
389       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
390       if (rc)
391         {
392           err = gpg_error_from_errno (rc);
393           goto leave;
394         }
395       else
396         {
397           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
398             {
399               err = gpg_error_from_errno (errno);
400               goto leave;
401             }
402           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
403                                  NULL, NULL, NULL);
404           free (optstr);
405           if (err)
406             goto leave;
407
408           err = _gpgme_getenv ("TERM", &dft_ttytype);
409           if (err)
410             goto leave;
411           if (dft_ttytype)
412             {
413               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
414                 {
415                   free (dft_ttytype);
416                   err = gpg_error_from_errno (errno);
417                   goto leave;
418                 }
419               free (dft_ttytype);
420
421               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
422                                      NULL, NULL, NULL, NULL);
423               free (optstr);
424               if (err)
425                 goto leave;
426             }
427         }
428     }
429
430   /* Ask gpgsm to enable the audit log support.  */
431   if (!err)
432     {
433       err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
434                              NULL, NULL, NULL, NULL, NULL, NULL);
435       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
436         err = 0; /* This is an optional feature of gpgsm.  */
437     }
438
439
440 #ifdef HAVE_W32_SYSTEM
441   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
442      gpgsm to tell us when it needs it.  */
443   if (!err)
444     {
445       err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
446                              NULL, NULL, NULL, NULL, NULL, NULL);
447       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
448         err = 0; /* This is a new feature of gpgsm.  */
449     }
450 #endif /*HAVE_W32_SYSTEM*/
451
452 #if !USE_DESCRIPTOR_PASSING
453   if (!err
454       && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
455                                       close_notify_handler, gpgsm)
456           || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
457                                          close_notify_handler, gpgsm)
458           || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
459                                          close_notify_handler, gpgsm)))
460     {
461       err = gpg_error (GPG_ERR_GENERAL);
462       goto leave;
463     }
464 #endif
465
466  leave:
467   /* Close the server ends of the pipes (because of this, we must use
468      the stored server_fd_str in the function start).  Our ends are
469      closed in gpgsm_release().  */
470 #if !USE_DESCRIPTOR_PASSING
471   if (gpgsm->input_cb.server_fd != -1)
472     _gpgme_io_close (gpgsm->input_cb.server_fd);
473   if (gpgsm->output_cb.server_fd != -1)
474     _gpgme_io_close (gpgsm->output_cb.server_fd);
475   if (gpgsm->message_cb.server_fd != -1)
476     _gpgme_io_close (gpgsm->message_cb.server_fd);
477 #endif
478
479   if (err)
480     gpgsm_release (gpgsm);
481   else
482     *engine = gpgsm;
483
484   return err;
485 }
486
487
488 static gpgme_error_t
489 gpgsm_set_locale (void *engine, int category, const char *value)
490 {
491   engine_gpgsm_t gpgsm = engine;
492   gpgme_error_t err;
493   char *optstr;
494   char *catstr;
495
496   /* FIXME: If value is NULL, we need to reset the option to default.
497      But we can't do this.  So we error out here.  GPGSM needs support
498      for this.  */
499   if (category == LC_CTYPE)
500     {
501       catstr = "lc-ctype";
502       if (!value && gpgsm->lc_ctype_set)
503         return gpg_error (GPG_ERR_INV_VALUE);
504       if (value)
505         gpgsm->lc_ctype_set = 1;
506     }
507 #ifdef LC_MESSAGES
508   else if (category == LC_MESSAGES)
509     {
510       catstr = "lc-messages";
511       if (!value && gpgsm->lc_messages_set)
512         return gpg_error (GPG_ERR_INV_VALUE);
513       if (value)
514         gpgsm->lc_messages_set = 1;
515     }
516 #endif /* LC_MESSAGES */
517   else
518     return gpg_error (GPG_ERR_INV_VALUE);
519
520   /* FIXME: Reset value to default.  */
521   if (!value) 
522     return 0;
523
524   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
525     err = gpg_error_from_errno (errno);
526   else
527     {
528       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
529                              NULL, NULL, NULL, NULL);
530       free (optstr);
531     }
532
533   return err;
534 }
535
536
537 /* Forward declaration.  */
538 static gpgme_status_code_t parse_status (const char *name);
539
540 static gpgme_error_t
541 gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
542                              engine_status_handler_t status_fnc,
543                              void *status_fnc_value)
544 {
545   gpg_error_t err;
546   char *line;
547   size_t linelen;
548
549   err = assuan_write_line (ctx, cmd);
550   if (err)
551     return err;
552
553   do
554     {
555       err = assuan_read_line (ctx, &line, &linelen);
556       if (err)
557         return err;
558
559       if (*line == '#' || !linelen)
560         continue;
561
562       if (linelen >= 2
563           && line[0] == 'O' && line[1] == 'K'
564           && (line[2] == '\0' || line[2] == ' '))
565         return 0;
566       else if (linelen >= 4
567           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
568           && line[3] == ' ')
569         err = atoi (&line[4]);
570       else if (linelen >= 2
571                && line[0] == 'S' && line[1] == ' ')
572         {
573           char *rest;
574           gpgme_status_code_t r;
575
576           rest = strchr (line + 2, ' ');
577           if (!rest)
578             rest = line + linelen; /* set to an empty string */
579           else
580             *(rest++) = 0;
581
582           r = parse_status (line + 2);
583
584           if (r >= 0 && status_fnc)
585             err = status_fnc (status_fnc_value, r, rest);
586           else
587             err = gpg_error (GPG_ERR_GENERAL);
588         }
589       else
590         err = gpg_error (GPG_ERR_GENERAL);
591     }
592   while (!err);
593
594   return err;
595 }
596
597
598 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
599
600 static void
601 gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
602 {
603 #if !USE_DESCRIPTOR_PASSING
604   switch (fd_type)
605     {
606     case INPUT_FD:
607       _gpgme_io_close (gpgsm->input_cb.fd);
608       break;
609     case OUTPUT_FD:
610       _gpgme_io_close (gpgsm->output_cb.fd);
611       break;
612     case MESSAGE_FD:
613       _gpgme_io_close (gpgsm->message_cb.fd);
614       break;
615     }
616 #endif
617 }
618
619 #define COMMANDLINELEN 40
620 static gpgme_error_t
621 gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
622 {
623   gpg_error_t err = 0;
624   char line[COMMANDLINELEN];
625   char *which;
626   iocb_data_t *iocb_data;
627   int dir;
628
629   switch (fd_type)
630     {
631     case INPUT_FD:
632       which = "INPUT";
633       iocb_data = &gpgsm->input_cb;
634       break;
635
636     case OUTPUT_FD:
637       which = "OUTPUT";
638       iocb_data = &gpgsm->output_cb;
639       break;
640
641     case MESSAGE_FD:
642       which = "MESSAGE";
643       iocb_data = &gpgsm->message_cb;
644       break;
645
646     default:
647       return gpg_error (GPG_ERR_INV_VALUE);
648     }
649
650   dir = iocb_data->dir;
651
652 #if USE_DESCRIPTOR_PASSING
653   /* We try to short-cut the communication by giving GPGSM direct
654      access to the file descriptor, rather than using a pipe.  */
655   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
656   if (iocb_data->server_fd < 0)
657     {
658       int fds[2];
659
660       if (_gpgme_io_pipe (fds, 0) < 0)
661         return gpg_error_from_errno (errno);
662
663       iocb_data->fd = dir ? fds[0] : fds[1];
664       iocb_data->server_fd = dir ? fds[1] : fds[0];
665
666       if (_gpgme_io_set_close_notify (iocb_data->fd,
667                                       close_notify_handler, gpgsm))
668         {
669           err = gpg_error (GPG_ERR_GENERAL);
670           goto leave_set_fd;
671         }
672     }
673
674   err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
675   if (err)
676     goto leave_set_fd;
677
678   _gpgme_io_close (iocb_data->server_fd);
679   iocb_data->server_fd = -1;
680
681   if (opt)
682     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
683   else
684     snprintf (line, COMMANDLINELEN, "%s FD", which);
685 #else
686   if (opt)
687     snprintf (line, COMMANDLINELEN, "%s FD=%s %s", 
688               which, iocb_data->server_fd_str, opt);
689   else
690     snprintf (line, COMMANDLINELEN, "%s FD=%s", 
691               which, iocb_data->server_fd_str);
692 #endif
693
694   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
695
696 #if USE_DESCRIPTOR_PASSING
697  leave_set_fd:
698   if (err)
699     {
700       _gpgme_io_close (iocb_data->fd);
701       iocb_data->fd = -1;
702       if (iocb_data->server_fd != -1)
703         {
704           _gpgme_io_close (iocb_data->server_fd);
705           iocb_data->server_fd = -1;
706         }
707     }
708 #endif
709
710   return err;
711 }
712
713
714 static const char *
715 map_data_enc (gpgme_data_t d)
716 {
717   switch (gpgme_data_get_encoding (d))
718     {
719     case GPGME_DATA_ENCODING_NONE:
720       break;
721     case GPGME_DATA_ENCODING_BINARY:
722       return "--binary";
723     case GPGME_DATA_ENCODING_BASE64:
724       return "--base64";
725     case GPGME_DATA_ENCODING_ARMOR:
726       return "--armor";
727     default:
728       break;
729     }
730   return NULL;
731 }
732
733
734 static int
735 status_cmp (const void *ap, const void *bp)
736 {
737   const struct status_table_s *a = ap;
738   const struct status_table_s *b = bp;
739
740   return strcmp (a->name, b->name);
741 }
742
743
744 static gpgme_status_code_t
745 parse_status (const char *name)
746 {
747   struct status_table_s t, *r;
748   t.name = name;
749   r = bsearch (&t, status_table, DIM(status_table) - 1,
750                sizeof t, status_cmp);
751   return r ? r->code : -1;
752 }
753
754
755 static gpgme_error_t
756 status_handler (void *opaque, int fd)
757 {
758   struct io_cb_data *data = (struct io_cb_data *) opaque;
759   engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
760   gpgme_error_t err = 0;
761   char *line;
762   size_t linelen;
763
764   do
765     {
766       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
767       if (err)
768         {
769           /* Try our best to terminate the connection friendly.  */
770           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
771           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
772                   "fd 0x%x: error from assuan (%d) getting status line : %s",
773                   fd, err, gpg_strerror (err));
774         }
775       else if (linelen >= 3
776                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
777                && (line[3] == '\0' || line[3] == ' '))
778         {
779           if (line[3] == ' ')
780             err = atoi (&line[4]);
781           if (! err)
782             err = gpg_error (GPG_ERR_GENERAL);
783           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
784                   "fd 0x%x: ERR line - mapped to: %s",
785                   fd, err ? gpg_strerror (err) : "ok");
786           /* Try our best to terminate the connection friendly.  */
787           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
788         }
789       else if (linelen >= 2
790                && line[0] == 'O' && line[1] == 'K'
791                && (line[2] == '\0' || line[2] == ' '))
792         {
793           if (gpgsm->status.fnc)
794             err = gpgsm->status.fnc (gpgsm->status.fnc_value,
795                                      GPGME_STATUS_EOF, "");
796           
797           if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
798             {
799               /* We must tell a colon function about the EOF. We do
800                  this only when we have seen any data lines.  Note
801                  that this inlined use of colon data lines will
802                  eventually be changed into using a regular data
803                  channel. */
804               gpgsm->colon.any = 0;
805               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
806             }
807           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
808                   "fd 0x%x: OK line - final status: %s",
809                   fd, err ? gpg_strerror (err) : "ok");
810           _gpgme_io_close (gpgsm->status_cb.fd);
811           return err;
812         }
813       else if (linelen > 2
814                && line[0] == 'D' && line[1] == ' '
815                && gpgsm->colon.fnc)
816         {
817           /* We are using the colon handler even for plain inline data
818              - strange name for that function but for historic reasons
819              we keep it.  */
820           /* FIXME We can't use this for binary data because we
821              assume this is a string.  For the current usage of colon
822              output it is correct.  */
823           char *src = line + 2;
824           char *end = line + linelen;
825           char *dst;
826           char **aline = &gpgsm->colon.attic.line;
827           int *alinelen = &gpgsm->colon.attic.linelen;
828
829           if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
830             {
831               char *newline = realloc (*aline, *alinelen + linelen + 1);
832               if (!newline)
833                 err = gpg_error_from_errno (errno);
834               else
835                 {
836                   *aline = newline;
837                   gpgsm->colon.attic.linesize += linelen + 1;
838                 }
839             }
840           if (!err)
841             {
842               dst = *aline + *alinelen;
843
844               while (!err && src < end)
845                 {
846                   if (*src == '%' && src + 2 < end)
847                     {
848                       /* Handle escaped characters.  */
849                       ++src;
850                       *dst = _gpgme_hextobyte (src);
851                       (*alinelen)++;
852                       src += 2;
853                     }
854                   else
855                     {
856                       *dst = *src++;
857                       (*alinelen)++;
858                     }
859                   
860                   if (*dst == '\n')
861                     {
862                       /* Terminate the pending line, pass it to the colon
863                          handler and reset it.  */
864                       
865                       gpgsm->colon.any = 1;
866                       if (*alinelen > 1 && *(dst - 1) == '\r')
867                         dst--;
868                       *dst = '\0';
869
870                       /* FIXME How should we handle the return code?  */
871                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
872                       if (!err)
873                         {
874                           dst = *aline;
875                           *alinelen = 0;
876                         }
877                     }
878                   else
879                     dst++;
880                 }
881             }
882           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
883                   "fd 0x%x: D line; final status: %s",
884                   fd, err? gpg_strerror (err):"ok");
885         }
886       else if (linelen > 2
887                && line[0] == 'D' && line[1] == ' '
888                && gpgsm->inline_data)
889         {
890           char *src = line + 2;
891           char *end = line + linelen;
892           char *dst = src;
893           ssize_t nwritten;
894
895           linelen = 0;
896           while (src < end)
897             {
898               if (*src == '%' && src + 2 < end)
899                 {
900                   /* Handle escaped characters.  */
901                   ++src;
902                   *dst++ = _gpgme_hextobyte (src);
903                   src += 2;
904                 }
905               else
906                 *dst++ = *src++;
907               
908               linelen++;
909             }
910           
911           src = line + 2;
912           while (linelen > 0)
913             {
914               nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
915               if (!nwritten || (nwritten < 0 && errno != EINTR)
916                   || nwritten > linelen)
917                 {
918                   err = gpg_error_from_errno (errno);
919                   break;
920                 }
921               src += nwritten;
922               linelen -= nwritten;
923             }
924
925           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
926                   "fd 0x%x: D inlinedata; final status: %s",
927                   fd, err? gpg_strerror (err):"ok");
928         }
929       else if (linelen > 2
930                && line[0] == 'S' && line[1] == ' ')
931         {
932           char *rest;
933           gpgme_status_code_t r;
934           
935           rest = strchr (line + 2, ' ');
936           if (!rest)
937             rest = line + linelen; /* set to an empty string */
938           else
939             *(rest++) = 0;
940
941           r = parse_status (line + 2);
942
943           if (r >= 0)
944             {
945               if (gpgsm->status.fnc)
946                 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
947             }
948           else
949             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
950           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
951                   "fd 0x%x: S line (%s) - final status: %s",
952                   fd, line+2, err? gpg_strerror (err):"ok");
953         }
954       else if (linelen >= 7
955                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
956                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
957                && line[6] == 'E' 
958                && (line[7] == '\0' || line[7] == ' '))
959         {
960           char *keyword = line+7;
961
962           while (*keyword == ' ')
963             keyword++;;
964           default_inq_cb (gpgsm, keyword);
965           assuan_write_line (gpgsm->assuan_ctx, "END");
966         }
967
968     }
969   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
970           
971   return err;
972 }
973
974
975 static gpgme_error_t
976 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
977 {
978   gpgme_error_t err;
979
980   TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
981               "fd %d, dir %d", iocbd->fd, iocbd->dir);
982   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
983                               iocbd->fd, iocbd->dir,
984                               handler, iocbd->data, &iocbd->tag);
985   if (err)
986     return TRACE_ERR (err);
987   if (!iocbd->dir)
988     /* FIXME Kludge around poll() problem.  */
989     err = _gpgme_io_set_nonblocking (iocbd->fd);
990   return TRACE_ERR (err);
991 }
992
993
994 static gpgme_error_t
995 start (engine_gpgsm_t gpgsm, const char *command)
996 {
997   gpgme_error_t err;
998   int fdlist[5];
999   int nfds;
1000
1001   /* We need to know the fd used by assuan for reads.  We do this by
1002      using the assumption that the first returned fd from
1003      assuan_get_active_fds() is always this one.  */
1004   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
1005                                 fdlist, DIM (fdlist));
1006   if (nfds < 1)
1007     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1008
1009   /* We "duplicate" the file descriptor, so we can close it here (we
1010      can't close fdlist[0], as that is closed by libassuan, and
1011      closing it here might cause libassuan to close some unrelated FD
1012      later).  Alternatively, we could special case status_fd and
1013      register/unregister it manually as needed, but this increases
1014      code duplication and is more complicated as we can not use the
1015      close notifications etc.  A third alternative would be to let
1016      Assuan know that we closed the FD, but that complicates the
1017      Assuan interface.  */
1018
1019   gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
1020   if (gpgsm->status_cb.fd < 0)
1021     return gpg_error_from_syserror ();
1022
1023   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
1024                                   close_notify_handler, gpgsm))
1025     {
1026       _gpgme_io_close (gpgsm->status_cb.fd);
1027       gpgsm->status_cb.fd = -1;
1028       return gpg_error (GPG_ERR_GENERAL);
1029     }
1030
1031   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1032   if (!err && gpgsm->input_cb.fd != -1)
1033     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1034   if (!err && gpgsm->output_cb.fd != -1)
1035     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1036   if (!err && gpgsm->message_cb.fd != -1)
1037     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1038
1039   if (!err)
1040     err = assuan_write_line (gpgsm->assuan_ctx, command);
1041
1042   if (!err)
1043     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
1044
1045   return err;
1046 }
1047
1048
1049 #if USE_DESCRIPTOR_PASSING
1050 static gpgme_error_t
1051 gpgsm_reset (void *engine)
1052 {
1053   engine_gpgsm_t gpgsm = engine;
1054
1055   /* We must send a reset because we need to reset the list of
1056      signers.  Note that RESET does not reset OPTION commands. */
1057   return gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1058 }
1059 #endif
1060
1061
1062 static gpgme_error_t
1063 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1064 {
1065   engine_gpgsm_t gpgsm = engine;
1066   gpgme_error_t err;
1067
1068   if (!gpgsm)
1069     return gpg_error (GPG_ERR_INV_VALUE);
1070
1071   gpgsm->input_cb.data = ciph;
1072   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1073   if (err)
1074     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1075   gpgsm->output_cb.data = plain;
1076   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1077   if (err)
1078     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1079   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1080   gpgsm->inline_data = NULL;
1081
1082   err = start (engine, "DECRYPT");
1083   return err;
1084 }
1085
1086
1087 static gpgme_error_t
1088 gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
1089 {
1090   engine_gpgsm_t gpgsm = engine;
1091   gpgme_error_t err;
1092   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1093   char *linep = fpr;
1094   char *line;
1095   int length = 8;       /* "DELKEYS " */
1096
1097   if (!fpr)
1098     return gpg_error (GPG_ERR_INV_VALUE);
1099
1100   while (*linep)
1101     {
1102       length++;
1103       if (*linep == '%' || *linep == ' ' || *linep == '+')
1104         length += 2;
1105       linep++;
1106     }
1107   length++;
1108
1109   line = malloc (length);
1110   if (!line)
1111     return gpg_error_from_errno (errno);
1112
1113   strcpy (line, "DELKEYS ");
1114   linep = &line[8];
1115
1116   while (*fpr)
1117     {
1118       switch (*fpr)
1119         {
1120         case '%':
1121           *(linep++) = '%';
1122           *(linep++) = '2';
1123           *(linep++) = '5';
1124           break;
1125         case ' ':
1126           *(linep++) = '%';
1127           *(linep++) = '2';
1128           *(linep++) = '0';
1129           break;
1130         case '+':
1131           *(linep++) = '%';
1132           *(linep++) = '2';
1133           *(linep++) = 'B';
1134           break;
1135         default:
1136           *(linep++) = *fpr;
1137           break;
1138         }
1139       fpr++;
1140     }
1141   *linep = '\0';
1142
1143   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1144   gpgsm_clear_fd (gpgsm, INPUT_FD);
1145   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1146   gpgsm->inline_data = NULL;
1147
1148   err = start (gpgsm, line);
1149   free (line);
1150
1151   return err;
1152 }
1153
1154
1155 static gpgme_error_t
1156 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1157 {
1158   gpgme_error_t err = 0;
1159   assuan_context_t ctx = gpgsm->assuan_ctx;
1160   char *line;
1161   int linelen;
1162   int invalid_recipients = 0;
1163   int i = 0;
1164
1165   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1166   line = malloc (10 + 40 + 1);
1167   if (!line)
1168     return gpg_error_from_errno (errno);
1169   strcpy (line, "RECIPIENT ");
1170   while (!err && recp[i])
1171     {
1172       char *fpr;
1173       int newlen;
1174
1175       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1176         {
1177           invalid_recipients++;
1178           continue;
1179         }
1180       fpr = recp[i]->subkeys->fpr;
1181
1182       newlen = 11 + strlen (fpr);
1183       if (linelen < newlen)
1184         {
1185           char *newline = realloc (line, newlen);
1186           if (! newline)
1187             {
1188               int saved_errno = errno;
1189               free (line);
1190               return gpg_error_from_errno (saved_errno);
1191             }
1192           line = newline;
1193           linelen = newlen;
1194         }
1195       strcpy (&line[10], fpr);
1196
1197       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
1198                                          gpgsm->status.fnc_value);
1199       /* FIXME: This requires more work.  */
1200       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1201         invalid_recipients++;
1202       else if (err)
1203         {
1204           free (line);
1205           return err;
1206         }
1207       i++;
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     gpgsm_decrypt,
1918     gpgsm_delete,
1919     NULL,               /* edit */
1920     gpgsm_encrypt,
1921     NULL,               /* encrypt_sign */
1922     gpgsm_export,
1923     gpgsm_export_ext,
1924     gpgsm_genkey,
1925     gpgsm_import,
1926     gpgsm_keylist,
1927     gpgsm_keylist_ext,
1928     gpgsm_sign,
1929     NULL,               /* trustlist */
1930     gpgsm_verify,
1931     gpgsm_getauditlog,
1932     NULL,               /* opassuan_transact */
1933     NULL,               /* conf_load */
1934     NULL,               /* conf_save */
1935     gpgsm_set_io_cbs,
1936     gpgsm_io_event,
1937     gpgsm_cancel,
1938     NULL                /* cancel_op */
1939   };