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