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