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