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