2005-10-01 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   /* FIXME: Check error.  */
410
411   /* We need to know the fd used by assuan for reads.  We do this by
412      using the assumption that the first returned fd from
413      assuan_get_active_fds() is always this one.  */
414   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
415                                 fdlist, DIM (fdlist));
416   if (nfds < 1)
417     {
418       err = gpg_error (GPG_ERR_GENERAL);        /* FIXME */
419       goto leave;
420     }
421   /* We duplicate the file descriptor, so we can close it without
422      disturbing assuan.  Alternatively, we could special case
423      status_fd and register/unregister it manually as needed, but this
424      increases code duplication and is more complicated as we can not
425      use the close notifications etc.  */
426   gpgsm->status_cb.fd = dup (fdlist[0]);
427   if (gpgsm->status_cb.fd < 0)
428     {
429       err = gpg_error (GPG_ERR_GENERAL);        /* FIXME */
430       goto leave;
431     }
432   gpgsm->status_cb.dir = 1;
433   gpgsm->status_cb.data = gpgsm;
434
435   err = _gpgme_getenv ("DISPLAY", &dft_display);
436   if (err)
437     goto leave;
438   if (dft_display)
439     {
440       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
441         {
442           free (dft_display);
443           err = gpg_error_from_errno (errno);
444           goto leave;
445         }
446       free (dft_display);
447
448       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
449                              NULL, NULL, NULL);
450       free (optstr);
451       if (err)
452         {
453           err = map_assuan_error (err);
454           goto leave;
455         }
456     }
457
458   if (isatty (1))
459     {
460       if (ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)))
461         {
462           err = gpg_error_from_errno (errno);
463           goto leave;
464         }
465       else
466         {
467           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
468             {
469               err = gpg_error_from_errno (errno);
470               goto leave;
471             }
472           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
473                                  NULL, NULL, NULL);
474           free (optstr);
475           if (err)
476             {
477               err = map_assuan_error (err);
478               goto leave;
479             }
480
481           err = _gpgme_getenv ("TERM", &dft_ttytype);
482           if (err)
483             goto leave;
484           if (dft_ttytype)
485             {
486               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
487                 {
488                   free (dft_ttytype);
489                   err = gpg_error_from_errno (errno);
490                   goto leave;
491                 }
492               free (dft_ttytype);
493
494               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
495                                      NULL, NULL, NULL, NULL);
496               free (optstr);
497               if (err)
498                 {
499                   err = map_assuan_error (err);
500                   goto leave;
501                 }
502             }
503         }
504     }
505
506   if (lc_ctype)
507     {
508       if (asprintf (&optstr, "OPTION lc-ctype=%s", lc_ctype) < 0)
509         err = gpg_error_from_errno (errno);
510       else
511         {
512           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
513                                  NULL, NULL, NULL, NULL);
514           free (optstr);
515           if (err)
516             err = map_assuan_error (err);
517         }
518     }
519   if (err)
520     goto leave;
521   
522   if (lc_messages)
523     {
524       if (asprintf (&optstr, "OPTION lc-messages=%s", lc_messages) < 0)
525         err = gpg_error_from_errno (errno);
526       else
527         {
528           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
529                                  NULL, NULL, NULL, NULL);
530           free (optstr);
531           if (err)
532             err = map_assuan_error (err);
533         }
534     }
535   if (err)
536     goto leave;
537
538   if (!err
539       && (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
540                                       close_notify_handler, gpgsm)
541           || _gpgme_io_set_close_notify (gpgsm->input_cb.fd,
542                                          close_notify_handler, gpgsm)
543           || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
544                                          close_notify_handler, gpgsm)
545           || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
546                                          close_notify_handler, gpgsm)))
547     {
548       err = gpg_error (GPG_ERR_GENERAL);
549       goto leave;
550     }
551       
552  leave:
553   /* Close the server ends of the pipes.  Our ends are closed in
554      gpgsm_release().  */
555   if (gpgsm->input_fd_server != -1)
556     _gpgme_io_close (gpgsm->input_fd_server);
557   if (gpgsm->output_fd_server != -1)
558     _gpgme_io_close (gpgsm->output_fd_server);
559   if (gpgsm->message_fd_server != -1)
560     _gpgme_io_close (gpgsm->message_fd_server);
561
562   if (err)
563     gpgsm_release (gpgsm);
564   else
565     *engine = gpgsm;
566
567   return err;
568 }
569
570
571 /* Forward declaration.  */
572 static gpgme_status_code_t parse_status (const char *name);
573
574 static gpgme_error_t
575 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *cmd,
576                              engine_status_handler_t status_fnc,
577                              void *status_fnc_value)
578 {
579   AssuanError err;
580   char *line;
581   size_t linelen;
582
583   err = assuan_write_line (ctx, cmd);
584   if (err)
585     return map_assuan_error (err);
586
587   do
588     {
589       err = assuan_read_line (ctx, &line, &linelen);
590       if (err)
591         return map_assuan_error (err);
592
593       if (*line == '#' || !linelen)
594         continue;
595
596       if (linelen >= 2
597           && line[0] == 'O' && line[1] == 'K'
598           && (line[2] == '\0' || line[2] == ' '))
599         return 0;
600       else if (linelen >= 4
601           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
602           && line[3] == ' ')
603         err = map_assuan_error (atoi (&line[4]));
604       else if (linelen >= 2
605                && line[0] == 'S' && line[1] == ' ')
606         {
607           char *rest;
608           gpgme_status_code_t r;
609
610           rest = strchr (line + 2, ' ');
611           if (!rest)
612             rest = line + linelen; /* set to an empty string */
613           else
614             *(rest++) = 0;
615
616           r = parse_status (line + 2);
617
618           if (r >= 0 && status_fnc)
619             err = status_fnc (status_fnc_value, r, rest);
620           else
621             err = gpg_error (GPG_ERR_GENERAL);
622         }
623       else
624         err = gpg_error (GPG_ERR_GENERAL);
625     }
626   while (!err);
627
628   return err;
629 }
630
631
632 #define COMMANDLINELEN 40
633 static gpgme_error_t
634 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
635 {
636   char line[COMMANDLINELEN];
637
638   if (opt)
639     snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
640   else
641     snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
642
643   return gpgsm_assuan_simple_command (ctx, line, NULL, NULL);
644 }
645
646
647 static const char *
648 map_input_enc (gpgme_data_t d)
649 {
650   switch (gpgme_data_get_encoding (d))
651     {
652     case GPGME_DATA_ENCODING_NONE:
653       break;
654     case GPGME_DATA_ENCODING_BINARY:
655       return "--binary";
656     case GPGME_DATA_ENCODING_BASE64:
657       return "--base64";
658     case GPGME_DATA_ENCODING_ARMOR:
659       return "--armor";
660     default:
661       break;
662     }
663   return NULL;
664 }
665
666
667 static int
668 status_cmp (const void *ap, const void *bp)
669 {
670   const struct status_table_s *a = ap;
671   const struct status_table_s *b = bp;
672
673   return strcmp (a->name, b->name);
674 }
675
676
677 static gpgme_status_code_t
678 parse_status (const char *name)
679 {
680   struct status_table_s t, *r;
681   t.name = name;
682   r = bsearch (&t, status_table, DIM(status_table) - 1,
683                sizeof t, status_cmp);
684   return r ? r->code : -1;
685 }
686
687
688 static gpgme_error_t
689 status_handler (void *opaque, int fd)
690 {
691   AssuanError assuan_err;
692   gpgme_error_t err = 0;
693   engine_gpgsm_t gpgsm = opaque;
694   char *line;
695   size_t linelen;
696
697   do
698     {
699       assuan_err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
700       if (assuan_err)
701         {
702           /* Try our best to terminate the connection friendly.  */
703           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
704           err = map_assuan_error (assuan_err);
705           DEBUG3 ("fd %d: error from assuan (%d) getting status line : %s \n",
706                   fd, assuan_err, gpg_strerror (err));
707         }
708       else if (linelen >= 3
709                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
710                && (line[3] == '\0' || line[3] == ' '))
711         {
712           if (line[3] == ' ')
713             err = map_assuan_error (atoi (&line[4]));
714           else
715             err = gpg_error (GPG_ERR_GENERAL);
716           DEBUG2 ("fd %d: ERR line - mapped to: %s\n",
717                   fd, err? gpg_strerror (err):"ok");
718         }
719       else if (linelen >= 2
720                && line[0] == 'O' && line[1] == 'K'
721                && (line[2] == '\0' || line[2] == ' '))
722         {
723           if (gpgsm->status.fnc)
724             err = gpgsm->status.fnc (gpgsm->status.fnc_value,
725                                      GPGME_STATUS_EOF, "");
726           
727           if (!err && gpgsm->colon.fnc && gpgsm->colon.any )
728             {
729               /* We must tell a colon function about the EOF. We do
730                  this only when we have seen any data lines.  Note
731                  that this inlined use of colon data lines will
732                  eventually be changed into using a regular data
733                  channel. */
734               gpgsm->colon.any = 0;
735               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
736             }
737           _gpgme_io_close (gpgsm->status_cb.fd);
738           DEBUG2 ("fd %d: OK line - final status: %s\n",
739                   fd, err? gpg_strerror (err):"ok");
740           return err;
741         }
742       else if (linelen > 2
743                && line[0] == 'D' && line[1] == ' '
744                && gpgsm->colon.fnc)
745         {
746           /* We are using the colon handler even for plain inline data
747              - strange name for that function but for historic reasons
748              we keep it.  */
749           /* FIXME We can't use this for binary data because we
750              assume this is a string.  For the current usage of colon
751              output it is correct.  */
752           char *src = line + 2;
753           char *end = line + linelen;
754           char *dst;
755           char **aline = &gpgsm->colon.attic.line;
756           int *alinelen = &gpgsm->colon.attic.linelen;
757
758           if (gpgsm->colon.attic.linesize
759               < *alinelen + linelen + 1)
760             {
761               char *newline = realloc (*aline, *alinelen + linelen + 1);
762               if (!newline)
763                 err = gpg_error_from_errno (errno);
764               else
765                 {
766                   *aline = newline;
767                   gpgsm->colon.attic.linesize += linelen + 1;
768                 }
769             }
770           if (!err)
771             {
772               dst = *aline + *alinelen;
773
774               while (!err && src < end)
775                 {
776                   if (*src == '%' && src + 2 < end)
777                     {
778                       /* Handle escaped characters.  */
779                       ++src;
780                       *dst = _gpgme_hextobyte (src);
781                       (*alinelen)++;
782                       src += 2;
783                     }
784                   else
785                     {
786                       *dst = *src++;
787                       (*alinelen)++;
788                     }
789                   
790                   if (*dst == '\n')
791                     {
792                       /* Terminate the pending line, pass it to the colon
793                          handler and reset it.  */
794                       
795                       gpgsm->colon.any = 1;
796                       if (*alinelen > 1 && *(dst - 1) == '\r')
797                         dst--;
798                       *dst = '\0';
799
800                       /* FIXME How should we handle the return code?  */
801                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
802                       if (!err)
803                         {
804                           dst = *aline;
805                           *alinelen = 0;
806                         }
807                     }
808                   else
809                     dst++;
810                 }
811             }
812           DEBUG2 ("fd %d: D line; final status: %s\n",
813                   fd, err? gpg_strerror (err):"ok");
814         }
815       else if (linelen > 2
816                && line[0] == 'S' && line[1] == ' ')
817         {
818           char *rest;
819           gpgme_status_code_t r;
820           
821           rest = strchr (line + 2, ' ');
822           if (!rest)
823             rest = line + linelen; /* set to an empty string */
824           else
825             *(rest++) = 0;
826
827           r = parse_status (line + 2);
828
829           if (r >= 0)
830             {
831               if (gpgsm->status.fnc)
832                 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
833             }
834           else
835             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
836           DEBUG3 ("fd %d: S line (%s) - final status: %s\n",
837                   fd, line+2, err? gpg_strerror (err):"ok");
838         }
839     }
840   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
841           
842   return err;
843 }
844
845
846 static gpgme_error_t
847 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
848 {
849   gpgme_error_t err;
850
851   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
852                               iocbd->fd, iocbd->dir,
853                               handler, iocbd->data, &iocbd->tag);
854   if (err)
855     return err;
856   if (!iocbd->dir)
857     /* FIXME Kludge around poll() problem.  */
858     err = _gpgme_io_set_nonblocking (iocbd->fd);
859   return err;
860 }
861
862
863 static gpgme_error_t
864 start (engine_gpgsm_t gpgsm, const char *command)
865 {
866   gpgme_error_t err;
867
868   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
869   if (!err && gpgsm->input_cb.fd != -1)
870     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
871   if (!err && gpgsm->output_cb.fd != -1)
872     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
873   if (!err && gpgsm->message_cb.fd != -1)
874     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
875
876   if (!err)
877     err = map_assuan_error (assuan_write_line (gpgsm->assuan_ctx, command));
878
879   if (!err)
880     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, GPGME_EVENT_START, NULL);
881
882   return err;
883 }
884
885
886 static gpgme_error_t
887 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
888 {
889   engine_gpgsm_t gpgsm = engine;
890   gpgme_error_t err;
891
892   if (!gpgsm)
893     return gpg_error (GPG_ERR_INV_VALUE);
894
895   gpgsm->input_cb.data = ciph;
896   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 
897                       map_input_enc (gpgsm->input_cb.data));
898   if (err)
899     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
900   gpgsm->output_cb.data = plain;
901   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server, 0);
902   if (err)
903     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
904   _gpgme_io_close (gpgsm->message_cb.fd);
905
906   err = start (engine, "DECRYPT");
907   return err;
908 }
909
910
911 static gpgme_error_t
912 gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
913 {
914   engine_gpgsm_t gpgsm = engine;
915   gpgme_error_t err;
916   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
917   char *linep = fpr;
918   char *line;
919   int length = 8;       /* "DELKEYS " */
920
921   if (!fpr)
922     return gpg_error (GPG_ERR_INV_VALUE);
923
924   while (*linep)
925     {
926       length++;
927       if (*linep == '%' || *linep == ' ' || *linep == '+')
928         length += 2;
929       linep++;
930     }
931   length++;
932
933   line = malloc (length);
934   if (!line)
935     return gpg_error_from_errno (errno);
936
937   strcpy (line, "DELKEYS ");
938   linep = &line[8];
939
940   while (*fpr)
941     {
942       switch (*fpr)
943         {
944         case '%':
945           *(linep++) = '%';
946           *(linep++) = '2';
947           *(linep++) = '5';
948           break;
949         case ' ':
950           *(linep++) = '%';
951           *(linep++) = '2';
952           *(linep++) = '0';
953           break;
954         case '+':
955           *(linep++) = '%';
956           *(linep++) = '2';
957           *(linep++) = 'B';
958           break;
959         default:
960           *(linep++) = *fpr;
961           break;
962         }
963       fpr++;
964     }
965   *linep = '\0';
966
967   _gpgme_io_close (gpgsm->output_cb.fd);
968   _gpgme_io_close (gpgsm->input_cb.fd);
969   _gpgme_io_close (gpgsm->message_cb.fd);
970
971   err = start (gpgsm, line);
972   free (line);
973
974   return err;
975 }
976
977
978 static gpgme_error_t
979 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
980 {
981   gpgme_error_t err = 0;
982   ASSUAN_CONTEXT ctx = gpgsm->assuan_ctx;
983   char *line;
984   int linelen;
985   int invalid_recipients = 0;
986   int i = 0;
987
988   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
989   line = malloc (10 + 40 + 1);
990   if (!line)
991     return gpg_error_from_errno (errno);
992   strcpy (line, "RECIPIENT ");
993   while (!err && recp[i])
994     {
995       char *fpr;
996       int newlen;
997
998       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
999         {
1000           invalid_recipients++;
1001           continue;
1002         }
1003       fpr = recp[i]->subkeys->fpr;
1004
1005       newlen = 11 + strlen (fpr);
1006       if (linelen < newlen)
1007         {
1008           char *newline = realloc (line, newlen);
1009           if (! newline)
1010             {
1011               int saved_errno = errno;
1012               free (line);
1013               return gpg_error_from_errno (saved_errno);
1014             }
1015           line = newline;
1016           linelen = newlen;
1017         }
1018       strcpy (&line[10], fpr);
1019
1020       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
1021                                          gpgsm->status.fnc_value);
1022       /* FIXME: This requires more work.  */
1023       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1024         invalid_recipients++;
1025       else if (err)
1026         {
1027           free (line);
1028           return err;
1029         }
1030       i++;
1031     }
1032   free (line);
1033   return gpg_error (invalid_recipients
1034                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1035 }
1036
1037
1038 static gpgme_error_t
1039 gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1040                gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1041 {
1042   engine_gpgsm_t gpgsm = engine;
1043   gpgme_error_t err;
1044
1045   if (!gpgsm)
1046     return gpg_error (GPG_ERR_INV_VALUE);
1047   if (!recp)
1048     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1049
1050   gpgsm->input_cb.data = plain;
1051   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1052                       map_input_enc (gpgsm->input_cb.data));
1053   if (err)
1054     return err;
1055   gpgsm->output_cb.data = ciph;
1056   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1057                       use_armor ? "--armor" : 0);
1058   if (err)
1059     return err;
1060   _gpgme_io_close (gpgsm->message_cb.fd);
1061
1062   err = set_recipients (gpgsm, recp);
1063
1064   if (!err)
1065     err = start (gpgsm, "ENCRYPT");
1066
1067   return err;
1068 }
1069
1070
1071 static gpgme_error_t
1072 gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
1073               gpgme_data_t keydata, int use_armor)
1074 {
1075   engine_gpgsm_t gpgsm = engine;
1076   gpgme_error_t err = 0;
1077   char *cmd;
1078
1079   if (!gpgsm || reserved)
1080     return gpg_error (GPG_ERR_INV_VALUE);
1081
1082   if (!pattern)
1083     pattern = "";
1084
1085   cmd = malloc (7 + strlen (pattern) + 1);
1086   if (!cmd)
1087     return gpg_error_from_errno (errno);
1088   strcpy (cmd, "EXPORT ");
1089   strcpy (&cmd[7], pattern);
1090
1091   gpgsm->output_cb.data = keydata;
1092   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1093                       use_armor ? "--armor" : 0);
1094   if (err)
1095     return err;
1096   _gpgme_io_close (gpgsm->input_cb.fd);
1097   _gpgme_io_close (gpgsm->message_cb.fd);
1098
1099   err = start (gpgsm, cmd);
1100   free (cmd);
1101   return err;
1102 }
1103
1104
1105 static gpgme_error_t
1106 gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
1107                   gpgme_data_t keydata, int use_armor)
1108 {
1109   engine_gpgsm_t gpgsm = engine;
1110   gpgme_error_t err = 0;
1111   char *line;
1112   /* Length is "EXPORT " + p + '\0'.  */
1113   int length = 7 + 1;
1114   char *linep;
1115
1116   if (!gpgsm || reserved)
1117     return gpg_error (GPG_ERR_INV_VALUE);
1118
1119   if (pattern && *pattern)
1120     {
1121       const char **pat = pattern;
1122
1123       while (*pat)
1124         {
1125           const char *patlet = *pat;
1126
1127           while (*patlet)
1128             {
1129               length++;
1130               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1131                 length += 2;
1132               patlet++;
1133             }
1134           pat++;
1135           length++;
1136         }
1137     }
1138   line = malloc (length);
1139   if (!line)
1140     return gpg_error_from_errno (errno);
1141
1142   strcpy (line, "EXPORT ");
1143   linep = &line[7];
1144
1145   if (pattern && *pattern)
1146     {
1147       while (*pattern)
1148         {
1149           const char *patlet = *pattern;
1150
1151           while (*patlet)
1152             {
1153               switch (*patlet)
1154                 {
1155                 case '%':
1156                   *(linep++) = '%';
1157                   *(linep++) = '2';
1158                   *(linep++) = '5';
1159                   break;
1160                 case ' ':
1161                   *(linep++) = '%';
1162                   *(linep++) = '2';
1163                   *(linep++) = '0';
1164                   break;
1165                 case '+':
1166                   *(linep++) = '%';
1167                   *(linep++) = '2';
1168                   *(linep++) = 'B';
1169                   break;
1170                 default:
1171                   *(linep++) = *patlet;
1172                   break;
1173                 }
1174               patlet++;
1175             }
1176           pattern++;
1177           if (*pattern)
1178             *linep++ = ' ';
1179         }
1180     }
1181   *linep = '\0';
1182
1183   gpgsm->output_cb.data = keydata;
1184   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1185                       use_armor ? "--armor" : 0);
1186   if (err)
1187     return err;
1188   _gpgme_io_close (gpgsm->input_cb.fd);
1189   _gpgme_io_close (gpgsm->message_cb.fd);
1190
1191   err = start (gpgsm, line);
1192   free (line);
1193   return err;
1194 }
1195
1196
1197 static gpgme_error_t
1198 gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1199               gpgme_data_t pubkey, gpgme_data_t seckey)
1200 {
1201   engine_gpgsm_t gpgsm = engine;
1202   gpgme_error_t err;
1203
1204   if (!gpgsm || !pubkey || seckey)
1205     return gpg_error (GPG_ERR_INV_VALUE);
1206
1207   gpgsm->input_cb.data = help_data;
1208   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1209                       map_input_enc (gpgsm->input_cb.data));
1210   if (err)
1211     return err;
1212   gpgsm->output_cb.data = pubkey;
1213   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1214                       use_armor ? "--armor" : 0);
1215   if (err)
1216     return err;
1217   _gpgme_io_close (gpgsm->message_cb.fd);
1218
1219   err = start (gpgsm, "GENKEY");
1220   return err;
1221 }
1222
1223
1224 static gpgme_error_t
1225 gpgsm_import (void *engine, gpgme_data_t keydata)
1226 {
1227   engine_gpgsm_t gpgsm = engine;
1228   gpgme_error_t err;
1229
1230   if (!gpgsm)
1231     return gpg_error (GPG_ERR_INV_VALUE);
1232
1233   gpgsm->input_cb.data = keydata;
1234   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1235                       map_input_enc (gpgsm->input_cb.data));
1236   if (err)
1237     return err;
1238   _gpgme_io_close (gpgsm->output_cb.fd);
1239   _gpgme_io_close (gpgsm->message_cb.fd);
1240
1241   err = start (gpgsm, "IMPORT");
1242   return err;
1243 }
1244
1245
1246 static gpgme_error_t
1247 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1248                gpgme_keylist_mode_t mode)
1249 {
1250   engine_gpgsm_t gpgsm = engine;
1251   char *line;
1252   gpgme_error_t err;
1253   int list_mode = 0;
1254
1255   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1256     list_mode |= 1;
1257   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1258     list_mode |= 2;
1259
1260   if (!pattern)
1261     pattern = "";
1262
1263   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1264     return gpg_error_from_errno (errno);
1265   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1266   free (line);
1267   if (err)
1268     return err;
1269
1270
1271   /* Use the validation mode if required.  We don't check for an error
1272      yet because this is a pretty fresh gpgsm features. */
1273   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1274                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1275                                "OPTION with-validation=1":
1276                                "OPTION with-validation=0" ,
1277                                NULL, NULL);
1278
1279
1280   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1281   line = malloc (15 + strlen (pattern) + 1);
1282   if (!line)
1283     return gpg_error_from_errno (errno);
1284   if (secret_only)
1285     {
1286       strcpy (line, "LISTSECRETKEYS ");
1287       strcpy (&line[15], pattern);
1288     }
1289   else
1290     {
1291       strcpy (line, "LISTKEYS ");
1292       strcpy (&line[9], pattern);
1293     }
1294
1295   _gpgme_io_close (gpgsm->input_cb.fd);
1296   _gpgme_io_close (gpgsm->output_cb.fd);
1297   _gpgme_io_close (gpgsm->message_cb.fd);
1298
1299   err = start (gpgsm, line);
1300   free (line);
1301   return err;
1302 }
1303
1304
1305 static gpgme_error_t
1306 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1307                    int reserved, gpgme_keylist_mode_t mode)
1308 {
1309   engine_gpgsm_t gpgsm = engine;
1310   char *line;
1311   gpgme_error_t err;
1312   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1313   int length = 15 + 1;
1314   char *linep;
1315   int any_pattern = 0;
1316   int list_mode = 0;
1317
1318   if (reserved)
1319     return gpg_error (GPG_ERR_INV_VALUE);
1320
1321   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1322     list_mode |= 1;
1323   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1324     list_mode |= 2;
1325
1326   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1327     return gpg_error_from_errno (errno);
1328   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1329   free (line);
1330   if (err)
1331     return err;
1332
1333   /* Use the validation mode if required.  We don't check for an error
1334      yet because this is a pretty fresh gpgsm features. */
1335   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1336                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1337                                "OPTION with-validation=1":
1338                                "OPTION with-validation=0" ,
1339                                NULL, NULL);
1340
1341
1342   if (pattern && *pattern)
1343     {
1344       const char **pat = pattern;
1345
1346       while (*pat)
1347         {
1348           const char *patlet = *pat;
1349
1350           while (*patlet)
1351             {
1352               length++;
1353               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1354                 length += 2;
1355               patlet++;
1356             }
1357           pat++;
1358           length++;
1359         }
1360     }
1361   line = malloc (length);
1362   if (!line)
1363     return gpg_error_from_errno (errno);
1364   if (secret_only)
1365     {
1366       strcpy (line, "LISTSECRETKEYS ");
1367       linep = &line[15];
1368     }
1369   else
1370     {
1371       strcpy (line, "LISTKEYS ");
1372       linep = &line[9];
1373     }
1374
1375   if (pattern && *pattern)
1376     {
1377       while (*pattern)
1378         {
1379           const char *patlet = *pattern;
1380
1381           while (*patlet)
1382             {
1383               switch (*patlet)
1384                 {
1385                 case '%':
1386                   *(linep++) = '%';
1387                   *(linep++) = '2';
1388                   *(linep++) = '5';
1389                   break;
1390                 case ' ':
1391                   *(linep++) = '%';
1392                   *(linep++) = '2';
1393                   *(linep++) = '0';
1394                   break;
1395                 case '+':
1396                   *(linep++) = '%';
1397                   *(linep++) = '2';
1398                   *(linep++) = 'B';
1399                   break;
1400                 default:
1401                   *(linep++) = *patlet;
1402                   break;
1403                 }
1404               patlet++;
1405             }
1406           any_pattern = 1;
1407           *linep++ = ' ';
1408           pattern++;
1409         }
1410     }
1411   if (any_pattern)
1412     linep--;
1413   *linep = '\0';
1414
1415   _gpgme_io_close (gpgsm->input_cb.fd);
1416   _gpgme_io_close (gpgsm->output_cb.fd);
1417   _gpgme_io_close (gpgsm->message_cb.fd);
1418
1419   err = start (gpgsm, line);
1420   free (line);
1421   return err;
1422 }
1423
1424
1425 static gpgme_error_t
1426 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1427             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1428             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1429 {
1430   engine_gpgsm_t gpgsm = engine;
1431   gpgme_error_t err;
1432   char *assuan_cmd;
1433   int i;
1434   gpgme_key_t key;
1435
1436   if (!gpgsm)
1437     return gpg_error (GPG_ERR_INV_VALUE);
1438
1439   /* We must send a reset because we need to reset the list of
1440      signers.  Note that RESET does not reset OPTION commands. */
1441   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1442   if (err)
1443     return err;
1444
1445   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1446     {
1447       /* FIXME: Make sure that if we run multiple operations, that we
1448          can reset any previously set value in case the default is
1449          requested.  */
1450
1451       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1452         return gpg_error_from_errno (errno);
1453       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
1454                                          NULL, NULL);
1455       free (assuan_cmd);
1456       if (err)
1457         return err;
1458     }
1459
1460   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1461     {
1462       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1463       if (s && strlen (s) < 80)
1464         {
1465           char buf[100];
1466
1467           strcpy (stpcpy (buf, "SIGNER "), s);
1468           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1469                                              NULL, NULL);
1470         }
1471       else
1472         err = gpg_error (GPG_ERR_INV_VALUE);
1473       gpgme_key_unref (key);
1474       if (err) 
1475         return err;
1476     }
1477
1478   gpgsm->input_cb.data = in;
1479   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1480                       map_input_enc (gpgsm->input_cb.data));
1481   if (err)
1482     return err;
1483   gpgsm->output_cb.data = out;
1484   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1485                       use_armor ? "--armor" : 0);
1486   if (err)
1487     return err;
1488   _gpgme_io_close (gpgsm->message_cb.fd);
1489
1490   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1491                ? "SIGN --detached" : "SIGN");
1492   return err;
1493 }
1494
1495
1496 static gpgme_error_t
1497 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1498               gpgme_data_t plaintext)
1499 {
1500   engine_gpgsm_t gpgsm = engine;
1501   gpgme_error_t err;
1502
1503   if (!gpgsm)
1504     return gpg_error (GPG_ERR_INV_VALUE);
1505
1506   gpgsm->input_cb.data = sig;
1507   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1508                       map_input_enc (gpgsm->input_cb.data));
1509   if (err)
1510     return err;
1511   if (plaintext)
1512     {
1513       /* Normal or cleartext signature.  */
1514       gpgsm->output_cb.data = plaintext;
1515       err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1516                           0);
1517       _gpgme_io_close (gpgsm->message_cb.fd);
1518     }
1519   else
1520     {
1521       /* Detached signature.  */
1522       gpgsm->message_cb.data = signed_text;
1523       err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1524                           gpgsm->message_fd_server, 0);
1525       _gpgme_io_close (gpgsm->output_cb.fd);
1526     }
1527
1528   if (!err)
1529     err = start (gpgsm, "VERIFY");
1530
1531   return err;
1532 }
1533
1534
1535 static void
1536 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1537                           void *fnc_value) 
1538 {
1539   engine_gpgsm_t gpgsm = engine;
1540
1541   gpgsm->status.fnc = fnc;
1542   gpgsm->status.fnc_value = fnc_value;
1543 }
1544
1545
1546 static gpgme_error_t
1547 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1548                               void *fnc_value) 
1549 {
1550   engine_gpgsm_t gpgsm = engine;
1551
1552   gpgsm->colon.fnc = fnc;
1553   gpgsm->colon.fnc_value = fnc_value;
1554   gpgsm->colon.any = 0;
1555   return 0;
1556 }
1557
1558
1559 static void
1560 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1561 {
1562   engine_gpgsm_t gpgsm = engine;
1563   gpgsm->io_cbs = *io_cbs;
1564 }
1565
1566
1567 static void
1568 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1569 {
1570   engine_gpgsm_t gpgsm = engine;
1571
1572   if (gpgsm->io_cbs.event)
1573     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1574 }
1575
1576
1577 struct engine_ops _gpgme_engine_ops_gpgsm =
1578   {
1579     /* Static functions.  */
1580     _gpgme_get_gpgsm_path,
1581     gpgsm_get_version,
1582     gpgsm_get_req_version,
1583     gpgsm_new,
1584
1585     /* Member functions.  */
1586     gpgsm_release,
1587     gpgsm_set_status_handler,
1588     NULL,               /* set_command_handler */
1589     gpgsm_set_colon_line_handler,
1590     gpgsm_decrypt,
1591     gpgsm_delete,
1592     NULL,               /* edit */
1593     gpgsm_encrypt,
1594     NULL,               /* encrypt_sign */
1595     gpgsm_export,
1596     gpgsm_export_ext,
1597     gpgsm_genkey,
1598     gpgsm_import,
1599     gpgsm_keylist,
1600     gpgsm_keylist_ext,
1601     gpgsm_sign,
1602     NULL,               /* trustlist */
1603     gpgsm_verify,
1604     gpgsm_set_io_cbs,
1605     gpgsm_io_event,
1606     gpgsm_cancel
1607   };
1608
1609 #endif /*!HAVE_W32_SYSTEM*/