2002-01-22 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 g10 Code GmbH
4  *
5  * This file is part of GPGME.
6  *
7  * GPGME is free software; you can redistribute it and/or modify
8  * it 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,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 /* FIXME: Correct check?  */
27 #ifdef GPGSM_PATH
28 #define ENABLE_GPGSM 1
29 #endif
30
31 #ifdef ENABLE_GPGSM
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <assert.h>
37 #include <fcntl.h> /* FIXME */
38
39 #include "rungpg.h"
40 #include "status-table.h"
41
42 #include "gpgme.h"
43 #include "util.h"
44 #include "types.h"
45 #include "ops.h"
46 #include "wait.h"
47 #include "io.h"
48 #include "key.h"
49
50 #include "engine-gpgsm.h"
51
52 #include "assuan.h"
53
54 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
55                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
56 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
57
58
59 struct gpgsm_object_s
60 {
61   ASSUAN_CONTEXT assuan_ctx;
62
63   /* Input, output etc are from the servers perspective.  */
64   int input_fd;
65   int input_fd_server;
66   GpgmeData input_data;
67   int output_fd;
68   int output_fd_server;
69   GpgmeData output_data;
70   int message_fd;
71   int message_fd_server;
72   GpgmeData message_data;
73
74   char *command;
75
76   struct
77   {
78     GpgStatusHandler fnc;
79     void *fnc_value;
80   } status;
81
82   struct
83   {
84     GpgColonLineHandler fnc;
85     void *fnc_value;
86     struct
87     {
88       unsigned char *line;
89       int linesize;
90       int linelen;
91     } attic;
92   } colon; 
93 };
94
95 const char *
96 _gpgme_gpgsm_get_version (void)
97 {
98   static const char *gpgsm_version;
99
100   /* FIXME: Locking.  */
101   if (!gpgsm_version)
102     gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
103
104   return gpgsm_version;
105 }
106
107 GpgmeError
108 _gpgme_gpgsm_check_version (void)
109 {
110     return _gpgme_compare_versions (_gpgme_gpgsm_get_version (),
111                                   NEED_GPGSM_VERSION)
112     ? 0 : mk_error (Invalid_Engine);
113 }
114
115 static void
116 close_notify_handler (int fd, void *opaque)
117 {
118   GpgsmObject gpgsm = opaque;
119
120   assert (fd != -1);
121   if (gpgsm->input_fd == fd)
122     gpgsm->input_fd = -1;
123   else if (gpgsm->output_fd == fd)
124     gpgsm->output_fd = -1;
125   else if (gpgsm->message_fd == fd)
126     gpgsm->message_fd = -1;
127 }
128
129 GpgmeError
130 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
131 {
132   GpgmeError err = 0;
133   GpgsmObject gpgsm;
134   char *argv[] = { "gpgsm", "--server", NULL };
135   int fds[2];
136   int child_fds[4];
137
138   *r_gpgsm = NULL;
139   gpgsm = xtrycalloc (1, sizeof *gpgsm);
140   if (!gpgsm)
141     {
142       err = mk_error (Out_Of_Core);
143       return err;
144     }
145
146   gpgsm->input_fd = -1;
147   gpgsm->input_fd_server = -1;
148   gpgsm->output_fd = -1;
149   gpgsm->output_fd_server = -1;
150   gpgsm->message_fd = -1;
151   gpgsm->message_fd_server = -1;
152
153   gpgsm->status.fnc = 0;
154   gpgsm->colon.fnc = 0;
155   gpgsm->colon.attic.line = 0;
156   gpgsm->colon.attic.linesize = 0;
157   gpgsm->colon.attic.linelen = 0;
158
159   if (_gpgme_io_pipe (fds, 0) < 0)
160     {
161       err = mk_error (Pipe_Error);
162       goto leave;
163     }
164   gpgsm->input_fd = fds[1];
165   gpgsm->input_fd_server = fds[0];
166
167   if (_gpgme_io_pipe (fds, 1) < 0)
168     {
169       err = mk_error (Pipe_Error);
170       goto leave;
171     }
172   gpgsm->output_fd = fds[0];
173   gpgsm->output_fd_server = fds[1];
174
175   if (_gpgme_io_pipe (fds, 0) < 0)
176     {
177       err = mk_error (Pipe_Error);
178       goto leave;
179     }
180   gpgsm->message_fd = fds[1];
181   gpgsm->message_fd_server = fds[0];
182
183   child_fds[0] = gpgsm->input_fd_server;
184   child_fds[1] = gpgsm->output_fd_server;
185   child_fds[2] = gpgsm->message_fd_server;
186   child_fds[3] = -1;
187   err = assuan_pipe_connect (&gpgsm->assuan_ctx,
188                              _gpgme_get_gpgsm_path (), argv, child_fds);
189
190   if (!err &&
191       (_gpgme_io_set_close_notify (gpgsm->input_fd,
192                                    close_notify_handler, gpgsm)
193        || _gpgme_io_set_close_notify (gpgsm->output_fd,
194                                       close_notify_handler, gpgsm)
195        || _gpgme_io_set_close_notify (gpgsm->message_fd,
196                                       close_notify_handler, gpgsm)))
197     {
198       err = mk_error (General_Error);
199       goto leave;
200     }
201       
202  leave:
203   /* Close the server ends of the pipes.  Our ends are closed in
204      _gpgme_gpgsm_release.  */
205   if (gpgsm->input_fd_server != -1)
206     _gpgme_io_close (gpgsm->input_fd_server);
207   if (gpgsm->output_fd_server != -1)
208     _gpgme_io_close (gpgsm->output_fd_server);
209   if (gpgsm->message_fd_server != -1)
210     _gpgme_io_close (gpgsm->message_fd_server);
211
212   if (err)
213     _gpgme_gpgsm_release (gpgsm);
214   else
215     *r_gpgsm = gpgsm;
216
217   return err;
218 }
219
220 void
221 _gpgme_gpgsm_release (GpgsmObject gpgsm)
222 {
223   pid_t pid;
224
225   if (!gpgsm)
226     return;
227
228   pid = assuan_get_pid (gpgsm->assuan_ctx);
229   if (pid != -1)
230     _gpgme_remove_proc_from_wait_queue (pid);
231
232   if (gpgsm->input_fd != -1)
233     _gpgme_io_close (gpgsm->input_fd);
234   if (gpgsm->output_fd != -1)
235     _gpgme_io_close (gpgsm->output_fd);
236   if (gpgsm->message_fd != -1)
237     _gpgme_io_close (gpgsm->message_fd);
238
239   assuan_disconnect (gpgsm->assuan_ctx);
240
241   xfree (gpgsm->colon.attic.line);
242   xfree (gpgsm->command);
243   xfree (gpgsm);
244 }
245
246 static AssuanError
247 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *cmd)
248 {
249   AssuanError err;
250   char *line;
251   size_t linelen;
252
253   err = assuan_write_line (ctx, cmd);
254   if (err)
255     return err;
256
257   do
258     {
259       err = assuan_read_line (ctx, &line, &linelen);
260       if (err)
261         return err;
262     }
263   while (*line == '#' || !linelen);
264   
265   if (linelen >= 2
266       && line[0] == 'O' && line[1] == 'K'
267       && (line[2] == '\0' || line[2] == ' '))
268     return 0;
269   else
270     return ASSUAN_General_Error;
271 }
272
273 #define COMMANDLINELEN 40
274 static AssuanError
275 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
276 {
277   char line[COMMANDLINELEN];
278
279   if (opt)
280     snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
281   else
282     snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
283
284   return gpgsm_assuan_simple_command (ctx, line);
285 }
286
287 GpgmeError
288 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
289 {
290   AssuanError err;
291
292   if (!gpgsm)
293     return mk_error (Invalid_Value);
294
295   gpgsm->command = xtrystrdup ("DECRYPT");
296   if (!gpgsm->command)
297     return mk_error (Out_Of_Core);
298
299   gpgsm->input_data = ciph;
300   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
301   if (err)
302     return mk_error (General_Error);    /* FIXME */
303   gpgsm->output_data = plain;
304   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server, 0);
305   if (err)
306     return mk_error (General_Error);    /* FIXME */
307   _gpgme_io_close (gpgsm->message_fd);
308
309   return 0;
310 }
311
312 GpgmeError
313 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
314 {
315   /* FIXME */
316   return mk_error (Not_Implemented);
317 }
318
319 static AssuanError
320 gpgsm_set_recipients (ASSUAN_CONTEXT ctx, GpgmeRecipients recp)
321 {
322   AssuanError err;
323   char *line;
324   int linelen;
325   struct user_id_s *r;
326
327   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
328   line = xtrymalloc (10 + 40 + 1);
329   if (!line)
330     return ASSUAN_Out_Of_Core;
331   strcpy (line, "RECIPIENT ");
332   for (r = recp->list; r; r = r->next)
333     {
334       int newlen = 11 + strlen (r->name);
335       if (linelen < newlen)
336         {
337           char *newline = xtryrealloc (line, newlen);
338           if (! newline)
339             {
340               xfree (line);
341               return ASSUAN_Out_Of_Core;
342             }
343           line = newline;
344           linelen = newlen;
345         }
346       strcpy (&line[10], r->name);
347       
348       err = gpgsm_assuan_simple_command (ctx, line);
349       if (err)
350         {
351           xfree (line);
352           return err;
353         }
354     }
355   xfree (line);
356   return 0;
357 }
358
359 GpgmeError
360 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
361                          GpgmeData plain, GpgmeData ciph, int use_armor)
362 {
363   AssuanError err;
364
365   if (!gpgsm)
366     return mk_error (Invalid_Value);
367
368   gpgsm->command = xtrystrdup ("ENCRYPT");
369   if (!gpgsm->command)
370     return mk_error (Out_Of_Core);
371
372   gpgsm->input_data = plain;
373   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
374   if (err)
375     return mk_error (General_Error);    /* FIXME */
376   gpgsm->output_data = ciph;
377   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
378                       use_armor ? "--armor" : 0);
379   if (err)
380     return mk_error (General_Error);    /* FIXME */
381   _gpgme_io_close (gpgsm->message_fd);
382
383   err = gpgsm_set_recipients (gpgsm->assuan_ctx, recp);
384   if (err)
385     return mk_error (General_Error);
386
387   return 0;
388 }
389
390 GpgmeError
391 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
392                         GpgmeData keydata, int use_armor)
393 {
394   /* FIXME */
395   return mk_error (Not_Implemented);
396 }
397
398 GpgmeError
399 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor)
400 {
401   /* FIXME */
402   return mk_error (Not_Implemented);
403 }
404
405 GpgmeError
406 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
407 {
408   AssuanError err;
409
410   if (!gpgsm)
411     return mk_error (Invalid_Value);
412
413   gpgsm->command = xtrystrdup ("IMPORT");
414   if (!gpgsm->command)
415     return mk_error (Out_Of_Core);
416
417   gpgsm->input_data = keydata;
418   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
419   if (err)
420     return mk_error (General_Error);    /* FIXME */
421   _gpgme_io_close (gpgsm->output_fd);
422   _gpgme_io_close (gpgsm->message_fd);
423
424   return 0;
425 }
426
427 GpgmeError
428 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
429                          int secret_only, int keylist_mode)
430 {
431   char *line;
432
433   if (!pattern)
434     pattern = "";
435
436   line = xtrymalloc (9 + strlen (pattern) + 1); /* "LISTKEYS " + p + '\0'.  */
437   if (!line)
438     return mk_error (Out_Of_Core);
439   strcpy (line, "LISTKEYS ");
440   strcpy (&line[9], pattern);
441
442   _gpgme_io_close (gpgsm->input_fd);
443   _gpgme_io_close (gpgsm->output_fd);
444   _gpgme_io_close (gpgsm->message_fd);
445
446   gpgsm->command = line;
447   return 0;
448 }
449
450 GpgmeError
451 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
452                       GpgmeSigMode mode, int use_armor,
453                       int use_textmode, GpgmeCtx ctx /* FIXME */)
454 {
455   AssuanError err;
456
457   if (!gpgsm)
458     return mk_error (Invalid_Value);
459
460   gpgsm->command = xtrystrdup (mode == GPGME_SIG_MODE_DETACH
461                                ? "SIGN --detached" : "SIGN");
462   if (!gpgsm->command)
463     return mk_error (Out_Of_Core);
464
465   gpgsm->input_data = in;
466   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
467   if (err)
468     return mk_error (General_Error);    /* FIXME */
469   gpgsm->output_data = out;
470   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
471                       use_armor ? "--armor" : 0);
472   if (err)
473     return mk_error (General_Error);    /* FIXME */
474   _gpgme_io_close (gpgsm->message_fd);
475
476   return 0;
477 }
478
479 GpgmeError
480 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
481 {
482   /* FIXME */
483   return mk_error (Not_Implemented);
484 }
485
486 GpgmeError
487 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
488 {
489   AssuanError err;
490
491   if (!gpgsm)
492     return mk_error (Invalid_Value);
493
494   gpgsm->command = xtrystrdup ("VERIFY");
495   if (!gpgsm->command)
496     return mk_error (Out_Of_Core);
497
498   gpgsm->input_data = sig;
499   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 0);
500   if (err)
501     return mk_error (General_Error);    /* FIXME */
502   gpgsm->message_data = text;
503   err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE", gpgsm->message_fd_server,
504                       0);
505   if (err)
506     return mk_error (General_Error);    /* FIXME */
507   _gpgme_io_close (gpgsm->output_fd);
508
509   return 0;
510 }
511
512 static int
513 status_cmp (const void *ap, const void *bp)
514 {
515   const struct status_table_s *a = ap;
516   const struct status_table_s *b = bp;
517
518   return strcmp (a->name, b->name);
519 }
520
521 static int
522 gpgsm_status_handler (void *opaque, int pid, int fd)
523 {
524   int err;
525   GpgsmObject gpgsm = opaque;
526   char *line;
527   size_t linelen;
528
529   do
530     {
531       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
532
533       if (err
534           || (linelen >= 2
535               && line[0] == 'O' && line[1] == 'K'
536               && (line[2] == '\0' || line[2] == ' '))
537           || (linelen >= 3
538               && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
539               && (line[3] == '\0' || line[3] == ' ')))
540         {
541           /* FIXME Save error somewhere.  */
542           if (gpgsm->status.fnc)
543             gpgsm->status.fnc (gpgsm->status.fnc_value, STATUS_EOF, "");
544           return 1;
545         }
546
547       if (linelen > 2
548           && line[0] == 'D' && line[1] == ' '
549           && gpgsm->colon.fnc)
550         {
551           /* We are using the colon handler even for plain inline data
552              - strange name for that function but for historic reasons
553              we keep it.  */
554           /* FIXME We can't use this for binary data because we
555              assume this is a string.  For the current usage of colon
556              output it is correct.  */
557           unsigned char *src = line + 2;
558           unsigned char *end = line + linelen;
559           unsigned char *dst;
560           unsigned char **aline = &gpgsm->colon.attic.line;
561           int *alinelen = &gpgsm->colon.attic.linelen;
562
563           if (gpgsm->colon.attic.linesize
564               < *alinelen + linelen + 1)
565             {
566               unsigned char *newline = xtryrealloc (*aline,
567                                                     *alinelen + linelen + 1);
568               if (!newline)
569                 return mk_error (Out_Of_Core);
570               *aline = newline;
571               gpgsm->colon.attic.linesize += linelen + 1;
572             }
573
574           dst = *aline + *alinelen;
575
576           while (src < end)
577             {
578               if (*src == '%' && src + 2 < end)
579                 {
580                   /* Handle escaped characters.  */
581                   ++src;
582                   *dst = xtoi_2 (src);
583                   (*alinelen)++;
584                   src += 2;
585                 }
586               else
587                 {
588                   *dst = *src++;
589                   (*alinelen)++;
590                 }
591
592               if (*dst == '\n')
593                 {
594                   /* Terminate the pending line, pass it to the colon
595                      handler and reset it.  */
596
597                   if (*alinelen > 1 && *(dst - 1) == '\r')
598                     dst--;
599                   *dst = '\0';
600
601                   /* FIXME How should we handle the return code? */
602                   gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
603                   dst = *aline;
604                   *alinelen = 0;
605                 }
606               else
607                 dst++;
608             }
609         }
610       else if (linelen > 2
611           && line[0] == 'S' && line[1] == ' ')
612         {
613           struct status_table_s t, *r;
614           char *rest;
615           
616           rest = strchr (line + 2, ' ');
617           if (!rest)
618             rest = line + linelen; /* set to an empty string */
619           else
620             *rest++ = 0;
621
622           t.name = line + 2;
623           r = bsearch (&t, status_table, DIM(status_table) - 1,
624                        sizeof t, status_cmp);
625
626           if (r)
627             {
628               if (gpgsm->status.fnc)
629                 gpgsm->status.fnc (gpgsm->status.fnc_value, r->code, rest);
630             }
631           else
632             fprintf (stderr, "[UNKNOWN STATUS]%s %s", t.name, rest);
633         }
634     }
635   while (assuan_pending_line (gpgsm->assuan_ctx));
636   
637   return 0;
638 }
639
640 void
641 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
642                                  GpgStatusHandler fnc, void *fnc_value) 
643 {
644   assert (gpgsm);
645
646   gpgsm->status.fnc = fnc;
647   gpgsm->status.fnc_value = fnc_value;
648 }
649
650 void
651 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
652                                      GpgColonLineHandler fnc, void *fnc_value) 
653 {
654   assert (gpgsm);
655
656   gpgsm->colon.fnc = fnc;
657   gpgsm->colon.fnc_value = fnc_value;
658 }
659
660 GpgmeError
661 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
662 {
663   GpgmeError err = 0;
664   pid_t pid;
665   int fdlist[5];
666   int nfds;
667
668   if (!gpgsm)
669     return mk_error (Invalid_Value);
670
671   pid = assuan_get_pid (gpgsm->assuan_ctx);
672
673   /* We need to know the fd used by assuan for reads.  We do this by
674      using the assumption that the first returned fd from
675      assuan_get_active_fds() is always this one. */
676   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
677                                 fdlist, DIM (fdlist));
678   if (nfds < 1)
679     return mk_error (General_Error);  /* FIXME */
680   err = _gpgme_register_pipe_handler (opaque, gpgsm_status_handler, gpgsm, pid,
681                                       fdlist[0], 1);
682
683
684   if (gpgsm->input_fd != -1)
685     {
686       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
687                                           gpgsm->input_data, pid,
688                                           gpgsm->input_fd, 0);
689       if (!err) /* FIXME Kludge around poll() problem.  */
690         err = _gpgme_io_set_nonblocking (gpgsm->input_fd);
691     }
692   if (!err && gpgsm->output_fd != -1)
693     err = _gpgme_register_pipe_handler (opaque, _gpgme_data_inbound_handler,
694                                         gpgsm->output_data, pid,
695                                         gpgsm->output_fd, 1);
696   if (!err && gpgsm->message_fd != -1)
697     {
698       err = _gpgme_register_pipe_handler (opaque, _gpgme_data_outbound_handler,
699                                           gpgsm->message_data, pid,
700                                           gpgsm->message_fd, 0);
701       if (!err) /* FIXME Kludge around poll() problem.  */
702         err = _gpgme_io_set_nonblocking (gpgsm->message_fd);
703     }
704
705   if (!err)
706     err = assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
707
708   return err;
709 }
710
711 #else   /* ENABLE_GPGSM */
712
713 #include <stddef.h>
714 #include "util.h"
715
716 #include "engine-gpgsm.h"
717
718 const char *
719 _gpgme_gpgsm_get_version (void)
720 {
721   return NULL;
722 }
723
724 GpgmeError
725 _gpgme_gpgsm_check_version (void)
726 {
727   return mk_error (Invalid_Engine);
728 }
729
730 GpgmeError
731 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
732 {
733   return mk_error (Invalid_Engine);
734 }
735
736 void
737 _gpgme_gpgsm_release (GpgsmObject gpgsm)
738 {
739   return;
740 }
741
742 void
743 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
744                                  GpgStatusHandler fnc, void *fnc_value) 
745 {
746   return;
747 }
748
749 GpgmeError
750 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
751 {
752   return mk_error (Invalid_Engine);
753 }
754
755 GpgmeError
756 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
757 {
758   return mk_error (Invalid_Engine);
759 }
760
761 GpgmeError
762 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
763                          GpgmeData plain, GpgmeData ciph, int use_armor)
764 {
765   return mk_error (Invalid_Engine);
766 }
767
768 GpgmeError
769 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
770                         GpgmeData keydata, int use_armor)
771 {
772   return mk_error (Invalid_Engine);
773 }
774
775 GpgmeError
776 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor)
777 {
778   return mk_error (Invalid_Engine);
779 }
780   
781 GpgmeError
782 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
783 {
784   return mk_error (Invalid_Engine);
785 }
786
787 GpgmeError
788 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
789                          int secret_only, int keylist_mode)
790 {
791   return mk_error (Invalid_Engine);
792 }
793
794 GpgmeError
795 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
796                       GpgmeSigMode mode, int use_armor,
797                       int use_textmode, GpgmeCtx ctx /* FIXME */)
798 {
799   return mk_error (Invalid_Engine);
800 }
801
802 GpgmeError
803 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
804 {
805   return mk_error (Invalid_Engine);
806 }
807
808 GpgmeError
809 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
810 {
811   return mk_error (Invalid_Engine);
812 }
813
814 void
815 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
816                                      GpgColonLineHandler fnc, void *fnc_value) 
817 {
818 }
819
820 GpgmeError
821 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
822 {
823   return mk_error (Invalid_Engine);
824 }
825
826 #endif  /* ! ENABLE_GPGSM */