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