2009-10-26 Marcus Brinkmann <marcus@g10code.de>
[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_ext
336     (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
337      argv, NULL, NULL, NULL, 1);
338 #else
339   err = assuan_pipe_connect
340     (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
341      argv, child_fds);
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   /* assuan_pipe_connect in this case uses _gpgme_io_spawn which
366      closes the child fds for us.  */
367   gpgsm->input_cb.server_fd = -1;
368   gpgsm->output_cb.server_fd = -1;
369   gpgsm->message_cb.server_fd = -1;
370
371   err = _gpgme_getenv ("DISPLAY", &dft_display);
372   if (err)
373     goto leave;
374   if (dft_display)
375     {
376       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
377         {
378           free (dft_display);
379           err = gpg_error_from_errno (errno);
380           goto leave;
381         }
382       free (dft_display);
383
384       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
385                              NULL, NULL, NULL);
386       free (optstr);
387       if (err)
388         goto leave;
389     }
390
391   if (isatty (1))
392     {
393       int rc;
394
395       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
396       if (rc)
397         {
398           err = gpg_error_from_errno (rc);
399           goto leave;
400         }
401       else
402         {
403           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
404             {
405               err = gpg_error_from_errno (errno);
406               goto leave;
407             }
408           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
409                                  NULL, NULL, NULL);
410           free (optstr);
411           if (err)
412             goto leave;
413
414           err = _gpgme_getenv ("TERM", &dft_ttytype);
415           if (err)
416             goto leave;
417           if (dft_ttytype)
418             {
419               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
420                 {
421                   free (dft_ttytype);
422                   err = gpg_error_from_errno (errno);
423                   goto leave;
424                 }
425               free (dft_ttytype);
426
427               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
428                                      NULL, NULL, NULL, NULL);
429               free (optstr);
430               if (err)
431                 goto leave;
432             }
433         }
434     }
435
436   /* Ask gpgsm to enable the audit log support.  */
437   if (!err)
438     {
439       err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
440                              NULL, NULL, NULL, NULL, NULL, NULL);
441       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
442         err = 0; /* This is an optional feature of gpgsm.  */
443     }
444
445
446 #ifdef HAVE_W32_SYSTEM
447   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
448      gpgsm to tell us when it needs it.  */
449   if (!err)
450     {
451       err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
452                              NULL, NULL, NULL, NULL, NULL, NULL);
453       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
454         err = 0; /* This is a new feature of gpgsm.  */
455     }
456 #endif /*HAVE_W32_SYSTEM*/
457
458 #if !USE_DESCRIPTOR_PASSING
459   if (!err
460       && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
461                                       close_notify_handler, gpgsm)
462           || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
463                                          close_notify_handler, gpgsm)
464           || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
465                                          close_notify_handler, gpgsm)))
466     {
467       err = gpg_error (GPG_ERR_GENERAL);
468       goto leave;
469     }
470 #endif
471
472  leave:
473   /* Close the server ends of the pipes (because of this, we must use
474      the stored server_fd_str in the function start).  Our ends are
475      closed in gpgsm_release().  */
476 #if !USE_DESCRIPTOR_PASSING
477   if (gpgsm->input_cb.server_fd != -1)
478     _gpgme_io_close (gpgsm->input_cb.server_fd);
479   if (gpgsm->output_cb.server_fd != -1)
480     _gpgme_io_close (gpgsm->output_cb.server_fd);
481   if (gpgsm->message_cb.server_fd != -1)
482     _gpgme_io_close (gpgsm->message_cb.server_fd);
483 #endif
484
485   if (err)
486     gpgsm_release (gpgsm);
487   else
488     *engine = gpgsm;
489
490   return err;
491 }
492
493
494 static gpgme_error_t
495 gpgsm_set_locale (void *engine, int category, const char *value)
496 {
497   engine_gpgsm_t gpgsm = engine;
498   gpgme_error_t err;
499   char *optstr;
500   char *catstr;
501
502   /* FIXME: If value is NULL, we need to reset the option to default.
503      But we can't do this.  So we error out here.  GPGSM needs support
504      for this.  */
505   if (category == LC_CTYPE)
506     {
507       catstr = "lc-ctype";
508       if (!value && gpgsm->lc_ctype_set)
509         return gpg_error (GPG_ERR_INV_VALUE);
510       if (value)
511         gpgsm->lc_ctype_set = 1;
512     }
513 #ifdef LC_MESSAGES
514   else if (category == LC_MESSAGES)
515     {
516       catstr = "lc-messages";
517       if (!value && gpgsm->lc_messages_set)
518         return gpg_error (GPG_ERR_INV_VALUE);
519       if (value)
520         gpgsm->lc_messages_set = 1;
521     }
522 #endif /* LC_MESSAGES */
523   else
524     return gpg_error (GPG_ERR_INV_VALUE);
525
526   /* FIXME: Reset value to default.  */
527   if (!value) 
528     return 0;
529
530   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
531     err = gpg_error_from_errno (errno);
532   else
533     {
534       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
535                              NULL, NULL, NULL, NULL);
536       free (optstr);
537     }
538
539   return err;
540 }
541
542
543 /* Forward declaration.  */
544 static gpgme_status_code_t parse_status (const char *name);
545
546 static gpgme_error_t
547 gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
548                              engine_status_handler_t status_fnc,
549                              void *status_fnc_value)
550 {
551   gpg_error_t err;
552   char *line;
553   size_t linelen;
554
555   err = assuan_write_line (ctx, cmd);
556   if (err)
557     return err;
558
559   do
560     {
561       err = assuan_read_line (ctx, &line, &linelen);
562       if (err)
563         return err;
564
565       if (*line == '#' || !linelen)
566         continue;
567
568       if (linelen >= 2
569           && line[0] == 'O' && line[1] == 'K'
570           && (line[2] == '\0' || line[2] == ' '))
571         return 0;
572       else if (linelen >= 4
573           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
574           && line[3] == ' ')
575         err = atoi (&line[4]);
576       else if (linelen >= 2
577                && line[0] == 'S' && line[1] == ' ')
578         {
579           char *rest;
580           gpgme_status_code_t r;
581
582           rest = strchr (line + 2, ' ');
583           if (!rest)
584             rest = line + linelen; /* set to an empty string */
585           else
586             *(rest++) = 0;
587
588           r = parse_status (line + 2);
589
590           if (r >= 0 && status_fnc)
591             err = status_fnc (status_fnc_value, r, rest);
592           else
593             err = gpg_error (GPG_ERR_GENERAL);
594         }
595       else
596         err = gpg_error (GPG_ERR_GENERAL);
597     }
598   while (!err);
599
600   return err;
601 }
602
603
604 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
605
606 static void
607 gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
608 {
609 #if !USE_DESCRIPTOR_PASSING
610   switch (fd_type)
611     {
612     case INPUT_FD:
613       _gpgme_io_close (gpgsm->input_cb.fd);
614       break;
615     case OUTPUT_FD:
616       _gpgme_io_close (gpgsm->output_cb.fd);
617       break;
618     case MESSAGE_FD:
619       _gpgme_io_close (gpgsm->message_cb.fd);
620       break;
621     }
622 #endif
623 }
624
625 #define COMMANDLINELEN 40
626 static gpgme_error_t
627 gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
628 {
629   gpg_error_t err = 0;
630   char line[COMMANDLINELEN];
631   char *which;
632   iocb_data_t *iocb_data;
633   int dir;
634
635   switch (fd_type)
636     {
637     case INPUT_FD:
638       which = "INPUT";
639       iocb_data = &gpgsm->input_cb;
640       break;
641
642     case OUTPUT_FD:
643       which = "OUTPUT";
644       iocb_data = &gpgsm->output_cb;
645       break;
646
647     case MESSAGE_FD:
648       which = "MESSAGE";
649       iocb_data = &gpgsm->message_cb;
650       break;
651
652     default:
653       return gpg_error (GPG_ERR_INV_VALUE);
654     }
655
656   dir = iocb_data->dir;
657
658 #if USE_DESCRIPTOR_PASSING
659   /* We try to short-cut the communication by giving GPGSM direct
660      access to the file descriptor, rather than using a pipe.  */
661   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
662   if (iocb_data->server_fd < 0)
663     {
664       int fds[2];
665
666       if (_gpgme_io_pipe (fds, 0) < 0)
667         return gpg_error_from_errno (errno);
668
669       iocb_data->fd = dir ? fds[0] : fds[1];
670       iocb_data->server_fd = dir ? fds[1] : fds[0];
671
672       if (_gpgme_io_set_close_notify (iocb_data->fd,
673                                       close_notify_handler, gpgsm))
674         {
675           err = gpg_error (GPG_ERR_GENERAL);
676           goto leave_set_fd;
677         }
678     }
679
680   err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
681   if (err)
682     goto leave_set_fd;
683
684   _gpgme_io_close (iocb_data->server_fd);
685   iocb_data->server_fd = -1;
686
687   if (opt)
688     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
689   else
690     snprintf (line, COMMANDLINELEN, "%s FD", which);
691 #else
692   if (opt)
693     snprintf (line, COMMANDLINELEN, "%s FD=%s %s", 
694               which, iocb_data->server_fd_str, opt);
695   else
696     snprintf (line, COMMANDLINELEN, "%s FD=%s", 
697               which, iocb_data->server_fd_str);
698 #endif
699
700   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
701
702 #if USE_DESCRIPTOR_PASSING
703  leave_set_fd:
704   if (err)
705     {
706       _gpgme_io_close (iocb_data->fd);
707       iocb_data->fd = -1;
708       if (iocb_data->server_fd != -1)
709         {
710           _gpgme_io_close (iocb_data->server_fd);
711           iocb_data->server_fd = -1;
712         }
713     }
714 #endif
715
716   return err;
717 }
718
719
720 static const char *
721 map_data_enc (gpgme_data_t d)
722 {
723   switch (gpgme_data_get_encoding (d))
724     {
725     case GPGME_DATA_ENCODING_NONE:
726       break;
727     case GPGME_DATA_ENCODING_BINARY:
728       return "--binary";
729     case GPGME_DATA_ENCODING_BASE64:
730       return "--base64";
731     case GPGME_DATA_ENCODING_ARMOR:
732       return "--armor";
733     default:
734       break;
735     }
736   return NULL;
737 }
738
739
740 static int
741 status_cmp (const void *ap, const void *bp)
742 {
743   const struct status_table_s *a = ap;
744   const struct status_table_s *b = bp;
745
746   return strcmp (a->name, b->name);
747 }
748
749
750 static gpgme_status_code_t
751 parse_status (const char *name)
752 {
753   struct status_table_s t, *r;
754   t.name = name;
755   r = bsearch (&t, status_table, DIM(status_table) - 1,
756                sizeof t, status_cmp);
757   return r ? r->code : -1;
758 }
759
760
761 static gpgme_error_t
762 status_handler (void *opaque, int fd)
763 {
764   struct io_cb_data *data = (struct io_cb_data *) opaque;
765   engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
766   gpgme_error_t err = 0;
767   char *line;
768   size_t linelen;
769
770   do
771     {
772       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
773       if (err)
774         {
775           /* Try our best to terminate the connection friendly.  */
776           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
777           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
778                   "fd 0x%x: error from assuan (%d) getting status line : %s",
779                   fd, err, gpg_strerror (err));
780         }
781       else if (linelen >= 3
782                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
783                && (line[3] == '\0' || line[3] == ' '))
784         {
785           if (line[3] == ' ')
786             err = atoi (&line[4]);
787           if (! err)
788             err = gpg_error (GPG_ERR_GENERAL);
789           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
790                   "fd 0x%x: ERR line - mapped to: %s",
791                   fd, err ? gpg_strerror (err) : "ok");
792           /* Try our best to terminate the connection friendly.  */
793           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
794         }
795       else if (linelen >= 2
796                && line[0] == 'O' && line[1] == 'K'
797                && (line[2] == '\0' || line[2] == ' '))
798         {
799           if (gpgsm->status.fnc)
800             err = gpgsm->status.fnc (gpgsm->status.fnc_value,
801                                      GPGME_STATUS_EOF, "");
802           
803           if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
804             {
805               /* We must tell a colon function about the EOF. We do
806                  this only when we have seen any data lines.  Note
807                  that this inlined use of colon data lines will
808                  eventually be changed into using a regular data
809                  channel. */
810               gpgsm->colon.any = 0;
811               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
812             }
813           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
814                   "fd 0x%x: OK line - final status: %s",
815                   fd, err ? gpg_strerror (err) : "ok");
816           _gpgme_io_close (gpgsm->status_cb.fd);
817           return err;
818         }
819       else if (linelen > 2
820                && line[0] == 'D' && line[1] == ' '
821                && gpgsm->colon.fnc)
822         {
823           /* We are using the colon handler even for plain inline data
824              - strange name for that function but for historic reasons
825              we keep it.  */
826           /* FIXME We can't use this for binary data because we
827              assume this is a string.  For the current usage of colon
828              output it is correct.  */
829           char *src = line + 2;
830           char *end = line + linelen;
831           char *dst;
832           char **aline = &gpgsm->colon.attic.line;
833           int *alinelen = &gpgsm->colon.attic.linelen;
834
835           if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
836             {
837               char *newline = realloc (*aline, *alinelen + linelen + 1);
838               if (!newline)
839                 err = gpg_error_from_errno (errno);
840               else
841                 {
842                   *aline = newline;
843                   gpgsm->colon.attic.linesize += linelen + 1;
844                 }
845             }
846           if (!err)
847             {
848               dst = *aline + *alinelen;
849
850               while (!err && src < end)
851                 {
852                   if (*src == '%' && src + 2 < end)
853                     {
854                       /* Handle escaped characters.  */
855                       ++src;
856                       *dst = _gpgme_hextobyte (src);
857                       (*alinelen)++;
858                       src += 2;
859                     }
860                   else
861                     {
862                       *dst = *src++;
863                       (*alinelen)++;
864                     }
865                   
866                   if (*dst == '\n')
867                     {
868                       /* Terminate the pending line, pass it to the colon
869                          handler and reset it.  */
870                       
871                       gpgsm->colon.any = 1;
872                       if (*alinelen > 1 && *(dst - 1) == '\r')
873                         dst--;
874                       *dst = '\0';
875
876                       /* FIXME How should we handle the return code?  */
877                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
878                       if (!err)
879                         {
880                           dst = *aline;
881                           *alinelen = 0;
882                         }
883                     }
884                   else
885                     dst++;
886                 }
887             }
888           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
889                   "fd 0x%x: D line; final status: %s",
890                   fd, err? gpg_strerror (err):"ok");
891         }
892       else if (linelen > 2
893                && line[0] == 'D' && line[1] == ' '
894                && gpgsm->inline_data)
895         {
896           char *src = line + 2;
897           char *end = line + linelen;
898           char *dst = src;
899           ssize_t nwritten;
900
901           linelen = 0;
902           while (src < end)
903             {
904               if (*src == '%' && src + 2 < end)
905                 {
906                   /* Handle escaped characters.  */
907                   ++src;
908                   *dst++ = _gpgme_hextobyte (src);
909                   src += 2;
910                 }
911               else
912                 *dst++ = *src++;
913               
914               linelen++;
915             }
916           
917           src = line + 2;
918           while (linelen > 0)
919             {
920               nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
921               if (!nwritten || (nwritten < 0 && errno != EINTR)
922                   || nwritten > linelen)
923                 {
924                   err = gpg_error_from_errno (errno);
925                   break;
926                 }
927               src += nwritten;
928               linelen -= nwritten;
929             }
930
931           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
932                   "fd 0x%x: D inlinedata; final status: %s",
933                   fd, err? gpg_strerror (err):"ok");
934         }
935       else if (linelen > 2
936                && line[0] == 'S' && line[1] == ' ')
937         {
938           char *rest;
939           gpgme_status_code_t r;
940           
941           rest = strchr (line + 2, ' ');
942           if (!rest)
943             rest = line + linelen; /* set to an empty string */
944           else
945             *(rest++) = 0;
946
947           r = parse_status (line + 2);
948
949           if (r >= 0)
950             {
951               if (gpgsm->status.fnc)
952                 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
953             }
954           else
955             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
956           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
957                   "fd 0x%x: S line (%s) - final status: %s",
958                   fd, line+2, err? gpg_strerror (err):"ok");
959         }
960       else if (linelen >= 7
961                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
962                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
963                && line[6] == 'E' 
964                && (line[7] == '\0' || line[7] == ' '))
965         {
966           char *keyword = line+7;
967
968           while (*keyword == ' ')
969             keyword++;;
970           default_inq_cb (gpgsm, keyword);
971           assuan_write_line (gpgsm->assuan_ctx, "END");
972         }
973
974     }
975   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
976           
977   return err;
978 }
979
980
981 static gpgme_error_t
982 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
983 {
984   gpgme_error_t err;
985
986   TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
987               "fd %d, dir %d", iocbd->fd, iocbd->dir);
988   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
989                               iocbd->fd, iocbd->dir,
990                               handler, iocbd->data, &iocbd->tag);
991   if (err)
992     return TRACE_ERR (err);
993   if (!iocbd->dir)
994     /* FIXME Kludge around poll() problem.  */
995     err = _gpgme_io_set_nonblocking (iocbd->fd);
996   return TRACE_ERR (err);
997 }
998
999
1000 static gpgme_error_t
1001 start (engine_gpgsm_t gpgsm, const char *command)
1002 {
1003   gpgme_error_t err;
1004   int fdlist[5];
1005   int nfds;
1006
1007   /* We need to know the fd used by assuan for reads.  We do this by
1008      using the assumption that the first returned fd from
1009      assuan_get_active_fds() is always this one.  */
1010   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
1011                                 fdlist, DIM (fdlist));
1012   if (nfds < 1)
1013     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1014
1015   /* We "duplicate" the file descriptor, so we can close it here (we
1016      can't close fdlist[0], as that is closed by libassuan, and
1017      closing it here might cause libassuan to close some unrelated FD
1018      later).  Alternatively, we could special case status_fd and
1019      register/unregister it manually as needed, but this increases
1020      code duplication and is more complicated as we can not use the
1021      close notifications etc.  A third alternative would be to let
1022      Assuan know that we closed the FD, but that complicates the
1023      Assuan interface.  */
1024
1025   gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
1026   if (gpgsm->status_cb.fd < 0)
1027     return gpg_error_from_syserror ();
1028
1029   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
1030                                   close_notify_handler, gpgsm))
1031     {
1032       _gpgme_io_close (gpgsm->status_cb.fd);
1033       gpgsm->status_cb.fd = -1;
1034       return gpg_error (GPG_ERR_GENERAL);
1035     }
1036
1037   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1038   if (!err && gpgsm->input_cb.fd != -1)
1039     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1040   if (!err && gpgsm->output_cb.fd != -1)
1041     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1042   if (!err && gpgsm->message_cb.fd != -1)
1043     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1044
1045   if (!err)
1046     err = assuan_write_line (gpgsm->assuan_ctx, command);
1047
1048   if (!err)
1049     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
1050
1051   return err;
1052 }
1053
1054
1055 #if USE_DESCRIPTOR_PASSING
1056 static gpgme_error_t
1057 gpgsm_reset (void *engine)
1058 {
1059   engine_gpgsm_t gpgsm = engine;
1060
1061   /* We must send a reset because we need to reset the list of
1062      signers.  Note that RESET does not reset OPTION commands. */
1063   return gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1064 }
1065 #endif
1066
1067
1068 static gpgme_error_t
1069 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1070 {
1071   engine_gpgsm_t gpgsm = engine;
1072   gpgme_error_t err;
1073
1074   if (!gpgsm)
1075     return gpg_error (GPG_ERR_INV_VALUE);
1076
1077   gpgsm->input_cb.data = ciph;
1078   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1079   if (err)
1080     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1081   gpgsm->output_cb.data = plain;
1082   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1083   if (err)
1084     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1085   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1086   gpgsm->inline_data = NULL;
1087
1088   err = start (engine, "DECRYPT");
1089   return err;
1090 }
1091
1092
1093 static gpgme_error_t
1094 gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
1095 {
1096   engine_gpgsm_t gpgsm = engine;
1097   gpgme_error_t err;
1098   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1099   char *linep = fpr;
1100   char *line;
1101   int length = 8;       /* "DELKEYS " */
1102
1103   if (!fpr)
1104     return gpg_error (GPG_ERR_INV_VALUE);
1105
1106   while (*linep)
1107     {
1108       length++;
1109       if (*linep == '%' || *linep == ' ' || *linep == '+')
1110         length += 2;
1111       linep++;
1112     }
1113   length++;
1114
1115   line = malloc (length);
1116   if (!line)
1117     return gpg_error_from_errno (errno);
1118
1119   strcpy (line, "DELKEYS ");
1120   linep = &line[8];
1121
1122   while (*fpr)
1123     {
1124       switch (*fpr)
1125         {
1126         case '%':
1127           *(linep++) = '%';
1128           *(linep++) = '2';
1129           *(linep++) = '5';
1130           break;
1131         case ' ':
1132           *(linep++) = '%';
1133           *(linep++) = '2';
1134           *(linep++) = '0';
1135           break;
1136         case '+':
1137           *(linep++) = '%';
1138           *(linep++) = '2';
1139           *(linep++) = 'B';
1140           break;
1141         default:
1142           *(linep++) = *fpr;
1143           break;
1144         }
1145       fpr++;
1146     }
1147   *linep = '\0';
1148
1149   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1150   gpgsm_clear_fd (gpgsm, INPUT_FD);
1151   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1152   gpgsm->inline_data = NULL;
1153
1154   err = start (gpgsm, line);
1155   free (line);
1156
1157   return err;
1158 }
1159
1160
1161 static gpgme_error_t
1162 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1163 {
1164   gpgme_error_t err = 0;
1165   assuan_context_t ctx = gpgsm->assuan_ctx;
1166   char *line;
1167   int linelen;
1168   int invalid_recipients = 0;
1169   int i = 0;
1170
1171   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1172   line = malloc (10 + 40 + 1);
1173   if (!line)
1174     return gpg_error_from_errno (errno);
1175   strcpy (line, "RECIPIENT ");
1176   while (!err && recp[i])
1177     {
1178       char *fpr;
1179       int newlen;
1180
1181       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1182         {
1183           invalid_recipients++;
1184           continue;
1185         }
1186       fpr = recp[i]->subkeys->fpr;
1187
1188       newlen = 11 + strlen (fpr);
1189       if (linelen < newlen)
1190         {
1191           char *newline = realloc (line, newlen);
1192           if (! newline)
1193             {
1194               int saved_errno = errno;
1195               free (line);
1196               return gpg_error_from_errno (saved_errno);
1197             }
1198           line = newline;
1199           linelen = newlen;
1200         }
1201       strcpy (&line[10], fpr);
1202
1203       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
1204                                          gpgsm->status.fnc_value);
1205       /* FIXME: This requires more work.  */
1206       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1207         invalid_recipients++;
1208       else if (err)
1209         {
1210           free (line);
1211           return err;
1212         }
1213       i++;
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 struct engine_ops _gpgme_engine_ops_gpgsm =
1904   {
1905     /* Static functions.  */
1906     _gpgme_get_gpgsm_path,
1907     NULL,
1908     gpgsm_get_version,
1909     gpgsm_get_req_version,
1910     gpgsm_new,
1911
1912     /* Member functions.  */
1913     gpgsm_release,
1914 #if USE_DESCRIPTOR_PASSING
1915     gpgsm_reset,
1916 #else
1917     NULL,                       /* reset */
1918 #endif
1919     gpgsm_set_status_handler,
1920     NULL,               /* set_command_handler */
1921     gpgsm_set_colon_line_handler,
1922     gpgsm_set_locale,
1923     gpgsm_decrypt,
1924     gpgsm_delete,
1925     NULL,               /* edit */
1926     gpgsm_encrypt,
1927     NULL,               /* encrypt_sign */
1928     gpgsm_export,
1929     gpgsm_export_ext,
1930     gpgsm_genkey,
1931     gpgsm_import,
1932     gpgsm_keylist,
1933     gpgsm_keylist_ext,
1934     gpgsm_sign,
1935     NULL,               /* trustlist */
1936     gpgsm_verify,
1937     gpgsm_getauditlog,
1938     NULL,               /* opassuan_transact */
1939     NULL,               /* conf_load */
1940     NULL,               /* conf_save */
1941     gpgsm_set_io_cbs,
1942     gpgsm_io_event,
1943     gpgsm_cancel,
1944     NULL                /* cancel_op */
1945   };