First take on the low-level assuan interface.
[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 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 required.  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
1586
1587   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1588   line = malloc (15 + strlen (pattern) + 1);
1589   if (!line)
1590     return gpg_error_from_errno (errno);
1591   if (secret_only)
1592     {
1593       strcpy (line, "LISTSECRETKEYS ");
1594       strcpy (&line[15], pattern);
1595     }
1596   else
1597     {
1598       strcpy (line, "LISTKEYS ");
1599       strcpy (&line[9], pattern);
1600     }
1601
1602   gpgsm_clear_fd (gpgsm, INPUT_FD);
1603   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1604   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1605   gpgsm->inline_data = NULL;
1606
1607   err = start (gpgsm, line);
1608   free (line);
1609   return err;
1610 }
1611
1612
1613 static gpgme_error_t
1614 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1615                    int reserved, gpgme_keylist_mode_t mode)
1616 {
1617   engine_gpgsm_t gpgsm = engine;
1618   char *line;
1619   gpgme_error_t err;
1620   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1621   int length = 15 + 1;
1622   char *linep;
1623   int any_pattern = 0;
1624   int list_mode = 0;
1625
1626   if (reserved)
1627     return gpg_error (GPG_ERR_INV_VALUE);
1628
1629   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1630     list_mode |= 1;
1631   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1632     list_mode |= 2;
1633
1634   /* Always send list-mode option because RESET does not reset it.  */
1635   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1636     return gpg_error_from_errno (errno);
1637   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1638   free (line);
1639   if (err)
1640     return err;
1641
1642   /* Always send key validation because RESET does not reset it.  */
1643   /* Use the validation mode if required.  We don't check for an error
1644      yet because this is a pretty fresh gpgsm features. */
1645   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1646                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1647                                "OPTION with-validation=1":
1648                                "OPTION with-validation=0" ,
1649                                NULL, NULL);
1650
1651
1652   if (pattern && *pattern)
1653     {
1654       const char **pat = pattern;
1655
1656       while (*pat)
1657         {
1658           const char *patlet = *pat;
1659
1660           while (*patlet)
1661             {
1662               length++;
1663               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1664                 length += 2;
1665               patlet++;
1666             }
1667           pat++;
1668           length++;
1669         }
1670     }
1671   line = malloc (length);
1672   if (!line)
1673     return gpg_error_from_errno (errno);
1674   if (secret_only)
1675     {
1676       strcpy (line, "LISTSECRETKEYS ");
1677       linep = &line[15];
1678     }
1679   else
1680     {
1681       strcpy (line, "LISTKEYS ");
1682       linep = &line[9];
1683     }
1684
1685   if (pattern && *pattern)
1686     {
1687       while (*pattern)
1688         {
1689           const char *patlet = *pattern;
1690
1691           while (*patlet)
1692             {
1693               switch (*patlet)
1694                 {
1695                 case '%':
1696                   *(linep++) = '%';
1697                   *(linep++) = '2';
1698                   *(linep++) = '5';
1699                   break;
1700                 case ' ':
1701                   *(linep++) = '%';
1702                   *(linep++) = '2';
1703                   *(linep++) = '0';
1704                   break;
1705                 case '+':
1706                   *(linep++) = '%';
1707                   *(linep++) = '2';
1708                   *(linep++) = 'B';
1709                   break;
1710                 default:
1711                   *(linep++) = *patlet;
1712                   break;
1713                 }
1714               patlet++;
1715             }
1716           any_pattern = 1;
1717           *linep++ = ' ';
1718           pattern++;
1719         }
1720     }
1721   if (any_pattern)
1722     linep--;
1723   *linep = '\0';
1724
1725   gpgsm_clear_fd (gpgsm, INPUT_FD);
1726   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1727   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1728   gpgsm->inline_data = NULL;
1729
1730   err = start (gpgsm, line);
1731   free (line);
1732   return err;
1733 }
1734
1735
1736 static gpgme_error_t
1737 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1738             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1739             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1740 {
1741   engine_gpgsm_t gpgsm = engine;
1742   gpgme_error_t err;
1743   char *assuan_cmd;
1744   int i;
1745   gpgme_key_t key;
1746
1747   if (!gpgsm)
1748     return gpg_error (GPG_ERR_INV_VALUE);
1749
1750   /* FIXME: This does not work as RESET does not reset it so we can't
1751      revert back to default.  */
1752   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1753     {
1754       /* FIXME: Make sure that if we run multiple operations, that we
1755          can reset any previously set value in case the default is
1756          requested.  */
1757
1758       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1759         return gpg_error_from_errno (errno);
1760       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
1761                                          NULL, NULL);
1762       free (assuan_cmd);
1763       if (err)
1764         return err;
1765     }
1766
1767   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1768     {
1769       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1770       if (s && strlen (s) < 80)
1771         {
1772           char buf[100];
1773
1774           strcpy (stpcpy (buf, "SIGNER "), s);
1775           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1776                                              NULL, NULL);
1777         }
1778       else
1779         err = gpg_error (GPG_ERR_INV_VALUE);
1780       gpgme_key_unref (key);
1781       if (err) 
1782         return err;
1783     }
1784
1785   gpgsm->input_cb.data = in;
1786   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1787   if (err)
1788     return err;
1789   gpgsm->output_cb.data = out;
1790   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1791                       : map_data_enc (gpgsm->output_cb.data));
1792   if (err)
1793     return err;
1794   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1795   gpgsm->inline_data = NULL;
1796
1797   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1798                ? "SIGN --detached" : "SIGN");
1799   return err;
1800 }
1801
1802
1803 static gpgme_error_t
1804 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1805               gpgme_data_t plaintext)
1806 {
1807   engine_gpgsm_t gpgsm = engine;
1808   gpgme_error_t err;
1809
1810   if (!gpgsm)
1811     return gpg_error (GPG_ERR_INV_VALUE);
1812
1813   gpgsm->input_cb.data = sig;
1814   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1815   if (err)
1816     return err;
1817   if (plaintext)
1818     {
1819       /* Normal or cleartext signature.  */
1820       gpgsm->output_cb.data = plaintext;
1821       err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1822       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1823     }
1824   else
1825     {
1826       /* Detached signature.  */
1827       gpgsm->message_cb.data = signed_text;
1828       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
1829       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1830     }
1831   gpgsm->inline_data = NULL;
1832
1833   if (!err)
1834     err = start (gpgsm, "VERIFY");
1835
1836   return err;
1837 }
1838
1839
1840 /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
1841    object.  */
1842 static gpgme_error_t
1843 gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
1844 {
1845   engine_gpgsm_t gpgsm = engine;
1846   gpgme_error_t err = 0;
1847
1848   if (!gpgsm || !output)
1849     return gpg_error (GPG_ERR_INV_VALUE);
1850
1851 #if USE_DESCRIPTOR_PASSING
1852   gpgsm->output_cb.data = output;
1853   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1854   if (err)
1855     return err;
1856
1857   gpgsm_clear_fd (gpgsm, INPUT_FD);
1858   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1859   gpgsm->inline_data = NULL;
1860 # define CMD  "GETAUDITLOG"
1861 #else
1862   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1863   gpgsm_clear_fd (gpgsm, INPUT_FD);
1864   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1865   gpgsm->inline_data = output;
1866 # define CMD  "GETAUDITLOG --data"
1867 #endif
1868
1869   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
1870
1871   return err;
1872 }
1873
1874
1875
1876 static void
1877 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1878                           void *fnc_value) 
1879 {
1880   engine_gpgsm_t gpgsm = engine;
1881
1882   gpgsm->status.fnc = fnc;
1883   gpgsm->status.fnc_value = fnc_value;
1884 }
1885
1886
1887 static gpgme_error_t
1888 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1889                               void *fnc_value) 
1890 {
1891   engine_gpgsm_t gpgsm = engine;
1892
1893   gpgsm->colon.fnc = fnc;
1894   gpgsm->colon.fnc_value = fnc_value;
1895   gpgsm->colon.any = 0;
1896   return 0;
1897 }
1898
1899
1900 static void
1901 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1902 {
1903   engine_gpgsm_t gpgsm = engine;
1904   gpgsm->io_cbs = *io_cbs;
1905 }
1906
1907
1908 static void
1909 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1910 {
1911   engine_gpgsm_t gpgsm = engine;
1912
1913   TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
1914           "event %p, type %d, type_data %p",
1915           gpgsm->io_cbs.event, type, type_data);
1916   if (gpgsm->io_cbs.event)
1917     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1918 }
1919
1920
1921 struct engine_ops _gpgme_engine_ops_gpgsm =
1922   {
1923     /* Static functions.  */
1924     _gpgme_get_gpgsm_path,
1925     NULL,
1926     gpgsm_get_version,
1927     gpgsm_get_req_version,
1928     gpgsm_new,
1929
1930     /* Member functions.  */
1931     gpgsm_release,
1932 #if USE_DESCRIPTOR_PASSING
1933     gpgsm_reset,
1934 #else
1935     NULL,                       /* reset */
1936 #endif
1937     gpgsm_set_status_handler,
1938     NULL,               /* set_command_handler */
1939     gpgsm_set_colon_line_handler,
1940     gpgsm_set_locale,
1941     gpgsm_decrypt,
1942     gpgsm_delete,
1943     NULL,               /* edit */
1944     gpgsm_encrypt,
1945     NULL,               /* encrypt_sign */
1946     gpgsm_export,
1947     gpgsm_export_ext,
1948     gpgsm_genkey,
1949     gpgsm_import,
1950     gpgsm_keylist,
1951     gpgsm_keylist_ext,
1952     gpgsm_sign,
1953     NULL,               /* trustlist */
1954     gpgsm_verify,
1955     gpgsm_getauditlog,
1956     NULL,               /* opassuan_transact */
1957     NULL,               /* conf_load */
1958     NULL,               /* conf_save */
1959     gpgsm_set_io_cbs,
1960     gpgsm_io_event,
1961     gpgsm_cancel
1962   };