Improved W32 SetForegroundWindow hacks.
[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_foreground_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   if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)
1353     {
1354       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx,
1355                                          "OPTION no-encrypt-to", NULL, NULL);
1356       if (err)
1357         return err;
1358     }
1359
1360   gpgsm->input_cb.data = plain;
1361   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1362   if (err)
1363     return err;
1364   gpgsm->output_cb.data = ciph;
1365   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1366                       : map_data_enc (gpgsm->output_cb.data));
1367   if (err)
1368     return err;
1369   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1370   gpgsm->inline_data = NULL;
1371
1372   err = set_recipients (gpgsm, recp);
1373
1374   if (!err)
1375     err = start (gpgsm, "ENCRYPT");
1376
1377   return err;
1378 }
1379
1380
1381 static gpgme_error_t
1382 gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
1383               gpgme_data_t keydata, int use_armor)
1384 {
1385   engine_gpgsm_t gpgsm = engine;
1386   gpgme_error_t err = 0;
1387   char *cmd;
1388
1389   if (!gpgsm || reserved)
1390     return gpg_error (GPG_ERR_INV_VALUE);
1391
1392   if (!pattern)
1393     pattern = "";
1394
1395   cmd = malloc (7 + strlen (pattern) + 1);
1396   if (!cmd)
1397     return gpg_error_from_errno (errno);
1398   strcpy (cmd, "EXPORT ");
1399   strcpy (&cmd[7], pattern);
1400
1401   gpgsm->output_cb.data = keydata;
1402   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1403                       : map_data_enc (gpgsm->output_cb.data));
1404   if (err)
1405     return err;
1406   gpgsm_clear_fd (gpgsm, INPUT_FD);
1407   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1408   gpgsm->inline_data = NULL;
1409
1410   err = start (gpgsm, cmd);
1411   free (cmd);
1412   return err;
1413 }
1414
1415
1416 static gpgme_error_t
1417 gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
1418                   gpgme_data_t keydata, int use_armor)
1419 {
1420   engine_gpgsm_t gpgsm = engine;
1421   gpgme_error_t err = 0;
1422   char *line;
1423   /* Length is "EXPORT " + p + '\0'.  */
1424   int length = 7 + 1;
1425   char *linep;
1426
1427   if (!gpgsm || reserved)
1428     return gpg_error (GPG_ERR_INV_VALUE);
1429
1430   if (pattern && *pattern)
1431     {
1432       const char **pat = pattern;
1433
1434       while (*pat)
1435         {
1436           const char *patlet = *pat;
1437
1438           while (*patlet)
1439             {
1440               length++;
1441               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1442                 length += 2;
1443               patlet++;
1444             }
1445           pat++;
1446           length++;
1447         }
1448     }
1449   line = malloc (length);
1450   if (!line)
1451     return gpg_error_from_errno (errno);
1452
1453   strcpy (line, "EXPORT ");
1454   linep = &line[7];
1455
1456   if (pattern && *pattern)
1457     {
1458       while (*pattern)
1459         {
1460           const char *patlet = *pattern;
1461
1462           while (*patlet)
1463             {
1464               switch (*patlet)
1465                 {
1466                 case '%':
1467                   *(linep++) = '%';
1468                   *(linep++) = '2';
1469                   *(linep++) = '5';
1470                   break;
1471                 case ' ':
1472                   *(linep++) = '%';
1473                   *(linep++) = '2';
1474                   *(linep++) = '0';
1475                   break;
1476                 case '+':
1477                   *(linep++) = '%';
1478                   *(linep++) = '2';
1479                   *(linep++) = 'B';
1480                   break;
1481                 default:
1482                   *(linep++) = *patlet;
1483                   break;
1484                 }
1485               patlet++;
1486             }
1487           pattern++;
1488           if (*pattern)
1489             *linep++ = ' ';
1490         }
1491     }
1492   *linep = '\0';
1493
1494   gpgsm->output_cb.data = keydata;
1495   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1496                       : map_data_enc (gpgsm->output_cb.data));
1497   if (err)
1498     return err;
1499   gpgsm_clear_fd (gpgsm, INPUT_FD);
1500   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1501   gpgsm->inline_data = NULL;
1502
1503   err = start (gpgsm, line);
1504   free (line);
1505   return err;
1506 }
1507
1508
1509 static gpgme_error_t
1510 gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1511               gpgme_data_t pubkey, gpgme_data_t seckey)
1512 {
1513   engine_gpgsm_t gpgsm = engine;
1514   gpgme_error_t err;
1515
1516   if (!gpgsm || !pubkey || seckey)
1517     return gpg_error (GPG_ERR_INV_VALUE);
1518
1519   gpgsm->input_cb.data = help_data;
1520   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1521   if (err)
1522     return err;
1523   gpgsm->output_cb.data = pubkey;
1524   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1525                       : map_data_enc (gpgsm->output_cb.data));
1526   if (err)
1527     return err;
1528   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1529   gpgsm->inline_data = NULL;
1530
1531   err = start (gpgsm, "GENKEY");
1532   return err;
1533 }
1534
1535
1536 static gpgme_error_t
1537 gpgsm_import (void *engine, gpgme_data_t keydata)
1538 {
1539   engine_gpgsm_t gpgsm = engine;
1540   gpgme_error_t err;
1541
1542   if (!gpgsm)
1543     return gpg_error (GPG_ERR_INV_VALUE);
1544
1545   gpgsm->input_cb.data = keydata;
1546   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1547   if (err)
1548     return err;
1549   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1550   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1551   gpgsm->inline_data = NULL;
1552
1553   err = start (gpgsm, "IMPORT");
1554   return err;
1555 }
1556
1557
1558 static gpgme_error_t
1559 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1560                gpgme_keylist_mode_t mode)
1561 {
1562   engine_gpgsm_t gpgsm = engine;
1563   char *line;
1564   gpgme_error_t err;
1565   int list_mode = 0;
1566
1567   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1568     list_mode |= 1;
1569   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1570     list_mode |= 2;
1571
1572   if (!pattern)
1573     pattern = "";
1574
1575   /* Always send list-mode option because RESET does not reset it.  */
1576   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1577     return gpg_error_from_errno (errno);
1578   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1579   free (line);
1580   if (err)
1581     return err;
1582
1583
1584   /* Always send key validation because RESET does not reset it.  */
1585
1586   /* Use the validation mode if requested.  We don't check for an error
1587      yet because this is a pretty fresh gpgsm features. */
1588   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1589                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1590                                "OPTION with-validation=1":
1591                                "OPTION with-validation=0" ,
1592                                NULL, NULL);
1593   /* Include the ephemeral keys if requested.  We don't check for an error
1594      yet because this is a pretty fresh gpgsm features. */
1595   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1596                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
1597                                "OPTION with-ephemeral-keys=1":
1598                                "OPTION with-ephemeral-keys=0" ,
1599                                NULL, NULL);
1600
1601
1602   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1603   line = malloc (15 + strlen (pattern) + 1);
1604   if (!line)
1605     return gpg_error_from_errno (errno);
1606   if (secret_only)
1607     {
1608       strcpy (line, "LISTSECRETKEYS ");
1609       strcpy (&line[15], pattern);
1610     }
1611   else
1612     {
1613       strcpy (line, "LISTKEYS ");
1614       strcpy (&line[9], pattern);
1615     }
1616
1617   gpgsm_clear_fd (gpgsm, INPUT_FD);
1618   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1619   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1620   gpgsm->inline_data = NULL;
1621
1622   err = start (gpgsm, line);
1623   free (line);
1624   return err;
1625 }
1626
1627
1628 static gpgme_error_t
1629 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1630                    int reserved, gpgme_keylist_mode_t mode)
1631 {
1632   engine_gpgsm_t gpgsm = engine;
1633   char *line;
1634   gpgme_error_t err;
1635   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1636   int length = 15 + 1;
1637   char *linep;
1638   int any_pattern = 0;
1639   int list_mode = 0;
1640
1641   if (reserved)
1642     return gpg_error (GPG_ERR_INV_VALUE);
1643
1644   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1645     list_mode |= 1;
1646   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1647     list_mode |= 2;
1648
1649   /* Always send list-mode option because RESET does not reset it.  */
1650   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1651     return gpg_error_from_errno (errno);
1652   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1653   free (line);
1654   if (err)
1655     return err;
1656
1657   /* Always send key validation because RESET does not reset it.  */
1658   /* Use the validation mode if required.  We don't check for an error
1659      yet because this is a pretty fresh gpgsm features. */
1660   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1661                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1662                                "OPTION with-validation=1":
1663                                "OPTION with-validation=0" ,
1664                                NULL, NULL);
1665
1666
1667   if (pattern && *pattern)
1668     {
1669       const char **pat = pattern;
1670
1671       while (*pat)
1672         {
1673           const char *patlet = *pat;
1674
1675           while (*patlet)
1676             {
1677               length++;
1678               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1679                 length += 2;
1680               patlet++;
1681             }
1682           pat++;
1683           length++;
1684         }
1685     }
1686   line = malloc (length);
1687   if (!line)
1688     return gpg_error_from_errno (errno);
1689   if (secret_only)
1690     {
1691       strcpy (line, "LISTSECRETKEYS ");
1692       linep = &line[15];
1693     }
1694   else
1695     {
1696       strcpy (line, "LISTKEYS ");
1697       linep = &line[9];
1698     }
1699
1700   if (pattern && *pattern)
1701     {
1702       while (*pattern)
1703         {
1704           const char *patlet = *pattern;
1705
1706           while (*patlet)
1707             {
1708               switch (*patlet)
1709                 {
1710                 case '%':
1711                   *(linep++) = '%';
1712                   *(linep++) = '2';
1713                   *(linep++) = '5';
1714                   break;
1715                 case ' ':
1716                   *(linep++) = '%';
1717                   *(linep++) = '2';
1718                   *(linep++) = '0';
1719                   break;
1720                 case '+':
1721                   *(linep++) = '%';
1722                   *(linep++) = '2';
1723                   *(linep++) = 'B';
1724                   break;
1725                 default:
1726                   *(linep++) = *patlet;
1727                   break;
1728                 }
1729               patlet++;
1730             }
1731           any_pattern = 1;
1732           *linep++ = ' ';
1733           pattern++;
1734         }
1735     }
1736   if (any_pattern)
1737     linep--;
1738   *linep = '\0';
1739
1740   gpgsm_clear_fd (gpgsm, INPUT_FD);
1741   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1742   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1743   gpgsm->inline_data = NULL;
1744
1745   err = start (gpgsm, line);
1746   free (line);
1747   return err;
1748 }
1749
1750
1751 static gpgme_error_t
1752 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1753             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1754             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1755 {
1756   engine_gpgsm_t gpgsm = engine;
1757   gpgme_error_t err;
1758   char *assuan_cmd;
1759   int i;
1760   gpgme_key_t key;
1761
1762   if (!gpgsm)
1763     return gpg_error (GPG_ERR_INV_VALUE);
1764
1765   /* FIXME: This does not work as RESET does not reset it so we can't
1766      revert back to default.  */
1767   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1768     {
1769       /* FIXME: Make sure that if we run multiple operations, that we
1770          can reset any previously set value in case the default is
1771          requested.  */
1772
1773       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1774         return gpg_error_from_errno (errno);
1775       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
1776                                          NULL, NULL);
1777       free (assuan_cmd);
1778       if (err)
1779         return err;
1780     }
1781
1782   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1783     {
1784       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1785       if (s && strlen (s) < 80)
1786         {
1787           char buf[100];
1788
1789           strcpy (stpcpy (buf, "SIGNER "), s);
1790           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1791                                              NULL, NULL);
1792         }
1793       else
1794         err = gpg_error (GPG_ERR_INV_VALUE);
1795       gpgme_key_unref (key);
1796       if (err) 
1797         return err;
1798     }
1799
1800   gpgsm->input_cb.data = in;
1801   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1802   if (err)
1803     return err;
1804   gpgsm->output_cb.data = out;
1805   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1806                       : map_data_enc (gpgsm->output_cb.data));
1807   if (err)
1808     return err;
1809   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1810   gpgsm->inline_data = NULL;
1811
1812   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1813                ? "SIGN --detached" : "SIGN");
1814   return err;
1815 }
1816
1817
1818 static gpgme_error_t
1819 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1820               gpgme_data_t plaintext)
1821 {
1822   engine_gpgsm_t gpgsm = engine;
1823   gpgme_error_t err;
1824
1825   if (!gpgsm)
1826     return gpg_error (GPG_ERR_INV_VALUE);
1827
1828   gpgsm->input_cb.data = sig;
1829   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1830   if (err)
1831     return err;
1832   if (plaintext)
1833     {
1834       /* Normal or cleartext signature.  */
1835       gpgsm->output_cb.data = plaintext;
1836       err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1837       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1838     }
1839   else
1840     {
1841       /* Detached signature.  */
1842       gpgsm->message_cb.data = signed_text;
1843       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
1844       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1845     }
1846   gpgsm->inline_data = NULL;
1847
1848   if (!err)
1849     err = start (gpgsm, "VERIFY");
1850
1851   return err;
1852 }
1853
1854
1855 /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
1856    object.  */
1857 static gpgme_error_t
1858 gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
1859 {
1860   engine_gpgsm_t gpgsm = engine;
1861   gpgme_error_t err = 0;
1862
1863   if (!gpgsm || !output)
1864     return gpg_error (GPG_ERR_INV_VALUE);
1865
1866 #if USE_DESCRIPTOR_PASSING
1867   gpgsm->output_cb.data = output;
1868   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1869   if (err)
1870     return err;
1871
1872   gpgsm_clear_fd (gpgsm, INPUT_FD);
1873   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1874   gpgsm->inline_data = NULL;
1875 # define CMD  "GETAUDITLOG"
1876 #else
1877   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1878   gpgsm_clear_fd (gpgsm, INPUT_FD);
1879   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1880   gpgsm->inline_data = output;
1881 # define CMD  "GETAUDITLOG --data"
1882 #endif
1883
1884   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
1885
1886   return err;
1887 }
1888
1889
1890
1891 static void
1892 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1893                           void *fnc_value) 
1894 {
1895   engine_gpgsm_t gpgsm = engine;
1896
1897   gpgsm->status.fnc = fnc;
1898   gpgsm->status.fnc_value = fnc_value;
1899 }
1900
1901
1902 static gpgme_error_t
1903 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1904                               void *fnc_value) 
1905 {
1906   engine_gpgsm_t gpgsm = engine;
1907
1908   gpgsm->colon.fnc = fnc;
1909   gpgsm->colon.fnc_value = fnc_value;
1910   gpgsm->colon.any = 0;
1911   return 0;
1912 }
1913
1914
1915 static void
1916 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1917 {
1918   engine_gpgsm_t gpgsm = engine;
1919   gpgsm->io_cbs = *io_cbs;
1920 }
1921
1922
1923 static void
1924 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1925 {
1926   engine_gpgsm_t gpgsm = engine;
1927
1928   TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
1929           "event %p, type %d, type_data %p",
1930           gpgsm->io_cbs.event, type, type_data);
1931   if (gpgsm->io_cbs.event)
1932     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1933 }
1934
1935
1936 struct engine_ops _gpgme_engine_ops_gpgsm =
1937   {
1938     /* Static functions.  */
1939     _gpgme_get_gpgsm_path,
1940     NULL,
1941     gpgsm_get_version,
1942     gpgsm_get_req_version,
1943     gpgsm_new,
1944
1945     /* Member functions.  */
1946     gpgsm_release,
1947 #if USE_DESCRIPTOR_PASSING
1948     gpgsm_reset,
1949 #else
1950     NULL,                       /* reset */
1951 #endif
1952     gpgsm_set_status_handler,
1953     NULL,               /* set_command_handler */
1954     gpgsm_set_colon_line_handler,
1955     gpgsm_set_locale,
1956     gpgsm_decrypt,
1957     gpgsm_delete,
1958     NULL,               /* edit */
1959     gpgsm_encrypt,
1960     NULL,               /* encrypt_sign */
1961     gpgsm_export,
1962     gpgsm_export_ext,
1963     gpgsm_genkey,
1964     gpgsm_import,
1965     gpgsm_keylist,
1966     gpgsm_keylist_ext,
1967     gpgsm_sign,
1968     NULL,               /* trustlist */
1969     gpgsm_verify,
1970     gpgsm_getauditlog,
1971     NULL,               /* opassuan_transact */
1972     NULL,               /* conf_load */
1973     NULL,               /* conf_save */
1974     gpgsm_set_io_cbs,
1975     gpgsm_io_event,
1976     gpgsm_cancel
1977   };