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