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