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