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