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