w32: Remove all support for WindowsCE
[gpgme.git] / src / engine-g13.c
1 /* engine-g13.c - G13 engine.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
4
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_SYS_TYPES_H
29 # include <sys/types.h>
30 #endif
31 #include <assert.h>
32 #ifdef HAVE_UNISTD_H
33 # include <unistd.h>
34 #endif
35 #ifdef HAVE_LOCALE_H
36 #include <locale.h>
37 #endif
38 #include <fcntl.h> /* FIXME */
39 #include <errno.h>
40
41 #include "gpgme.h"
42 #include "util.h"
43 #include "ops.h"
44 #include "wait.h"
45 #include "priv-io.h"
46 #include "sema.h"
47
48 #include "assuan.h"
49 #include "debug.h"
50
51 #include "engine-backend.h"
52
53 \f
54 typedef struct
55 {
56   int fd;       /* FD we talk about.  */
57   int server_fd;/* Server FD for this connection.  */
58   int dir;      /* Inbound/Outbound, maybe given implicit?  */
59   void *data;   /* Handler-specific data.  */
60   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
61   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
62                              need this because _gpgme_io_fd2str can't
63                              be used on a closed descriptor.  */
64 } iocb_data_t;
65
66
67 struct engine_g13
68 {
69   assuan_context_t assuan_ctx;
70
71   int lc_ctype_set;
72   int lc_messages_set;
73
74   iocb_data_t status_cb;
75
76   struct gpgme_io_cbs io_cbs;
77
78   /* User provided callbacks.  */
79   struct {
80     gpgme_assuan_data_cb_t data_cb;
81     void *data_cb_value;
82
83     gpgme_assuan_inquire_cb_t inq_cb;
84     void *inq_cb_value;
85
86     gpgme_assuan_status_cb_t status_cb;
87     void *status_cb_value;
88   } user;
89 };
90
91 typedef struct engine_g13 *engine_g13_t;
92
93
94 static void g13_io_event (void *engine,
95                             gpgme_event_io_t type, void *type_data);
96
97
98 \f
99 static char *
100 g13_get_version (const char *file_name)
101 {
102   return _gpgme_get_program_version (file_name ? file_name
103                                      : _gpgme_get_default_g13_name ());
104 }
105
106
107 static const char *
108 g13_get_req_version (void)
109 {
110   return "2.1.0";
111 }
112
113 \f
114 static void
115 close_notify_handler (int fd, void *opaque)
116 {
117   engine_g13_t g13 = opaque;
118
119   assert (fd != -1);
120   if (g13->status_cb.fd == fd)
121     {
122       if (g13->status_cb.tag)
123         (*g13->io_cbs.remove) (g13->status_cb.tag);
124       g13->status_cb.fd = -1;
125       g13->status_cb.tag = NULL;
126     }
127 }
128
129
130 /* This is the default inquiry callback.  We use it to handle the
131    Pinentry notifications.  */
132 static gpgme_error_t
133 default_inq_cb (engine_g13_t g13, const char *keyword, const char *args)
134 {
135   gpg_error_t err;
136
137   if (!strcmp (keyword, "PINENTRY_LAUNCHED"))
138     {
139       _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
140     }
141
142   if (g13->user.inq_cb)
143     {
144       gpgme_data_t data = NULL;
145
146       err = g13->user.inq_cb (g13->user.inq_cb_value,
147                                 keyword, args, &data);
148       if (!err && data)
149         {
150           /* FIXME: Returning data is not yet implemented.  However we
151              need to allow the caller to cleanup his data object.
152              Thus we run the callback in finish mode immediately.  */
153           err = g13->user.inq_cb (g13->user.inq_cb_value,
154                                   NULL, NULL, &data);
155         }
156     }
157   else
158     err = 0;
159
160   return err;
161 }
162
163
164 static gpgme_error_t
165 g13_cancel (void *engine)
166 {
167   engine_g13_t g13 = engine;
168
169   if (!g13)
170     return gpg_error (GPG_ERR_INV_VALUE);
171
172   if (g13->status_cb.fd != -1)
173     _gpgme_io_close (g13->status_cb.fd);
174
175   if (g13->assuan_ctx)
176     {
177       assuan_release (g13->assuan_ctx);
178       g13->assuan_ctx = NULL;
179     }
180
181   return 0;
182 }
183
184
185 static gpgme_error_t
186 g13_cancel_op (void *engine)
187 {
188   engine_g13_t g13 = engine;
189
190   if (!g13)
191     return gpg_error (GPG_ERR_INV_VALUE);
192
193   if (g13->status_cb.fd != -1)
194     _gpgme_io_close (g13->status_cb.fd);
195
196   return 0;
197 }
198
199
200 static void
201 g13_release (void *engine)
202 {
203   engine_g13_t g13 = engine;
204
205   if (!g13)
206     return;
207
208   g13_cancel (engine);
209
210   free (g13);
211 }
212
213
214 static gpgme_error_t
215 g13_new (void **engine, const char *file_name, const char *home_dir,
216          const char *version)
217 {
218   gpgme_error_t err = 0;
219   engine_g13_t g13;
220   const char *pgmname;
221   int argc;
222   const char *argv[5];
223   char *dft_display = NULL;
224   char dft_ttyname[64];
225   char *env_tty = NULL;
226   char *dft_ttytype = NULL;
227   char *optstr;
228
229   (void)version; /* Not yet used.  */
230
231   g13 = calloc (1, sizeof *g13);
232   if (!g13)
233     return gpg_error_from_syserror ();
234
235   g13->status_cb.fd = -1;
236   g13->status_cb.dir = 1;
237   g13->status_cb.tag = 0;
238   g13->status_cb.data = g13;
239
240   pgmname = file_name ? file_name : _gpgme_get_default_g13_name ();
241   argc = 0;
242   argv[argc++] = _gpgme_get_basename (pgmname);
243   if (home_dir)
244     {
245       argv[argc++] = "--homedir";
246       argv[argc++] = home_dir;
247     }
248   argv[argc++] = "--server";
249   argv[argc++] = NULL;
250
251   err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
252                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
253                         NULL);
254   if (err)
255     goto leave;
256   assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
257
258 #if USE_DESCRIPTOR_PASSING
259   err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
260                              NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
261 #else
262   err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
263                              NULL, NULL, NULL, 0);
264 #endif
265   if (err)
266     goto leave;
267
268   err = _gpgme_getenv ("DISPLAY", &dft_display);
269   if (err)
270     goto leave;
271   if (dft_display)
272     {
273       if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
274         {
275           free (dft_display);
276           err = gpg_error_from_syserror ();
277           goto leave;
278         }
279       free (dft_display);
280
281       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
282                              NULL, NULL, NULL);
283       gpgrt_free (optstr);
284       if (err)
285         goto leave;
286     }
287
288   err = _gpgme_getenv ("GPG_TTY", &env_tty);
289   if (isatty (1) || env_tty || err)
290     {
291       int rc = 0;
292
293       if (err)
294         goto leave;
295       else if (env_tty)
296         {
297           snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
298           free (env_tty);
299         }
300       else
301         rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
302
303       /* Even though isatty() returns 1, ttyname_r() may fail in many
304          ways, e.g., when /dev/pts is not accessible under chroot.  */
305       if (!rc)
306         {
307           if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
308             {
309               err = gpg_error_from_syserror ();
310               goto leave;
311             }
312           err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
313                                  NULL, NULL, NULL);
314           gpgrt_free (optstr);
315           if (err)
316             goto leave;
317
318           err = _gpgme_getenv ("TERM", &dft_ttytype);
319           if (err)
320             goto leave;
321           if (dft_ttytype)
322             {
323               if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
324                 {
325                   free (dft_ttytype);
326                   err = gpg_error_from_syserror ();
327                   goto leave;
328                 }
329               free (dft_ttytype);
330
331               err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
332                                      NULL, NULL, NULL, NULL);
333               gpgrt_free (optstr);
334               if (err)
335                 goto leave;
336             }
337         }
338     }
339
340 #ifdef HAVE_W32_SYSTEM
341   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
342      g13 to tell us when it needs it.  */
343   if (!err)
344     {
345       err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
346                              NULL, NULL, NULL, NULL, NULL, NULL);
347       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
348         err = 0; /* This is a new feature of g13.  */
349     }
350 #endif /*HAVE_W32_SYSTEM*/
351
352  leave:
353
354   if (err)
355     g13_release (g13);
356   else
357     *engine = g13;
358
359   return err;
360 }
361
362
363 static gpgme_error_t
364 g13_set_locale (void *engine, int category, const char *value)
365 {
366   engine_g13_t g13 = engine;
367   gpgme_error_t err;
368   char *optstr;
369   const char *catstr;
370
371   /* FIXME: If value is NULL, we need to reset the option to default.
372      But we can't do this.  So we error out here.  G13 needs support
373      for this.  */
374   if (0)
375     ;
376 #ifdef LC_CTYPE
377   else if (category == LC_CTYPE)
378     {
379       catstr = "lc-ctype";
380       if (!value && g13->lc_ctype_set)
381         return gpg_error (GPG_ERR_INV_VALUE);
382       if (value)
383         g13->lc_ctype_set = 1;
384     }
385 #endif
386 #ifdef LC_MESSAGES
387   else if (category == LC_MESSAGES)
388     {
389       catstr = "lc-messages";
390       if (!value && g13->lc_messages_set)
391         return gpg_error (GPG_ERR_INV_VALUE);
392       if (value)
393         g13->lc_messages_set = 1;
394     }
395 #endif /* LC_MESSAGES */
396   else
397     return gpg_error (GPG_ERR_INV_VALUE);
398
399   /* FIXME: Reset value to default.  */
400   if (!value)
401     return 0;
402
403   if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
404     err = gpg_error_from_syserror ();
405   else
406     {
407       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
408                              NULL, NULL, NULL, NULL);
409       gpgrt_free (optstr);
410     }
411
412   return err;
413 }
414
415
416 #if USE_DESCRIPTOR_PASSING
417 static gpgme_error_t
418 g13_assuan_simple_command (assuan_context_t ctx, const char *cmd,
419                            engine_status_handler_t status_fnc,
420                            void *status_fnc_value)
421 {
422   gpg_error_t err;
423   char *line;
424   size_t linelen;
425
426   (void)status_fnc;
427   (void)status_fnc_value;
428
429   err = assuan_write_line (ctx, cmd);
430   if (err)
431     return err;
432
433   do
434     {
435       err = assuan_read_line (ctx, &line, &linelen);
436       if (err)
437         return err;
438
439       if (*line == '#' || !linelen)
440         continue;
441
442       if (linelen >= 2
443           && line[0] == 'O' && line[1] == 'K'
444           && (line[2] == '\0' || line[2] == ' '))
445         return 0;
446       else if (linelen >= 4
447           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
448           && line[3] == ' ')
449         err = atoi (&line[4]);
450       else if (linelen >= 2
451                && line[0] == 'S' && line[1] == ' ')
452         {
453           char *rest;
454
455           rest = strchr (line + 2, ' ');
456           if (!rest)
457             rest = line + linelen; /* set to an empty string */
458           else
459             *(rest++) = 0;
460
461           /* Nothing to do with status lines.  */
462         }
463       else
464         err = gpg_error (GPG_ERR_GENERAL);
465     }
466   while (!err);
467
468   return err;
469 }
470 #endif
471
472
473 static gpgme_error_t
474 status_handler (void *opaque, int fd)
475 {
476   struct io_cb_data *data = (struct io_cb_data *) opaque;
477   engine_g13_t g13 = (engine_g13_t) data->handler_value;
478   gpgme_error_t err = 0;
479   char *line;
480   size_t linelen;
481
482   do
483     {
484       err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
485       if (err)
486         {
487           /* Try our best to terminate the connection friendly.  */
488           /*      assuan_write_line (g13->assuan_ctx, "BYE"); */
489           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
490                   "fd 0x%x: error reading assuan line: %s",
491                   fd, gpg_strerror (err));
492         }
493       else if (linelen >= 3
494                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
495                && (line[3] == '\0' || line[3] == ' '))
496         {
497           if (line[3] == ' ')
498             err = atoi (&line[4]);
499           if (! err)
500             err = gpg_error (GPG_ERR_GENERAL);
501           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
502                   "fd 0x%x: ERR line: %s",
503                   fd, err ? gpg_strerror (err) : "ok");
504
505           /* Command execution errors are not fatal, as we use
506              a session based protocol.  */
507           data->op_err = err;
508
509           /* The caller will do the rest (namely, call cancel_op,
510              which closes status_fd).  */
511           return 0;
512         }
513       else if (linelen >= 2
514                && line[0] == 'O' && line[1] == 'K'
515                && (line[2] == '\0' || line[2] == ' '))
516         {
517           TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
518                   "fd 0x%x: OK line", fd);
519
520           _gpgme_io_close (g13->status_cb.fd);
521           return 0;
522         }
523       else if (linelen > 2
524                && line[0] == 'D' && line[1] == ' ')
525         {
526           /* We are using the colon handler even for plain inline data
527              - strange name for that function but for historic reasons
528              we keep it.  */
529           /* FIXME We can't use this for binary data because we
530              assume this is a string.  For the current usage of colon
531              output it is correct.  */
532           char *src = line + 2;
533           char *end = line + linelen;
534           char *dst = src;
535
536           linelen = 0;
537           while (src < end)
538             {
539               if (*src == '%' && src + 2 < end)
540                 {
541                   /* Handle escaped characters.  */
542                   ++src;
543                   *dst++ = _gpgme_hextobyte (src);
544                   src += 2;
545                 }
546               else
547                 *dst++ = *src++;
548
549               linelen++;
550             }
551
552           src = line + 2;
553           if (linelen && g13->user.data_cb)
554             err = g13->user.data_cb (g13->user.data_cb_value,
555                                        src, linelen);
556           else
557             err = 0;
558
559           TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
560                   "fd 0x%x: D inlinedata; status from cb: %s",
561                   fd, (g13->user.data_cb ?
562                        (err? gpg_strerror (err):"ok"):"no callback"));
563
564         }
565       else if (linelen > 2
566                && line[0] == 'S' && line[1] == ' ')
567         {
568           char *src;
569           char *args;
570
571           src = line + 2;
572           while (*src == ' ')
573             src++;
574
575           args = strchr (line + 2, ' ');
576           if (!args)
577             args = line + linelen; /* set to an empty string */
578           else
579             *(args++) = 0;
580
581           while (*args == ' ')
582             args++;
583
584           if (g13->user.status_cb)
585             err = g13->user.status_cb (g13->user.status_cb_value,
586                                        src, args);
587           else
588             err = 0;
589
590           TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
591                   "fd 0x%x: S line (%s) - status from cb: %s",
592                   fd, line+2, (g13->user.status_cb ?
593                                (err? gpg_strerror (err):"ok"):"no callback"));
594         }
595       else if (linelen >= 7
596                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
597                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
598                && line[6] == 'E'
599                && (line[7] == '\0' || line[7] == ' '))
600         {
601           char *src;
602           char *args;
603
604           for (src=line+7; *src == ' '; src++)
605             ;
606
607           args = strchr (src, ' ');
608           if (!args)
609             args = line + linelen; /* Let it point to an empty string.  */
610           else
611             *(args++) = 0;
612
613           while (*args == ' ')
614             args++;
615
616           err = default_inq_cb (g13, src, args);
617           if (!err)
618             {
619               /* Flush and send END.  */
620               err = assuan_send_data (g13->assuan_ctx, NULL, 0);
621             }
622           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
623             {
624               /* Flush and send CANcel.  */
625               err = assuan_send_data (g13->assuan_ctx, NULL, 1);
626             }
627           assuan_write_line (g13->assuan_ctx, "END");
628         }
629     }
630   while (!err && assuan_pending_line (g13->assuan_ctx));
631
632   return err;
633 }
634
635
636 static gpgme_error_t
637 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
638 {
639   gpgme_error_t err;
640
641   TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
642               "fd %d, dir %d", iocbd->fd, iocbd->dir);
643   err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
644                               iocbd->fd, iocbd->dir,
645                               handler, iocbd->data, &iocbd->tag);
646   if (err)
647     return TRACE_ERR (err);
648   if (!iocbd->dir)
649     /* FIXME Kludge around poll() problem.  */
650     err = _gpgme_io_set_nonblocking (iocbd->fd);
651   return TRACE_ERR (err);
652 }
653
654
655 static gpgme_error_t
656 start (engine_g13_t g13, const char *command)
657 {
658   gpgme_error_t err;
659   assuan_fd_t afdlist[5];
660   int fdlist[5];
661   int nfds;
662   int i;
663
664   /* We need to know the fd used by assuan for reads.  We do this by
665      using the assumption that the first returned fd from
666      assuan_get_active_fds() is always this one.  */
667   nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
668                                 afdlist, DIM (afdlist));
669   if (nfds < 1)
670     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
671   /* For now... */
672   for (i = 0; i < nfds; i++)
673     fdlist[i] = (int) afdlist[i];
674
675   /* We "duplicate" the file descriptor, so we can close it here (we
676      can't close fdlist[0], as that is closed by libassuan, and
677      closing it here might cause libassuan to close some unrelated FD
678      later).  Alternatively, we could special case status_fd and
679      register/unregister it manually as needed, but this increases
680      code duplication and is more complicated as we can not use the
681      close notifications etc.  A third alternative would be to let
682      Assuan know that we closed the FD, but that complicates the
683      Assuan interface.  */
684
685   g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
686   if (g13->status_cb.fd < 0)
687     return gpg_error_from_syserror ();
688
689   if (_gpgme_io_set_close_notify (g13->status_cb.fd,
690                                   close_notify_handler, g13))
691     {
692       _gpgme_io_close (g13->status_cb.fd);
693       g13->status_cb.fd = -1;
694       return gpg_error (GPG_ERR_GENERAL);
695     }
696
697   err = add_io_cb (g13, &g13->status_cb, status_handler);
698   if (!err)
699     err = assuan_write_line (g13->assuan_ctx, command);
700
701   if (!err)
702     g13_io_event (g13, GPGME_EVENT_START, NULL);
703
704   return err;
705 }
706
707
708 #if USE_DESCRIPTOR_PASSING
709 static gpgme_error_t
710 g13_reset (void *engine)
711 {
712   engine_g13_t g13 = engine;
713
714   /* We must send a reset because we need to reset the list of
715      signers.  Note that RESET does not reset OPTION commands. */
716   return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
717 }
718 #endif
719
720
721 static gpgme_error_t
722 g13_transact (void *engine,
723                 const char *command,
724                 gpgme_assuan_data_cb_t data_cb,
725                 void *data_cb_value,
726                 gpgme_assuan_inquire_cb_t inq_cb,
727                 void *inq_cb_value,
728                 gpgme_assuan_status_cb_t status_cb,
729                 void *status_cb_value)
730 {
731   engine_g13_t g13 = engine;
732   gpgme_error_t err;
733
734   if (!g13 || !command || !*command)
735     return gpg_error (GPG_ERR_INV_VALUE);
736
737   g13->user.data_cb = data_cb;
738   g13->user.data_cb_value = data_cb_value;
739   g13->user.inq_cb = inq_cb;
740   g13->user.inq_cb_value = inq_cb_value;
741   g13->user.status_cb = status_cb;
742   g13->user.status_cb_value = status_cb_value;
743
744   err = start (g13, command);
745   return err;
746 }
747
748
749
750 static void
751 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
752 {
753   engine_g13_t g13 = engine;
754   g13->io_cbs = *io_cbs;
755 }
756
757
758 static void
759 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
760 {
761   engine_g13_t g13 = engine;
762
763   TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
764           "event %p, type %d, type_data %p",
765           g13->io_cbs.event, type, type_data);
766   if (g13->io_cbs.event)
767     (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
768 }
769
770
771 struct engine_ops _gpgme_engine_ops_g13 =
772   {
773     /* Static functions.  */
774     _gpgme_get_default_g13_name,
775     NULL,
776     g13_get_version,
777     g13_get_req_version,
778     g13_new,
779
780     /* Member functions.  */
781     g13_release,
782 #if USE_DESCRIPTOR_PASSING
783     g13_reset,
784 #else
785     NULL,                       /* reset */
786 #endif
787     NULL,               /* set_status_cb */
788     NULL,               /* set_status_handler */
789     NULL,               /* set_command_handler */
790     NULL,               /* set_colon_line_handler */
791     g13_set_locale,
792     NULL,               /* set_protocol */
793     NULL,               /* set_engine_flags */
794     NULL,               /* decrypt */
795     NULL,               /* delete */
796     NULL,               /* edit */
797     NULL,               /* encrypt */
798     NULL,               /* encrypt_sign */
799     NULL,               /* export */
800     NULL,               /* export_ext */
801     NULL,               /* genkey */
802     NULL,               /* import */
803     NULL,               /* keylist */
804     NULL,               /* keylist_ext */
805     NULL,               /* keylist_data */
806     NULL,               /* keysign */
807     NULL,               /* tofu_policy */
808     NULL,               /* sign */
809     NULL,               /* trustlist */
810     NULL,               /* verify */
811     NULL,               /* getauditlog */
812     g13_transact,
813     NULL,               /* conf_load */
814     NULL,               /* conf_save */
815     NULL,               /* conf_dir */
816     NULL,               /* query_swdb */
817     g13_set_io_cbs,
818     g13_io_event,
819     g13_cancel,
820     g13_cancel_op,
821     NULL,               /* passwd */
822     NULL,               /* set_pinentry_mode */
823     NULL                /* opspawn */
824   };