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