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