core: Use const char * where appropriate.
[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, int use_armor,
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, use_armor ? "--armor"
1472                           : map_data_enc (gpgsm->output_cb.data));
1473       if (err)
1474         return err;
1475       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1476       gpgsm->inline_data = NULL;
1477
1478       err = start (gpgsm, "GENKEY");
1479       return err;
1480     }
1481
1482   (void)userid;
1483   (void)algo;
1484   (void)expires;
1485   (void)key;
1486   (void)flags;
1487
1488   /* The new interface has not yet been implemented,  */
1489   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1490 }
1491
1492
1493 static gpgme_error_t
1494 gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
1495 {
1496   engine_gpgsm_t gpgsm = engine;
1497   gpgme_error_t err;
1498   gpgme_data_encoding_t dataenc;
1499   int idx;
1500
1501   if (!gpgsm)
1502     return gpg_error (GPG_ERR_INV_VALUE);
1503
1504   if (keydata && keyarray)
1505     return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
1506
1507   dataenc = gpgme_data_get_encoding (keydata);
1508
1509   if (keyarray)
1510     {
1511       size_t buflen;
1512       char *buffer, *p;
1513
1514       /* Fist check whether the engine already features the
1515          --re-import option.  */
1516       err = gpgsm_assuan_simple_command
1517         (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
1518       if (err)
1519         return gpg_error (GPG_ERR_NOT_SUPPORTED);
1520
1521       /* Create an internal data object with a list of all
1522          fingerprints.  The data object and its memory (to avoid an
1523          extra copy by gpgme_data_new_from_mem) are stored in two
1524          variables which are released by the close_notify_handler.  */
1525       for (idx=0, buflen=0; keyarray[idx]; idx++)
1526         {
1527           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1528               && keyarray[idx]->subkeys
1529               && keyarray[idx]->subkeys->fpr
1530               && *keyarray[idx]->subkeys->fpr)
1531             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
1532         }
1533       /* Allocate a bufer with extra space for the trailing Nul
1534          introduced by the use of stpcpy.  */
1535       buffer = malloc (buflen+1);
1536       if (!buffer)
1537         return gpg_error_from_syserror ();
1538       for (idx=0, p = buffer; keyarray[idx]; idx++)
1539         {
1540           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1541               && keyarray[idx]->subkeys
1542               && keyarray[idx]->subkeys->fpr
1543               && *keyarray[idx]->subkeys->fpr)
1544             p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
1545         }
1546
1547       err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
1548                                      buffer, buflen, 0);
1549       if (err)
1550         {
1551           free (buffer);
1552           return err;
1553         }
1554       gpgsm->input_helper_memory = buffer;
1555
1556       gpgsm->input_cb.data = gpgsm->input_helper_data;
1557       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1558       if (err)
1559         {
1560           gpgme_data_release (gpgsm->input_helper_data);
1561           gpgsm->input_helper_data = NULL;
1562           free (gpgsm->input_helper_memory);
1563           gpgsm->input_helper_memory = NULL;
1564           return err;
1565         }
1566       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1567       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1568       gpgsm->inline_data = NULL;
1569
1570       return start (gpgsm, "IMPORT --re-import");
1571     }
1572   else if (dataenc == GPGME_DATA_ENCODING_URL
1573            || dataenc == GPGME_DATA_ENCODING_URL0
1574            || dataenc == GPGME_DATA_ENCODING_URLESC)
1575     {
1576       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1577     }
1578   else
1579     {
1580       gpgsm->input_cb.data = keydata;
1581       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1582       if (err)
1583         return err;
1584       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1585       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1586       gpgsm->inline_data = NULL;
1587
1588       return start (gpgsm, "IMPORT");
1589     }
1590 }
1591
1592
1593 static gpgme_error_t
1594 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1595                gpgme_keylist_mode_t mode, int engine_flags)
1596 {
1597   engine_gpgsm_t gpgsm = engine;
1598   char *line;
1599   gpgme_error_t err;
1600   int list_mode = 0;
1601
1602   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1603     list_mode |= 1;
1604   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1605     list_mode |= 2;
1606
1607   if (!pattern)
1608     pattern = "";
1609
1610   /* Hack to make sure that the agent is started.  Only if the agent
1611      has been started an application may connect to the agent via
1612      GPGME_PROTOCOL_ASSUAN - for example to look for smartcards.  We
1613      do this only if a secret key listing has been requested.  In
1614      general this is not needed because a secret key listing starts
1615      the agent.  However on a fresh installation no public keys are
1616      available and thus there is no need for gpgsm to ask the agent
1617      whether a secret key exists for the public key.  */
1618   if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
1619     gpgsm_assuan_simple_command (gpgsm, "GETINFO agent-check", NULL, NULL);
1620
1621   /* Always send list-mode option because RESET does not reset it.  */
1622   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1623     return gpg_error_from_syserror ();
1624   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
1625   free (line);
1626   if (err)
1627     return err;
1628
1629
1630   /* Always send key validation because RESET does not reset it.  */
1631
1632   /* Use the validation mode if requested.  We don't check for an error
1633      yet because this is a pretty fresh gpgsm features. */
1634   gpgsm_assuan_simple_command (gpgsm,
1635                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1636                                "OPTION with-validation=1":
1637                                "OPTION with-validation=0" ,
1638                                NULL, NULL);
1639   /* Include the ephemeral keys if requested.  We don't check for an error
1640      yet because this is a pretty fresh gpgsm features. */
1641   gpgsm_assuan_simple_command (gpgsm,
1642                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
1643                                "OPTION with-ephemeral-keys=1":
1644                                "OPTION with-ephemeral-keys=0" ,
1645                                NULL, NULL);
1646   gpgsm_assuan_simple_command (gpgsm,
1647                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1648                                "OPTION with-secret=1":
1649                                "OPTION with-secret=0" ,
1650                                NULL, NULL);
1651   gpgsm_assuan_simple_command (gpgsm,
1652                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1653                                "OPTION offline=1":
1654                                "OPTION offline=0" ,
1655                                NULL, NULL);
1656
1657
1658   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1659   line = malloc (15 + strlen (pattern) + 1);
1660   if (!line)
1661     return gpg_error_from_syserror ();
1662   if (secret_only)
1663     {
1664       strcpy (line, "LISTSECRETKEYS ");
1665       strcpy (&line[15], pattern);
1666     }
1667   else
1668     {
1669       strcpy (line, "LISTKEYS ");
1670       strcpy (&line[9], pattern);
1671     }
1672
1673   gpgsm_clear_fd (gpgsm, INPUT_FD);
1674   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1675   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1676   gpgsm->inline_data = NULL;
1677
1678   err = start (gpgsm, line);
1679   free (line);
1680   return err;
1681 }
1682
1683
1684 static gpgme_error_t
1685 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1686                    int reserved, gpgme_keylist_mode_t mode, int engine_flags)
1687 {
1688   engine_gpgsm_t gpgsm = engine;
1689   char *line;
1690   gpgme_error_t err;
1691   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1692   int length = 15 + 1;
1693   char *linep;
1694   int any_pattern = 0;
1695   int list_mode = 0;
1696
1697   if (reserved)
1698     return gpg_error (GPG_ERR_INV_VALUE);
1699
1700   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1701     list_mode |= 1;
1702   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1703     list_mode |= 2;
1704
1705   /* Always send list-mode option because RESET does not reset it.  */
1706   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1707     return gpg_error_from_syserror ();
1708   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
1709   free (line);
1710   if (err)
1711     return err;
1712
1713   /* Always send key validation because RESET does not reset it.  */
1714   /* Use the validation mode if required.  We don't check for an error
1715      yet because this is a pretty fresh gpgsm features. */
1716   gpgsm_assuan_simple_command (gpgsm,
1717                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1718                                "OPTION with-validation=1":
1719                                "OPTION with-validation=0" ,
1720                                NULL, NULL);
1721   gpgsm_assuan_simple_command (gpgsm,
1722                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1723                                "OPTION with-secret=1":
1724                                "OPTION with-secret=0" ,
1725                                NULL, NULL);
1726   gpgsm_assuan_simple_command (gpgsm,
1727                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1728                                "OPTION offline=1":
1729                                "OPTION offline=0" ,
1730                                NULL, NULL);
1731
1732   if (pattern && *pattern)
1733     {
1734       const char **pat = pattern;
1735
1736       while (*pat)
1737         {
1738           const char *patlet = *pat;
1739
1740           while (*patlet)
1741             {
1742               length++;
1743               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1744                 length += 2;
1745               patlet++;
1746             }
1747           pat++;
1748           length++;
1749         }
1750     }
1751   line = malloc (length);
1752   if (!line)
1753     return gpg_error_from_syserror ();
1754   if (secret_only)
1755     {
1756       strcpy (line, "LISTSECRETKEYS ");
1757       linep = &line[15];
1758     }
1759   else
1760     {
1761       strcpy (line, "LISTKEYS ");
1762       linep = &line[9];
1763     }
1764
1765   if (pattern && *pattern)
1766     {
1767       while (*pattern)
1768         {
1769           const char *patlet = *pattern;
1770
1771           while (*patlet)
1772             {
1773               switch (*patlet)
1774                 {
1775                 case '%':
1776                   *(linep++) = '%';
1777                   *(linep++) = '2';
1778                   *(linep++) = '5';
1779                   break;
1780                 case ' ':
1781                   *(linep++) = '%';
1782                   *(linep++) = '2';
1783                   *(linep++) = '0';
1784                   break;
1785                 case '+':
1786                   *(linep++) = '%';
1787                   *(linep++) = '2';
1788                   *(linep++) = 'B';
1789                   break;
1790                 default:
1791                   *(linep++) = *patlet;
1792                   break;
1793                 }
1794               patlet++;
1795             }
1796           any_pattern = 1;
1797           *linep++ = ' ';
1798           pattern++;
1799         }
1800     }
1801   if (any_pattern)
1802     linep--;
1803   *linep = '\0';
1804
1805   gpgsm_clear_fd (gpgsm, INPUT_FD);
1806   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1807   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1808   gpgsm->inline_data = NULL;
1809
1810   err = start (gpgsm, line);
1811   free (line);
1812   return err;
1813 }
1814
1815
1816 static gpgme_error_t
1817 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1818             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1819             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1820 {
1821   engine_gpgsm_t gpgsm = engine;
1822   gpgme_error_t err;
1823   char *assuan_cmd;
1824   int i;
1825   gpgme_key_t key;
1826
1827   (void)use_textmode;
1828
1829   if (!gpgsm)
1830     return gpg_error (GPG_ERR_INV_VALUE);
1831
1832   /* FIXME: This does not work as RESET does not reset it so we can't
1833      revert back to default.  */
1834   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1835     {
1836       /* FIXME: Make sure that if we run multiple operations, that we
1837          can reset any previously set value in case the default is
1838          requested.  */
1839
1840       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1841         return gpg_error_from_syserror ();
1842       err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
1843       free (assuan_cmd);
1844       if (err)
1845         return err;
1846     }
1847
1848   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1849     {
1850       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1851       if (s && strlen (s) < 80)
1852         {
1853           char buf[100];
1854
1855           strcpy (stpcpy (buf, "SIGNER "), s);
1856           err = gpgsm_assuan_simple_command (gpgsm, buf,
1857                                              gpgsm->status.fnc,
1858                                              gpgsm->status.fnc_value);
1859         }
1860       else
1861         err = gpg_error (GPG_ERR_INV_VALUE);
1862       gpgme_key_unref (key);
1863       if (err)
1864         return err;
1865     }
1866
1867   gpgsm->input_cb.data = in;
1868   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1869   if (err)
1870     return err;
1871   gpgsm->output_cb.data = out;
1872   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1873                       : map_data_enc (gpgsm->output_cb.data));
1874   if (err)
1875     return err;
1876   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1877   gpgsm->inline_data = NULL;
1878
1879   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1880                ? "SIGN --detached" : "SIGN");
1881   return err;
1882 }
1883
1884
1885 static gpgme_error_t
1886 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1887               gpgme_data_t plaintext)
1888 {
1889   engine_gpgsm_t gpgsm = engine;
1890   gpgme_error_t err;
1891
1892   if (!gpgsm)
1893     return gpg_error (GPG_ERR_INV_VALUE);
1894
1895   gpgsm->input_cb.data = sig;
1896   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1897   if (err)
1898     return err;
1899   if (plaintext)
1900     {
1901       /* Normal or cleartext signature.  */
1902       gpgsm->output_cb.data = plaintext;
1903       err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1904       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1905     }
1906   else
1907     {
1908       /* Detached signature.  */
1909       gpgsm->message_cb.data = signed_text;
1910       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
1911       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1912     }
1913   gpgsm->inline_data = NULL;
1914
1915   if (!err)
1916     err = start (gpgsm, "VERIFY");
1917
1918   return err;
1919 }
1920
1921
1922 /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
1923    object.  */
1924 static gpgme_error_t
1925 gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
1926 {
1927   engine_gpgsm_t gpgsm = engine;
1928   gpgme_error_t err = 0;
1929
1930   if (!gpgsm || !output)
1931     return gpg_error (GPG_ERR_INV_VALUE);
1932
1933 #if USE_DESCRIPTOR_PASSING
1934   gpgsm->output_cb.data = output;
1935   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1936   if (err)
1937     return err;
1938
1939   gpgsm_clear_fd (gpgsm, INPUT_FD);
1940   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1941   gpgsm->inline_data = NULL;
1942 # define CMD  "GETAUDITLOG"
1943 #else
1944   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1945   gpgsm_clear_fd (gpgsm, INPUT_FD);
1946   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1947   gpgsm->inline_data = output;
1948 # define CMD  "GETAUDITLOG --data"
1949 #endif
1950
1951   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
1952
1953   return err;
1954 }
1955
1956
1957 /* This sets a status callback for monitoring status lines before they
1958  * are passed to a caller set handler.  */
1959 static void
1960 gpgsm_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
1961 {
1962   engine_gpgsm_t gpgsm = engine;
1963
1964   gpgsm->status.mon_cb = cb;
1965   gpgsm->status.mon_cb_value = cb_value;
1966 }
1967
1968
1969 static void
1970 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1971                           void *fnc_value)
1972 {
1973   engine_gpgsm_t gpgsm = engine;
1974
1975   gpgsm->status.fnc = fnc;
1976   gpgsm->status.fnc_value = fnc_value;
1977 }
1978
1979
1980 static gpgme_error_t
1981 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1982                               void *fnc_value)
1983 {
1984   engine_gpgsm_t gpgsm = engine;
1985
1986   gpgsm->colon.fnc = fnc;
1987   gpgsm->colon.fnc_value = fnc_value;
1988   gpgsm->colon.any = 0;
1989   return 0;
1990 }
1991
1992
1993 static void
1994 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1995 {
1996   engine_gpgsm_t gpgsm = engine;
1997   gpgsm->io_cbs = *io_cbs;
1998 }
1999
2000
2001 static void
2002 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
2003 {
2004   engine_gpgsm_t gpgsm = engine;
2005
2006   TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
2007           "event %p, type %d, type_data %p",
2008           gpgsm->io_cbs.event, type, type_data);
2009   if (gpgsm->io_cbs.event)
2010     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
2011 }
2012
2013
2014 static gpgme_error_t
2015 gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
2016 {
2017   engine_gpgsm_t gpgsm = engine;
2018   gpgme_error_t err;
2019   char *line;
2020
2021   (void)flags;
2022
2023   if (!key || !key->subkeys || !key->subkeys->fpr)
2024     return gpg_error (GPG_ERR_INV_CERT_OBJ);
2025
2026   if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
2027     return gpg_error_from_syserror ();
2028
2029   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2030   gpgsm_clear_fd (gpgsm, INPUT_FD);
2031   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2032   gpgsm->inline_data = NULL;
2033
2034   err = start (gpgsm, line);
2035   free (line);
2036
2037   return err;
2038 }
2039
2040
2041
2042 struct engine_ops _gpgme_engine_ops_gpgsm =
2043   {
2044     /* Static functions.  */
2045     _gpgme_get_default_gpgsm_name,
2046     NULL,
2047     gpgsm_get_version,
2048     gpgsm_get_req_version,
2049     gpgsm_new,
2050
2051     /* Member functions.  */
2052     gpgsm_release,
2053 #if USE_DESCRIPTOR_PASSING
2054     gpgsm_reset,
2055 #else
2056     NULL,                       /* reset */
2057 #endif
2058     gpgsm_set_status_cb,
2059     gpgsm_set_status_handler,
2060     NULL,               /* set_command_handler */
2061     gpgsm_set_colon_line_handler,
2062     gpgsm_set_locale,
2063     NULL,               /* set_protocol */
2064     gpgsm_decrypt,
2065     gpgsm_decrypt,
2066     gpgsm_delete,       /* decrypt_verify */
2067     NULL,               /* edit */
2068     gpgsm_encrypt,
2069     NULL,               /* encrypt_sign */
2070     gpgsm_export,
2071     gpgsm_export_ext,
2072     gpgsm_genkey,
2073     gpgsm_import,
2074     gpgsm_keylist,
2075     gpgsm_keylist_ext,
2076     gpgsm_sign,
2077     NULL,               /* trustlist */
2078     gpgsm_verify,
2079     gpgsm_getauditlog,
2080     NULL,               /* opassuan_transact */
2081     NULL,               /* conf_load */
2082     NULL,               /* conf_save */
2083     gpgsm_set_io_cbs,
2084     gpgsm_io_event,
2085     gpgsm_cancel,
2086     NULL,               /* cancel_op */
2087     gpgsm_passwd,
2088     NULL,               /* set_pinentry_mode */
2089     NULL                /* opspawn */
2090   };