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