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