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