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