core: Pass the engine's version string to the engine's new function.
[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 *dft_ttytype = NULL;
226   char *optstr;
227
228   (void)version; /* Not yet used.  */
229
230   g13 = calloc (1, sizeof *g13);
231   if (!g13)
232     return gpg_error_from_syserror ();
233
234   g13->status_cb.fd = -1;
235   g13->status_cb.dir = 1;
236   g13->status_cb.tag = 0;
237   g13->status_cb.data = g13;
238
239   pgmname = file_name ? file_name : _gpgme_get_default_g13_name ();
240   argc = 0;
241   argv[argc++] = _gpgme_get_basename (pgmname);
242   if (home_dir)
243     {
244       argv[argc++] = "--homedir";
245       argv[argc++] = home_dir;
246     }
247   argv[argc++] = "--server";
248   argv[argc++] = NULL;
249
250   err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
251                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
252                         NULL);
253   if (err)
254     goto leave;
255   assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
256
257 #if USE_DESCRIPTOR_PASSING
258   err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
259                              NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
260 #else
261   err = assuan_pipe_connect (g13->assuan_ctx, pgmname, argv,
262                              NULL, NULL, NULL, 0);
263 #endif
264   if (err)
265     goto leave;
266
267   err = _gpgme_getenv ("DISPLAY", &dft_display);
268   if (err)
269     goto leave;
270   if (dft_display)
271     {
272       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
273         {
274           free (dft_display);
275           err = gpg_error_from_syserror ();
276           goto leave;
277         }
278       free (dft_display);
279
280       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
281                              NULL, NULL, NULL);
282       free (optstr);
283       if (err)
284         goto leave;
285     }
286
287   if (isatty (1))
288     {
289       int rc;
290
291       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
292
293       /* Even though isatty() returns 1, ttyname_r() may fail in many
294          ways, e.g., when /dev/pts is not accessible under chroot.  */
295       if (!rc)
296         {
297           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
298             {
299               err = gpg_error_from_syserror ();
300               goto leave;
301             }
302           err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
303                                  NULL, NULL, NULL);
304           free (optstr);
305           if (err)
306             goto leave;
307
308           err = _gpgme_getenv ("TERM", &dft_ttytype);
309           if (err)
310             goto leave;
311           if (dft_ttytype)
312             {
313               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
314                 {
315                   free (dft_ttytype);
316                   err = gpg_error_from_syserror ();
317                   goto leave;
318                 }
319               free (dft_ttytype);
320
321               err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
322                                      NULL, NULL, NULL, NULL);
323               free (optstr);
324               if (err)
325                 goto leave;
326             }
327         }
328     }
329
330 #ifdef HAVE_W32_SYSTEM
331   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
332      g13 to tell us when it needs it.  */
333   if (!err)
334     {
335       err = assuan_transact (g13->assuan_ctx, "OPTION allow-pinentry-notify",
336                              NULL, NULL, NULL, NULL, NULL, NULL);
337       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
338         err = 0; /* This is a new feature of g13.  */
339     }
340 #endif /*HAVE_W32_SYSTEM*/
341
342  leave:
343
344   if (err)
345     g13_release (g13);
346   else
347     *engine = g13;
348
349   return err;
350 }
351
352
353 static gpgme_error_t
354 g13_set_locale (void *engine, int category, const char *value)
355 {
356   engine_g13_t g13 = engine;
357   gpgme_error_t err;
358   char *optstr;
359   char *catstr;
360
361   /* FIXME: If value is NULL, we need to reset the option to default.
362      But we can't do this.  So we error out here.  G13 needs support
363      for this.  */
364   if (0)
365     ;
366 #ifdef LC_CTYPE
367   else if (category == LC_CTYPE)
368     {
369       catstr = "lc-ctype";
370       if (!value && g13->lc_ctype_set)
371         return gpg_error (GPG_ERR_INV_VALUE);
372       if (value)
373         g13->lc_ctype_set = 1;
374     }
375 #endif
376 #ifdef LC_MESSAGES
377   else if (category == LC_MESSAGES)
378     {
379       catstr = "lc-messages";
380       if (!value && g13->lc_messages_set)
381         return gpg_error (GPG_ERR_INV_VALUE);
382       if (value)
383         g13->lc_messages_set = 1;
384     }
385 #endif /* LC_MESSAGES */
386   else
387     return gpg_error (GPG_ERR_INV_VALUE);
388
389   /* FIXME: Reset value to default.  */
390   if (!value)
391     return 0;
392
393   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
394     err = gpg_error_from_syserror ();
395   else
396     {
397       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
398                              NULL, NULL, NULL, NULL);
399       free (optstr);
400     }
401
402   return err;
403 }
404
405
406 #if USE_DESCRIPTOR_PASSING
407 static gpgme_error_t
408 g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
409                            engine_status_handler_t status_fnc,
410                            void *status_fnc_value)
411 {
412   gpg_error_t err;
413   char *line;
414   size_t linelen;
415
416   err = assuan_write_line (ctx, cmd);
417   if (err)
418     return err;
419
420   do
421     {
422       err = assuan_read_line (ctx, &line, &linelen);
423       if (err)
424         return err;
425
426       if (*line == '#' || !linelen)
427         continue;
428
429       if (linelen >= 2
430           && line[0] == 'O' && line[1] == 'K'
431           && (line[2] == '\0' || line[2] == ' '))
432         return 0;
433       else if (linelen >= 4
434           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
435           && line[3] == ' ')
436         err = atoi (&line[4]);
437       else if (linelen >= 2
438                && line[0] == 'S' && line[1] == ' ')
439         {
440           char *rest;
441
442           rest = strchr (line + 2, ' ');
443           if (!rest)
444             rest = line + linelen; /* set to an empty string */
445           else
446             *(rest++) = 0;
447
448           /* Nothing to do with status lines.  */
449         }
450       else
451         err = gpg_error (GPG_ERR_GENERAL);
452     }
453   while (!err);
454
455   return err;
456 }
457 #endif
458
459
460 static gpgme_error_t
461 status_handler (void *opaque, int fd)
462 {
463   struct io_cb_data *data = (struct io_cb_data *) opaque;
464   engine_g13_t g13 = (engine_g13_t) data->handler_value;
465   gpgme_error_t err = 0;
466   char *line;
467   size_t linelen;
468
469   do
470     {
471       err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
472       if (err)
473         {
474           /* Try our best to terminate the connection friendly.  */
475           /*      assuan_write_line (g13->assuan_ctx, "BYE"); */
476           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
477                   "fd 0x%x: error reading assuan line: %s",
478                   fd, gpg_strerror (err));
479         }
480       else if (linelen >= 3
481                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
482                && (line[3] == '\0' || line[3] == ' '))
483         {
484           if (line[3] == ' ')
485             err = atoi (&line[4]);
486           if (! err)
487             err = gpg_error (GPG_ERR_GENERAL);
488           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
489                   "fd 0x%x: ERR line: %s",
490                   fd, err ? gpg_strerror (err) : "ok");
491
492           /* Command execution errors are not fatal, as we use
493              a session based protocol.  */
494           data->op_err = err;
495
496           /* The caller will do the rest (namely, call cancel_op,
497              which closes status_fd).  */
498           return 0;
499         }
500       else if (linelen >= 2
501                && line[0] == 'O' && line[1] == 'K'
502                && (line[2] == '\0' || line[2] == ' '))
503         {
504           TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
505                   "fd 0x%x: OK line", fd);
506
507           _gpgme_io_close (g13->status_cb.fd);
508           return 0;
509         }
510       else if (linelen > 2
511                && line[0] == 'D' && line[1] == ' ')
512         {
513           /* We are using the colon handler even for plain inline data
514              - strange name for that function but for historic reasons
515              we keep it.  */
516           /* FIXME We can't use this for binary data because we
517              assume this is a string.  For the current usage of colon
518              output it is correct.  */
519           char *src = line + 2;
520           char *end = line + linelen;
521           char *dst = src;
522
523           linelen = 0;
524           while (src < end)
525             {
526               if (*src == '%' && src + 2 < end)
527                 {
528                   /* Handle escaped characters.  */
529                   ++src;
530                   *dst++ = _gpgme_hextobyte (src);
531                   src += 2;
532                 }
533               else
534                 *dst++ = *src++;
535
536               linelen++;
537             }
538
539           src = line + 2;
540           if (linelen && g13->user.data_cb)
541             err = g13->user.data_cb (g13->user.data_cb_value,
542                                        src, linelen);
543           else
544             err = 0;
545
546           TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
547                   "fd 0x%x: D inlinedata; status from cb: %s",
548                   fd, (g13->user.data_cb ?
549                        (err? gpg_strerror (err):"ok"):"no callback"));
550
551         }
552       else if (linelen > 2
553                && line[0] == 'S' && line[1] == ' ')
554         {
555           char *src;
556           char *args;
557
558           src = line + 2;
559           while (*src == ' ')
560             src++;
561
562           args = strchr (line + 2, ' ');
563           if (!args)
564             args = line + linelen; /* set to an empty string */
565           else
566             *(args++) = 0;
567
568           while (*args == ' ')
569             args++;
570
571           if (g13->user.status_cb)
572             err = g13->user.status_cb (g13->user.status_cb_value,
573                                        src, args);
574           else
575             err = 0;
576
577           TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
578                   "fd 0x%x: S line (%s) - status from cb: %s",
579                   fd, line+2, (g13->user.status_cb ?
580                                (err? gpg_strerror (err):"ok"):"no callback"));
581         }
582       else if (linelen >= 7
583                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
584                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
585                && line[6] == 'E'
586                && (line[7] == '\0' || line[7] == ' '))
587         {
588           char *src;
589           char *args;
590
591           for (src=line+7; *src == ' '; src++)
592             ;
593
594           args = strchr (src, ' ');
595           if (!args)
596             args = line + linelen; /* Let it point to an empty string.  */
597           else
598             *(args++) = 0;
599
600           while (*args == ' ')
601             args++;
602
603           err = default_inq_cb (g13, src, args);
604           if (!err)
605             {
606               /* Flush and send END.  */
607               err = assuan_send_data (g13->assuan_ctx, NULL, 0);
608             }
609           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
610             {
611               /* Flush and send CANcel.  */
612               err = assuan_send_data (g13->assuan_ctx, NULL, 1);
613             }
614           assuan_write_line (g13->assuan_ctx, "END");
615         }
616     }
617   while (!err && assuan_pending_line (g13->assuan_ctx));
618
619   return err;
620 }
621
622
623 static gpgme_error_t
624 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
625 {
626   gpgme_error_t err;
627
628   TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
629               "fd %d, dir %d", iocbd->fd, iocbd->dir);
630   err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
631                               iocbd->fd, iocbd->dir,
632                               handler, iocbd->data, &iocbd->tag);
633   if (err)
634     return TRACE_ERR (err);
635   if (!iocbd->dir)
636     /* FIXME Kludge around poll() problem.  */
637     err = _gpgme_io_set_nonblocking (iocbd->fd);
638   return TRACE_ERR (err);
639 }
640
641
642 static gpgme_error_t
643 start (engine_g13_t g13, const char *command)
644 {
645   gpgme_error_t err;
646   assuan_fd_t afdlist[5];
647   int fdlist[5];
648   int nfds;
649   int i;
650
651   /* We need to know the fd used by assuan for reads.  We do this by
652      using the assumption that the first returned fd from
653      assuan_get_active_fds() is always this one.  */
654   nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
655                                 afdlist, DIM (afdlist));
656   if (nfds < 1)
657     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
658   /* For now... */
659   for (i = 0; i < nfds; i++)
660     fdlist[i] = (int) afdlist[i];
661
662   /* We "duplicate" the file descriptor, so we can close it here (we
663      can't close fdlist[0], as that is closed by libassuan, and
664      closing it here might cause libassuan to close some unrelated FD
665      later).  Alternatively, we could special case status_fd and
666      register/unregister it manually as needed, but this increases
667      code duplication and is more complicated as we can not use the
668      close notifications etc.  A third alternative would be to let
669      Assuan know that we closed the FD, but that complicates the
670      Assuan interface.  */
671
672   g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
673   if (g13->status_cb.fd < 0)
674     return gpg_error_from_syserror ();
675
676   if (_gpgme_io_set_close_notify (g13->status_cb.fd,
677                                   close_notify_handler, g13))
678     {
679       _gpgme_io_close (g13->status_cb.fd);
680       g13->status_cb.fd = -1;
681       return gpg_error (GPG_ERR_GENERAL);
682     }
683
684   err = add_io_cb (g13, &g13->status_cb, status_handler);
685   if (!err)
686     err = assuan_write_line (g13->assuan_ctx, command);
687
688   if (!err)
689     g13_io_event (g13, GPGME_EVENT_START, NULL);
690
691   return err;
692 }
693
694
695 #if USE_DESCRIPTOR_PASSING
696 static gpgme_error_t
697 g13_reset (void *engine)
698 {
699   engine_g13_t g13 = engine;
700
701   /* We must send a reset because we need to reset the list of
702      signers.  Note that RESET does not reset OPTION commands. */
703   return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
704 }
705 #endif
706
707
708 static gpgme_error_t
709 g13_transact (void *engine,
710                 const char *command,
711                 gpgme_assuan_data_cb_t data_cb,
712                 void *data_cb_value,
713                 gpgme_assuan_inquire_cb_t inq_cb,
714                 void *inq_cb_value,
715                 gpgme_assuan_status_cb_t status_cb,
716                 void *status_cb_value)
717 {
718   engine_g13_t g13 = engine;
719   gpgme_error_t err;
720
721   if (!g13 || !command || !*command)
722     return gpg_error (GPG_ERR_INV_VALUE);
723
724   g13->user.data_cb = data_cb;
725   g13->user.data_cb_value = data_cb_value;
726   g13->user.inq_cb = inq_cb;
727   g13->user.inq_cb_value = inq_cb_value;
728   g13->user.status_cb = status_cb;
729   g13->user.status_cb_value = status_cb_value;
730
731   err = start (g13, command);
732   return err;
733 }
734
735
736
737 static void
738 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
739 {
740   engine_g13_t g13 = engine;
741   g13->io_cbs = *io_cbs;
742 }
743
744
745 static void
746 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
747 {
748   engine_g13_t g13 = engine;
749
750   TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
751           "event %p, type %d, type_data %p",
752           g13->io_cbs.event, type, type_data);
753   if (g13->io_cbs.event)
754     (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
755 }
756
757
758 struct engine_ops _gpgme_engine_ops_g13 =
759   {
760     /* Static functions.  */
761     _gpgme_get_default_g13_name,
762     NULL,
763     g13_get_version,
764     g13_get_req_version,
765     g13_new,
766
767     /* Member functions.  */
768     g13_release,
769 #if USE_DESCRIPTOR_PASSING
770     g13_reset,
771 #else
772     NULL,                       /* reset */
773 #endif
774     NULL,               /* set_status_cb */
775     NULL,               /* set_status_handler */
776     NULL,               /* set_command_handler */
777     NULL,               /* set_colon_line_handler */
778     g13_set_locale,
779     NULL,               /* set_protocol */
780     NULL,               /* decrypt */
781     NULL,               /* decrypt_verify */
782     NULL,               /* delete */
783     NULL,               /* edit */
784     NULL,               /* encrypt */
785     NULL,               /* encrypt_sign */
786     NULL,               /* export */
787     NULL,               /* export_ext */
788     NULL,               /* genkey */
789     NULL,               /* import */
790     NULL,               /* keylist */
791     NULL,               /* keylist_ext */
792     NULL,               /* sign */
793     NULL,               /* trustlist */
794     NULL,               /* verify */
795     NULL,               /* getauditlog */
796     g13_transact,
797     NULL,               /* conf_load */
798     NULL,               /* conf_save */
799     g13_set_io_cbs,
800     g13_io_event,
801     g13_cancel,
802     g13_cancel_op,
803     NULL,               /* passwd */
804     NULL,               /* set_pinentry_mode */
805     NULL                /* opspawn */
806   };