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