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