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