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