24d3b2a8dab68bb995124c22221c0b4e8d93c993
[gpgme.git] / src / engine-gpgsm.c
1 /* engine-gpgsm.c - GpgSM engine.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009,
4                  2010 g10 Code GmbH
5
6    This file is part of GPGME.
7
8    GPGME is free software; you can redistribute it and/or modify it
9    under the terms of the GNU Lesser General Public License as
10    published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    GPGME is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 #if HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef HAVE_SYS_TYPES_H
30 # include <sys/types.h>
31 #endif
32 #include <assert.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #ifdef HAVE_LOCALE_H
37 #include <locale.h>
38 #endif
39 #include <fcntl.h> /* FIXME */
40 #include <errno.h>
41
42 #include "gpgme.h"
43 #include "util.h"
44 #include "ops.h"
45 #include "wait.h"
46 #include "priv-io.h"
47 #include "sema.h"
48 #include "data.h"
49
50 #include "assuan.h"
51 #include "debug.h"
52
53 #include "engine-backend.h"
54
55 \f
56 typedef struct
57 {
58   int fd;       /* FD we talk about.  */
59   int server_fd;/* Server FD for this connection.  */
60   int dir;      /* Inbound/Outbound, maybe given implicit?  */
61   void *data;   /* Handler-specific data.  */
62   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
63   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
64                              need this because _gpgme_io_fd2str can't
65                              be used on a closed descriptor.  */
66 } iocb_data_t;
67
68
69 struct engine_gpgsm
70 {
71   assuan_context_t assuan_ctx;
72
73   int lc_ctype_set;
74   int lc_messages_set;
75
76   iocb_data_t status_cb;
77
78   /* Input, output etc are from the servers perspective.  */
79   iocb_data_t input_cb;
80   gpgme_data_t input_helper_data;  /* Input helper data object.  */
81   void *input_helper_memory;       /* Input helper memory block.  */
82
83   iocb_data_t output_cb;
84
85   iocb_data_t message_cb;
86
87   struct
88   {
89     engine_status_handler_t fnc;
90     void *fnc_value;
91   } status;
92
93   struct
94   {
95     engine_colon_line_handler_t fnc;
96     void *fnc_value;
97     struct
98     {
99       char *line;
100       int linesize;
101       int linelen;
102     } attic;
103     int any; /* any data line seen */
104   } colon;
105
106   gpgme_data_t inline_data;  /* Used to collect D lines.  */
107
108   struct gpgme_io_cbs io_cbs;
109 };
110
111 typedef struct engine_gpgsm *engine_gpgsm_t;
112
113
114 static void gpgsm_io_event (void *engine,
115                             gpgme_event_io_t type, void *type_data);
116
117
118 \f
119 static char *
120 gpgsm_get_version (const char *file_name)
121 {
122   return _gpgme_get_program_version (file_name ? file_name
123                                      : _gpgme_get_default_gpgsm_name ());
124 }
125
126
127 static const char *
128 gpgsm_get_req_version (void)
129 {
130   return "2.0.4";
131 }
132
133 \f
134 static void
135 close_notify_handler (int fd, void *opaque)
136 {
137   engine_gpgsm_t gpgsm = opaque;
138
139   assert (fd != -1);
140   if (gpgsm->status_cb.fd == fd)
141     {
142       if (gpgsm->status_cb.tag)
143         (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
144       gpgsm->status_cb.fd = -1;
145       gpgsm->status_cb.tag = NULL;
146     }
147   else if (gpgsm->input_cb.fd == fd)
148     {
149       if (gpgsm->input_cb.tag)
150         (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
151       gpgsm->input_cb.fd = -1;
152       gpgsm->input_cb.tag = NULL;
153       if (gpgsm->input_helper_data)
154         {
155           gpgme_data_release (gpgsm->input_helper_data);
156           gpgsm->input_helper_data = NULL;
157         }
158       if (gpgsm->input_helper_memory)
159         {
160           free (gpgsm->input_helper_memory);
161           gpgsm->input_helper_memory = NULL;
162         }
163     }
164   else if (gpgsm->output_cb.fd == fd)
165     {
166       if (gpgsm->output_cb.tag)
167         (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
168       gpgsm->output_cb.fd = -1;
169       gpgsm->output_cb.tag = NULL;
170     }
171   else if (gpgsm->message_cb.fd == fd)
172     {
173       if (gpgsm->message_cb.tag)
174         (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
175       gpgsm->message_cb.fd = -1;
176       gpgsm->message_cb.tag = NULL;
177     }
178 }
179
180
181 /* This is the default inquiry callback.  We use it to handle the
182    Pinentry notifications.  */
183 static gpgme_error_t
184 default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
185 {
186   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
187     {
188       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
189     }
190
191   return 0;
192 }
193
194
195 static gpgme_error_t
196 gpgsm_cancel (void *engine)
197 {
198   engine_gpgsm_t gpgsm = engine;
199
200   if (!gpgsm)
201     return gpg_error (GPG_ERR_INV_VALUE);
202
203   if (gpgsm->status_cb.fd != -1)
204     _gpgme_io_close (gpgsm->status_cb.fd);
205   if (gpgsm->input_cb.fd != -1)
206     _gpgme_io_close (gpgsm->input_cb.fd);
207   if (gpgsm->output_cb.fd != -1)
208     _gpgme_io_close (gpgsm->output_cb.fd);
209   if (gpgsm->message_cb.fd != -1)
210     _gpgme_io_close (gpgsm->message_cb.fd);
211
212   if (gpgsm->assuan_ctx)
213     {
214       assuan_release (gpgsm->assuan_ctx);
215       gpgsm->assuan_ctx = NULL;
216     }
217
218   return 0;
219 }
220
221
222 static void
223 gpgsm_release (void *engine)
224 {
225   engine_gpgsm_t gpgsm = engine;
226
227   if (!gpgsm)
228     return;
229
230   gpgsm_cancel (engine);
231
232   free (gpgsm->colon.attic.line);
233   free (gpgsm);
234 }
235
236
237 static gpgme_error_t
238 gpgsm_new (void **engine, const char *file_name, const char *home_dir)
239 {
240   gpgme_error_t err = 0;
241   engine_gpgsm_t gpgsm;
242   const char *pgmname;
243   const char *argv[5];
244   int argc;
245 #if !USE_DESCRIPTOR_PASSING
246   int fds[2];
247   int child_fds[4];
248 #endif
249   char *dft_display = NULL;
250   char dft_ttyname[64];
251   char *dft_ttytype = NULL;
252   char *optstr;
253
254   gpgsm = calloc (1, sizeof *gpgsm);
255   if (!gpgsm)
256     return gpg_error_from_syserror ();
257
258   gpgsm->status_cb.fd = -1;
259   gpgsm->status_cb.dir = 1;
260   gpgsm->status_cb.tag = 0;
261   gpgsm->status_cb.data = gpgsm;
262
263   gpgsm->input_cb.fd = -1;
264   gpgsm->input_cb.dir = 0;
265   gpgsm->input_cb.tag = 0;
266   gpgsm->input_cb.server_fd = -1;
267   *gpgsm->input_cb.server_fd_str = 0;
268   gpgsm->output_cb.fd = -1;
269   gpgsm->output_cb.dir = 1;
270   gpgsm->output_cb.tag = 0;
271   gpgsm->output_cb.server_fd = -1;
272   *gpgsm->output_cb.server_fd_str = 0;
273   gpgsm->message_cb.fd = -1;
274   gpgsm->message_cb.dir = 0;
275   gpgsm->message_cb.tag = 0;
276   gpgsm->message_cb.server_fd = -1;
277   *gpgsm->message_cb.server_fd_str = 0;
278
279   gpgsm->status.fnc = 0;
280   gpgsm->colon.fnc = 0;
281   gpgsm->colon.attic.line = 0;
282   gpgsm->colon.attic.linesize = 0;
283   gpgsm->colon.attic.linelen = 0;
284   gpgsm->colon.any = 0;
285
286   gpgsm->inline_data = NULL;
287
288   gpgsm->io_cbs.add = NULL;
289   gpgsm->io_cbs.add_priv = NULL;
290   gpgsm->io_cbs.remove = NULL;
291   gpgsm->io_cbs.event = NULL;
292   gpgsm->io_cbs.event_priv = NULL;
293
294 #if !USE_DESCRIPTOR_PASSING
295   if (_gpgme_io_pipe (fds, 0) < 0)
296     {
297       err = gpg_error_from_syserror ();
298       goto leave;
299     }
300   gpgsm->input_cb.fd = fds[1];
301   gpgsm->input_cb.server_fd = fds[0];
302
303   if (_gpgme_io_pipe (fds, 1) < 0)
304     {
305       err = gpg_error_from_syserror ();
306       goto leave;
307     }
308   gpgsm->output_cb.fd = fds[0];
309   gpgsm->output_cb.server_fd = fds[1];
310
311   if (_gpgme_io_pipe (fds, 0) < 0)
312     {
313       err = gpg_error_from_syserror ();
314       goto leave;
315     }
316   gpgsm->message_cb.fd = fds[1];
317   gpgsm->message_cb.server_fd = fds[0];
318
319   child_fds[0] = gpgsm->input_cb.server_fd;
320   child_fds[1] = gpgsm->output_cb.server_fd;
321   child_fds[2] = gpgsm->message_cb.server_fd;
322   child_fds[3] = -1;
323 #endif
324
325   pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
326
327   argc = 0;
328   argv[argc++] = _gpgme_get_basename (pgmname);
329   if (home_dir)
330     {
331       argv[argc++] = "--homedir";
332       argv[argc++] = home_dir;
333     }
334   argv[argc++] = "--server";
335   argv[argc++] = NULL;
336
337   err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
338                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
339                         NULL);
340   if (err)
341     goto leave;
342   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
343
344 #if USE_DESCRIPTOR_PASSING
345   err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
346                              NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
347 #else
348   {
349     assuan_fd_t achild_fds[4];
350     int i;
351
352     /* For now... */
353     for (i = 0; i < 4; i++)
354       achild_fds[i] = (assuan_fd_t) child_fds[i];
355
356     err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
357                                achild_fds, NULL, NULL, 0);
358
359     /* For now... */
360     for (i = 0; i < 4; i++)
361       child_fds[i] = (int) achild_fds[i];
362   }
363
364   /* On Windows, handles are inserted in the spawned process with
365      DuplicateHandle, and child_fds contains the server-local names
366      for the inserted handles when assuan_pipe_connect returns.  */
367   if (!err)
368     {
369       /* Note: We don't use _gpgme_io_fd2str here.  On W32 the
370          returned handles are real W32 system handles, not whatever
371          GPGME uses internally (which may be a system handle, a C
372          library handle or a GLib/Qt channel.  Confusing, yes, but
373          remember these are server-local names, so they are not part
374          of GPGME at all.  */
375       snprintf (gpgsm->input_cb.server_fd_str,
376                 sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
377       snprintf (gpgsm->output_cb.server_fd_str,
378                 sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
379       snprintf (gpgsm->message_cb.server_fd_str,
380                 sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
381     }
382 #endif
383   if (err)
384     goto leave;
385
386   err = _gpgme_getenv ("DISPLAY", &dft_display);
387   if (err)
388     goto leave;
389   if (dft_display)
390     {
391       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
392         {
393           free (dft_display);
394           err = gpg_error_from_syserror ();
395           goto leave;
396         }
397       free (dft_display);
398
399       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
400                              NULL, NULL, NULL);
401       free (optstr);
402       if (err)
403         goto leave;
404     }
405
406   if (isatty (1))
407     {
408       int rc;
409
410       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
411       if (rc)
412         {
413           err = gpg_error_from_errno (rc);
414           goto leave;
415         }
416       else
417         {
418           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
419             {
420               err = gpg_error_from_syserror ();
421               goto leave;
422             }
423           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
424                                  NULL, NULL, NULL);
425           free (optstr);
426           if (err)
427             goto leave;
428
429           err = _gpgme_getenv ("TERM", &dft_ttytype);
430           if (err)
431             goto leave;
432           if (dft_ttytype)
433             {
434               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
435                 {
436                   free (dft_ttytype);
437                   err = gpg_error_from_syserror ();
438                   goto leave;
439                 }
440               free (dft_ttytype);
441
442               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
443                                      NULL, NULL, NULL, NULL);
444               free (optstr);
445               if (err)
446                 goto leave;
447             }
448         }
449     }
450
451   /* Ask gpgsm to enable the audit log support.  */
452   if (!err)
453     {
454       err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
455                              NULL, NULL, NULL, NULL, NULL, NULL);
456       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
457         err = 0; /* This is an optional feature of gpgsm.  */
458     }
459
460
461 #ifdef HAVE_W32_SYSTEM
462   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
463      gpgsm to tell us when it needs it.  */
464   if (!err)
465     {
466       err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
467                              NULL, NULL, NULL, NULL, NULL, NULL);
468       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
469         err = 0; /* This is a new feature of gpgsm.  */
470     }
471 #endif /*HAVE_W32_SYSTEM*/
472
473 #if !USE_DESCRIPTOR_PASSING
474   if (!err
475       && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
476                                       close_notify_handler, gpgsm)
477           || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
478                                          close_notify_handler, gpgsm)
479           || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
480                                          close_notify_handler, gpgsm)))
481     {
482       err = gpg_error (GPG_ERR_GENERAL);
483       goto leave;
484     }
485 #endif
486
487  leave:
488   /* Close the server ends of the pipes (because of this, we must use
489      the stored server_fd_str in the function start).  Our ends are
490      closed in gpgsm_release().  */
491 #if !USE_DESCRIPTOR_PASSING
492   if (gpgsm->input_cb.server_fd != -1)
493     _gpgme_io_close (gpgsm->input_cb.server_fd);
494   if (gpgsm->output_cb.server_fd != -1)
495     _gpgme_io_close (gpgsm->output_cb.server_fd);
496   if (gpgsm->message_cb.server_fd != -1)
497     _gpgme_io_close (gpgsm->message_cb.server_fd);
498 #endif
499
500   if (err)
501     gpgsm_release (gpgsm);
502   else
503     *engine = gpgsm;
504
505   return err;
506 }
507
508
509 static gpgme_error_t
510 gpgsm_set_locale (void *engine, int category, const char *value)
511 {
512   engine_gpgsm_t gpgsm = engine;
513   gpgme_error_t err;
514   char *optstr;
515   char *catstr;
516
517   /* FIXME: If value is NULL, we need to reset the option to default.
518      But we can't do this.  So we error out here.  GPGSM needs support
519      for this.  */
520   if (0)
521     ;
522 #ifdef LC_CTYPE
523   else if (category == LC_CTYPE)
524     {
525       catstr = "lc-ctype";
526       if (!value && gpgsm->lc_ctype_set)
527         return gpg_error (GPG_ERR_INV_VALUE);
528       if (value)
529         gpgsm->lc_ctype_set = 1;
530     }
531 #endif
532 #ifdef LC_MESSAGES
533   else if (category == LC_MESSAGES)
534     {
535       catstr = "lc-messages";
536       if (!value && gpgsm->lc_messages_set)
537         return gpg_error (GPG_ERR_INV_VALUE);
538       if (value)
539         gpgsm->lc_messages_set = 1;
540     }
541 #endif /* LC_MESSAGES */
542   else
543     return gpg_error (GPG_ERR_INV_VALUE);
544
545   /* FIXME: Reset value to default.  */
546   if (!value)
547     return 0;
548
549   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
550     err = gpg_error_from_syserror ();
551   else
552     {
553       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
554                              NULL, NULL, NULL, NULL);
555       free (optstr);
556     }
557
558   return err;
559 }
560
561
562 static gpgme_error_t
563 gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
564                              engine_status_handler_t status_fnc,
565                              void *status_fnc_value)
566 {
567   gpg_error_t err, cb_err;
568   char *line;
569   size_t linelen;
570
571   err = assuan_write_line (ctx, cmd);
572   if (err)
573     return err;
574
575   cb_err = 0;
576   do
577     {
578       err = assuan_read_line (ctx, &line, &linelen);
579       if (err)
580         return err;
581
582       if (*line == '#' || !linelen)
583         continue;
584
585       if (linelen >= 2
586           && line[0] == 'O' && line[1] == 'K'
587           && (line[2] == '\0' || line[2] == ' '))
588         return cb_err;
589       else if (linelen >= 4
590           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
591           && line[3] == ' ')
592         {
593           /* We prefer a callback generated error because that one is
594              more related to gpgme and thus probably more important
595              than the error returned by the engine.  */
596           err = cb_err? cb_err : atoi (&line[4]);
597         }
598       else if (linelen >= 2
599                && line[0] == 'S' && line[1] == ' ')
600         {
601           /* After an error from a status callback we skip all further
602              status lines.  */
603           if (!cb_err)
604             {
605               char *rest;
606               gpgme_status_code_t r;
607
608               rest = strchr (line + 2, ' ');
609               if (!rest)
610                 rest = line + linelen; /* set to an empty string */
611               else
612                 *(rest++) = 0;
613
614               r = _gpgme_parse_status (line + 2);
615
616               if (r >= 0 && status_fnc)
617                 cb_err = status_fnc (status_fnc_value, r, rest);
618             }
619         }
620       else
621         {
622           /* Invalid line or INQUIRY.  We can't do anything else than
623              to stop.  As with ERR we prefer a status callback
624              generated error code, though.  */
625           err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
626         }
627     }
628   while (!err);
629
630   return err;
631 }
632
633
634 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
635
636 static void
637 gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
638 {
639 #if !USE_DESCRIPTOR_PASSING
640   switch (fd_type)
641     {
642     case INPUT_FD:
643       _gpgme_io_close (gpgsm->input_cb.fd);
644       break;
645     case OUTPUT_FD:
646       _gpgme_io_close (gpgsm->output_cb.fd);
647       break;
648     case MESSAGE_FD:
649       _gpgme_io_close (gpgsm->message_cb.fd);
650       break;
651     }
652 #endif
653 }
654
655 #define COMMANDLINELEN 40
656 static gpgme_error_t
657 gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
658 {
659   gpg_error_t err = 0;
660   char line[COMMANDLINELEN];
661   char *which;
662   iocb_data_t *iocb_data;
663 #if USE_DESCRIPTOR_PASSING
664   int dir;
665 #endif
666
667   switch (fd_type)
668     {
669     case INPUT_FD:
670       which = "INPUT";
671       iocb_data = &gpgsm->input_cb;
672       break;
673
674     case OUTPUT_FD:
675       which = "OUTPUT";
676       iocb_data = &gpgsm->output_cb;
677       break;
678
679     case MESSAGE_FD:
680       which = "MESSAGE";
681       iocb_data = &gpgsm->message_cb;
682       break;
683
684     default:
685       return gpg_error (GPG_ERR_INV_VALUE);
686     }
687
688 #if USE_DESCRIPTOR_PASSING
689   dir = iocb_data->dir;
690   /* We try to short-cut the communication by giving GPGSM direct
691      access to the file descriptor, rather than using a pipe.  */
692   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
693   if (iocb_data->server_fd < 0)
694     {
695       int fds[2];
696
697       if (_gpgme_io_pipe (fds, dir) < 0)
698         return gpg_error_from_syserror ();
699
700       iocb_data->fd = dir ? fds[0] : fds[1];
701       iocb_data->server_fd = dir ? fds[1] : fds[0];
702
703       if (_gpgme_io_set_close_notify (iocb_data->fd,
704                                       close_notify_handler, gpgsm))
705         {
706           err = gpg_error (GPG_ERR_GENERAL);
707           goto leave_set_fd;
708         }
709     }
710
711   err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
712   if (err)
713     goto leave_set_fd;
714
715   _gpgme_io_close (iocb_data->server_fd);
716   iocb_data->server_fd = -1;
717
718   if (opt)
719     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
720   else
721     snprintf (line, COMMANDLINELEN, "%s FD", which);
722 #else
723   if (opt)
724     snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
725               which, iocb_data->server_fd_str, opt);
726   else
727     snprintf (line, COMMANDLINELEN, "%s FD=%s",
728               which, iocb_data->server_fd_str);
729 #endif
730
731   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
732
733 #if USE_DESCRIPTOR_PASSING
734  leave_set_fd:
735   if (err)
736     {
737       _gpgme_io_close (iocb_data->fd);
738       iocb_data->fd = -1;
739       if (iocb_data->server_fd != -1)
740         {
741           _gpgme_io_close (iocb_data->server_fd);
742           iocb_data->server_fd = -1;
743         }
744     }
745 #endif
746
747   return err;
748 }
749
750
751 static const char *
752 map_data_enc (gpgme_data_t d)
753 {
754   switch (gpgme_data_get_encoding (d))
755     {
756     case GPGME_DATA_ENCODING_NONE:
757       break;
758     case GPGME_DATA_ENCODING_BINARY:
759       return "--binary";
760     case GPGME_DATA_ENCODING_BASE64:
761       return "--base64";
762     case GPGME_DATA_ENCODING_ARMOR:
763       return "--armor";
764     default:
765       break;
766     }
767   return NULL;
768 }
769
770
771 static gpgme_error_t
772 status_handler (void *opaque, int fd)
773 {
774   struct io_cb_data *data = (struct io_cb_data *) opaque;
775   engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
776   gpgme_error_t err = 0;
777   char *line;
778   size_t linelen;
779
780   do
781     {
782       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
783       if (err)
784         {
785           /* Try our best to terminate the connection friendly.  */
786           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
787           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
788                   "fd 0x%x: error from assuan (%d) getting status line : %s",
789                   fd, err, gpg_strerror (err));
790         }
791       else if (linelen >= 3
792                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
793                && (line[3] == '\0' || line[3] == ' '))
794         {
795           if (line[3] == ' ')
796             err = atoi (&line[4]);
797           if (! err)
798             err = gpg_error (GPG_ERR_GENERAL);
799           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
800                   "fd 0x%x: ERR line - mapped to: %s",
801                   fd, err ? gpg_strerror (err) : "ok");
802           /* Try our best to terminate the connection friendly.  */
803           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
804         }
805       else if (linelen >= 2
806                && line[0] == 'O' && line[1] == 'K'
807                && (line[2] == '\0' || line[2] == ' '))
808         {
809           if (gpgsm->status.fnc)
810             err = gpgsm->status.fnc (gpgsm->status.fnc_value,
811                                      GPGME_STATUS_EOF, "");
812
813           if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
814             {
815               /* We must tell a colon function about the EOF. We do
816                  this only when we have seen any data lines.  Note
817                  that this inlined use of colon data lines will
818                  eventually be changed into using a regular data
819                  channel. */
820               gpgsm->colon.any = 0;
821               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
822             }
823           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
824                   "fd 0x%x: OK line - final status: %s",
825                   fd, err ? gpg_strerror (err) : "ok");
826           _gpgme_io_close (gpgsm->status_cb.fd);
827           return err;
828         }
829       else if (linelen > 2
830                && line[0] == 'D' && line[1] == ' '
831                && gpgsm->colon.fnc)
832         {
833           /* We are using the colon handler even for plain inline data
834              - strange name for that function but for historic reasons
835              we keep it.  */
836           /* FIXME We can't use this for binary data because we
837              assume this is a string.  For the current usage of colon
838              output it is correct.  */
839           char *src = line + 2;
840           char *end = line + linelen;
841           char *dst;
842           char **aline = &gpgsm->colon.attic.line;
843           int *alinelen = &gpgsm->colon.attic.linelen;
844
845           if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
846             {
847               char *newline = realloc (*aline, *alinelen + linelen + 1);
848               if (!newline)
849                 err = gpg_error_from_syserror ();
850               else
851                 {
852                   *aline = newline;
853                   gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
854                 }
855             }
856           if (!err)
857             {
858               dst = *aline + *alinelen;
859
860               while (!err && src < end)
861                 {
862                   if (*src == '%' && src + 2 < end)
863                     {
864                       /* Handle escaped characters.  */
865                       ++src;
866                       *dst = _gpgme_hextobyte (src);
867                       (*alinelen)++;
868                       src += 2;
869                     }
870                   else
871                     {
872                       *dst = *src++;
873                       (*alinelen)++;
874                     }
875
876                   if (*dst == '\n')
877                     {
878                       /* Terminate the pending line, pass it to the colon
879                          handler and reset it.  */
880
881                       gpgsm->colon.any = 1;
882                       if (*alinelen > 1 && *(dst - 1) == '\r')
883                         dst--;
884                       *dst = '\0';
885
886                       /* FIXME How should we handle the return code?  */
887                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
888                       if (!err)
889                         {
890                           dst = *aline;
891                           *alinelen = 0;
892                         }
893                     }
894                   else
895                     dst++;
896                 }
897             }
898           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
899                   "fd 0x%x: D line; final status: %s",
900                   fd, err? gpg_strerror (err):"ok");
901         }
902       else if (linelen > 2
903                && line[0] == 'D' && line[1] == ' '
904                && gpgsm->inline_data)
905         {
906           char *src = line + 2;
907           char *end = line + linelen;
908           char *dst = src;
909           gpgme_ssize_t nwritten;
910
911           linelen = 0;
912           while (src < end)
913             {
914               if (*src == '%' && src + 2 < end)
915                 {
916                   /* Handle escaped characters.  */
917                   ++src;
918                   *dst++ = _gpgme_hextobyte (src);
919                   src += 2;
920                 }
921               else
922                 *dst++ = *src++;
923
924               linelen++;
925             }
926
927           src = line + 2;
928           while (linelen > 0)
929             {
930               nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
931               if (!nwritten || (nwritten < 0 && errno != EINTR)
932                   || nwritten > linelen)
933                 {
934                   err = gpg_error_from_syserror ();
935                   break;
936                 }
937               src += nwritten;
938               linelen -= nwritten;
939             }
940
941           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
942                   "fd 0x%x: D inlinedata; final status: %s",
943                   fd, err? gpg_strerror (err):"ok");
944         }
945       else if (linelen > 2
946                && line[0] == 'S' && line[1] == ' ')
947         {
948           char *rest;
949           gpgme_status_code_t r;
950
951           rest = strchr (line + 2, ' ');
952           if (!rest)
953             rest = line + linelen; /* set to an empty string */
954           else
955             *(rest++) = 0;
956
957           r = _gpgme_parse_status (line + 2);
958
959           if (r >= 0)
960             {
961               if (gpgsm->status.fnc)
962                 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
963             }
964           else
965             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
966           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
967                   "fd 0x%x: S line (%s) - final status: %s",
968                   fd, line+2, err? gpg_strerror (err):"ok");
969         }
970       else if (linelen >= 7
971                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
972                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
973                && line[6] == 'E'
974                && (line[7] == '\0' || line[7] == ' '))
975         {
976           char *keyword = line+7;
977
978           while (*keyword == ' ')
979             keyword++;;
980           default_inq_cb (gpgsm, keyword);
981           assuan_write_line (gpgsm->assuan_ctx, "END");
982         }
983
984     }
985   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
986
987   return err;
988 }
989
990
991 static gpgme_error_t
992 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
993 {
994   gpgme_error_t err;
995
996   TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
997               "fd %d, dir %d", iocbd->fd, iocbd->dir);
998   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
999                               iocbd->fd, iocbd->dir,
1000                               handler, iocbd->data, &iocbd->tag);
1001   if (err)
1002     return TRACE_ERR (err);
1003   if (!iocbd->dir)
1004     /* FIXME Kludge around poll() problem.  */
1005     err = _gpgme_io_set_nonblocking (iocbd->fd);
1006   return TRACE_ERR (err);
1007 }
1008
1009
1010 static gpgme_error_t
1011 start (engine_gpgsm_t gpgsm, const char *command)
1012 {
1013   gpgme_error_t err;
1014   assuan_fd_t afdlist[5];
1015   int fdlist[5];
1016   int nfds;
1017   int i;
1018
1019   /* We need to know the fd used by assuan for reads.  We do this by
1020      using the assumption that the first returned fd from
1021      assuan_get_active_fds() is always this one.  */
1022   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
1023                                 afdlist, DIM (afdlist));
1024   if (nfds < 1)
1025     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1026   /* For now... */
1027   for (i = 0; i < nfds; i++)
1028     fdlist[i] = (int) afdlist[i];
1029
1030   /* We "duplicate" the file descriptor, so we can close it here (we
1031      can't close fdlist[0], as that is closed by libassuan, and
1032      closing it here might cause libassuan to close some unrelated FD
1033      later).  Alternatively, we could special case status_fd and
1034      register/unregister it manually as needed, but this increases
1035      code duplication and is more complicated as we can not use the
1036      close notifications etc.  A third alternative would be to let
1037      Assuan know that we closed the FD, but that complicates the
1038      Assuan interface.  */
1039
1040   gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
1041   if (gpgsm->status_cb.fd < 0)
1042     return gpg_error_from_syserror ();
1043
1044   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
1045                                   close_notify_handler, gpgsm))
1046     {
1047       _gpgme_io_close (gpgsm->status_cb.fd);
1048       gpgsm->status_cb.fd = -1;
1049       return gpg_error (GPG_ERR_GENERAL);
1050     }
1051
1052   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1053   if (!err && gpgsm->input_cb.fd != -1)
1054     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1055   if (!err && gpgsm->output_cb.fd != -1)
1056     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1057   if (!err && gpgsm->message_cb.fd != -1)
1058     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1059
1060   if (!err)
1061     err = assuan_write_line (gpgsm->assuan_ctx, command);
1062
1063   if (!err)
1064     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
1065
1066   return err;
1067 }
1068
1069
1070 #if USE_DESCRIPTOR_PASSING
1071 static gpgme_error_t
1072 gpgsm_reset (void *engine)
1073 {
1074   engine_gpgsm_t gpgsm = engine;
1075
1076   /* IF we have an active connection we must send a reset because we
1077      need to reset the list of signers.  Note that RESET does not
1078      reset OPTION commands. */
1079   return (gpgsm->assuan_ctx
1080           ? gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET",
1081                                          NULL, NULL)
1082           : 0);
1083 }
1084 #endif
1085
1086
1087
1088 static gpgme_error_t
1089 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1090 {
1091   engine_gpgsm_t gpgsm = engine;
1092   gpgme_error_t err;
1093
1094   if (!gpgsm)
1095     return gpg_error (GPG_ERR_INV_VALUE);
1096
1097   gpgsm->input_cb.data = ciph;
1098   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1099   if (err)
1100     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1101   gpgsm->output_cb.data = plain;
1102   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1103   if (err)
1104     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1105   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1106   gpgsm->inline_data = NULL;
1107
1108   err = start (engine, "DECRYPT");
1109   return err;
1110 }
1111
1112
1113 static gpgme_error_t
1114 gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
1115 {
1116   engine_gpgsm_t gpgsm = engine;
1117   gpgme_error_t err;
1118   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1119   char *linep = fpr;
1120   char *line;
1121   int length = 8;       /* "DELKEYS " */
1122
1123   if (!fpr)
1124     return gpg_error (GPG_ERR_INV_VALUE);
1125
1126   while (*linep)
1127     {
1128       length++;
1129       if (*linep == '%' || *linep == ' ' || *linep == '+')
1130         length += 2;
1131       linep++;
1132     }
1133   length++;
1134
1135   line = malloc (length);
1136   if (!line)
1137     return gpg_error_from_syserror ();
1138
1139   strcpy (line, "DELKEYS ");
1140   linep = &line[8];
1141
1142   while (*fpr)
1143     {
1144       switch (*fpr)
1145         {
1146         case '%':
1147           *(linep++) = '%';
1148           *(linep++) = '2';
1149           *(linep++) = '5';
1150           break;
1151         case ' ':
1152           *(linep++) = '%';
1153           *(linep++) = '2';
1154           *(linep++) = '0';
1155           break;
1156         case '+':
1157           *(linep++) = '%';
1158           *(linep++) = '2';
1159           *(linep++) = 'B';
1160           break;
1161         default:
1162           *(linep++) = *fpr;
1163           break;
1164         }
1165       fpr++;
1166     }
1167   *linep = '\0';
1168
1169   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1170   gpgsm_clear_fd (gpgsm, INPUT_FD);
1171   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1172   gpgsm->inline_data = NULL;
1173
1174   err = start (gpgsm, line);
1175   free (line);
1176
1177   return err;
1178 }
1179
1180
1181 static gpgme_error_t
1182 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1183 {
1184   gpgme_error_t err = 0;
1185   assuan_context_t ctx = gpgsm->assuan_ctx;
1186   char *line;
1187   int linelen;
1188   int invalid_recipients = 0;
1189   int i;
1190
1191   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1192   line = malloc (10 + 40 + 1);
1193   if (!line)
1194     return gpg_error_from_syserror ();
1195   strcpy (line, "RECIPIENT ");
1196   for (i =0; !err && recp[i]; i++)
1197     {
1198       char *fpr;
1199       int newlen;
1200
1201       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1202         {
1203           invalid_recipients++;
1204           continue;
1205         }
1206       fpr = recp[i]->subkeys->fpr;
1207
1208       newlen = 11 + strlen (fpr);
1209       if (linelen < newlen)
1210         {
1211           char *newline = realloc (line, newlen);
1212           if (! newline)
1213             {
1214               int saved_err = gpg_error_from_syserror ();
1215               free (line);
1216               return saved_err;
1217             }
1218           line = newline;
1219           linelen = newlen;
1220         }
1221       strcpy (&line[10], fpr);
1222
1223       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
1224                                          gpgsm->status.fnc_value);
1225       /* FIXME: This requires more work.  */
1226       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1227         invalid_recipients++;
1228       else if (err)
1229         {
1230           free (line);
1231           return err;
1232         }
1233     }
1234   free (line);
1235   return gpg_error (invalid_recipients
1236                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1237 }
1238
1239
1240 static gpgme_error_t
1241 gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1242                gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1243 {
1244   engine_gpgsm_t gpgsm = engine;
1245   gpgme_error_t err;
1246
1247   if (!gpgsm)
1248     return gpg_error (GPG_ERR_INV_VALUE);
1249   if (!recp)
1250     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1251
1252   if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)
1253     {
1254       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1255                                          "OPTION no-encrypt-to", NULL, NULL);
1256       if (err)
1257         return err;
1258     }
1259
1260   gpgsm->input_cb.data = plain;
1261   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1262   if (err)
1263     return err;
1264   gpgsm->output_cb.data = ciph;
1265   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1266                       : map_data_enc (gpgsm->output_cb.data));
1267   if (err)
1268     return err;
1269   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1270   gpgsm->inline_data = NULL;
1271
1272   err = set_recipients (gpgsm, recp);
1273
1274   if (!err)
1275     err = start (gpgsm, "ENCRYPT");
1276
1277   return err;
1278 }
1279
1280
1281 static gpgme_error_t
1282 gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
1283               gpgme_data_t keydata, int use_armor)
1284 {
1285   engine_gpgsm_t gpgsm = engine;
1286   gpgme_error_t err = 0;
1287   char *cmd;
1288
1289   if (!gpgsm)
1290     return gpg_error (GPG_ERR_INV_VALUE);
1291
1292   if (!pattern)
1293     pattern = "";
1294
1295   cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
1296   if (!cmd)
1297     return gpg_error_from_syserror ();
1298
1299   strcpy (cmd, "EXPORT ");
1300   if ((mode & GPGME_EXPORT_MODE_SECRET))
1301     {
1302       strcat (cmd, "--secret ");
1303       if ((mode & GPGME_EXPORT_MODE_RAW))
1304         strcat (cmd, "--raw ");
1305       else if ((mode & GPGME_EXPORT_MODE_PKCS12))
1306         strcat (cmd, "--pkcs12 ");
1307     }
1308   strcat (cmd, pattern);
1309
1310   gpgsm->output_cb.data = keydata;
1311   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1312                       : map_data_enc (gpgsm->output_cb.data));
1313   if (err)
1314     return err;
1315   gpgsm_clear_fd (gpgsm, INPUT_FD);
1316   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1317   gpgsm->inline_data = NULL;
1318
1319   err = start (gpgsm, cmd);
1320   free (cmd);
1321   return err;
1322 }
1323
1324
1325 static gpgme_error_t
1326 gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
1327                   gpgme_data_t keydata, int use_armor)
1328 {
1329   engine_gpgsm_t gpgsm = engine;
1330   gpgme_error_t err = 0;
1331   char *line;
1332   /* Length is "EXPORT " + "--secret " + "--pkcs12 " + p + '\0'.  */
1333   int length = 7 + 9 + 9 + 1;
1334   char *linep;
1335
1336   if (!gpgsm)
1337     return gpg_error (GPG_ERR_INV_VALUE);
1338
1339   if (pattern && *pattern)
1340     {
1341       const char **pat = pattern;
1342
1343       while (*pat)
1344         {
1345           const char *patlet = *pat;
1346
1347           while (*patlet)
1348             {
1349               length++;
1350               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1351                 length += 2;
1352               patlet++;
1353             }
1354           pat++;
1355           length++;
1356         }
1357     }
1358   line = malloc (length);
1359   if (!line)
1360     return gpg_error_from_syserror ();
1361
1362   strcpy (line, "EXPORT ");
1363   if ((mode & GPGME_EXPORT_MODE_SECRET))
1364     {
1365       strcat (line, "--secret ");
1366       if ((mode & GPGME_EXPORT_MODE_RAW))
1367         strcat (line, "--raw ");
1368       else if ((mode & GPGME_EXPORT_MODE_PKCS12))
1369         strcat (line, "--pkcs12 ");
1370     }
1371   linep = &line[strlen (line)];
1372
1373   if (pattern && *pattern)
1374     {
1375       while (*pattern)
1376         {
1377           const char *patlet = *pattern;
1378
1379           while (*patlet)
1380             {
1381               switch (*patlet)
1382                 {
1383                 case '%':
1384                   *(linep++) = '%';
1385                   *(linep++) = '2';
1386                   *(linep++) = '5';
1387                   break;
1388                 case ' ':
1389                   *(linep++) = '%';
1390                   *(linep++) = '2';
1391                   *(linep++) = '0';
1392                   break;
1393                 case '+':
1394                   *(linep++) = '%';
1395                   *(linep++) = '2';
1396                   *(linep++) = 'B';
1397                   break;
1398                 default:
1399                   *(linep++) = *patlet;
1400                   break;
1401                 }
1402               patlet++;
1403             }
1404           pattern++;
1405           if (*pattern)
1406             *linep++ = ' ';
1407         }
1408     }
1409   *linep = '\0';
1410
1411   gpgsm->output_cb.data = keydata;
1412   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1413                       : map_data_enc (gpgsm->output_cb.data));
1414   if (err)
1415     return err;
1416   gpgsm_clear_fd (gpgsm, INPUT_FD);
1417   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1418   gpgsm->inline_data = NULL;
1419
1420   err = start (gpgsm, line);
1421   free (line);
1422   return err;
1423 }
1424
1425
1426 static gpgme_error_t
1427 gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1428               gpgme_data_t pubkey, gpgme_data_t seckey)
1429 {
1430   engine_gpgsm_t gpgsm = engine;
1431   gpgme_error_t err;
1432
1433   if (!gpgsm || !pubkey || seckey)
1434     return gpg_error (GPG_ERR_INV_VALUE);
1435
1436   gpgsm->input_cb.data = help_data;
1437   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1438   if (err)
1439     return err;
1440   gpgsm->output_cb.data = pubkey;
1441   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1442                       : map_data_enc (gpgsm->output_cb.data));
1443   if (err)
1444     return err;
1445   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1446   gpgsm->inline_data = NULL;
1447
1448   err = start (gpgsm, "GENKEY");
1449   return err;
1450 }
1451
1452
1453 static gpgme_error_t
1454 gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
1455 {
1456   engine_gpgsm_t gpgsm = engine;
1457   gpgme_error_t err;
1458   gpgme_data_encoding_t dataenc;
1459   int idx;
1460
1461   if (!gpgsm)
1462     return gpg_error (GPG_ERR_INV_VALUE);
1463
1464   if (keydata && keyarray)
1465     return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
1466
1467   dataenc = gpgme_data_get_encoding (keydata);
1468
1469   if (keyarray)
1470     {
1471       size_t buflen;
1472       char *buffer, *p;
1473
1474       /* Fist check whether the engine already features the
1475          --re-import option.  */
1476       err = gpgsm_assuan_simple_command
1477         (gpgsm->assuan_ctx,
1478          "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
1479       if (err)
1480         return gpg_error (GPG_ERR_NOT_SUPPORTED);
1481
1482       /* Create an internal data object with a list of all
1483          fingerprints.  The data object and its memory (to avoid an
1484          extra copy by gpgme_data_new_from_mem) are stored in two
1485          variables which are released by the close_notify_handler.  */
1486       for (idx=0, buflen=0; keyarray[idx]; idx++)
1487         {
1488           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1489               && keyarray[idx]->subkeys
1490               && keyarray[idx]->subkeys->fpr
1491               && *keyarray[idx]->subkeys->fpr)
1492             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
1493         }
1494       /* Allocate a bufer with extra space for the trailing Nul
1495          introduced by the use of stpcpy.  */
1496       buffer = malloc (buflen+1);
1497       if (!buffer)
1498         return gpg_error_from_syserror ();
1499       for (idx=0, p = buffer; keyarray[idx]; idx++)
1500         {
1501           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1502               && keyarray[idx]->subkeys
1503               && keyarray[idx]->subkeys->fpr
1504               && *keyarray[idx]->subkeys->fpr)
1505             p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
1506         }
1507
1508       err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
1509                                      buffer, buflen, 0);
1510       if (err)
1511         {
1512           free (buffer);
1513           return err;
1514         }
1515       gpgsm->input_helper_memory = buffer;
1516
1517       gpgsm->input_cb.data = gpgsm->input_helper_data;
1518       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1519       if (err)
1520         {
1521           gpgme_data_release (gpgsm->input_helper_data);
1522           gpgsm->input_helper_data = NULL;
1523           free (gpgsm->input_helper_memory);
1524           gpgsm->input_helper_memory = NULL;
1525           return err;
1526         }
1527       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1528       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1529       gpgsm->inline_data = NULL;
1530
1531       return start (gpgsm, "IMPORT --re-import");
1532     }
1533   else if (dataenc == GPGME_DATA_ENCODING_URL
1534            || dataenc == GPGME_DATA_ENCODING_URL0
1535            || dataenc == GPGME_DATA_ENCODING_URLESC)
1536     {
1537       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1538     }
1539   else
1540     {
1541       gpgsm->input_cb.data = keydata;
1542       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1543       if (err)
1544         return err;
1545       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1546       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1547       gpgsm->inline_data = NULL;
1548
1549       return start (gpgsm, "IMPORT");
1550     }
1551 }
1552
1553
1554 static gpgme_error_t
1555 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1556                gpgme_keylist_mode_t mode, int engine_flags)
1557 {
1558   engine_gpgsm_t gpgsm = engine;
1559   char *line;
1560   gpgme_error_t err;
1561   int list_mode = 0;
1562
1563   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1564     list_mode |= 1;
1565   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1566     list_mode |= 2;
1567
1568   if (!pattern)
1569     pattern = "";
1570
1571   /* Hack to make sure that the agent is started.  Only if the agent
1572      has been started an application may connect to the agent via
1573      GPGME_PROTOCOL_ASSUAN - for example to look for smartcards.  We
1574      do this only if a secret key listing has been requested.  In
1575      general this is not needed because a secret key listing starts
1576      the agent.  However on a fresh installation no public keys are
1577      available and thus there is no need for gpgsm to ask the agent
1578      whether a secret key exists for the public key.  */
1579   if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
1580     gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check",
1581                                  NULL, NULL);
1582
1583   /* Always send list-mode option because RESET does not reset it.  */
1584   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1585     return gpg_error_from_syserror ();
1586   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1587   free (line);
1588   if (err)
1589     return err;
1590
1591
1592   /* Always send key validation because RESET does not reset it.  */
1593
1594   /* Use the validation mode if requested.  We don't check for an error
1595      yet because this is a pretty fresh gpgsm features. */
1596   gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1597                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1598                                "OPTION with-validation=1":
1599                                "OPTION with-validation=0" ,
1600                                NULL, NULL);
1601   /* Include the ephemeral keys if requested.  We don't check for an error
1602      yet because this is a pretty fresh gpgsm features. */
1603   gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1604                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
1605                                "OPTION with-ephemeral-keys=1":
1606                                "OPTION with-ephemeral-keys=0" ,
1607                                NULL, NULL);
1608   gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1609                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1610                                "OPTION with-secret=1":
1611                                "OPTION with-secret=0" ,
1612                                NULL, NULL);
1613   gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1614                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1615                                "OPTION offline=1":
1616                                "OPTION offline=0" ,
1617                                NULL, NULL);
1618
1619
1620   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1621   line = malloc (15 + strlen (pattern) + 1);
1622   if (!line)
1623     return gpg_error_from_syserror ();
1624   if (secret_only)
1625     {
1626       strcpy (line, "LISTSECRETKEYS ");
1627       strcpy (&line[15], pattern);
1628     }
1629   else
1630     {
1631       strcpy (line, "LISTKEYS ");
1632       strcpy (&line[9], pattern);
1633     }
1634
1635   gpgsm_clear_fd (gpgsm, INPUT_FD);
1636   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1637   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1638   gpgsm->inline_data = NULL;
1639
1640   err = start (gpgsm, line);
1641   free (line);
1642   return err;
1643 }
1644
1645
1646 static gpgme_error_t
1647 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1648                    int reserved, gpgme_keylist_mode_t mode, int engine_flags)
1649 {
1650   engine_gpgsm_t gpgsm = engine;
1651   char *line;
1652   gpgme_error_t err;
1653   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1654   int length = 15 + 1;
1655   char *linep;
1656   int any_pattern = 0;
1657   int list_mode = 0;
1658
1659   if (reserved)
1660     return gpg_error (GPG_ERR_INV_VALUE);
1661
1662   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1663     list_mode |= 1;
1664   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1665     list_mode |= 2;
1666
1667   /* Always send list-mode option because RESET does not reset it.  */
1668   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1669     return gpg_error_from_syserror ();
1670   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1671   free (line);
1672   if (err)
1673     return err;
1674
1675   /* Always send key validation because RESET does not reset it.  */
1676   /* Use the validation mode if required.  We don't check for an error
1677      yet because this is a pretty fresh gpgsm features. */
1678   gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1679                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1680                                "OPTION with-validation=1":
1681                                "OPTION with-validation=0" ,
1682                                NULL, NULL);
1683   gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1684                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1685                                "OPTION with-secret=1":
1686                                "OPTION with-secret=0" ,
1687                                NULL, NULL);
1688   gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1689                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1690                                "OPTION offline=1":
1691                                "OPTION offline=0" ,
1692                                NULL, NULL);
1693
1694   if (pattern && *pattern)
1695     {
1696       const char **pat = pattern;
1697
1698       while (*pat)
1699         {
1700           const char *patlet = *pat;
1701
1702           while (*patlet)
1703             {
1704               length++;
1705               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1706                 length += 2;
1707               patlet++;
1708             }
1709           pat++;
1710           length++;
1711         }
1712     }
1713   line = malloc (length);
1714   if (!line)
1715     return gpg_error_from_syserror ();
1716   if (secret_only)
1717     {
1718       strcpy (line, "LISTSECRETKEYS ");
1719       linep = &line[15];
1720     }
1721   else
1722     {
1723       strcpy (line, "LISTKEYS ");
1724       linep = &line[9];
1725     }
1726
1727   if (pattern && *pattern)
1728     {
1729       while (*pattern)
1730         {
1731           const char *patlet = *pattern;
1732
1733           while (*patlet)
1734             {
1735               switch (*patlet)
1736                 {
1737                 case '%':
1738                   *(linep++) = '%';
1739                   *(linep++) = '2';
1740                   *(linep++) = '5';
1741                   break;
1742                 case ' ':
1743                   *(linep++) = '%';
1744                   *(linep++) = '2';
1745                   *(linep++) = '0';
1746                   break;
1747                 case '+':
1748                   *(linep++) = '%';
1749                   *(linep++) = '2';
1750                   *(linep++) = 'B';
1751                   break;
1752                 default:
1753                   *(linep++) = *patlet;
1754                   break;
1755                 }
1756               patlet++;
1757             }
1758           any_pattern = 1;
1759           *linep++ = ' ';
1760           pattern++;
1761         }
1762     }
1763   if (any_pattern)
1764     linep--;
1765   *linep = '\0';
1766
1767   gpgsm_clear_fd (gpgsm, INPUT_FD);
1768   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1769   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1770   gpgsm->inline_data = NULL;
1771
1772   err = start (gpgsm, line);
1773   free (line);
1774   return err;
1775 }
1776
1777
1778 static gpgme_error_t
1779 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1780             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1781             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1782 {
1783   engine_gpgsm_t gpgsm = engine;
1784   gpgme_error_t err;
1785   char *assuan_cmd;
1786   int i;
1787   gpgme_key_t key;
1788
1789   if (!gpgsm)
1790     return gpg_error (GPG_ERR_INV_VALUE);
1791
1792   /* FIXME: This does not work as RESET does not reset it so we can't
1793      revert back to default.  */
1794   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1795     {
1796       /* FIXME: Make sure that if we run multiple operations, that we
1797          can reset any previously set value in case the default is
1798          requested.  */
1799
1800       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1801         return gpg_error_from_syserror ();
1802       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
1803                                          NULL, NULL);
1804       free (assuan_cmd);
1805       if (err)
1806         return err;
1807     }
1808
1809   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1810     {
1811       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1812       if (s && strlen (s) < 80)
1813         {
1814           char buf[100];
1815
1816           strcpy (stpcpy (buf, "SIGNER "), s);
1817           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1818                                              gpgsm->status.fnc,
1819                                              gpgsm->status.fnc_value);
1820         }
1821       else
1822         err = gpg_error (GPG_ERR_INV_VALUE);
1823       gpgme_key_unref (key);
1824       if (err)
1825         return err;
1826     }
1827
1828   gpgsm->input_cb.data = in;
1829   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1830   if (err)
1831     return err;
1832   gpgsm->output_cb.data = out;
1833   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1834                       : map_data_enc (gpgsm->output_cb.data));
1835   if (err)
1836     return err;
1837   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1838   gpgsm->inline_data = NULL;
1839
1840   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1841                ? "SIGN --detached" : "SIGN");
1842   return err;
1843 }
1844
1845
1846 static gpgme_error_t
1847 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1848               gpgme_data_t plaintext)
1849 {
1850   engine_gpgsm_t gpgsm = engine;
1851   gpgme_error_t err;
1852
1853   if (!gpgsm)
1854     return gpg_error (GPG_ERR_INV_VALUE);
1855
1856   gpgsm->input_cb.data = sig;
1857   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1858   if (err)
1859     return err;
1860   if (plaintext)
1861     {
1862       /* Normal or cleartext signature.  */
1863       gpgsm->output_cb.data = plaintext;
1864       err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1865       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1866     }
1867   else
1868     {
1869       /* Detached signature.  */
1870       gpgsm->message_cb.data = signed_text;
1871       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
1872       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1873     }
1874   gpgsm->inline_data = NULL;
1875
1876   if (!err)
1877     err = start (gpgsm, "VERIFY");
1878
1879   return err;
1880 }
1881
1882
1883 /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
1884    object.  */
1885 static gpgme_error_t
1886 gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
1887 {
1888   engine_gpgsm_t gpgsm = engine;
1889   gpgme_error_t err = 0;
1890
1891   if (!gpgsm || !output)
1892     return gpg_error (GPG_ERR_INV_VALUE);
1893
1894 #if USE_DESCRIPTOR_PASSING
1895   gpgsm->output_cb.data = output;
1896   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1897   if (err)
1898     return err;
1899
1900   gpgsm_clear_fd (gpgsm, INPUT_FD);
1901   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1902   gpgsm->inline_data = NULL;
1903 # define CMD  "GETAUDITLOG"
1904 #else
1905   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1906   gpgsm_clear_fd (gpgsm, INPUT_FD);
1907   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1908   gpgsm->inline_data = output;
1909 # define CMD  "GETAUDITLOG --data"
1910 #endif
1911
1912   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
1913
1914   return err;
1915 }
1916
1917
1918
1919 static void
1920 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1921                           void *fnc_value)
1922 {
1923   engine_gpgsm_t gpgsm = engine;
1924
1925   gpgsm->status.fnc = fnc;
1926   gpgsm->status.fnc_value = fnc_value;
1927 }
1928
1929
1930 static gpgme_error_t
1931 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1932                               void *fnc_value)
1933 {
1934   engine_gpgsm_t gpgsm = engine;
1935
1936   gpgsm->colon.fnc = fnc;
1937   gpgsm->colon.fnc_value = fnc_value;
1938   gpgsm->colon.any = 0;
1939   return 0;
1940 }
1941
1942
1943 static void
1944 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1945 {
1946   engine_gpgsm_t gpgsm = engine;
1947   gpgsm->io_cbs = *io_cbs;
1948 }
1949
1950
1951 static void
1952 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1953 {
1954   engine_gpgsm_t gpgsm = engine;
1955
1956   TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
1957           "event %p, type %d, type_data %p",
1958           gpgsm->io_cbs.event, type, type_data);
1959   if (gpgsm->io_cbs.event)
1960     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1961 }
1962
1963
1964 static gpgme_error_t
1965 gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
1966 {
1967   engine_gpgsm_t gpgsm = engine;
1968   gpgme_error_t err;
1969   char *line;
1970
1971   if (!key || !key->subkeys || !key->subkeys->fpr)
1972     return gpg_error (GPG_ERR_INV_CERT_OBJ);
1973
1974   if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
1975     return gpg_error_from_syserror ();
1976
1977   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1978   gpgsm_clear_fd (gpgsm, INPUT_FD);
1979   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1980   gpgsm->inline_data = NULL;
1981
1982   err = start (gpgsm, line);
1983   free (line);
1984
1985   return err;
1986 }
1987
1988
1989
1990 struct engine_ops _gpgme_engine_ops_gpgsm =
1991   {
1992     /* Static functions.  */
1993     _gpgme_get_default_gpgsm_name,
1994     NULL,
1995     gpgsm_get_version,
1996     gpgsm_get_req_version,
1997     gpgsm_new,
1998
1999     /* Member functions.  */
2000     gpgsm_release,
2001 #if USE_DESCRIPTOR_PASSING
2002     gpgsm_reset,
2003 #else
2004     NULL,                       /* reset */
2005 #endif
2006     gpgsm_set_status_handler,
2007     NULL,               /* set_command_handler */
2008     gpgsm_set_colon_line_handler,
2009     gpgsm_set_locale,
2010     NULL,               /* set_protocol */
2011     gpgsm_decrypt,
2012     gpgsm_decrypt,
2013     gpgsm_delete,       /* decrypt_verify */
2014     NULL,               /* edit */
2015     gpgsm_encrypt,
2016     NULL,               /* encrypt_sign */
2017     gpgsm_export,
2018     gpgsm_export_ext,
2019     gpgsm_genkey,
2020     gpgsm_import,
2021     gpgsm_keylist,
2022     gpgsm_keylist_ext,
2023     gpgsm_sign,
2024     NULL,               /* trustlist */
2025     gpgsm_verify,
2026     gpgsm_getauditlog,
2027     NULL,               /* opassuan_transact */
2028     NULL,               /* conf_load */
2029     NULL,               /* conf_save */
2030     gpgsm_set_io_cbs,
2031     gpgsm_io_event,
2032     gpgsm_cancel,
2033     NULL,               /* cancel_op */
2034     gpgsm_passwd,
2035     NULL,               /* set_pinentry_mode */
2036     NULL                /* opspawn */
2037   };