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