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