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