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