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 117 : uiserver_get_version (const char *file_name)
125 : {
126 : (void)file_name;
127 117 : return NULL;
128 : }
129 :
130 :
131 : static const char *
132 117 : uiserver_get_req_version (void)
133 : {
134 117 : 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,
963 : gpgme_decrypt_flags_t flags,
964 : gpgme_data_t ciph, gpgme_data_t plain,
965 : int export_session_key, const char *override_session_key,
966 : int auto_key_retrieve)
967 : {
968 0 : engine_uiserver_t uiserver = engine;
969 : gpgme_error_t err;
970 : const char *protocol;
971 : char *cmd;
972 0 : int verify = !!(flags & GPGME_DECRYPT_VERIFY);
973 :
974 : (void)override_session_key; /* Fixme: We need to see now to add this
975 : * to the UI server protocol */
976 : (void)auto_key_retrieve; /* Not yet supported. */
977 :
978 :
979 0 : if (!uiserver)
980 0 : return gpg_error (GPG_ERR_INV_VALUE);
981 0 : if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
982 0 : protocol = "";
983 0 : else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
984 0 : protocol = " --protocol=OpenPGP";
985 0 : else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
986 0 : protocol = " --protocol=CMS";
987 : else
988 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
989 :
990 0 : if (gpgrt_asprintf (&cmd, "DECRYPT%s%s%s", protocol,
991 : verify ? "" : " --no-verify",
992 : export_session_key ? " --export-session-key" : "") < 0)
993 0 : return gpg_error_from_syserror ();
994 :
995 0 : uiserver->input_cb.data = ciph;
996 0 : err = uiserver_set_fd (uiserver, INPUT_FD,
997 0 : map_data_enc (uiserver->input_cb.data));
998 0 : if (err)
999 : {
1000 0 : gpgrt_free (cmd);
1001 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1002 : }
1003 0 : uiserver->output_cb.data = plain;
1004 0 : err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1005 0 : if (err)
1006 : {
1007 0 : gpgrt_free (cmd);
1008 0 : return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1009 : }
1010 0 : uiserver->inline_data = NULL;
1011 :
1012 0 : err = start (engine, cmd);
1013 0 : gpgrt_free (cmd);
1014 0 : return err;
1015 : }
1016 :
1017 :
1018 : static gpgme_error_t
1019 0 : set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[])
1020 : {
1021 0 : gpgme_error_t err = 0;
1022 : char *line;
1023 : int linelen;
1024 0 : int invalid_recipients = 0;
1025 : int i;
1026 :
1027 0 : linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */
1028 0 : line = malloc (10 + 40 + 1);
1029 0 : if (!line)
1030 0 : return gpg_error_from_syserror ();
1031 0 : strcpy (line, "RECIPIENT ");
1032 0 : for (i=0; !err && recp[i]; i++)
1033 : {
1034 : char *uid;
1035 : int newlen;
1036 :
1037 : /* We use only the first user ID of the key. */
1038 0 : if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid)
1039 : {
1040 0 : invalid_recipients++;
1041 0 : continue;
1042 : }
1043 :
1044 0 : newlen = 11 + strlen (uid);
1045 0 : if (linelen < newlen)
1046 : {
1047 0 : char *newline = realloc (line, newlen);
1048 0 : if (! newline)
1049 : {
1050 0 : int saved_err = gpg_error_from_syserror ();
1051 0 : free (line);
1052 0 : return saved_err;
1053 : }
1054 0 : line = newline;
1055 0 : linelen = newlen;
1056 : }
1057 : /* FIXME: need to do proper escaping */
1058 0 : strcpy (&line[10], uid);
1059 :
1060 0 : err = uiserver_assuan_simple_command (uiserver, line,
1061 : uiserver->status.fnc,
1062 : uiserver->status.fnc_value);
1063 : /* FIXME: This might requires more work. */
1064 0 : if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1065 0 : invalid_recipients++;
1066 0 : else if (err)
1067 : {
1068 0 : free (line);
1069 0 : return err;
1070 : }
1071 : }
1072 0 : free (line);
1073 0 : return gpg_error (invalid_recipients
1074 : ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1075 : }
1076 :
1077 :
1078 : /* Take recipients from the LF delimited STRING and send RECIPIENT
1079 : * commands to gpgsm. */
1080 : static gpgme_error_t
1081 0 : set_recipients_from_string (engine_uiserver_t uiserver, const char *string)
1082 : {
1083 0 : gpg_error_t err = 0;
1084 0 : char *line = NULL;
1085 0 : int no_pubkey = 0;
1086 : const char *s;
1087 : int n;
1088 :
1089 : for (;;)
1090 : {
1091 0 : while (*string == ' ' || *string == '\t')
1092 0 : string++;
1093 0 : if (!*string)
1094 0 : break;
1095 :
1096 0 : s = strchr (string, '\n');
1097 0 : if (s)
1098 0 : n = s - string;
1099 : else
1100 0 : n = strlen (string);
1101 0 : while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
1102 0 : n--;
1103 :
1104 0 : gpgrt_free (line);
1105 0 : if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
1106 : {
1107 0 : err = gpg_error_from_syserror ();
1108 0 : break;
1109 : }
1110 0 : string += n + !!s;
1111 :
1112 0 : err = uiserver_assuan_simple_command (uiserver, line,
1113 : uiserver->status.fnc,
1114 : uiserver->status.fnc_value);
1115 :
1116 : /* Fixme: Improve error reporting. */
1117 0 : if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1118 0 : no_pubkey++;
1119 0 : else if (err)
1120 0 : break;
1121 : }
1122 0 : gpgrt_free (line);
1123 0 : return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
1124 : }
1125 :
1126 :
1127 : static gpgme_error_t
1128 0 : uiserver_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
1129 : gpgme_encrypt_flags_t flags,
1130 : gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1131 : {
1132 0 : engine_uiserver_t uiserver = engine;
1133 : gpgme_error_t err;
1134 : const char *protocol;
1135 : char *cmd;
1136 :
1137 0 : if (!uiserver)
1138 0 : return gpg_error (GPG_ERR_INV_VALUE);
1139 0 : if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1140 0 : protocol = "";
1141 0 : else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1142 0 : protocol = " --protocol=OpenPGP";
1143 0 : else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1144 0 : protocol = " --protocol=CMS";
1145 : else
1146 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1147 :
1148 0 : if (flags & GPGME_ENCRYPT_PREPARE)
1149 : {
1150 0 : if (!recp || plain || ciph)
1151 0 : return gpg_error (GPG_ERR_INV_VALUE);
1152 :
1153 0 : if (gpgrt_asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol,
1154 0 : (flags & GPGME_ENCRYPT_EXPECT_SIGN)
1155 : ? " --expect-sign" : "") < 0)
1156 0 : return gpg_error_from_syserror ();
1157 : }
1158 : else
1159 : {
1160 0 : if (!plain || !ciph)
1161 0 : return gpg_error (GPG_ERR_INV_VALUE);
1162 :
1163 0 : if (gpgrt_asprintf (&cmd, "ENCRYPT%s", protocol) < 0)
1164 0 : return gpg_error_from_syserror ();
1165 : }
1166 :
1167 0 : if (plain)
1168 : {
1169 0 : uiserver->input_cb.data = plain;
1170 0 : err = uiserver_set_fd (uiserver, INPUT_FD,
1171 0 : map_data_enc (uiserver->input_cb.data));
1172 0 : if (err)
1173 : {
1174 0 : gpgrt_free (cmd);
1175 0 : return err;
1176 : }
1177 : }
1178 :
1179 0 : if (ciph)
1180 : {
1181 0 : uiserver->output_cb.data = ciph;
1182 0 : err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1183 0 : : map_data_enc (uiserver->output_cb.data));
1184 0 : if (err)
1185 : {
1186 0 : gpgrt_free (cmd);
1187 0 : return err;
1188 : }
1189 : }
1190 :
1191 0 : uiserver->inline_data = NULL;
1192 :
1193 0 : if (recp || recpstring)
1194 : {
1195 0 : if (recp)
1196 0 : err = set_recipients (uiserver, recp);
1197 : else
1198 0 : err = set_recipients_from_string (uiserver, recpstring);
1199 0 : if (err)
1200 : {
1201 0 : gpgrt_free (cmd);
1202 0 : return err;
1203 : }
1204 : }
1205 :
1206 0 : err = start (uiserver, cmd);
1207 0 : gpgrt_free (cmd);
1208 0 : return err;
1209 : }
1210 :
1211 :
1212 : static gpgme_error_t
1213 0 : uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1214 : gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1215 : int include_certs, gpgme_ctx_t ctx /* FIXME */)
1216 : {
1217 0 : engine_uiserver_t uiserver = engine;
1218 0 : gpgme_error_t err = 0;
1219 : const char *protocol;
1220 : char *cmd;
1221 : gpgme_key_t key;
1222 :
1223 : (void)use_textmode;
1224 : (void)include_certs;
1225 :
1226 0 : if (!uiserver || !in || !out)
1227 0 : return gpg_error (GPG_ERR_INV_VALUE);
1228 0 : if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1229 0 : protocol = "";
1230 0 : else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1231 0 : protocol = " --protocol=OpenPGP";
1232 0 : else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1233 0 : protocol = " --protocol=CMS";
1234 : else
1235 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1236 :
1237 0 : if (gpgrt_asprintf (&cmd, "SIGN%s%s", protocol,
1238 : (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0)
1239 0 : return gpg_error_from_syserror ();
1240 :
1241 0 : key = gpgme_signers_enum (ctx, 0);
1242 0 : if (key)
1243 : {
1244 0 : const char *s = NULL;
1245 :
1246 0 : if (key && key->uids)
1247 0 : s = key->uids->email;
1248 :
1249 0 : if (s && strlen (s) < 80)
1250 0 : {
1251 : char buf[100];
1252 :
1253 0 : strcpy (stpcpy (buf, "SENDER --info "), s);
1254 0 : err = uiserver_assuan_simple_command (uiserver, buf,
1255 : uiserver->status.fnc,
1256 : uiserver->status.fnc_value);
1257 : }
1258 : else
1259 0 : err = gpg_error (GPG_ERR_INV_VALUE);
1260 0 : gpgme_key_unref (key);
1261 0 : if (err)
1262 : {
1263 0 : gpgrt_free (cmd);
1264 0 : return err;
1265 : }
1266 : }
1267 :
1268 0 : uiserver->input_cb.data = in;
1269 0 : err = uiserver_set_fd (uiserver, INPUT_FD,
1270 0 : map_data_enc (uiserver->input_cb.data));
1271 0 : if (err)
1272 : {
1273 0 : gpgrt_free (cmd);
1274 0 : return err;
1275 : }
1276 0 : uiserver->output_cb.data = out;
1277 0 : err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor"
1278 0 : : map_data_enc (uiserver->output_cb.data));
1279 0 : if (err)
1280 : {
1281 0 : gpgrt_free (cmd);
1282 0 : return err;
1283 : }
1284 0 : uiserver->inline_data = NULL;
1285 :
1286 0 : err = start (uiserver, cmd);
1287 0 : gpgrt_free (cmd);
1288 0 : return err;
1289 : }
1290 :
1291 :
1292 : /* FIXME: Missing a way to specify --silent. */
1293 : static gpgme_error_t
1294 0 : uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1295 : gpgme_data_t plaintext, gpgme_ctx_t ctx)
1296 : {
1297 0 : engine_uiserver_t uiserver = engine;
1298 : gpgme_error_t err;
1299 : const char *protocol;
1300 : char *cmd;
1301 :
1302 : (void)ctx; /* FIXME: We should to add a --sender option to the
1303 : * UISever protocol. */
1304 :
1305 0 : if (!uiserver)
1306 0 : return gpg_error (GPG_ERR_INV_VALUE);
1307 0 : if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT)
1308 0 : protocol = "";
1309 0 : else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP)
1310 0 : protocol = " --protocol=OpenPGP";
1311 0 : else if (uiserver->protocol == GPGME_PROTOCOL_CMS)
1312 0 : protocol = " --protocol=CMS";
1313 : else
1314 0 : return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
1315 :
1316 0 : if (gpgrt_asprintf (&cmd, "VERIFY%s", protocol) < 0)
1317 0 : return gpg_error_from_syserror ();
1318 :
1319 0 : uiserver->input_cb.data = sig;
1320 0 : err = uiserver_set_fd (uiserver, INPUT_FD,
1321 0 : map_data_enc (uiserver->input_cb.data));
1322 0 : if (err)
1323 : {
1324 0 : gpgrt_free (cmd);
1325 0 : return err;
1326 : }
1327 0 : if (plaintext)
1328 : {
1329 : /* Normal or cleartext signature. */
1330 0 : uiserver->output_cb.data = plaintext;
1331 0 : err = uiserver_set_fd (uiserver, OUTPUT_FD, 0);
1332 : }
1333 : else
1334 : {
1335 : /* Detached signature. */
1336 0 : uiserver->message_cb.data = signed_text;
1337 0 : err = uiserver_set_fd (uiserver, MESSAGE_FD, 0);
1338 : }
1339 0 : uiserver->inline_data = NULL;
1340 :
1341 0 : if (!err)
1342 0 : err = start (uiserver, cmd);
1343 :
1344 0 : gpgrt_free (cmd);
1345 0 : return err;
1346 : }
1347 :
1348 :
1349 : /* This sets a status callback for monitoring status lines before they
1350 : * are passed to a caller set handler. */
1351 : static void
1352 0 : uiserver_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
1353 : {
1354 0 : engine_uiserver_t uiserver = engine;
1355 :
1356 0 : uiserver->status.mon_cb = cb;
1357 0 : uiserver->status.mon_cb_value = cb_value;
1358 0 : }
1359 :
1360 :
1361 : static void
1362 0 : uiserver_set_status_handler (void *engine, engine_status_handler_t fnc,
1363 : void *fnc_value)
1364 : {
1365 0 : engine_uiserver_t uiserver = engine;
1366 :
1367 0 : uiserver->status.fnc = fnc;
1368 0 : uiserver->status.fnc_value = fnc_value;
1369 0 : }
1370 :
1371 :
1372 : static gpgme_error_t
1373 0 : uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1374 : void *fnc_value)
1375 : {
1376 0 : engine_uiserver_t uiserver = engine;
1377 :
1378 0 : uiserver->colon.fnc = fnc;
1379 0 : uiserver->colon.fnc_value = fnc_value;
1380 0 : uiserver->colon.any = 0;
1381 0 : return 0;
1382 : }
1383 :
1384 :
1385 : static void
1386 0 : uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1387 : {
1388 0 : engine_uiserver_t uiserver = engine;
1389 0 : uiserver->io_cbs = *io_cbs;
1390 0 : }
1391 :
1392 :
1393 : static void
1394 0 : uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1395 : {
1396 0 : engine_uiserver_t uiserver = engine;
1397 :
1398 0 : TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver,
1399 : "event %p, type %d, type_data %p",
1400 : uiserver->io_cbs.event, type, type_data);
1401 0 : if (uiserver->io_cbs.event)
1402 0 : (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data);
1403 0 : }
1404 :
1405 :
1406 : struct engine_ops _gpgme_engine_ops_uiserver =
1407 : {
1408 : /* Static functions. */
1409 : _gpgme_get_default_uisrv_socket,
1410 : NULL,
1411 : uiserver_get_version,
1412 : uiserver_get_req_version,
1413 : uiserver_new,
1414 :
1415 : /* Member functions. */
1416 : uiserver_release,
1417 : uiserver_reset,
1418 : uiserver_set_status_cb,
1419 : uiserver_set_status_handler,
1420 : NULL, /* set_command_handler */
1421 : uiserver_set_colon_line_handler,
1422 : uiserver_set_locale,
1423 : uiserver_set_protocol,
1424 : NULL, /* set_engine_flags */
1425 : uiserver_decrypt,
1426 : NULL, /* delete */
1427 : NULL, /* edit */
1428 : uiserver_encrypt,
1429 : NULL, /* encrypt_sign */
1430 : NULL, /* export */
1431 : NULL, /* export_ext */
1432 : NULL, /* genkey */
1433 : NULL, /* import */
1434 : NULL, /* keylist */
1435 : NULL, /* keylist_ext */
1436 : NULL, /* keylist_data */
1437 : NULL, /* keysign */
1438 : NULL, /* tofu_policy */
1439 : uiserver_sign,
1440 : NULL, /* trustlist */
1441 : uiserver_verify,
1442 : NULL, /* getauditlog */
1443 : NULL, /* opassuan_transact */
1444 : NULL, /* conf_load */
1445 : NULL, /* conf_save */
1446 : NULL, /* conf_dir */
1447 : NULL, /* query_swdb */
1448 : uiserver_set_io_cbs,
1449 : uiserver_io_event,
1450 : uiserver_cancel,
1451 : NULL, /* cancel_op */
1452 : NULL, /* passwd */
1453 : NULL, /* set_pinentry_mode */
1454 : NULL /* opspawn */
1455 : };
|