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