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