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 : char request_origin[10]; /* Copy from the CTX. */
100 : };
101 : typedef struct engine_llass *engine_llass_t;
102 :
103 :
104 0 : gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
105 : {
106 0 : engine_llass_t llass = engine;
107 0 : 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 :
119 : /* return the default home directory. */
120 : static const char *
121 112 : 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 112 : return "!GPG_AGENT";
130 : }
131 :
132 : static char *
133 112 : llass_get_version (const char *file_name)
134 : {
135 : (void)file_name;
136 112 : return NULL;
137 : }
138 :
139 :
140 : static const char *
141 98 : llass_get_req_version (void)
142 : {
143 98 : return NULL;
144 : }
145 :
146 :
147 : static void
148 16 : close_notify_handler (int fd, void *opaque)
149 : {
150 16 : engine_llass_t llass = opaque;
151 :
152 16 : assert (fd != -1);
153 16 : if (llass->status_cb.fd == fd)
154 : {
155 16 : if (llass->status_cb.tag)
156 16 : llass->io_cbs.remove (llass->status_cb.tag);
157 16 : llass->status_cb.fd = -1;
158 16 : llass->status_cb.tag = NULL;
159 : }
160 16 : }
161 :
162 :
163 :
164 : static gpgme_error_t
165 14 : llass_cancel (void *engine)
166 : {
167 14 : engine_llass_t llass = engine;
168 :
169 14 : if (!llass)
170 0 : return gpg_error (GPG_ERR_INV_VALUE);
171 :
172 14 : if (llass->status_cb.fd != -1)
173 0 : _gpgme_io_close (llass->status_cb.fd);
174 :
175 14 : if (llass->assuan_ctx)
176 : {
177 14 : assuan_release (llass->assuan_ctx);
178 14 : llass->assuan_ctx = NULL;
179 : }
180 :
181 14 : return 0;
182 : }
183 :
184 :
185 : static gpgme_error_t
186 2 : llass_cancel_op (void *engine)
187 : {
188 2 : engine_llass_t llass = engine;
189 :
190 2 : if (!llass)
191 0 : return gpg_error (GPG_ERR_INV_VALUE);
192 :
193 2 : if (llass->status_cb.fd != -1)
194 2 : _gpgme_io_close (llass->status_cb.fd);
195 :
196 2 : return 0;
197 : }
198 :
199 :
200 : static void
201 14 : llass_release (void *engine)
202 : {
203 14 : engine_llass_t llass = engine;
204 :
205 14 : if (!llass)
206 0 : return;
207 :
208 14 : llass_cancel (engine);
209 :
210 14 : 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 14 : llass_new (void **engine, const char *file_name, const char *home_dir,
218 : const char *version)
219 : {
220 14 : gpgme_error_t err = 0;
221 : engine_llass_t llass;
222 : char *optstr;
223 14 : char *env_tty = NULL;
224 :
225 : (void)version; /* Not yet used. */
226 :
227 14 : llass = calloc (1, sizeof *llass);
228 14 : if (!llass)
229 0 : return gpg_error_from_syserror ();
230 :
231 14 : llass->status_cb.fd = -1;
232 14 : llass->status_cb.dir = 1;
233 14 : llass->status_cb.tag = 0;
234 14 : llass->status_cb.data = llass;
235 :
236 : /* Parse_options. */
237 14 : if (home_dir && *home_dir == '!')
238 : {
239 14 : 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 14 : if (!strncmp (home_dir, "GPG_AGENT", 9)
244 14 : && (!home_dir[9] || home_dir[9] == ' '))
245 14 : llass->opt.gpg_agent = 1;
246 : }
247 :
248 14 : err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
249 : &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
250 : NULL);
251 14 : if (err)
252 0 : goto leave;
253 14 : assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
254 14 : assuan_set_flag (llass->assuan_ctx, ASSUAN_CONVEY_COMMENTS, 1);
255 :
256 14 : err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
257 14 : if (err)
258 12 : goto leave;
259 :
260 2 : if (llass->opt.gpg_agent)
261 : {
262 2 : char *dft_display = NULL;
263 :
264 2 : err = _gpgme_getenv ("DISPLAY", &dft_display);
265 2 : if (err)
266 0 : goto leave;
267 2 : if (dft_display)
268 : {
269 2 : if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
270 : {
271 0 : err = gpg_error_from_syserror ();
272 0 : free (dft_display);
273 0 : goto leave;
274 : }
275 2 : free (dft_display);
276 :
277 2 : err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
278 : NULL, NULL, NULL);
279 2 : gpgrt_free (optstr);
280 2 : if (err)
281 0 : goto leave;
282 : }
283 : }
284 :
285 2 : if (llass->opt.gpg_agent)
286 2 : err = _gpgme_getenv ("GPG_TTY", &env_tty);
287 :
288 2 : if (llass->opt.gpg_agent && (isatty (1) || env_tty || err))
289 : {
290 2 : int rc = 0;
291 : char dft_ttyname[64];
292 2 : char *dft_ttytype = NULL;
293 :
294 2 : if (err)
295 0 : goto leave;
296 2 : else if (env_tty)
297 : {
298 0 : snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
299 0 : free (env_tty);
300 : }
301 : else
302 2 : 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 2 : if (!rc)
307 : {
308 2 : if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
309 : {
310 0 : err = gpg_error_from_syserror ();
311 0 : goto leave;
312 : }
313 2 : err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
314 : NULL, NULL, NULL);
315 2 : gpgrt_free (optstr);
316 2 : if (err)
317 0 : goto leave;
318 :
319 2 : err = _gpgme_getenv ("TERM", &dft_ttytype);
320 2 : if (err)
321 0 : goto leave;
322 2 : if (dft_ttytype)
323 : {
324 2 : if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
325 : {
326 0 : err = gpg_error_from_syserror ();
327 0 : free (dft_ttytype);
328 0 : goto leave;
329 : }
330 2 : free (dft_ttytype);
331 :
332 2 : err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
333 : NULL, NULL, NULL, NULL);
334 2 : gpgrt_free (optstr);
335 2 : if (err)
336 0 : 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 14 : if (err)
361 12 : llass_release (llass);
362 : else
363 2 : *engine = llass;
364 :
365 14 : return err;
366 : }
367 :
368 :
369 : /* Copy flags from CTX into the engine object. */
370 : static void
371 2 : llass_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
372 : {
373 2 : engine_llass_t llass = engine;
374 :
375 2 : if (ctx->request_origin)
376 : {
377 0 : if (strlen (ctx->request_origin) + 1 > sizeof llass->request_origin)
378 0 : strcpy (llass->request_origin, "xxx"); /* Too long - force error */
379 : else
380 0 : strcpy (llass->request_origin, ctx->request_origin);
381 : }
382 : else
383 2 : *llass->request_origin = 0;
384 2 : }
385 :
386 :
387 : static gpgme_error_t
388 4 : llass_set_locale (void *engine, int category, const char *value)
389 : {
390 : gpgme_error_t err;
391 4 : engine_llass_t llass = engine;
392 : char *optstr;
393 : const char *catstr;
394 :
395 4 : if (!llass->opt.gpg_agent)
396 0 : 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 4 : else if (category == LC_CTYPE)
405 : {
406 2 : catstr = "lc-ctype";
407 2 : if (!value && llass->lc_ctype_set)
408 0 : return gpg_error (GPG_ERR_INV_VALUE);
409 2 : if (value)
410 0 : llass->lc_ctype_set = 1;
411 : }
412 : #endif
413 : #ifdef LC_MESSAGES
414 2 : else if (category == LC_MESSAGES)
415 : {
416 2 : catstr = "lc-messages";
417 2 : if (!value && llass->lc_messages_set)
418 0 : return gpg_error (GPG_ERR_INV_VALUE);
419 2 : if (value)
420 0 : llass->lc_messages_set = 1;
421 : }
422 : #endif /* LC_MESSAGES */
423 : else
424 0 : return gpg_error (GPG_ERR_INV_VALUE);
425 :
426 : /* FIXME: Reset value to default. */
427 4 : if (!value)
428 4 : return 0;
429 :
430 0 : if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
431 0 : err = gpg_error_from_syserror ();
432 : else
433 : {
434 0 : err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
435 : NULL, NULL, NULL, NULL);
436 0 : gpgrt_free (optstr);
437 : }
438 0 : 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 0 : inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
446 : {
447 : gpg_error_t err;
448 :
449 0 : if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
450 : {
451 0 : _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
452 : }
453 :
454 0 : if (llass->user.inq_cb)
455 : {
456 0 : gpgme_data_t data = NULL;
457 :
458 0 : err = llass->user.inq_cb (llass->user.inq_cb_value,
459 : keyword, args, &data);
460 0 : 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 0 : err = llass->user.inq_cb (llass->user.inq_cb_value,
466 : NULL, NULL, &data);
467 : }
468 : }
469 : else
470 0 : err = 0;
471 :
472 0 : return err;
473 : }
474 :
475 :
476 : static gpgme_error_t
477 16 : llass_status_handler (void *opaque, int fd)
478 : {
479 16 : struct io_cb_data *data = (struct io_cb_data *) opaque;
480 16 : engine_llass_t llass = (engine_llass_t) data->handler_value;
481 16 : gpgme_error_t err = 0;
482 : char *line;
483 : size_t linelen;
484 :
485 : do
486 : {
487 22 : err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
488 22 : 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 0 : if (gpg_err_code (err) == GPG_ERR_EAGAIN)
494 : {
495 0 : TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
496 : "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
497 0 : err = 0;
498 0 : continue;
499 : }
500 :
501 0 : TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
502 : "fd 0x%x: error reading assuan line: %s",
503 : fd, gpg_strerror (err));
504 : }
505 22 : else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
506 4 : {
507 4 : char *src = line + 2;
508 4 : char *end = line + linelen;
509 4 : char *dst = src;
510 :
511 4 : linelen = 0;
512 48 : while (src < end)
513 : {
514 40 : if (*src == '%' && src + 2 < end)
515 : {
516 : /* Handle escaped characters. */
517 0 : ++src;
518 0 : *dst++ = _gpgme_hextobyte (src);
519 0 : src += 2;
520 : }
521 : else
522 40 : *dst++ = *src++;
523 :
524 40 : linelen++;
525 : }
526 :
527 4 : src = line + 2;
528 4 : if (linelen && llass->user.data_cb)
529 4 : err = llass->user.data_cb (llass->user.data_cb_value,
530 : src, linelen);
531 :
532 4 : 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 18 : else if (linelen >= 3
538 4 : && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
539 0 : && (line[3] == '\0' || line[3] == ' '))
540 : {
541 : /* END received. Tell the data callback. */
542 0 : if (llass->user.data_cb)
543 0 : err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
544 :
545 0 : 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 18 : else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
551 2 : {
552 : char *args;
553 : char *src;
554 :
555 2 : for (src=line+2; *src == ' '; src++)
556 : ;
557 :
558 2 : args = strchr (src, ' ');
559 2 : if (!args)
560 0 : args = line + linelen; /* Let it point to an empty string. */
561 : else
562 2 : *(args++) = 0;
563 :
564 4 : while (*args == ' ')
565 0 : args++;
566 :
567 2 : if (llass->user.status_cb)
568 2 : err = llass->user.status_cb (llass->user.status_cb_value,
569 : src, args);
570 :
571 2 : 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 16 : else if (linelen >= 7
577 2 : && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
578 0 : && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
579 0 : && line[6] == 'E'
580 0 : && (line[7] == '\0' || line[7] == ' '))
581 0 : {
582 : char *src;
583 : char *args;
584 :
585 0 : for (src=line+7; *src == ' '; src++)
586 : ;
587 :
588 0 : args = strchr (src, ' ');
589 0 : if (!args)
590 0 : args = line + linelen; /* Let it point to an empty string. */
591 : else
592 0 : *(args++) = 0;
593 :
594 0 : while (*args == ' ')
595 0 : args++;
596 :
597 0 : err = inquire_cb (llass, src, args);
598 0 : if (!err)
599 : {
600 : /* Flush and send END. */
601 0 : err = assuan_send_data (llass->assuan_ctx, NULL, 0);
602 : }
603 0 : else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
604 : {
605 : /* Flush and send CANcel. */
606 0 : err = assuan_send_data (llass->assuan_ctx, NULL, 1);
607 : }
608 : }
609 16 : else if (linelen >= 3
610 2 : && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
611 2 : && (line[3] == '\0' || line[3] == ' '))
612 : {
613 2 : if (line[3] == ' ')
614 2 : err = atoi (line+4);
615 : else
616 0 : err = gpg_error (GPG_ERR_GENERAL);
617 2 : 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 2 : data->op_err = err;
624 2 : llass->last_op_err = err;
625 :
626 : /* The caller will do the rest (namely, call cancel_op,
627 : which closes status_fd). */
628 2 : return 0;
629 : }
630 14 : else if (linelen >= 2
631 14 : && line[0] == 'O' && line[1] == 'K'
632 14 : && (line[2] == '\0' || line[2] == ' '))
633 : {
634 14 : TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
635 : "fd 0x%x: OK line", fd);
636 :
637 14 : llass->last_op_err = 0;
638 :
639 14 : _gpgme_io_close (llass->status_cb.fd);
640 14 : return 0;
641 : }
642 : else
643 : {
644 : /* Comment line or invalid line. */
645 : }
646 :
647 : }
648 6 : while (!err && assuan_pending_line (llass->assuan_ctx));
649 :
650 0 : return err;
651 : }
652 :
653 :
654 : static gpgme_error_t
655 16 : add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
656 : {
657 : gpgme_error_t err;
658 :
659 16 : TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
660 : "fd %d, dir %d", iocbd->fd, iocbd->dir);
661 16 : err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
662 : iocbd->fd, iocbd->dir,
663 : handler, iocbd->data, &iocbd->tag);
664 16 : if (err)
665 0 : return TRACE_ERR (err);
666 16 : if (!iocbd->dir)
667 : /* FIXME Kludge around poll() problem. */
668 0 : err = _gpgme_io_set_nonblocking (iocbd->fd);
669 16 : return TRACE_ERR (err);
670 : }
671 :
672 :
673 : static gpgme_error_t
674 16 : 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 16 : if (*llass->request_origin && llass->opt.gpg_agent)
683 : {
684 : char *cmd;
685 :
686 0 : cmd = _gpgme_strconcat ("OPTION pretend-request-origin=",
687 0 : llass->request_origin, NULL);
688 0 : if (!cmd)
689 0 : return gpg_error_from_syserror ();
690 0 : err = assuan_transact (llass->assuan_ctx, cmd, NULL, NULL, NULL,
691 : NULL, NULL, NULL);
692 0 : free (cmd);
693 0 : if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION)
694 0 : 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 16 : nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
701 : afdlist, DIM (afdlist));
702 16 : if (nfds < 1)
703 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
704 : /* For now... */
705 32 : for (i = 0; i < nfds; i++)
706 16 : 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 16 : llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
719 16 : if (llass->status_cb.fd < 0)
720 0 : return gpg_error_from_syserror ();
721 :
722 16 : if (_gpgme_io_set_close_notify (llass->status_cb.fd,
723 : close_notify_handler, llass))
724 : {
725 0 : _gpgme_io_close (llass->status_cb.fd);
726 0 : llass->status_cb.fd = -1;
727 0 : return gpg_error (GPG_ERR_GENERAL);
728 : }
729 :
730 16 : err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
731 16 : if (!err)
732 16 : err = assuan_write_line (llass->assuan_ctx, command);
733 :
734 : /* FIXME: If *command == '#' no answer is expected. */
735 :
736 16 : if (!err)
737 16 : llass_io_event (llass, GPGME_EVENT_START, NULL);
738 :
739 16 : return err;
740 : }
741 :
742 :
743 :
744 : static gpgme_error_t
745 16 : 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 16 : engine_llass_t llass = engine;
755 : gpgme_error_t err;
756 :
757 16 : if (!llass || !command || !*command)
758 0 : return gpg_error (GPG_ERR_INV_VALUE);
759 :
760 16 : llass->user.data_cb = data_cb;
761 16 : llass->user.data_cb_value = data_cb_value;
762 16 : llass->user.inq_cb = inq_cb;
763 16 : llass->user.inq_cb_value = inq_cb_value;
764 16 : llass->user.status_cb = status_cb;
765 16 : llass->user.status_cb_value = status_cb_value;
766 :
767 16 : err = start (llass, command);
768 16 : return err;
769 : }
770 :
771 :
772 :
773 : static void
774 16 : llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
775 : {
776 16 : engine_llass_t llass = engine;
777 16 : llass->io_cbs = *io_cbs;
778 16 : }
779 :
780 :
781 : static void
782 32 : llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
783 : {
784 32 : engine_llass_t llass = engine;
785 :
786 32 : 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 32 : if (llass->io_cbs.event)
790 32 : (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
791 32 : }
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 : };
|