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