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