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