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