2009-10-20 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   gpgme_error_t err = 0;
765   engine_gpgsm_t gpgsm = opaque;
766   char *line;
767   size_t linelen;
768
769   do
770     {
771       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
772       if (err)
773         {
774           /* Try our best to terminate the connection friendly.  */
775           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
776           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
777                   "fd 0x%x: error from assuan (%d) getting status line : %s",
778                   fd, err, gpg_strerror (err));
779         }
780       else if (linelen >= 3
781                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
782                && (line[3] == '\0' || line[3] == ' '))
783         {
784           if (line[3] == ' ')
785             err = atoi (&line[4]);
786           if (! err)
787             err = gpg_error (GPG_ERR_GENERAL);
788           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
789                   "fd 0x%x: ERR line - mapped to: %s",
790                   fd, err ? gpg_strerror (err) : "ok");
791           /* Try our best to terminate the connection friendly.  */
792           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
793         }
794       else if (linelen >= 2
795                && line[0] == 'O' && line[1] == 'K'
796                && (line[2] == '\0' || line[2] == ' '))
797         {
798           if (gpgsm->status.fnc)
799             err = gpgsm->status.fnc (gpgsm->status.fnc_value,
800                                      GPGME_STATUS_EOF, "");
801           
802           if (!err && gpgsm->colon.fnc && gpgsm->colon.any )
803             {
804               /* We must tell a colon function about the EOF. We do
805                  this only when we have seen any data lines.  Note
806                  that this inlined use of colon data lines will
807                  eventually be changed into using a regular data
808                  channel. */
809               gpgsm->colon.any = 0;
810               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
811             }
812           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
813                   "fd 0x%x: OK line - final status: %s",
814                   fd, err ? gpg_strerror (err) : "ok");
815           _gpgme_io_close (gpgsm->status_cb.fd);
816           return err;
817         }
818       else if (linelen > 2
819                && line[0] == 'D' && line[1] == ' '
820                && gpgsm->colon.fnc)
821         {
822           /* We are using the colon handler even for plain inline data
823              - strange name for that function but for historic reasons
824              we keep it.  */
825           /* FIXME We can't use this for binary data because we
826              assume this is a string.  For the current usage of colon
827              output it is correct.  */
828           char *src = line + 2;
829           char *end = line + linelen;
830           char *dst;
831           char **aline = &gpgsm->colon.attic.line;
832           int *alinelen = &gpgsm->colon.attic.linelen;
833
834           if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
835             {
836               char *newline = realloc (*aline, *alinelen + linelen + 1);
837               if (!newline)
838                 err = gpg_error_from_errno (errno);
839               else
840                 {
841                   *aline = newline;
842                   gpgsm->colon.attic.linesize += linelen + 1;
843                 }
844             }
845           if (!err)
846             {
847               dst = *aline + *alinelen;
848
849               while (!err && src < end)
850                 {
851                   if (*src == '%' && src + 2 < end)
852                     {
853                       /* Handle escaped characters.  */
854                       ++src;
855                       *dst = _gpgme_hextobyte (src);
856                       (*alinelen)++;
857                       src += 2;
858                     }
859                   else
860                     {
861                       *dst = *src++;
862                       (*alinelen)++;
863                     }
864                   
865                   if (*dst == '\n')
866                     {
867                       /* Terminate the pending line, pass it to the colon
868                          handler and reset it.  */
869                       
870                       gpgsm->colon.any = 1;
871                       if (*alinelen > 1 && *(dst - 1) == '\r')
872                         dst--;
873                       *dst = '\0';
874
875                       /* FIXME How should we handle the return code?  */
876                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
877                       if (!err)
878                         {
879                           dst = *aline;
880                           *alinelen = 0;
881                         }
882                     }
883                   else
884                     dst++;
885                 }
886             }
887           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
888                   "fd 0x%x: D line; final status: %s",
889                   fd, err? gpg_strerror (err):"ok");
890         }
891       else if (linelen > 2
892                && line[0] == 'D' && line[1] == ' '
893                && gpgsm->inline_data)
894         {
895           char *src = line + 2;
896           char *end = line + linelen;
897           char *dst = src;
898           ssize_t nwritten;
899
900           linelen = 0;
901           while (src < end)
902             {
903               if (*src == '%' && src + 2 < end)
904                 {
905                   /* Handle escaped characters.  */
906                   ++src;
907                   *dst++ = _gpgme_hextobyte (src);
908                   src += 2;
909                 }
910               else
911                 *dst++ = *src++;
912               
913               linelen++;
914             }
915           
916           src = line + 2;
917           while (linelen > 0)
918             {
919               nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
920               if (!nwritten || (nwritten < 0 && errno != EINTR)
921                   || nwritten > linelen)
922                 {
923                   err = gpg_error_from_errno (errno);
924                   break;
925                 }
926               src += nwritten;
927               linelen -= nwritten;
928             }
929
930           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
931                   "fd 0x%x: D inlinedata; final status: %s",
932                   fd, err? gpg_strerror (err):"ok");
933         }
934       else if (linelen > 2
935                && line[0] == 'S' && line[1] == ' ')
936         {
937           char *rest;
938           gpgme_status_code_t r;
939           
940           rest = strchr (line + 2, ' ');
941           if (!rest)
942             rest = line + linelen; /* set to an empty string */
943           else
944             *(rest++) = 0;
945
946           r = parse_status (line + 2);
947
948           if (r >= 0)
949             {
950               if (gpgsm->status.fnc)
951                 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
952             }
953           else
954             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
955           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
956                   "fd 0x%x: S line (%s) - final status: %s",
957                   fd, line+2, err? gpg_strerror (err):"ok");
958         }
959       else if (linelen >= 7
960                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
961                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
962                && line[6] == 'E' 
963                && (line[7] == '\0' || line[7] == ' '))
964         {
965           char *keyword = line+7;
966
967           while (*keyword == ' ')
968             keyword++;;
969           default_inq_cb (gpgsm, keyword);
970           assuan_write_line (gpgsm->assuan_ctx, "END");
971         }
972
973     }
974   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
975           
976   return err;
977 }
978
979
980 static gpgme_error_t
981 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
982 {
983   gpgme_error_t err;
984
985   TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
986               "fd %d, dir %d", iocbd->fd, iocbd->dir);
987   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
988                               iocbd->fd, iocbd->dir,
989                               handler, iocbd->data, &iocbd->tag);
990   if (err)
991     return TRACE_ERR (err);
992   if (!iocbd->dir)
993     /* FIXME Kludge around poll() problem.  */
994     err = _gpgme_io_set_nonblocking (iocbd->fd);
995   return TRACE_ERR (err);
996 }
997
998
999 static gpgme_error_t
1000 start (engine_gpgsm_t gpgsm, const char *command)
1001 {
1002   gpgme_error_t err;
1003   int fdlist[5];
1004   int nfds;
1005
1006   /* We need to know the fd used by assuan for reads.  We do this by
1007      using the assumption that the first returned fd from
1008      assuan_get_active_fds() is always this one.  */
1009   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
1010                                 fdlist, DIM (fdlist));
1011   if (nfds < 1)
1012     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1013
1014   /* We "duplicate" the file descriptor, so we can close it here (we
1015      can't close fdlist[0], as that is closed by libassuan, and
1016      closing it here might cause libassuan to close some unrelated FD
1017      later).  Alternatively, we could special case status_fd and
1018      register/unregister it manually as needed, but this increases
1019      code duplication and is more complicated as we can not use the
1020      close notifications etc.  A third alternative would be to let
1021      Assuan know that we closed the FD, but that complicates the
1022      Assuan interface.  */
1023
1024   gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
1025   if (gpgsm->status_cb.fd < 0)
1026     return gpg_error_from_syserror ();
1027
1028   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
1029                                   close_notify_handler, gpgsm))
1030     {
1031       _gpgme_io_close (gpgsm->status_cb.fd);
1032       gpgsm->status_cb.fd = -1;
1033       return gpg_error (GPG_ERR_GENERAL);
1034     }
1035
1036   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1037   if (!err && gpgsm->input_cb.fd != -1)
1038     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1039   if (!err && gpgsm->output_cb.fd != -1)
1040     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1041   if (!err && gpgsm->message_cb.fd != -1)
1042     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1043
1044   if (!err)
1045     err = assuan_write_line (gpgsm->assuan_ctx, command);
1046
1047   if (!err)
1048     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
1049
1050   return err;
1051 }
1052
1053
1054 #if USE_DESCRIPTOR_PASSING
1055 static gpgme_error_t
1056 gpgsm_reset (void *engine)
1057 {
1058   engine_gpgsm_t gpgsm = engine;
1059
1060   /* We must send a reset because we need to reset the list of
1061      signers.  Note that RESET does not reset OPTION commands. */
1062   return gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1063 }
1064 #endif
1065
1066
1067 static gpgme_error_t
1068 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1069 {
1070   engine_gpgsm_t gpgsm = engine;
1071   gpgme_error_t err;
1072
1073   if (!gpgsm)
1074     return gpg_error (GPG_ERR_INV_VALUE);
1075
1076   gpgsm->input_cb.data = ciph;
1077   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1078   if (err)
1079     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1080   gpgsm->output_cb.data = plain;
1081   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1082   if (err)
1083     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1084   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1085   gpgsm->inline_data = NULL;
1086
1087   err = start (engine, "DECRYPT");
1088   return err;
1089 }
1090
1091
1092 static gpgme_error_t
1093 gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
1094 {
1095   engine_gpgsm_t gpgsm = engine;
1096   gpgme_error_t err;
1097   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1098   char *linep = fpr;
1099   char *line;
1100   int length = 8;       /* "DELKEYS " */
1101
1102   if (!fpr)
1103     return gpg_error (GPG_ERR_INV_VALUE);
1104
1105   while (*linep)
1106     {
1107       length++;
1108       if (*linep == '%' || *linep == ' ' || *linep == '+')
1109         length += 2;
1110       linep++;
1111     }
1112   length++;
1113
1114   line = malloc (length);
1115   if (!line)
1116     return gpg_error_from_errno (errno);
1117
1118   strcpy (line, "DELKEYS ");
1119   linep = &line[8];
1120
1121   while (*fpr)
1122     {
1123       switch (*fpr)
1124         {
1125         case '%':
1126           *(linep++) = '%';
1127           *(linep++) = '2';
1128           *(linep++) = '5';
1129           break;
1130         case ' ':
1131           *(linep++) = '%';
1132           *(linep++) = '2';
1133           *(linep++) = '0';
1134           break;
1135         case '+':
1136           *(linep++) = '%';
1137           *(linep++) = '2';
1138           *(linep++) = 'B';
1139           break;
1140         default:
1141           *(linep++) = *fpr;
1142           break;
1143         }
1144       fpr++;
1145     }
1146   *linep = '\0';
1147
1148   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1149   gpgsm_clear_fd (gpgsm, INPUT_FD);
1150   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1151   gpgsm->inline_data = NULL;
1152
1153   err = start (gpgsm, line);
1154   free (line);
1155
1156   return err;
1157 }
1158
1159
1160 static gpgme_error_t
1161 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1162 {
1163   gpgme_error_t err = 0;
1164   assuan_context_t ctx = gpgsm->assuan_ctx;
1165   char *line;
1166   int linelen;
1167   int invalid_recipients = 0;
1168   int i = 0;
1169
1170   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1171   line = malloc (10 + 40 + 1);
1172   if (!line)
1173     return gpg_error_from_errno (errno);
1174   strcpy (line, "RECIPIENT ");
1175   while (!err && recp[i])
1176     {
1177       char *fpr;
1178       int newlen;
1179
1180       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1181         {
1182           invalid_recipients++;
1183           continue;
1184         }
1185       fpr = recp[i]->subkeys->fpr;
1186
1187       newlen = 11 + strlen (fpr);
1188       if (linelen < newlen)
1189         {
1190           char *newline = realloc (line, newlen);
1191           if (! newline)
1192             {
1193               int saved_errno = errno;
1194               free (line);
1195               return gpg_error_from_errno (saved_errno);
1196             }
1197           line = newline;
1198           linelen = newlen;
1199         }
1200       strcpy (&line[10], fpr);
1201
1202       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
1203                                          gpgsm->status.fnc_value);
1204       /* FIXME: This requires more work.  */
1205       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1206         invalid_recipients++;
1207       else if (err)
1208         {
1209           free (line);
1210           return err;
1211         }
1212       i++;
1213     }
1214   free (line);
1215   return gpg_error (invalid_recipients
1216                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1217 }
1218
1219
1220 static gpgme_error_t
1221 gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1222                gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1223 {
1224   engine_gpgsm_t gpgsm = engine;
1225   gpgme_error_t err;
1226
1227   if (!gpgsm)
1228     return gpg_error (GPG_ERR_INV_VALUE);
1229   if (!recp)
1230     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1231
1232   if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)
1233     {
1234       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1235                                          "OPTION no-encrypt-to", NULL, NULL);
1236       if (err)
1237         return err;
1238     }
1239
1240   gpgsm->input_cb.data = plain;
1241   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1242   if (err)
1243     return err;
1244   gpgsm->output_cb.data = ciph;
1245   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1246                       : map_data_enc (gpgsm->output_cb.data));
1247   if (err)
1248     return err;
1249   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1250   gpgsm->inline_data = NULL;
1251
1252   err = set_recipients (gpgsm, recp);
1253
1254   if (!err)
1255     err = start (gpgsm, "ENCRYPT");
1256
1257   return err;
1258 }
1259
1260
1261 static gpgme_error_t
1262 gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
1263               gpgme_data_t keydata, int use_armor)
1264 {
1265   engine_gpgsm_t gpgsm = engine;
1266   gpgme_error_t err = 0;
1267   char *cmd;
1268
1269   if (!gpgsm)
1270     return gpg_error (GPG_ERR_INV_VALUE);
1271   
1272   if (mode)
1273     return gpg_error (GPG_ERR_NOT_SUPPORTED);
1274
1275   if (!pattern)
1276     pattern = "";
1277
1278   cmd = malloc (7 + strlen (pattern) + 1);
1279   if (!cmd)
1280     return gpg_error_from_errno (errno);
1281   strcpy (cmd, "EXPORT ");
1282   strcpy (&cmd[7], pattern);
1283
1284   gpgsm->output_cb.data = keydata;
1285   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1286                       : map_data_enc (gpgsm->output_cb.data));
1287   if (err)
1288     return err;
1289   gpgsm_clear_fd (gpgsm, INPUT_FD);
1290   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1291   gpgsm->inline_data = NULL;
1292
1293   err = start (gpgsm, cmd);
1294   free (cmd);
1295   return err;
1296 }
1297
1298
1299 static gpgme_error_t
1300 gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
1301                   gpgme_data_t keydata, int use_armor)
1302 {
1303   engine_gpgsm_t gpgsm = engine;
1304   gpgme_error_t err = 0;
1305   char *line;
1306   /* Length is "EXPORT " + p + '\0'.  */
1307   int length = 7 + 1;
1308   char *linep;
1309
1310   if (!gpgsm)
1311     return gpg_error (GPG_ERR_INV_VALUE);
1312
1313   if (mode)
1314     return gpg_error (GPG_ERR_NOT_SUPPORTED);
1315
1316   if (pattern && *pattern)
1317     {
1318       const char **pat = pattern;
1319
1320       while (*pat)
1321         {
1322           const char *patlet = *pat;
1323
1324           while (*patlet)
1325             {
1326               length++;
1327               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1328                 length += 2;
1329               patlet++;
1330             }
1331           pat++;
1332           length++;
1333         }
1334     }
1335   line = malloc (length);
1336   if (!line)
1337     return gpg_error_from_errno (errno);
1338
1339   strcpy (line, "EXPORT ");
1340   linep = &line[7];
1341
1342   if (pattern && *pattern)
1343     {
1344       while (*pattern)
1345         {
1346           const char *patlet = *pattern;
1347
1348           while (*patlet)
1349             {
1350               switch (*patlet)
1351                 {
1352                 case '%':
1353                   *(linep++) = '%';
1354                   *(linep++) = '2';
1355                   *(linep++) = '5';
1356                   break;
1357                 case ' ':
1358                   *(linep++) = '%';
1359                   *(linep++) = '2';
1360                   *(linep++) = '0';
1361                   break;
1362                 case '+':
1363                   *(linep++) = '%';
1364                   *(linep++) = '2';
1365                   *(linep++) = 'B';
1366                   break;
1367                 default:
1368                   *(linep++) = *patlet;
1369                   break;
1370                 }
1371               patlet++;
1372             }
1373           pattern++;
1374           if (*pattern)
1375             *linep++ = ' ';
1376         }
1377     }
1378   *linep = '\0';
1379
1380   gpgsm->output_cb.data = keydata;
1381   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1382                       : map_data_enc (gpgsm->output_cb.data));
1383   if (err)
1384     return err;
1385   gpgsm_clear_fd (gpgsm, INPUT_FD);
1386   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1387   gpgsm->inline_data = NULL;
1388
1389   err = start (gpgsm, line);
1390   free (line);
1391   return err;
1392 }
1393
1394
1395 static gpgme_error_t
1396 gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1397               gpgme_data_t pubkey, gpgme_data_t seckey)
1398 {
1399   engine_gpgsm_t gpgsm = engine;
1400   gpgme_error_t err;
1401
1402   if (!gpgsm || !pubkey || seckey)
1403     return gpg_error (GPG_ERR_INV_VALUE);
1404
1405   gpgsm->input_cb.data = help_data;
1406   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1407   if (err)
1408     return err;
1409   gpgsm->output_cb.data = pubkey;
1410   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1411                       : map_data_enc (gpgsm->output_cb.data));
1412   if (err)
1413     return err;
1414   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1415   gpgsm->inline_data = NULL;
1416
1417   err = start (gpgsm, "GENKEY");
1418   return err;
1419 }
1420
1421
1422 static gpgme_error_t
1423 gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
1424 {
1425   engine_gpgsm_t gpgsm = engine;
1426   gpgme_error_t err;
1427   gpgme_data_encoding_t dataenc;
1428   int idx;
1429
1430   if (!gpgsm)
1431     return gpg_error (GPG_ERR_INV_VALUE);
1432
1433   if (keydata && keyarray)
1434     return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
1435
1436   dataenc = gpgme_data_get_encoding (keydata);
1437
1438   if (keyarray)
1439     {
1440       size_t buflen;
1441       char *buffer, *p;
1442
1443       /* Fist check whether the engine already features the
1444          --re-import option.  */
1445       err = gpgsm_assuan_simple_command 
1446         (gpgsm->assuan_ctx, 
1447          "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
1448       if (err)
1449         return gpg_error (GPG_ERR_NOT_SUPPORTED);
1450
1451       /* Create an internal data object with a list of all
1452          fingerprints.  The data object and its memory (to avoid an
1453          extra copy by gpgme_data_new_from_mem) are stored in two
1454          variables which are released by the close_notify_handler.  */
1455       for (idx=0, buflen=0; keyarray[idx]; idx++)
1456         {
1457           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1458               && keyarray[idx]->subkeys
1459               && keyarray[idx]->subkeys->fpr 
1460               && *keyarray[idx]->subkeys->fpr)
1461             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
1462         }
1463       /* Allocate a bufer with extra space for the trailing Nul
1464          introduced by the use of stpcpy.  */
1465       buffer = malloc (buflen+1);
1466       if (!buffer)
1467         return gpg_error_from_syserror ();
1468       for (idx=0, p = buffer; keyarray[idx]; idx++)
1469         {
1470           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1471               && keyarray[idx]->subkeys
1472               && keyarray[idx]->subkeys->fpr 
1473               && *keyarray[idx]->subkeys->fpr)
1474             p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
1475         }
1476       
1477       err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
1478                                      buffer, buflen, 0);
1479       if (err)
1480         {
1481           free (buffer);
1482           return err;
1483         }
1484       gpgsm->input_helper_memory = buffer;
1485
1486       gpgsm->input_cb.data = gpgsm->input_helper_data;
1487       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1488       if (err)
1489         {
1490           gpgme_data_release (gpgsm->input_helper_data);
1491           gpgsm->input_helper_data = NULL;
1492           free (gpgsm->input_helper_memory);
1493           gpgsm->input_helper_memory = NULL;
1494           return err;
1495         }
1496       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1497       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1498       gpgsm->inline_data = NULL;
1499
1500       return start (gpgsm, "IMPORT --re-import");
1501     }
1502   else if (dataenc == GPGME_DATA_ENCODING_URL
1503            || dataenc == GPGME_DATA_ENCODING_URL0
1504            || dataenc == GPGME_DATA_ENCODING_URLESC)
1505     {
1506       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1507     }
1508   else
1509     {
1510       gpgsm->input_cb.data = keydata;
1511       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1512       if (err)
1513         return err;
1514       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1515       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1516       gpgsm->inline_data = NULL;
1517
1518       return start (gpgsm, "IMPORT");
1519     }
1520 }
1521
1522
1523 static gpgme_error_t
1524 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1525                gpgme_keylist_mode_t mode)
1526 {
1527   engine_gpgsm_t gpgsm = engine;
1528   char *line;
1529   gpgme_error_t err;
1530   int list_mode = 0;
1531
1532   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1533     list_mode |= 1;
1534   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1535     list_mode |= 2;
1536
1537   if (!pattern)
1538     pattern = "";
1539
1540   /* Always send list-mode option because RESET does not reset it.  */
1541   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1542     return gpg_error_from_errno (errno);
1543   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1544   free (line);
1545   if (err)
1546     return err;
1547
1548
1549   /* Always send key validation because RESET does not reset it.  */
1550
1551   /* Use the validation mode if requested.  We don't check for an error
1552      yet because this is a pretty fresh gpgsm features. */
1553   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1554                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1555                                "OPTION with-validation=1":
1556                                "OPTION with-validation=0" ,
1557                                NULL, NULL);
1558   /* Include the ephemeral keys if requested.  We don't check for an error
1559      yet because this is a pretty fresh gpgsm features. */
1560   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1561                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
1562                                "OPTION with-ephemeral-keys=1":
1563                                "OPTION with-ephemeral-keys=0" ,
1564                                NULL, NULL);
1565
1566
1567   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1568   line = malloc (15 + strlen (pattern) + 1);
1569   if (!line)
1570     return gpg_error_from_errno (errno);
1571   if (secret_only)
1572     {
1573       strcpy (line, "LISTSECRETKEYS ");
1574       strcpy (&line[15], pattern);
1575     }
1576   else
1577     {
1578       strcpy (line, "LISTKEYS ");
1579       strcpy (&line[9], pattern);
1580     }
1581
1582   gpgsm_clear_fd (gpgsm, INPUT_FD);
1583   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1584   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1585   gpgsm->inline_data = NULL;
1586
1587   err = start (gpgsm, line);
1588   free (line);
1589   return err;
1590 }
1591
1592
1593 static gpgme_error_t
1594 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1595                    int reserved, gpgme_keylist_mode_t mode)
1596 {
1597   engine_gpgsm_t gpgsm = engine;
1598   char *line;
1599   gpgme_error_t err;
1600   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1601   int length = 15 + 1;
1602   char *linep;
1603   int any_pattern = 0;
1604   int list_mode = 0;
1605
1606   if (reserved)
1607     return gpg_error (GPG_ERR_INV_VALUE);
1608
1609   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1610     list_mode |= 1;
1611   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1612     list_mode |= 2;
1613
1614   /* Always send list-mode option because RESET does not reset it.  */
1615   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1616     return gpg_error_from_errno (errno);
1617   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1618   free (line);
1619   if (err)
1620     return err;
1621
1622   /* Always send key validation because RESET does not reset it.  */
1623   /* Use the validation mode if required.  We don't check for an error
1624      yet because this is a pretty fresh gpgsm features. */
1625   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1626                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1627                                "OPTION with-validation=1":
1628                                "OPTION with-validation=0" ,
1629                                NULL, NULL);
1630
1631
1632   if (pattern && *pattern)
1633     {
1634       const char **pat = pattern;
1635
1636       while (*pat)
1637         {
1638           const char *patlet = *pat;
1639
1640           while (*patlet)
1641             {
1642               length++;
1643               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1644                 length += 2;
1645               patlet++;
1646             }
1647           pat++;
1648           length++;
1649         }
1650     }
1651   line = malloc (length);
1652   if (!line)
1653     return gpg_error_from_errno (errno);
1654   if (secret_only)
1655     {
1656       strcpy (line, "LISTSECRETKEYS ");
1657       linep = &line[15];
1658     }
1659   else
1660     {
1661       strcpy (line, "LISTKEYS ");
1662       linep = &line[9];
1663     }
1664
1665   if (pattern && *pattern)
1666     {
1667       while (*pattern)
1668         {
1669           const char *patlet = *pattern;
1670
1671           while (*patlet)
1672             {
1673               switch (*patlet)
1674                 {
1675                 case '%':
1676                   *(linep++) = '%';
1677                   *(linep++) = '2';
1678                   *(linep++) = '5';
1679                   break;
1680                 case ' ':
1681                   *(linep++) = '%';
1682                   *(linep++) = '2';
1683                   *(linep++) = '0';
1684                   break;
1685                 case '+':
1686                   *(linep++) = '%';
1687                   *(linep++) = '2';
1688                   *(linep++) = 'B';
1689                   break;
1690                 default:
1691                   *(linep++) = *patlet;
1692                   break;
1693                 }
1694               patlet++;
1695             }
1696           any_pattern = 1;
1697           *linep++ = ' ';
1698           pattern++;
1699         }
1700     }
1701   if (any_pattern)
1702     linep--;
1703   *linep = '\0';
1704
1705   gpgsm_clear_fd (gpgsm, INPUT_FD);
1706   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1707   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1708   gpgsm->inline_data = NULL;
1709
1710   err = start (gpgsm, line);
1711   free (line);
1712   return err;
1713 }
1714
1715
1716 static gpgme_error_t
1717 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1718             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1719             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1720 {
1721   engine_gpgsm_t gpgsm = engine;
1722   gpgme_error_t err;
1723   char *assuan_cmd;
1724   int i;
1725   gpgme_key_t key;
1726
1727   if (!gpgsm)
1728     return gpg_error (GPG_ERR_INV_VALUE);
1729
1730   /* FIXME: This does not work as RESET does not reset it so we can't
1731      revert back to default.  */
1732   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1733     {
1734       /* FIXME: Make sure that if we run multiple operations, that we
1735          can reset any previously set value in case the default is
1736          requested.  */
1737
1738       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1739         return gpg_error_from_errno (errno);
1740       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
1741                                          NULL, NULL);
1742       free (assuan_cmd);
1743       if (err)
1744         return err;
1745     }
1746
1747   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1748     {
1749       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1750       if (s && strlen (s) < 80)
1751         {
1752           char buf[100];
1753
1754           strcpy (stpcpy (buf, "SIGNER "), s);
1755           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1756                                              gpgsm->status.fnc,
1757                                              gpgsm->status.fnc_value);
1758         }
1759       else
1760         err = gpg_error (GPG_ERR_INV_VALUE);
1761       gpgme_key_unref (key);
1762       if (err) 
1763         return err;
1764     }
1765
1766   gpgsm->input_cb.data = in;
1767   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1768   if (err)
1769     return err;
1770   gpgsm->output_cb.data = out;
1771   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1772                       : map_data_enc (gpgsm->output_cb.data));
1773   if (err)
1774     return err;
1775   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1776   gpgsm->inline_data = NULL;
1777
1778   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1779                ? "SIGN --detached" : "SIGN");
1780   return err;
1781 }
1782
1783
1784 static gpgme_error_t
1785 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1786               gpgme_data_t plaintext)
1787 {
1788   engine_gpgsm_t gpgsm = engine;
1789   gpgme_error_t err;
1790
1791   if (!gpgsm)
1792     return gpg_error (GPG_ERR_INV_VALUE);
1793
1794   gpgsm->input_cb.data = sig;
1795   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1796   if (err)
1797     return err;
1798   if (plaintext)
1799     {
1800       /* Normal or cleartext signature.  */
1801       gpgsm->output_cb.data = plaintext;
1802       err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1803       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1804     }
1805   else
1806     {
1807       /* Detached signature.  */
1808       gpgsm->message_cb.data = signed_text;
1809       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
1810       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1811     }
1812   gpgsm->inline_data = NULL;
1813
1814   if (!err)
1815     err = start (gpgsm, "VERIFY");
1816
1817   return err;
1818 }
1819
1820
1821 /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
1822    object.  */
1823 static gpgme_error_t
1824 gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
1825 {
1826   engine_gpgsm_t gpgsm = engine;
1827   gpgme_error_t err = 0;
1828
1829   if (!gpgsm || !output)
1830     return gpg_error (GPG_ERR_INV_VALUE);
1831
1832 #if USE_DESCRIPTOR_PASSING
1833   gpgsm->output_cb.data = output;
1834   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1835   if (err)
1836     return err;
1837
1838   gpgsm_clear_fd (gpgsm, INPUT_FD);
1839   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1840   gpgsm->inline_data = NULL;
1841 # define CMD  "GETAUDITLOG"
1842 #else
1843   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1844   gpgsm_clear_fd (gpgsm, INPUT_FD);
1845   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1846   gpgsm->inline_data = output;
1847 # define CMD  "GETAUDITLOG --data"
1848 #endif
1849
1850   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
1851
1852   return err;
1853 }
1854
1855
1856
1857 static void
1858 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1859                           void *fnc_value) 
1860 {
1861   engine_gpgsm_t gpgsm = engine;
1862
1863   gpgsm->status.fnc = fnc;
1864   gpgsm->status.fnc_value = fnc_value;
1865 }
1866
1867
1868 static gpgme_error_t
1869 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1870                               void *fnc_value) 
1871 {
1872   engine_gpgsm_t gpgsm = engine;
1873
1874   gpgsm->colon.fnc = fnc;
1875   gpgsm->colon.fnc_value = fnc_value;
1876   gpgsm->colon.any = 0;
1877   return 0;
1878 }
1879
1880
1881 static void
1882 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1883 {
1884   engine_gpgsm_t gpgsm = engine;
1885   gpgsm->io_cbs = *io_cbs;
1886 }
1887
1888
1889 static void
1890 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1891 {
1892   engine_gpgsm_t gpgsm = engine;
1893
1894   TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
1895           "event %p, type %d, type_data %p",
1896           gpgsm->io_cbs.event, type, type_data);
1897   if (gpgsm->io_cbs.event)
1898     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1899 }
1900
1901
1902 struct engine_ops _gpgme_engine_ops_gpgsm =
1903   {
1904     /* Static functions.  */
1905     _gpgme_get_gpgsm_path,
1906     NULL,
1907     gpgsm_get_version,
1908     gpgsm_get_req_version,
1909     gpgsm_new,
1910
1911     /* Member functions.  */
1912     gpgsm_release,
1913 #if USE_DESCRIPTOR_PASSING
1914     gpgsm_reset,
1915 #else
1916     NULL,                       /* reset */
1917 #endif
1918     gpgsm_set_status_handler,
1919     NULL,               /* set_command_handler */
1920     gpgsm_set_colon_line_handler,
1921     gpgsm_set_locale,
1922     gpgsm_decrypt,
1923     gpgsm_delete,
1924     NULL,               /* edit */
1925     gpgsm_encrypt,
1926     NULL,               /* encrypt_sign */
1927     gpgsm_export,
1928     gpgsm_export_ext,
1929     gpgsm_genkey,
1930     gpgsm_import,
1931     gpgsm_keylist,
1932     gpgsm_keylist_ext,
1933     gpgsm_sign,
1934     NULL,               /* trustlist */
1935     gpgsm_verify,
1936     gpgsm_getauditlog,
1937     NULL,               /* opassuan_transact */
1938     NULL,               /* conf_load */
1939     NULL,               /* conf_save */
1940     gpgsm_set_io_cbs,
1941     gpgsm_io_event,
1942     gpgsm_cancel
1943   };