2007-01-18 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / engine-gpgsm.c
1 /* engine-gpgsm.c - GpgSM engine.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
4  
5    This file is part of GPGME.
6
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11    
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #ifndef HAVE_W32_SYSTEM
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <assert.h>
32 #include <unistd.h>
33 #include <locale.h>
34 #include <fcntl.h> /* FIXME */
35 #include <errno.h>
36
37 #include "gpgme.h"
38 #include "util.h"
39 #include "ops.h"
40 #include "wait.h"
41 #include "priv-io.h"
42 #include "sema.h"
43
44 #include "assuan.h"
45 #include "status-table.h"
46 #include "debug.h"
47
48 #include "engine-backend.h"
49
50 \f
51 typedef struct
52 {
53   int fd;       /* FD we talk about.  */
54   int server_fd; /* Server FD for this connection.  */
55   int dir;      /* Inbound/Outbound, maybe given implicit?  */
56   void *data;   /* Handler-specific data.  */
57   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
58 } iocb_data_t;
59
60
61 struct engine_gpgsm
62 {
63   assuan_context_t assuan_ctx;
64
65   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   /* We try to short-cut the communication by giving GPGSM direct
696      access to the file descriptor, rather than using a pipe.  */
697   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
698   if (iocb_data->server_fd < 0)
699     {
700       int fds[2];
701
702       if (_gpgme_io_pipe (fds, 0) < 0)
703         return gpg_error_from_errno (errno);
704
705       iocb_data->fd = dir ? fds[0] : fds[1];
706       iocb_data->server_fd = dir ? fds[1] : fds[0];
707
708       if (_gpgme_io_set_close_notify (iocb_data->fd,
709                                       close_notify_handler, gpgsm))
710         {
711           err = gpg_error (GPG_ERR_GENERAL);
712           goto leave_set_fd;
713         }
714     }
715 #endif
716
717   fd = iocb_data->server_fd;
718
719 #if USE_DESCRIPTOR_PASSING
720   err = assuan_sendfd (gpgsm->assuan_ctx, fd);
721   if (err)
722     goto leave_set_fd;
723
724   _gpgme_io_close (fd);
725
726   if (opt)
727     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
728   else
729     snprintf (line, COMMANDLINELEN, "%s FD", which);
730 #else
731   if (opt)
732     snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
733   else
734     snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
735 #endif
736
737   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
738
739 #if USE_DESCRIPTOR_PASSING
740  leave_set_fd:
741   if (err)
742     {
743       _gpgme_io_close (iocb_data->fd);
744       _gpgme_io_close (iocb_data->server_fd);
745       iocb_data->fd = -1;
746       iocb_data->server_fd = -1;
747     }
748 #endif
749
750   return err;
751 }
752
753
754 static const char *
755 map_input_enc (gpgme_data_t d)
756 {
757   switch (gpgme_data_get_encoding (d))
758     {
759     case GPGME_DATA_ENCODING_NONE:
760       break;
761     case GPGME_DATA_ENCODING_BINARY:
762       return "--binary";
763     case GPGME_DATA_ENCODING_BASE64:
764       return "--base64";
765     case GPGME_DATA_ENCODING_ARMOR:
766       return "--armor";
767     default:
768       break;
769     }
770   return NULL;
771 }
772
773
774 static int
775 status_cmp (const void *ap, const void *bp)
776 {
777   const struct status_table_s *a = ap;
778   const struct status_table_s *b = bp;
779
780   return strcmp (a->name, b->name);
781 }
782
783
784 static gpgme_status_code_t
785 parse_status (const char *name)
786 {
787   struct status_table_s t, *r;
788   t.name = name;
789   r = bsearch (&t, status_table, DIM(status_table) - 1,
790                sizeof t, status_cmp);
791   return r ? r->code : -1;
792 }
793
794
795 static gpgme_error_t
796 status_handler (void *opaque, int fd)
797 {
798   gpg_error_t assuan_err;
799   gpgme_error_t err = 0;
800   engine_gpgsm_t gpgsm = opaque;
801   char *line;
802   size_t linelen;
803
804   do
805     {
806       assuan_err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
807       if (assuan_err)
808         {
809           /* Try our best to terminate the connection friendly.  */
810           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
811           err = map_assuan_error (assuan_err);
812           DEBUG3 ("fd %d: error from assuan (%d) getting status line : %s \n",
813                   fd, assuan_err, gpg_strerror (err));
814         }
815       else if (linelen >= 3
816                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
817                && (line[3] == '\0' || line[3] == ' '))
818         {
819           if (line[3] == ' ')
820             err = map_assuan_error (atoi (&line[4]));
821           else
822             err = gpg_error (GPG_ERR_GENERAL);
823           DEBUG2 ("fd %d: ERR line - mapped to: %s\n",
824                   fd, err? gpg_strerror (err):"ok");
825         }
826       else if (linelen >= 2
827                && line[0] == 'O' && line[1] == 'K'
828                && (line[2] == '\0' || line[2] == ' '))
829         {
830           if (gpgsm->status.fnc)
831             err = gpgsm->status.fnc (gpgsm->status.fnc_value,
832                                      GPGME_STATUS_EOF, "");
833           
834           if (!err && gpgsm->colon.fnc && gpgsm->colon.any )
835             {
836               /* We must tell a colon function about the EOF. We do
837                  this only when we have seen any data lines.  Note
838                  that this inlined use of colon data lines will
839                  eventually be changed into using a regular data
840                  channel. */
841               gpgsm->colon.any = 0;
842               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
843             }
844           _gpgme_io_close (gpgsm->status_cb.fd);
845           DEBUG2 ("fd %d: OK line - final status: %s\n",
846                   fd, err? gpg_strerror (err):"ok");
847           return err;
848         }
849       else if (linelen > 2
850                && line[0] == 'D' && line[1] == ' '
851                && gpgsm->colon.fnc)
852         {
853           /* We are using the colon handler even for plain inline data
854              - strange name for that function but for historic reasons
855              we keep it.  */
856           /* FIXME We can't use this for binary data because we
857              assume this is a string.  For the current usage of colon
858              output it is correct.  */
859           char *src = line + 2;
860           char *end = line + linelen;
861           char *dst;
862           char **aline = &gpgsm->colon.attic.line;
863           int *alinelen = &gpgsm->colon.attic.linelen;
864
865           if (gpgsm->colon.attic.linesize
866               < *alinelen + linelen + 1)
867             {
868               char *newline = realloc (*aline, *alinelen + linelen + 1);
869               if (!newline)
870                 err = gpg_error_from_errno (errno);
871               else
872                 {
873                   *aline = newline;
874                   gpgsm->colon.attic.linesize += linelen + 1;
875                 }
876             }
877           if (!err)
878             {
879               dst = *aline + *alinelen;
880
881               while (!err && src < end)
882                 {
883                   if (*src == '%' && src + 2 < end)
884                     {
885                       /* Handle escaped characters.  */
886                       ++src;
887                       *dst = _gpgme_hextobyte (src);
888                       (*alinelen)++;
889                       src += 2;
890                     }
891                   else
892                     {
893                       *dst = *src++;
894                       (*alinelen)++;
895                     }
896                   
897                   if (*dst == '\n')
898                     {
899                       /* Terminate the pending line, pass it to the colon
900                          handler and reset it.  */
901                       
902                       gpgsm->colon.any = 1;
903                       if (*alinelen > 1 && *(dst - 1) == '\r')
904                         dst--;
905                       *dst = '\0';
906
907                       /* FIXME How should we handle the return code?  */
908                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
909                       if (!err)
910                         {
911                           dst = *aline;
912                           *alinelen = 0;
913                         }
914                     }
915                   else
916                     dst++;
917                 }
918             }
919           DEBUG2 ("fd %d: D line; final status: %s\n",
920                   fd, err? gpg_strerror (err):"ok");
921         }
922       else if (linelen > 2
923                && line[0] == 'S' && line[1] == ' ')
924         {
925           char *rest;
926           gpgme_status_code_t r;
927           
928           rest = strchr (line + 2, ' ');
929           if (!rest)
930             rest = line + linelen; /* set to an empty string */
931           else
932             *(rest++) = 0;
933
934           r = parse_status (line + 2);
935
936           if (r >= 0)
937             {
938               if (gpgsm->status.fnc)
939                 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
940             }
941           else
942             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
943           DEBUG3 ("fd %d: S line (%s) - final status: %s\n",
944                   fd, line+2, err? gpg_strerror (err):"ok");
945         }
946     }
947   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
948           
949   return err;
950 }
951
952
953 static gpgme_error_t
954 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
955 {
956   gpgme_error_t err;
957
958   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
959                               iocbd->fd, iocbd->dir,
960                               handler, iocbd->data, &iocbd->tag);
961   if (err)
962     return err;
963   if (!iocbd->dir)
964     /* FIXME Kludge around poll() problem.  */
965     err = _gpgme_io_set_nonblocking (iocbd->fd);
966   return err;
967 }
968
969
970 static gpgme_error_t
971 start (engine_gpgsm_t gpgsm, const char *command)
972 {
973   gpgme_error_t err;
974   int fdlist[5];
975   int nfds;
976
977   /* We need to know the fd used by assuan for reads.  We do this by
978      using the assumption that the first returned fd from
979      assuan_get_active_fds() is always this one.  */
980   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
981                                 fdlist, DIM (fdlist));
982   if (nfds < 1)
983     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
984
985   /* We duplicate the file descriptor, so we can close it without
986      disturbing assuan.  Alternatively, we could special case
987      status_fd and register/unregister it manually as needed, but this
988      increases code duplication and is more complicated as we can not
989      use the close notifications etc.  */
990   gpgsm->status_cb.fd = dup (fdlist[0]);
991   if (gpgsm->status_cb.fd < 0)
992     return gpg_error_from_syserror ();
993
994   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
995                                   close_notify_handler, gpgsm))
996     {
997       close (gpgsm->status_cb.fd);
998       gpgsm->status_cb.fd = -1;
999       return gpg_error (GPG_ERR_GENERAL);
1000     }
1001
1002   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1003   if (!err && gpgsm->input_cb.fd != -1)
1004     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1005   if (!err && gpgsm->output_cb.fd != -1)
1006     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1007   if (!err && gpgsm->message_cb.fd != -1)
1008     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1009
1010   if (!err)
1011     err = map_assuan_error (assuan_write_line (gpgsm->assuan_ctx, command));
1012
1013   if (!err)
1014     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, GPGME_EVENT_START, NULL);
1015
1016   return err;
1017 }
1018
1019
1020 #if USE_DESCRIPTOR_PASSING
1021 static gpgme_error_t
1022 gpgsm_reset (void *engine)
1023 {
1024   engine_gpgsm_t gpgsm = engine;
1025
1026   /* We must send a reset because we need to reset the list of
1027      signers.  Note that RESET does not reset OPTION commands. */
1028   return gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1029 }
1030 #endif
1031
1032
1033 static gpgme_error_t
1034 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1035 {
1036   engine_gpgsm_t gpgsm = engine;
1037   gpgme_error_t err;
1038
1039   if (!gpgsm)
1040     return gpg_error (GPG_ERR_INV_VALUE);
1041
1042   gpgsm->input_cb.data = ciph;
1043   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_input_enc (gpgsm->input_cb.data));
1044   if (err)
1045     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1046   gpgsm->output_cb.data = plain;
1047   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1048   if (err)
1049     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1050   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1051
1052   err = start (engine, "DECRYPT");
1053   return err;
1054 }
1055
1056
1057 static gpgme_error_t
1058 gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
1059 {
1060   engine_gpgsm_t gpgsm = engine;
1061   gpgme_error_t err;
1062   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1063   char *linep = fpr;
1064   char *line;
1065   int length = 8;       /* "DELKEYS " */
1066
1067   if (!fpr)
1068     return gpg_error (GPG_ERR_INV_VALUE);
1069
1070   while (*linep)
1071     {
1072       length++;
1073       if (*linep == '%' || *linep == ' ' || *linep == '+')
1074         length += 2;
1075       linep++;
1076     }
1077   length++;
1078
1079   line = malloc (length);
1080   if (!line)
1081     return gpg_error_from_errno (errno);
1082
1083   strcpy (line, "DELKEYS ");
1084   linep = &line[8];
1085
1086   while (*fpr)
1087     {
1088       switch (*fpr)
1089         {
1090         case '%':
1091           *(linep++) = '%';
1092           *(linep++) = '2';
1093           *(linep++) = '5';
1094           break;
1095         case ' ':
1096           *(linep++) = '%';
1097           *(linep++) = '2';
1098           *(linep++) = '0';
1099           break;
1100         case '+':
1101           *(linep++) = '%';
1102           *(linep++) = '2';
1103           *(linep++) = 'B';
1104           break;
1105         default:
1106           *(linep++) = *fpr;
1107           break;
1108         }
1109       fpr++;
1110     }
1111   *linep = '\0';
1112
1113   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1114   gpgsm_clear_fd (gpgsm, INPUT_FD);
1115   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1116
1117   err = start (gpgsm, line);
1118   free (line);
1119
1120   return err;
1121 }
1122
1123
1124 static gpgme_error_t
1125 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1126 {
1127   gpgme_error_t err = 0;
1128   assuan_context_t ctx = gpgsm->assuan_ctx;
1129   char *line;
1130   int linelen;
1131   int invalid_recipients = 0;
1132   int i = 0;
1133
1134   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1135   line = malloc (10 + 40 + 1);
1136   if (!line)
1137     return gpg_error_from_errno (errno);
1138   strcpy (line, "RECIPIENT ");
1139   while (!err && recp[i])
1140     {
1141       char *fpr;
1142       int newlen;
1143
1144       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1145         {
1146           invalid_recipients++;
1147           continue;
1148         }
1149       fpr = recp[i]->subkeys->fpr;
1150
1151       newlen = 11 + strlen (fpr);
1152       if (linelen < newlen)
1153         {
1154           char *newline = realloc (line, newlen);
1155           if (! newline)
1156             {
1157               int saved_errno = errno;
1158               free (line);
1159               return gpg_error_from_errno (saved_errno);
1160             }
1161           line = newline;
1162           linelen = newlen;
1163         }
1164       strcpy (&line[10], fpr);
1165
1166       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
1167                                          gpgsm->status.fnc_value);
1168       /* FIXME: This requires more work.  */
1169       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1170         invalid_recipients++;
1171       else if (err)
1172         {
1173           free (line);
1174           return err;
1175         }
1176       i++;
1177     }
1178   free (line);
1179   return gpg_error (invalid_recipients
1180                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1181 }
1182
1183
1184 static gpgme_error_t
1185 gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1186                gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1187 {
1188   engine_gpgsm_t gpgsm = engine;
1189   gpgme_error_t err;
1190
1191   if (!gpgsm)
1192     return gpg_error (GPG_ERR_INV_VALUE);
1193   if (!recp)
1194     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1195
1196   gpgsm->input_cb.data = plain;
1197   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_input_enc (gpgsm->input_cb.data));
1198   if (err)
1199     return err;
1200   gpgsm->output_cb.data = ciph;
1201   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : 0);
1202   if (err)
1203     return err;
1204   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1205
1206   err = set_recipients (gpgsm, recp);
1207
1208   if (!err)
1209     err = start (gpgsm, "ENCRYPT");
1210
1211   return err;
1212 }
1213
1214
1215 static gpgme_error_t
1216 gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
1217               gpgme_data_t keydata, int use_armor)
1218 {
1219   engine_gpgsm_t gpgsm = engine;
1220   gpgme_error_t err = 0;
1221   char *cmd;
1222
1223   if (!gpgsm || reserved)
1224     return gpg_error (GPG_ERR_INV_VALUE);
1225
1226   if (!pattern)
1227     pattern = "";
1228
1229   cmd = malloc (7 + strlen (pattern) + 1);
1230   if (!cmd)
1231     return gpg_error_from_errno (errno);
1232   strcpy (cmd, "EXPORT ");
1233   strcpy (&cmd[7], pattern);
1234
1235   gpgsm->output_cb.data = keydata;
1236   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : 0);
1237   if (err)
1238     return err;
1239   gpgsm_clear_fd (gpgsm, INPUT_FD);
1240   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1241
1242   err = start (gpgsm, cmd);
1243   free (cmd);
1244   return err;
1245 }
1246
1247
1248 static gpgme_error_t
1249 gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
1250                   gpgme_data_t keydata, int use_armor)
1251 {
1252   engine_gpgsm_t gpgsm = engine;
1253   gpgme_error_t err = 0;
1254   char *line;
1255   /* Length is "EXPORT " + p + '\0'.  */
1256   int length = 7 + 1;
1257   char *linep;
1258
1259   if (!gpgsm || reserved)
1260     return gpg_error (GPG_ERR_INV_VALUE);
1261
1262   if (pattern && *pattern)
1263     {
1264       const char **pat = pattern;
1265
1266       while (*pat)
1267         {
1268           const char *patlet = *pat;
1269
1270           while (*patlet)
1271             {
1272               length++;
1273               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1274                 length += 2;
1275               patlet++;
1276             }
1277           pat++;
1278           length++;
1279         }
1280     }
1281   line = malloc (length);
1282   if (!line)
1283     return gpg_error_from_errno (errno);
1284
1285   strcpy (line, "EXPORT ");
1286   linep = &line[7];
1287
1288   if (pattern && *pattern)
1289     {
1290       while (*pattern)
1291         {
1292           const char *patlet = *pattern;
1293
1294           while (*patlet)
1295             {
1296               switch (*patlet)
1297                 {
1298                 case '%':
1299                   *(linep++) = '%';
1300                   *(linep++) = '2';
1301                   *(linep++) = '5';
1302                   break;
1303                 case ' ':
1304                   *(linep++) = '%';
1305                   *(linep++) = '2';
1306                   *(linep++) = '0';
1307                   break;
1308                 case '+':
1309                   *(linep++) = '%';
1310                   *(linep++) = '2';
1311                   *(linep++) = 'B';
1312                   break;
1313                 default:
1314                   *(linep++) = *patlet;
1315                   break;
1316                 }
1317               patlet++;
1318             }
1319           pattern++;
1320           if (*pattern)
1321             *linep++ = ' ';
1322         }
1323     }
1324   *linep = '\0';
1325
1326   gpgsm->output_cb.data = keydata;
1327   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : 0);
1328   if (err)
1329     return err;
1330   gpgsm_clear_fd (gpgsm, INPUT_FD);
1331   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1332
1333   err = start (gpgsm, line);
1334   free (line);
1335   return err;
1336 }
1337
1338
1339 static gpgme_error_t
1340 gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1341               gpgme_data_t pubkey, gpgme_data_t seckey)
1342 {
1343   engine_gpgsm_t gpgsm = engine;
1344   gpgme_error_t err;
1345
1346   if (!gpgsm || !pubkey || seckey)
1347     return gpg_error (GPG_ERR_INV_VALUE);
1348
1349   gpgsm->input_cb.data = help_data;
1350   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_input_enc (gpgsm->input_cb.data));
1351   if (err)
1352     return err;
1353   gpgsm->output_cb.data = pubkey;
1354   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : 0);
1355   if (err)
1356     return err;
1357   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1358
1359   err = start (gpgsm, "GENKEY");
1360   return err;
1361 }
1362
1363
1364 static gpgme_error_t
1365 gpgsm_import (void *engine, gpgme_data_t keydata)
1366 {
1367   engine_gpgsm_t gpgsm = engine;
1368   gpgme_error_t err;
1369
1370   if (!gpgsm)
1371     return gpg_error (GPG_ERR_INV_VALUE);
1372
1373   gpgsm->input_cb.data = keydata;
1374   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_input_enc (gpgsm->input_cb.data));
1375   if (err)
1376     return err;
1377   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1378   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1379
1380   err = start (gpgsm, "IMPORT");
1381   return err;
1382 }
1383
1384
1385 static gpgme_error_t
1386 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1387                gpgme_keylist_mode_t mode)
1388 {
1389   engine_gpgsm_t gpgsm = engine;
1390   char *line;
1391   gpgme_error_t err;
1392   int list_mode = 0;
1393
1394   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1395     list_mode |= 1;
1396   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1397     list_mode |= 2;
1398
1399   if (!pattern)
1400     pattern = "";
1401
1402   /* Always send list-mode option because RESET does not reset it.  */
1403   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1404     return gpg_error_from_errno (errno);
1405   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1406   free (line);
1407   if (err)
1408     return err;
1409
1410
1411   /* Always send key validation because RESET does not reset it.  */
1412
1413   /* Use the validation mode if required.  We don't check for an error
1414      yet because this is a pretty fresh gpgsm features. */
1415   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1416                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1417                                "OPTION with-validation=1":
1418                                "OPTION with-validation=0" ,
1419                                NULL, NULL);
1420
1421
1422   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1423   line = malloc (15 + strlen (pattern) + 1);
1424   if (!line)
1425     return gpg_error_from_errno (errno);
1426   if (secret_only)
1427     {
1428       strcpy (line, "LISTSECRETKEYS ");
1429       strcpy (&line[15], pattern);
1430     }
1431   else
1432     {
1433       strcpy (line, "LISTKEYS ");
1434       strcpy (&line[9], pattern);
1435     }
1436
1437   gpgsm_clear_fd (gpgsm, INPUT_FD);
1438   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1439   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1440
1441   err = start (gpgsm, line);
1442   free (line);
1443   return err;
1444 }
1445
1446
1447 static gpgme_error_t
1448 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1449                    int reserved, gpgme_keylist_mode_t mode)
1450 {
1451   engine_gpgsm_t gpgsm = engine;
1452   char *line;
1453   gpgme_error_t err;
1454   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1455   int length = 15 + 1;
1456   char *linep;
1457   int any_pattern = 0;
1458   int list_mode = 0;
1459
1460   if (reserved)
1461     return gpg_error (GPG_ERR_INV_VALUE);
1462
1463   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1464     list_mode |= 1;
1465   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1466     list_mode |= 2;
1467
1468   /* Always send list-mode option because RESET does not reset it.  */
1469   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1470     return gpg_error_from_errno (errno);
1471   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1472   free (line);
1473   if (err)
1474     return err;
1475
1476   /* Always send key validation because RESET does not reset it.  */
1477   /* Use the validation mode if required.  We don't check for an error
1478      yet because this is a pretty fresh gpgsm features. */
1479   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1480                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1481                                "OPTION with-validation=1":
1482                                "OPTION with-validation=0" ,
1483                                NULL, NULL);
1484
1485
1486   if (pattern && *pattern)
1487     {
1488       const char **pat = pattern;
1489
1490       while (*pat)
1491         {
1492           const char *patlet = *pat;
1493
1494           while (*patlet)
1495             {
1496               length++;
1497               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1498                 length += 2;
1499               patlet++;
1500             }
1501           pat++;
1502           length++;
1503         }
1504     }
1505   line = malloc (length);
1506   if (!line)
1507     return gpg_error_from_errno (errno);
1508   if (secret_only)
1509     {
1510       strcpy (line, "LISTSECRETKEYS ");
1511       linep = &line[15];
1512     }
1513   else
1514     {
1515       strcpy (line, "LISTKEYS ");
1516       linep = &line[9];
1517     }
1518
1519   if (pattern && *pattern)
1520     {
1521       while (*pattern)
1522         {
1523           const char *patlet = *pattern;
1524
1525           while (*patlet)
1526             {
1527               switch (*patlet)
1528                 {
1529                 case '%':
1530                   *(linep++) = '%';
1531                   *(linep++) = '2';
1532                   *(linep++) = '5';
1533                   break;
1534                 case ' ':
1535                   *(linep++) = '%';
1536                   *(linep++) = '2';
1537                   *(linep++) = '0';
1538                   break;
1539                 case '+':
1540                   *(linep++) = '%';
1541                   *(linep++) = '2';
1542                   *(linep++) = 'B';
1543                   break;
1544                 default:
1545                   *(linep++) = *patlet;
1546                   break;
1547                 }
1548               patlet++;
1549             }
1550           any_pattern = 1;
1551           *linep++ = ' ';
1552           pattern++;
1553         }
1554     }
1555   if (any_pattern)
1556     linep--;
1557   *linep = '\0';
1558
1559   gpgsm_clear_fd (gpgsm, INPUT_FD);
1560   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1561   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1562
1563   err = start (gpgsm, line);
1564   free (line);
1565   return err;
1566 }
1567
1568
1569 static gpgme_error_t
1570 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1571             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1572             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1573 {
1574   engine_gpgsm_t gpgsm = engine;
1575   gpgme_error_t err;
1576   char *assuan_cmd;
1577   int i;
1578   gpgme_key_t key;
1579
1580   if (!gpgsm)
1581     return gpg_error (GPG_ERR_INV_VALUE);
1582
1583   /* FIXME: This does not work as RESET does not reset it so we can't
1584      revert back to default.  */
1585   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1586     {
1587       /* FIXME: Make sure that if we run multiple operations, that we
1588          can reset any previously set value in case the default is
1589          requested.  */
1590
1591       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1592         return gpg_error_from_errno (errno);
1593       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
1594                                          NULL, NULL);
1595       free (assuan_cmd);
1596       if (err)
1597         return err;
1598     }
1599
1600   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1601     {
1602       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1603       if (s && strlen (s) < 80)
1604         {
1605           char buf[100];
1606
1607           strcpy (stpcpy (buf, "SIGNER "), s);
1608           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1609                                              NULL, NULL);
1610         }
1611       else
1612         err = gpg_error (GPG_ERR_INV_VALUE);
1613       gpgme_key_unref (key);
1614       if (err) 
1615         return err;
1616     }
1617
1618   gpgsm->input_cb.data = in;
1619   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_input_enc (gpgsm->input_cb.data));
1620   if (err)
1621     return err;
1622   gpgsm->output_cb.data = out;
1623   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : 0);
1624   if (err)
1625     return err;
1626   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1627
1628   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1629                ? "SIGN --detached" : "SIGN");
1630   return err;
1631 }
1632
1633
1634 static gpgme_error_t
1635 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1636               gpgme_data_t plaintext)
1637 {
1638   engine_gpgsm_t gpgsm = engine;
1639   gpgme_error_t err;
1640
1641   if (!gpgsm)
1642     return gpg_error (GPG_ERR_INV_VALUE);
1643
1644   gpgsm->input_cb.data = sig;
1645   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_input_enc (gpgsm->input_cb.data));
1646   if (err)
1647     return err;
1648   if (plaintext)
1649     {
1650       /* Normal or cleartext signature.  */
1651       gpgsm->output_cb.data = plaintext;
1652       err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1653       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1654     }
1655   else
1656     {
1657       /* Detached signature.  */
1658       gpgsm->message_cb.data = signed_text;
1659       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
1660       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1661     }
1662
1663   if (!err)
1664     err = start (gpgsm, "VERIFY");
1665
1666   return err;
1667 }
1668
1669
1670 static void
1671 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1672                           void *fnc_value) 
1673 {
1674   engine_gpgsm_t gpgsm = engine;
1675
1676   gpgsm->status.fnc = fnc;
1677   gpgsm->status.fnc_value = fnc_value;
1678 }
1679
1680
1681 static gpgme_error_t
1682 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1683                               void *fnc_value) 
1684 {
1685   engine_gpgsm_t gpgsm = engine;
1686
1687   gpgsm->colon.fnc = fnc;
1688   gpgsm->colon.fnc_value = fnc_value;
1689   gpgsm->colon.any = 0;
1690   return 0;
1691 }
1692
1693
1694 static void
1695 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1696 {
1697   engine_gpgsm_t gpgsm = engine;
1698   gpgsm->io_cbs = *io_cbs;
1699 }
1700
1701
1702 static void
1703 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1704 {
1705   engine_gpgsm_t gpgsm = engine;
1706
1707   if (gpgsm->io_cbs.event)
1708     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1709 }
1710
1711
1712 struct engine_ops _gpgme_engine_ops_gpgsm =
1713   {
1714     /* Static functions.  */
1715     _gpgme_get_gpgsm_path,
1716     gpgsm_get_version,
1717     gpgsm_get_req_version,
1718     gpgsm_new,
1719
1720     /* Member functions.  */
1721     gpgsm_release,
1722 #if USE_DESCRIPTOR_PASSING
1723     gpgsm_reset,
1724 #else
1725     NULL,                       /* reset */
1726 #endif
1727     gpgsm_set_status_handler,
1728     NULL,               /* set_command_handler */
1729     gpgsm_set_colon_line_handler,
1730     gpgsm_set_locale,
1731     gpgsm_decrypt,
1732     gpgsm_delete,
1733     NULL,               /* edit */
1734     gpgsm_encrypt,
1735     NULL,               /* encrypt_sign */
1736     gpgsm_export,
1737     gpgsm_export_ext,
1738     gpgsm_genkey,
1739     gpgsm_import,
1740     gpgsm_keylist,
1741     gpgsm_keylist_ext,
1742     gpgsm_sign,
1743     NULL,               /* trustlist */
1744     gpgsm_verify,
1745     gpgsm_set_io_cbs,
1746     gpgsm_io_event,
1747     gpgsm_cancel
1748   };
1749
1750 #endif /*!HAVE_W32_SYSTEM*/