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