Line data Source code
1 : /* engine-uiserver.c - Uiserver engine.
2 : Copyright (C) 2000 Werner Koch (dd9jn)
3 : Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
4 :
5 : This file is part of GPGME.
6 :
7 : GPGME is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU Lesser General Public License as
9 : published by the Free Software Foundation; either version 2.1 of
10 : the License, or (at your option) any later version.
11 :
12 : GPGME is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : Lesser General Public License for more details.
16 :
17 : You should have received a copy of the GNU Lesser General Public
18 : License along with this program; if not, write to the Free Software
19 : Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 : 02111-1307, USA. */
21 :
22 : /* Peculiar: Use special keys from email address for recipient and
23 : signer (==sender). Use no data objects with encryption for
24 : prep_encrypt. */
25 :
26 : #if HAVE_CONFIG_H
27 : #include <config.h>
28 : #endif
29 :
30 : #include <stdlib.h>
31 : #include <string.h>
32 : #ifdef HAVE_SYS_TYPES_H
33 : # include <sys/types.h>
34 : #endif
35 : #include <assert.h>
36 : #ifdef HAVE_UNISTD_H
37 : # include <unistd.h>
38 : #endif
39 : #include <locale.h>
40 : #include <fcntl.h> /* FIXME */
41 : #include <errno.h>
42 :
43 : #include "gpgme.h"
44 : #include "util.h"
45 : #include "ops.h"
46 : #include "wait.h"
47 : #include "priv-io.h"
48 : #include "sema.h"
49 : #include "data.h"
50 :
51 : #include "assuan.h"
52 : #include "debug.h"
53 :
54 : #include "engine-backend.h"
55 :
56 :
57 : typedef struct
58 : {
59 : int fd; /* FD we talk about. */
60 : int server_fd;/* Server FD for this connection. */
61 : int dir; /* Inbound/Outbound, maybe given implicit? */
62 : void *data; /* Handler-specific data. */
63 : void *tag; /* ID from the user for gpgme_remove_io_callback. */
64 : char server_fd_str[15]; /* Same as SERVER_FD but as a string. We
65 : need this because _gpgme_io_fd2str can't
66 : be used on a closed descriptor. */
67 : } iocb_data_t;
68 :
69 :
70 : struct engine_uiserver
71 : {
72 : assuan_context_t assuan_ctx;
73 :
74 : int lc_ctype_set;
75 : int lc_messages_set;
76 : gpgme_protocol_t protocol;
77 :
78 : iocb_data_t status_cb;
79 :
80 : /* Input, output etc are from the servers perspective. */
81 : iocb_data_t input_cb;
82 : gpgme_data_t input_helper_data; /* Input helper data object. */
83 : void *input_helper_memory; /* Input helper memory block. */
84 :
85 : iocb_data_t output_cb;
86 :
87 : iocb_data_t message_cb;
88 :
89 : struct
90 : {
91 : engine_status_handler_t fnc;
92 : void *fnc_value;
93 : } status;
94 :
95 : struct
96 : {
97 : engine_colon_line_handler_t fnc;
98 : void *fnc_value;
99 : struct
100 : {
101 : char *line;
102 : int linesize;
103 : int linelen;
104 : } attic;
105 : int any; /* any data line seen */
106 : } colon;
107 :
108 : gpgme_data_t inline_data; /* Used to collect D lines. */
109 :
110 : struct gpgme_io_cbs io_cbs;
111 : };
112 :
113 : typedef struct engine_uiserver *engine_uiserver_t;
114 :
115 :
116 : static void uiserver_io_event (void *engine,
117 : gpgme_event_io_t type, void *type_data);
118 :
119 :
120 :
121 : static char *
122 28 : uiserver_get_version (const char *file_name)
123 : {
124 28 : return strdup ("1.0");
125 : }
126 :
127 :
128 : static const char *
129 28 : uiserver_get_req_version (void)
130 : {
131 28 : return "1.0";
132 : }
133 :
134 :
135 : static void
136 0 : close_notify_handler (int fd, void *opaque)
137 : {
138 0 : engine_uiserver_t uiserver = opaque;
139 :
140 0 : assert (fd != -1);
141 0 : if (uiserver->status_cb.fd == fd)
142 : {
143 0 : if (uiserver->status_cb.tag)
144 0 : (*uiserver->io_cbs.remove) (uiserver->status_cb.tag);
145 0 : uiserver->status_cb.fd = -1;
146 0 : uiserver->status_cb.tag = NULL;
147 : }
148 0 : else if (uiserver->input_cb.fd == fd)
149 : {
150 0 : if (uiserver->input_cb.tag)
151 0 : (*uiserver->io_cbs.remove) (uiserver->input_cb.tag);
152 0 : uiserver->input_cb.fd = -1;
153 0 : uiserver->input_cb.tag = NULL;
154 0 : if (uiserver->input_helper_data)
155 : {
156 0 : gpgme_data_release (uiserver->input_helper_data);
157 0 : uiserver->input_helper_data = NULL;
158 : }
159 0 : if (uiserver->input_helper_memory)
160 : {
161 0 : free (uiserver->input_helper_memory);
162 0 : uiserver->input_helper_memory = NULL;
163 : }
164 : }
165 0 : else if (uiserver->output_cb.fd == fd)
166 : {
167 0 : if (uiserver->output_cb.tag)
168 0 : (*uiserver->io_cbs.remove) (uiserver->output_cb.tag);
169 0 : uiserver->output_cb.fd = -1;
170 0 : uiserver->output_cb.tag = NULL;
171 : }
172 0 : else if (uiserver->message_cb.fd == fd)
173 : {
174 0 : if (uiserver->message_cb.tag)
175 0 : (*uiserver->io_cbs.remove) (uiserver->message_cb.tag);
176 0 : uiserver->message_cb.fd = -1;
177 0 : uiserver->message_cb.tag = NULL;
178 : }
179 0 : }
180 :
181 :
182 : /* This is the default inquiry callback. We use it to handle the
183 : Pinentry notifications. */
184 : static gpgme_error_t
185 0 : default_inq_cb (engine_uiserver_t uiserver, const char *line)
186 : {
187 0 : if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
188 : {
189 0 : _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
190 : }
191 :
192 0 : return 0;
193 : }
194 :
195 :
196 : static gpgme_error_t
197 0 : uiserver_cancel (void *engine)
198 : {
199 0 : engine_uiserver_t uiserver = engine;
200 :
201 0 : if (!uiserver)
202 0 : return gpg_error (GPG_ERR_INV_VALUE);
203 :
204 0 : if (uiserver->status_cb.fd != -1)
205 0 : _gpgme_io_close (uiserver->status_cb.fd);
206 0 : if (uiserver->input_cb.fd != -1)
207 0 : _gpgme_io_close (uiserver->input_cb.fd);
208 0 : if (uiserver->output_cb.fd != -1)
209 0 : _gpgme_io_close (uiserver->output_cb.fd);
210 0 : if (uiserver->message_cb.fd != -1)
211 0 : _gpgme_io_close (uiserver->message_cb.fd);
212 :
213 0 : if (uiserver->assuan_ctx)
214 : {
215 0 : assuan_release (uiserver->assuan_ctx);
216 0 : uiserver->assuan_ctx = NULL;
217 : }
218 :
219 0 : return 0;
220 : }
221 :
222 :
223 : static void
224 0 : uiserver_release (void *engine)
225 : {
226 0 : engine_uiserver_t uiserver = engine;
227 :
228 0 : if (!uiserver)
229 0 : return;
230 :
231 0 : uiserver_cancel (engine);
232 :
233 0 : free (uiserver->colon.attic.line);
234 0 : free (uiserver);
235 : }
236 :
237 :
238 : static gpgme_error_t
239 0 : uiserver_new (void **engine, const char *file_name, const char *home_dir)
240 : {
241 0 : gpgme_error_t err = 0;
242 : engine_uiserver_t uiserver;
243 0 : char *dft_display = NULL;
244 : char dft_ttyname[64];
245 0 : char *dft_ttytype = NULL;
246 : char *optstr;
247 :
248 0 : uiserver = calloc (1, sizeof *uiserver);
249 0 : if (!uiserver)
250 0 : return gpg_error_from_syserror ();
251 :
252 0 : uiserver->protocol = GPGME_PROTOCOL_DEFAULT;
253 0 : uiserver->status_cb.fd = -1;
254 0 : uiserver->status_cb.dir = 1;
255 0 : uiserver->status_cb.tag = 0;
256 0 : uiserver->status_cb.data = uiserver;
257 :
258 0 : uiserver->input_cb.fd = -1;
259 0 : uiserver->input_cb.dir = 0;
260 0 : uiserver->input_cb.tag = 0;
261 0 : uiserver->input_cb.server_fd = -1;
262 0 : *uiserver->input_cb.server_fd_str = 0;
263 0 : uiserver->output_cb.fd = -1;
264 0 : uiserver->output_cb.dir = 1;
265 0 : uiserver->output_cb.tag = 0;
266 0 : uiserver->output_cb.server_fd = -1;
267 0 : *uiserver->output_cb.server_fd_str = 0;
268 0 : uiserver->message_cb.fd = -1;
269 0 : uiserver->message_cb.dir = 0;
270 0 : uiserver->message_cb.tag = 0;
271 0 : uiserver->message_cb.server_fd = -1;
272 0 : *uiserver->message_cb.server_fd_str = 0;
273 :
274 0 : uiserver->status.fnc = 0;
275 0 : uiserver->colon.fnc = 0;
276 0 : uiserver->colon.attic.line = 0;
277 0 : uiserver->colon.attic.linesize = 0;
278 0 : uiserver->colon.attic.linelen = 0;
279 0 : uiserver->colon.any = 0;
280 :
281 0 : uiserver->inline_data = NULL;
282 :
283 0 : uiserver->io_cbs.add = NULL;
284 0 : uiserver->io_cbs.add_priv = NULL;
285 0 : uiserver->io_cbs.remove = NULL;
286 0 : uiserver->io_cbs.event = NULL;
287 0 : uiserver->io_cbs.event_priv = NULL;
288 :
289 0 : err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME,
290 : &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
291 : NULL);
292 0 : if (err)
293 0 : goto leave;
294 0 : assuan_ctx_set_system_hooks (uiserver->assuan_ctx,
295 : &_gpgme_assuan_system_hooks);
296 :
297 0 : err = assuan_socket_connect (uiserver->assuan_ctx,
298 : file_name ?
299 : file_name : _gpgme_get_default_uisrv_socket (),
300 : 0, ASSUAN_SOCKET_SERVER_FDPASSING);
301 0 : if (err)
302 0 : goto leave;
303 :
304 0 : err = _gpgme_getenv ("DISPLAY", &dft_display);
305 0 : if (err)
306 0 : goto leave;
307 0 : if (dft_display)
308 : {
309 0 : if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
310 : {
311 0 : err = gpg_error_from_syserror ();
312 0 : free (dft_display);
313 0 : goto leave;
314 : }
315 0 : free (dft_display);
316 :
317 0 : err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
318 : NULL, NULL, NULL);
319 0 : free (optstr);
320 0 : if (err)
321 0 : goto leave;
322 : }
323 :
324 0 : if (isatty (1))
325 : {
326 : int rc;
327 :
328 0 : rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
329 :
330 : /* Even though isatty() returns 1, ttyname_r() may fail in many
331 : ways, e.g., when /dev/pts is not accessible under chroot. */
332 0 : if (!rc)
333 : {
334 0 : if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
335 : {
336 0 : err = gpg_error_from_syserror ();
337 0 : goto leave;
338 : }
339 0 : err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL,
340 : NULL, NULL, NULL);
341 0 : free (optstr);
342 0 : if (err)
343 0 : goto leave;
344 :
345 0 : err = _gpgme_getenv ("TERM", &dft_ttytype);
346 0 : if (err)
347 0 : goto leave;
348 0 : if (dft_ttytype)
349 : {
350 0 : if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
351 : {
352 0 : err = gpg_error_from_syserror ();
353 0 : free (dft_ttytype);
354 0 : goto leave;
355 : }
356 0 : free (dft_ttytype);
357 :
358 0 : err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
359 : NULL, NULL, NULL, NULL);
360 0 : free (optstr);
361 0 : if (err)
362 0 : goto leave;
363 : }
364 : }
365 : }
366 :
367 : #ifdef HAVE_W32_SYSTEM
368 : /* Under Windows we need to use AllowSetForegroundWindow. Tell
369 : uiserver to tell us when it needs it. */
370 : if (!err)
371 : {
372 : err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify",
373 : NULL, NULL, NULL, NULL, NULL, NULL);
374 : if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
375 : err = 0; /* This is a new feature of uiserver. */
376 : }
377 : #endif /*HAVE_W32_SYSTEM*/
378 :
379 : leave:
380 0 : if (err)
381 0 : uiserver_release (uiserver);
382 : else
383 0 : *engine = uiserver;
384 :
385 0 : return err;
386 : }
387 :
388 :
389 : static gpgme_error_t
390 0 : uiserver_set_locale (void *engine, int category, const char *value)
391 : {
392 0 : engine_uiserver_t uiserver = engine;
393 : gpgme_error_t err;
394 : char *optstr;
395 : char *catstr;
396 :
397 : /* FIXME: If value is NULL, we need to reset the option to default.
398 : But we can't do this. So we error out here. UISERVER needs support
399 : for this. */
400 0 : if (category == LC_CTYPE)
401 : {
402 0 : catstr = "lc-ctype";
403 0 : if (!value && uiserver->lc_ctype_set)
404 0 : return gpg_error (GPG_ERR_INV_VALUE);
405 0 : if (value)
406 0 : uiserver->lc_ctype_set = 1;
407 : }
408 : #ifdef LC_MESSAGES
409 0 : else if (category == LC_MESSAGES)
410 : {
411 0 : catstr = "lc-messages";
412 0 : if (!value && uiserver->lc_messages_set)
413 0 : return gpg_error (GPG_ERR_INV_VALUE);
414 0 : if (value)
415 0 : uiserver->lc_messages_set = 1;
416 : }
417 : #endif /* LC_MESSAGES */
418 : else
419 0 : return gpg_error (GPG_ERR_INV_VALUE);
420 :
421 : /* FIXME: Reset value to default. */
422 0 : if (!value)
423 0 : return 0;
424 :
425 0 : if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
426 0 : err = gpg_error_from_syserror ();
427 : else
428 : {
429 0 : err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL,
430 : NULL, NULL, NULL, NULL);
431 0 : free (optstr);
432 : }
433 :
434 0 : return err;
435 : }
436 :
437 :
438 : static gpgme_error_t
439 0 : uiserver_set_protocol (void *engine, gpgme_protocol_t protocol)
440 : {
441 0 : engine_uiserver_t uiserver = engine;
442 :
443 0 : if (protocol != GPGME_PROTOCOL_OpenPGP
444 0 : && protocol != GPGME_PROTOCOL_CMS
445 0 : && protocol != GPGME_PROTOCOL_DEFAULT)
446 0 : return gpg_error (GPG_ERR_INV_VALUE);
447 :
448 0 : uiserver->protocol = protocol;
449 0 : return 0;
450 : }
451 :
452 :
453 : static gpgme_error_t
454 0 : uiserver_assuan_simple_command (assuan_context_t ctx, char *cmd,
455 : engine_status_handler_t status_fnc,
456 : void *status_fnc_value)
457 : {
458 : gpg_error_t err;
459 : char *line;
460 : size_t linelen;
461 :
462 0 : err = assuan_write_line (ctx, cmd);
463 0 : if (err)
464 0 : return err;
465 :
466 : do
467 : {
468 0 : err = assuan_read_line (ctx, &line, &linelen);
469 0 : if (err)
470 0 : return err;
471 :
472 0 : if (*line == '#' || !linelen)
473 0 : continue;
474 :
475 0 : if (linelen >= 2
476 0 : && line[0] == 'O' && line[1] == 'K'
477 0 : && (line[2] == '\0' || line[2] == ' '))
478 0 : return 0;
479 0 : else if (linelen >= 4
480 0 : && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
481 0 : && line[3] == ' ')
482 0 : err = atoi (&line[4]);
483 0 : else if (linelen >= 2
484 0 : && line[0] == 'S' && line[1] == ' ')
485 0 : {
486 : char *rest;
487 : gpgme_status_code_t r;
488 :
489 0 : rest = strchr (line + 2, ' ');
490 0 : if (!rest)
491 0 : rest = line + linelen; /* set to an empty string */
492 : else
493 0 : *(rest++) = 0;
494 :
495 0 : r = _gpgme_parse_status (line + 2);
496 :
497 0 : if (r >= 0 && status_fnc)
498 0 : err = status_fnc (status_fnc_value, r, rest);
499 : else
500 0 : err = gpg_error (GPG_ERR_GENERAL);
501 : }
502 : else
503 0 : err = gpg_error (GPG_ERR_GENERAL);
504 : }
505 0 : while (!err);
506 :
507 0 : return err;
508 : }
509 :
510 :
511 : typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
512 :
513 : #define COMMANDLINELEN 40
514 : static gpgme_error_t
515 0 : uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt)
516 : {
517 0 : gpg_error_t err = 0;
518 : char line[COMMANDLINELEN];
519 : char *which;
520 : iocb_data_t *iocb_data;
521 : int dir;
522 :
523 0 : switch (fd_type)
524 : {
525 : case INPUT_FD:
526 0 : which = "INPUT";
527 0 : iocb_data = &uiserver->input_cb;
528 0 : break;
529 :
530 : case OUTPUT_FD:
531 0 : which = "OUTPUT";
532 0 : iocb_data = &uiserver->output_cb;
533 0 : break;
534 :
535 : case MESSAGE_FD:
536 0 : which = "MESSAGE";
537 0 : iocb_data = &uiserver->message_cb;
538 0 : break;
539 :
540 : default:
541 0 : return gpg_error (GPG_ERR_INV_VALUE);
542 : }
543 :
544 0 : dir = iocb_data->dir;
545 :
546 : /* We try to short-cut the communication by giving UISERVER direct
547 : access to the file descriptor, rather than using a pipe. */
548 0 : iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
549 0 : if (iocb_data->server_fd < 0)
550 : {
551 : int fds[2];
552 :
553 0 : if (_gpgme_io_pipe (fds, 0) < 0)
554 0 : return gpg_error_from_syserror ();
555 :
556 0 : iocb_data->fd = dir ? fds[0] : fds[1];
557 0 : iocb_data->server_fd = dir ? fds[1] : fds[0];
558 :
559 0 : if (_gpgme_io_set_close_notify (iocb_data->fd,
560 : close_notify_handler, uiserver))
561 : {
562 0 : err = gpg_error (GPG_ERR_GENERAL);
563 0 : goto leave_set_fd;
564 : }
565 : }
566 :
567 0 : err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd);
568 0 : if (err)
569 0 : goto leave_set_fd;
570 :
571 0 : _gpgme_io_close (iocb_data->server_fd);
572 0 : iocb_data->server_fd = -1;
573 :
574 0 : if (opt)
575 0 : snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
576 : else
577 0 : snprintf (line, COMMANDLINELEN, "%s FD", which);
578 :
579 0 : err = uiserver_assuan_simple_command (uiserver->assuan_ctx, line, NULL, NULL);
580 :
581 : leave_set_fd:
582 0 : if (err)
583 : {
584 0 : _gpgme_io_close (iocb_data->fd);
585 0 : iocb_data->fd = -1;
586 0 : if (iocb_data->server_fd != -1)
587 : {
588 0 : _gpgme_io_close (iocb_data->server_fd);
589 0 : iocb_data->server_fd = -1;
590 : }
591 : }
592 :
593 0 : return err;
594 : }
595 :
596 :
597 : static const char *
598 0 : map_data_enc (gpgme_data_t d)
599 : {
600 0 : switch (gpgme_data_get_encoding (d))
601 : {
602 : case GPGME_DATA_ENCODING_NONE:
603 0 : break;
604 : case GPGME_DATA_ENCODING_BINARY:
605 0 : return "--binary";
606 : case GPGME_DATA_ENCODING_BASE64:
607 0 : return "--base64";
608 : case GPGME_DATA_ENCODING_ARMOR:
609 0 : return "--armor";
610 : default:
611 0 : break;
612 : }
613 0 : return NULL;
614 : }
615 :
616 :
617 : static gpgme_error_t
618 0 : status_handler (void *opaque, int fd)
619 : {
620 0 : struct io_cb_data *data = (struct io_cb_data *) opaque;
621 0 : engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value;
622 0 : gpgme_error_t err = 0;
623 : char *line;
624 : size_t linelen;
625 :
626 : do
627 : {
628 0 : err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen);
629 0 : if (err)
630 : {
631 : /* Try our best to terminate the connection friendly. */
632 : /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
633 0 : TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
634 : "fd 0x%x: error from assuan (%d) getting status line : %s",
635 : fd, err, gpg_strerror (err));
636 : }
637 0 : else if (linelen >= 3
638 0 : && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
639 0 : && (line[3] == '\0' || line[3] == ' '))
640 : {
641 0 : if (line[3] == ' ')
642 0 : err = atoi (&line[4]);
643 0 : if (! err)
644 0 : err = gpg_error (GPG_ERR_GENERAL);
645 0 : TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
646 : "fd 0x%x: ERR line - mapped to: %s",
647 : fd, err ? gpg_strerror (err) : "ok");
648 : /* Try our best to terminate the connection friendly. */
649 : /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */
650 : }
651 0 : else if (linelen >= 2
652 0 : && line[0] == 'O' && line[1] == 'K'
653 0 : && (line[2] == '\0' || line[2] == ' '))
654 : {
655 0 : if (uiserver->status.fnc)
656 0 : err = uiserver->status.fnc (uiserver->status.fnc_value,
657 : GPGME_STATUS_EOF, "");
658 :
659 0 : if (!err && uiserver->colon.fnc && uiserver->colon.any)
660 : {
661 : /* We must tell a colon function about the EOF. We do
662 : this only when we have seen any data lines. Note
663 : that this inlined use of colon data lines will
664 : eventually be changed into using a regular data
665 : channel. */
666 0 : uiserver->colon.any = 0;
667 0 : err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL);
668 : }
669 0 : TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
670 : "fd 0x%x: OK line - final status: %s",
671 : fd, err ? gpg_strerror (err) : "ok");
672 0 : _gpgme_io_close (uiserver->status_cb.fd);
673 0 : return err;
674 : }
675 0 : else if (linelen > 2
676 0 : && line[0] == 'D' && line[1] == ' '
677 0 : && uiserver->colon.fnc)
678 0 : {
679 : /* We are using the colon handler even for plain inline data
680 : - strange name for that function but for historic reasons
681 : we keep it. */
682 : /* FIXME We can't use this for binary data because we
683 : assume this is a string. For the current usage of colon
684 : output it is correct. */
685 0 : char *src = line + 2;
686 0 : char *end = line + linelen;
687 : char *dst;
688 0 : char **aline = &uiserver->colon.attic.line;
689 0 : int *alinelen = &uiserver->colon.attic.linelen;
690 :
691 0 : if (uiserver->colon.attic.linesize < *alinelen + linelen + 1)
692 : {
693 0 : char *newline = realloc (*aline, *alinelen + linelen + 1);
694 0 : if (!newline)
695 0 : err = gpg_error_from_syserror ();
696 : else
697 : {
698 0 : *aline = newline;
699 0 : uiserver->colon.attic.linesize = *alinelen + linelen + 1;
700 : }
701 : }
702 0 : if (!err)
703 : {
704 0 : dst = *aline + *alinelen;
705 :
706 0 : while (!err && src < end)
707 : {
708 0 : if (*src == '%' && src + 2 < end)
709 : {
710 : /* Handle escaped characters. */
711 0 : ++src;
712 0 : *dst = _gpgme_hextobyte (src);
713 0 : (*alinelen)++;
714 0 : src += 2;
715 : }
716 : else
717 : {
718 0 : *dst = *src++;
719 0 : (*alinelen)++;
720 : }
721 :
722 0 : if (*dst == '\n')
723 : {
724 : /* Terminate the pending line, pass it to the colon
725 : handler and reset it. */
726 :
727 0 : uiserver->colon.any = 1;
728 0 : if (*alinelen > 1 && *(dst - 1) == '\r')
729 0 : dst--;
730 0 : *dst = '\0';
731 :
732 : /* FIXME How should we handle the return code? */
733 0 : err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline);
734 0 : if (!err)
735 : {
736 0 : dst = *aline;
737 0 : *alinelen = 0;
738 : }
739 : }
740 : else
741 0 : dst++;
742 : }
743 : }
744 0 : TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
745 : "fd 0x%x: D line; final status: %s",
746 : fd, err? gpg_strerror (err):"ok");
747 : }
748 0 : else if (linelen > 2
749 0 : && line[0] == 'D' && line[1] == ' '
750 0 : && uiserver->inline_data)
751 0 : {
752 0 : char *src = line + 2;
753 0 : char *end = line + linelen;
754 0 : char *dst = src;
755 : gpgme_ssize_t nwritten;
756 :
757 0 : linelen = 0;
758 0 : while (src < end)
759 : {
760 0 : if (*src == '%' && src + 2 < end)
761 : {
762 : /* Handle escaped characters. */
763 0 : ++src;
764 0 : *dst++ = _gpgme_hextobyte (src);
765 0 : src += 2;
766 : }
767 : else
768 0 : *dst++ = *src++;
769 :
770 0 : linelen++;
771 : }
772 :
773 0 : src = line + 2;
774 0 : while (linelen > 0)
775 : {
776 0 : nwritten = gpgme_data_write (uiserver->inline_data, src, linelen);
777 0 : if (!nwritten || (nwritten < 0 && errno != EINTR)
778 0 : || nwritten > linelen)
779 : {
780 0 : err = gpg_error_from_syserror ();
781 0 : break;
782 : }
783 0 : src += nwritten;
784 0 : linelen -= nwritten;
785 : }
786 :
787 0 : TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver,
788 : "fd 0x%x: D inlinedata; final status: %s",
789 : fd, err? gpg_strerror (err):"ok");
790 : }
791 0 : else if (linelen > 2
792 0 : && line[0] == 'S' && line[1] == ' ')
793 0 : {
794 : char *rest;
795 : gpgme_status_code_t r;
796 :
797 0 : rest = strchr (line + 2, ' ');
798 0 : if (!rest)
799 0 : rest = line + linelen; /* set to an empty string */
800 : else
801 0 : *(rest++) = 0;
802 :
803 0 : r = _gpgme_parse_status (line + 2);
804 :
805 : if (r >= 0)
806 : {
807 0 : if (uiserver->status.fnc)
808 0 : err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest);
809 : }
810 : else
811 : fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
812 0 : TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver,
813 : "fd 0x%x: S line (%s) - final status: %s",
814 : fd, line+2, err? gpg_strerror (err):"ok");
815 : }
816 0 : else if (linelen >= 7
817 0 : && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
818 0 : && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
819 0 : && line[6] == 'E'
820 0 : && (line[7] == '\0' || line[7] == ' '))
821 : {
822 0 : char *keyword = line+7;
823 :
824 0 : while (*keyword == ' ')
825 0 : keyword++;;
826 0 : default_inq_cb (uiserver, keyword);
827 0 : assuan_write_line (uiserver->assuan_ctx, "END");
828 : }
829 :
830 : }
831 0 : while (!err && assuan_pending_line (uiserver->assuan_ctx));
832 :
833 0 : return err;
834 : }
835 :
836 :
837 : static gpgme_error_t
838 0 : add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler)
839 : {
840 : gpgme_error_t err;
841 :
842 0 : TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
843 : "fd %d, dir %d", iocbd->fd, iocbd->dir);
844 0 : err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
845 : iocbd->fd, iocbd->dir,
846 : handler, iocbd->data, &iocbd->tag);
847 0 : if (err)
848 0 : return TRACE_ERR (err);
849 0 : if (!iocbd->dir)
850 : /* FIXME Kludge around poll() problem. */
851 0 : err = _gpgme_io_set_nonblocking (iocbd->fd);
852 0 : return TRACE_ERR (err);
853 : }
854 :
855 :
856 : static gpgme_error_t
857 0 : start (engine_uiserver_t uiserver, const char *command)
858 : {
859 : gpgme_error_t err;
860 : int fdlist[5];
861 : int nfds;
862 :
863 : /* We need to know the fd used by assuan for reads. We do this by
864 : using the assumption that the first returned fd from
865 : assuan_get_active_fds() is always this one. */
866 0 : nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */,
867 : fdlist, DIM (fdlist));
868 0 : if (nfds < 1)
869 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
870 :
871 : /* We "duplicate" the file descriptor, so we can close it here (we
872 : can't close fdlist[0], as that is closed by libassuan, and
873 : closing it here might cause libassuan to close some unrelated FD
874 : later). Alternatively, we could special case status_fd and
875 : register/unregister it manually as needed, but this increases
876 : code duplication and is more complicated as we can not use the
877 : close notifications etc. A third alternative would be to let
878 : Assuan know that we closed the FD, but that complicates the
879 : Assuan interface. */
880 :
881 0 : uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]);
882 0 : if (uiserver->status_cb.fd < 0)
883 0 : return gpg_error_from_syserror ();
884 :
885 0 : if (_gpgme_io_set_close_notify (uiserver->status_cb.fd,
886 : close_notify_handler, uiserver))
887 : {
888 0 : _gpgme_io_close (uiserver->status_cb.fd);
889 0 : uiserver->status_cb.fd = -1;
890 0 : return gpg_error (GPG_ERR_GENERAL);
891 : }
892 :
893 0 : err = add_io_cb (uiserver, &uiserver->status_cb, status_handler);
894 0 : if (!err && uiserver->input_cb.fd != -1)
895 0 : err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler);
896 0 : if (!err && uiserver->output_cb.fd != -1)
897 0 : err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler);
898 0 : if (!err && uiserver->message_cb.fd != -1)
899 0 : err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler);
900 :
901 0 : if (!err)
902 0 : err = assuan_write_line (uiserver->assuan_ctx, command);
903 :
904 0 : if (!err)
905 0 : uiserver_io_event (uiserver, GPGME_EVENT_START, NULL);
906 :
907 0 : return err;
908 : }
909 :
910 :
911 : static gpgme_error_t
912 0 : uiserver_reset (void *engine)
913 : {
914 0 : engine_uiserver_t uiserver = engine;
915 :
916 : /* We must send a reset because we need to reset the list of
917 : signers. Note that RESET does not reset OPTION commands. */
918 0 : return uiserver_assuan_simple_command (uiserver->assuan_ctx, "RESET", NULL, NULL);
919 : }
920 :
921 :
922 : static gpgme_error_t
923 0 : _uiserver_decrypt (void *engine, int verify,
924 : gpgme_data_t ciph, gpgme_data_t plain)
925 : {
926 0 : engine_uiserver_t uiserver = engine;
927 : gpgme_error_t err;
928 : const char *protocol;
929 : char *cmd;
930 :
931 0 : if (!uiserver)
932 0 : return gpg_error (GPG_ERR_INV_VALUE);
933 0 : if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
934 0 : protocol = "";
935 0 : else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
936 0 : protocol = " --protocol=OpenPGP";
937 0 : else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
938 0 : protocol = " --protocol=CMS";
939 : else
940 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
941 :
942 0 : if (asprintf (&cmd, "DECRYPT%s%s", protocol,
943 : verify ? "" : " --no-verify") < 0)
944 0 : return gpg_error_from_syserror ();
945 :
946 0 : uiserver->input_cb.data = ciph;
947 0 : err = uiserver_set_fd (uiserver, INPUT_FD,
948 0 : map_data_enc (uiserver->input_cb.data));
949 0 : if (err)
950 : {
951 0 : free (cmd);
952 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
953 : }
954 0 : uiserver->output_cb.data = plain;
955 0 : err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
956 0 : if (err)
957 : {
958 0 : free (cmd);
959 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
960 : }
961 0 : uiserver->inline_data = NULL;
962 :
963 0 : err = start (engine, cmd);
964 0 : free (cmd);
965 0 : return err;
966 : }
967 :
968 :
969 : static gpgme_error_t
970 0 : uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
971 : {
972 0 : return _uiserver_decrypt (engine, 0, ciph, plain);
973 : }
974 :
975 :
976 : static gpgme_error_t
977 0 : uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
978 : {
979 0 : return _uiserver_decrypt (engine, 1, ciph, plain);
980 : }
981 :
982 :
983 : static gpgme_error_t
984 0 : set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
985 : {
986 0 : gpgme_error_t err = 0;
987 0 : assuan_context_t ctx = uiserver->assuan_ctx;
988 : char *line;
989 : int linelen;
990 0 : int invalid_recipients = 0;
991 : int i;
992 :
993 0 : linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
994 0 : line = malloc (10 + 40 + 1);
995 0 : if (!line)
996 0 : return gpg_error_from_syserror ();
997 0 : strcpy (line, "RECIPIENT ");
998 0 : for (i=0; !err && recp[i]; i++)
999 : {
1000 : char *uid;
1001 : int newlen;
1002 :
1003 : /* We use only the first user ID of the key. */
1004 0 : if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
1005 : {
1006 0 : invalid_recipients++;
1007 0 : continue;
1008 : }
1009 :
1010 0 : newlen = 11 + strlen (uid);
1011 0 : if (linelen < newlen)
1012 : {
1013 0 : char *newline = realloc (line, newlen);
1014 0 : if (! newline)
1015 : {
1016 0 : int saved_err = gpg_error_from_syserror ();
1017 0 : free (line);
1018 0 : return saved_err;
1019 : }
1020 0 : line = newline;
1021 0 : linelen = newlen;
1022 : }
1023 : /* FIXME: need to do proper escaping */
1024 0 : strcpy (&line[10], uid);
1025 :
1026 0 : err = uiserver_assuan_simple_command (ctx, line, uiserver->status.fnc,
1027 : uiserver->status.fnc_value);
1028 : /* FIXME: This might requires more work. */
1029 0 : if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1030 0 : invalid_recipients++;
1031 0 : else if (err)
1032 : {
1033 0 : free (line);
1034 0 : return err;
1035 : }
1036 : }
1037 0 : free (line);
1038 0 : return gpg_error (invalid_recipients
1039 : ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1040 : }
1041 :
1042 :
1043 : static gpgme_error_t
1044 0 : uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1045 : gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1046 : {
1047 0 : engine_uiserver_t uiserver = engine;
1048 : gpgme_error_t err;
1049 : const char *protocol;
1050 : char *cmd;
1051 :
1052 0 : if (!uiserver)
1053 0 : return gpg_error (GPG_ERR_INV_VALUE);
1054 0 : if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1055 0 : protocol = "";
1056 0 : else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1057 0 : protocol = " --protocol=OpenPGP";
1058 0 : else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1059 0 : protocol = " --protocol=CMS";
1060 : else
1061 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1062 :
1063 0 : if (flags & GPGME_ENCRYPT_PREPARE)
1064 : {
1065 0 : if (!recp || plain || ciph)
1066 0 : return gpg_error (GPG_ERR_INV_VALUE);
1067 :
1068 0 : if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
1069 0 : (flags & GPGME_ENCRYPT_EXPECT_SIGN)
1070 : ? " --expect-sign" : "") < 0)
1071 0 : return gpg_error_from_syserror ();
1072 : }
1073 : else
1074 : {
1075 0 : if (!plain || !ciph)
1076 0 : return gpg_error (GPG_ERR_INV_VALUE);
1077 :
1078 0 : if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1079 0 : return gpg_error_from_syserror ();
1080 : }
1081 :
1082 0 : if (plain)
1083 : {
1084 0 : uiserver->input_cb.data = plain;
1085 0 : err = uiserver_set_fd (uiserver, INPUT_FD,
1086 0 : map_data_enc (uiserver->input_cb.data));
1087 0 : if (err)
1088 : {
1089 0 : free (cmd);
1090 0 : return err;
1091 : }
1092 : }
1093 :
1094 0 : if (ciph)
1095 : {
1096 0 : uiserver->output_cb.data = ciph;
1097 0 : err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1098 0 : : map_data_enc (uiserver->output_cb.data));
1099 0 : if (err)
1100 : {
1101 0 : free (cmd);
1102 0 : return err;
1103 : }
1104 : }
1105 :
1106 0 : uiserver->inline_data = NULL;
1107 :
1108 0 : if (recp)
1109 : {
1110 0 : err = set_recipients (uiserver, recp);
1111 0 : if (err)
1112 : {
1113 0 : free (cmd);
1114 0 : return err;
1115 : }
1116 : }
1117 :
1118 0 : err = start (uiserver, cmd);
1119 0 : free (cmd);
1120 0 : return err;
1121 : }
1122 :
1123 :
1124 : static gpgme_error_t
1125 0 : uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1126 : gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1127 : int include_certs, gpgme_ctx_t ctx /* FIXME */)
1128 : {
1129 0 : engine_uiserver_t uiserver = engine;
1130 0 : gpgme_error_t err = 0;
1131 : const char *protocol;
1132 : char *cmd;
1133 : gpgme_key_t key;
1134 :
1135 0 : if (!uiserver || !in || !out)
1136 0 : return gpg_error (GPG_ERR_INV_VALUE);
1137 0 : if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1138 0 : protocol = "";
1139 0 : else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1140 0 : protocol = " --protocol=OpenPGP";
1141 0 : else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1142 0 : protocol = " --protocol=CMS";
1143 : else
1144 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1145 :
1146 0 : if (asprintf (&cmd, "SIGN%s%s", protocol,
1147 : (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
1148 0 : return gpg_error_from_syserror ();
1149 :
1150 0 : key = gpgme_signers_enum (ctx, 0);
1151 0 : if (key)
1152 : {
1153 0 : const char *s = NULL;
1154 :
1155 0 : if (key && key->uids)
1156 0 : s = key->uids->email;
1157 :
1158 0 : if (s && strlen (s) < 80)
1159 0 : {
1160 : char buf[100];
1161 :
1162 0 : strcpy (stpcpy (buf, "SENDER --info "), s);
1163 0 : err = uiserver_assuan_simple_command (uiserver->assuan_ctx, buf,
1164 : uiserver->status.fnc,
1165 : uiserver->status.fnc_value);
1166 : }
1167 : else
1168 0 : err = gpg_error (GPG_ERR_INV_VALUE);
1169 0 : gpgme_key_unref (key);
1170 0 : if (err)
1171 : {
1172 0 : free (cmd);
1173 0 : return err;
1174 : }
1175 : }
1176 :
1177 0 : uiserver->input_cb.data = in;
1178 0 : err = uiserver_set_fd (uiserver, INPUT_FD,
1179 0 : map_data_enc (uiserver->input_cb.data));
1180 0 : if (err)
1181 : {
1182 0 : free (cmd);
1183 0 : return err;
1184 : }
1185 0 : uiserver->output_cb.data = out;
1186 0 : err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1187 0 : : map_data_enc (uiserver->output_cb.data));
1188 0 : if (err)
1189 : {
1190 0 : free (cmd);
1191 0 : return err;
1192 : }
1193 0 : uiserver->inline_data = NULL;
1194 :
1195 0 : err = start (uiserver, cmd);
1196 0 : free (cmd);
1197 0 : return err;
1198 : }
1199 :
1200 :
1201 : /* FIXME: Missing a way to specify --silent. */
1202 : static gpgme_error_t
1203 0 : uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1204 : gpgme_data_t plaintext)
1205 : {
1206 0 : engine_uiserver_t uiserver = engine;
1207 : gpgme_error_t err;
1208 : const char *protocol;
1209 : char *cmd;
1210 :
1211 0 : if (!uiserver)
1212 0 : return gpg_error (GPG_ERR_INV_VALUE);
1213 0 : if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1214 0 : protocol = "";
1215 0 : else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1216 0 : protocol = " --protocol=OpenPGP";
1217 0 : else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1218 0 : protocol = " --protocol=CMS";
1219 : else
1220 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1221 :
1222 0 : if (asprintf (&cmd, "VERIFY%s", protocol) < 0)
1223 0 : return gpg_error_from_syserror ();
1224 :
1225 0 : uiserver->input_cb.data = sig;
1226 0 : err = uiserver_set_fd (uiserver, INPUT_FD,
1227 0 : map_data_enc (uiserver->input_cb.data));
1228 0 : if (err)
1229 : {
1230 0 : free (cmd);
1231 0 : return err;
1232 : }
1233 0 : if (plaintext)
1234 : {
1235 : /* Normal or cleartext signature. */
1236 0 : uiserver->output_cb.data = plaintext;
1237 0 : err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1238 : }
1239 : else
1240 : {
1241 : /* Detached signature. */
1242 0 : uiserver->message_cb.data = signed_text;
1243 0 : err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
1244 : }
1245 0 : uiserver->inline_data = NULL;
1246 :
1247 0 : if (!err)
1248 0 : err = start (uiserver, cmd);
1249 :
1250 0 : free (cmd);
1251 0 : return err;
1252 : }
1253 :
1254 :
1255 : static void
1256 0 : uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1257 : void *fnc_value)
1258 : {
1259 0 : engine_uiserver_t uiserver = engine;
1260 :
1261 0 : uiserver->status.fnc = fnc;
1262 0 : uiserver->status.fnc_value = fnc_value;
1263 0 : }
1264 :
1265 :
1266 : static gpgme_error_t
1267 0 : uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1268 : void *fnc_value)
1269 : {
1270 0 : engine_uiserver_t uiserver = engine;
1271 :
1272 0 : uiserver->colon.fnc = fnc;
1273 0 : uiserver->colon.fnc_value = fnc_value;
1274 0 : uiserver->colon.any = 0;
1275 0 : return 0;
1276 : }
1277 :
1278 :
1279 : static void
1280 0 : uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1281 : {
1282 0 : engine_uiserver_t uiserver = engine;
1283 0 : uiserver->io_cbs = *io_cbs;
1284 0 : }
1285 :
1286 :
1287 : static void
1288 0 : uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1289 : {
1290 0 : engine_uiserver_t uiserver = engine;
1291 :
1292 0 : TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
1293 : "event %p, type %d, type_data %p",
1294 : uiserver->io_cbs.event, type, type_data);
1295 0 : if (uiserver->io_cbs.event)
1296 0 : (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
1297 0 : }
1298 :
1299 :
1300 : struct engine_ops _gpgme_engine_ops_uiserver =
1301 : {
1302 : /* Static functions. */
1303 : _gpgme_get_default_uisrv_socket,
1304 : NULL,
1305 : uiserver_get_version,
1306 : uiserver_get_req_version,
1307 : uiserver_new,
1308 :
1309 : /* Member functions. */
1310 : uiserver_release,
1311 : uiserver_reset,
1312 : uiserver_set_status_handler,
1313 : NULL, /* set_command_handler */
1314 : uiserver_set_colon_line_handler,
1315 : uiserver_set_locale,
1316 : uiserver_set_protocol,
1317 : uiserver_decrypt,
1318 : uiserver_decrypt_verify,
1319 : NULL, /* delete */
1320 : NULL, /* edit */
1321 : uiserver_encrypt,
1322 : NULL, /* encrypt_sign */
1323 : NULL, /* export */
1324 : NULL, /* export_ext */
1325 : NULL, /* genkey */
1326 : NULL, /* import */
1327 : NULL, /* keylist */
1328 : NULL, /* keylist_ext */
1329 : uiserver_sign,
1330 : NULL, /* trustlist */
1331 : uiserver_verify,
1332 : NULL, /* getauditlog */
1333 : NULL, /* opassuan_transact */
1334 : NULL, /* conf_load */
1335 : NULL, /* conf_save */
1336 : uiserver_set_io_cbs,
1337 : uiserver_io_event,
1338 : uiserver_cancel,
1339 : NULL, /* cancel_op */
1340 : NULL, /* passwd */
1341 : NULL, /* set_pinentry_mode */
1342 : NULL /* opspawn */
1343 : };
|