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