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