* engine-gpgsm.c (_gpgme_gpgsm_op_sign): Implement signer
[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
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 <unistd.h>
38 #include <locale.h>
39 #include <fcntl.h> /* FIXME */
40
41 #include "rungpg.h"
42 #include "status-table.h"
43
44 #include "gpgme.h"
45 #include "util.h"
46 #include "types.h"
47 #include "ops.h"
48 #include "wait.h"
49 #include "io.h"
50 #include "key.h"
51 #include "sema.h"
52
53 #include "engine-gpgsm.h"
54
55 #include "assuan.h"
56
57 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
58                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
59 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
60
61
62
63 typedef struct
64 {
65   int fd;       /* FD we talk about.  */
66   int dir;      /* Inbound/Outbound, maybe given implicit?  */
67   void *data;   /* Handler-specific data.  */
68   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
69 } iocb_data_t;
70
71 struct gpgsm_object_s
72 {
73   ASSUAN_CONTEXT assuan_ctx;
74
75   iocb_data_t status_cb;
76
77   /* Input, output etc are from the servers perspective.  */
78   iocb_data_t input_cb;
79   int input_fd_server;
80
81   iocb_data_t output_cb;
82   int output_fd_server;
83
84   iocb_data_t message_cb;
85   int message_fd_server;
86
87   char *command;
88
89   struct
90   {
91     GpgStatusHandler fnc;
92     void *fnc_value;
93   } status;
94
95   struct
96   {
97     GpgColonLineHandler fnc;
98     void *fnc_value;
99     struct
100     {
101       unsigned char *line;
102       int linesize;
103       int linelen;
104     } attic;
105   } colon; 
106
107   struct GpgmeIOCbs io_cbs;
108 };
109
110
111 const char *
112 _gpgme_gpgsm_get_version (void)
113 {
114   static const char *gpgsm_version;
115   DEFINE_STATIC_LOCK (gpgsm_version_lock);
116
117   LOCK (gpgsm_version_lock);
118   if (!gpgsm_version)
119     gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
120   UNLOCK (gpgsm_version_lock);
121
122   return gpgsm_version;
123 }
124
125
126 GpgmeError
127 _gpgme_gpgsm_check_version (void)
128 {
129     return _gpgme_compare_versions (_gpgme_gpgsm_get_version (),
130                                   NEED_GPGSM_VERSION)
131     ? 0 : mk_error (Invalid_Engine);
132 }
133
134
135 static void
136 close_notify_handler (int fd, void *opaque)
137 {
138   GpgsmObject gpgsm = opaque;
139   int possibly_done = 0;
140
141   assert (fd != -1);
142   if (gpgsm->status_cb.fd == fd)
143     {
144       if (gpgsm->status_cb.tag)
145         {
146           (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
147           possibly_done = 1;
148         }
149       gpgsm->status_cb.fd = -1;
150     }
151   else if (gpgsm->input_cb.fd == fd)
152     {
153       if (gpgsm->input_cb.tag)
154         {
155           (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
156           possibly_done = 1;
157         }
158       gpgsm->input_cb.fd = -1;
159     }
160   else if (gpgsm->output_cb.fd == fd)
161     {
162       if (gpgsm->output_cb.tag)
163         {      
164           (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
165           possibly_done = 1;
166         }
167       gpgsm->output_cb.fd = -1;
168     }
169   else if (gpgsm->message_cb.fd == fd)
170     {
171       if (gpgsm->message_cb.tag)
172         {
173           (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
174           possibly_done = 1;
175         }
176       gpgsm->message_cb.fd = -1;
177     }
178   if (possibly_done && gpgsm->io_cbs.event
179       && gpgsm->status_cb.fd == -1 && gpgsm->input_cb.fd == -1
180       && gpgsm->output_cb.fd == -1 && gpgsm->message_cb.fd == -1)
181     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, GPGME_EVENT_DONE, NULL);
182 }
183
184
185 static GpgmeError
186 map_assuan_error (AssuanError err)
187 {
188   switch (err)
189     {
190     case ASSUAN_No_Error:
191       return mk_error (No_Error);
192     case ASSUAN_General_Error:
193       return mk_error (General_Error);
194     case ASSUAN_Out_Of_Core:
195       return mk_error (Out_Of_Core);
196     case ASSUAN_Invalid_Value:
197       return mk_error (Invalid_Value);
198     case ASSUAN_Read_Error:
199       return mk_error (Read_Error);
200     case ASSUAN_Write_Error:
201       return mk_error (Write_Error);
202
203     case ASSUAN_Timeout:
204     case ASSUAN_Problem_Starting_Server:
205     case ASSUAN_Not_A_Server:
206     case ASSUAN_Not_A_Client:
207     case ASSUAN_Nested_Commands:
208     case ASSUAN_Invalid_Response:
209     case ASSUAN_No_Data_Callback:
210     case ASSUAN_No_Inquire_Callback:
211     case ASSUAN_Connect_Failed:
212     case ASSUAN_Accept_Failed:
213       return mk_error (General_Error);
214
215       /* The following error codes are meant as status codes.  */
216     case ASSUAN_Not_Implemented:
217       return mk_error (Not_Implemented);
218     case ASSUAN_Canceled:
219       return mk_error (Canceled);
220     case ASSUAN_Unsupported_Algorithm:
221       return mk_error (Not_Implemented);  /* XXX Argh.  */
222       
223     case ASSUAN_No_Data_Available:
224       return mk_error (EOF);
225       
226       /* These are errors internal to GPGME.  */
227     case ASSUAN_No_Input:
228     case ASSUAN_No_Output:
229     case ASSUAN_Invalid_Command:
230     case ASSUAN_Unknown_Command:
231     case ASSUAN_Syntax_Error:
232     case ASSUAN_Parameter_Error:
233     case ASSUAN_Parameter_Conflict:
234     case ASSUAN_Line_Too_Long:
235     case ASSUAN_Line_Not_Terminated:
236     case ASSUAN_Invalid_Data:
237     case ASSUAN_Unexpected_Command:
238     case ASSUAN_Too_Much_Data:
239     case ASSUAN_Inquire_Unknown:
240     case ASSUAN_Inquire_Error:
241     case ASSUAN_Invalid_Option:
242     case ASSUAN_Invalid_Index:
243     case ASSUAN_Unexpected_Status:
244     case ASSUAN_Unexpected_Data:
245     case ASSUAN_Invalid_Status:
246     case ASSUAN_Not_Confirmed:
247       return mk_error (General_Error);
248
249       /* These are errors in the server.  */
250     case ASSUAN_Server_Fault:
251     case ASSUAN_Server_Resource_Problem:
252     case ASSUAN_Server_IO_Error:
253     case ASSUAN_Server_Bug:
254     case ASSUAN_No_Agent:
255     case ASSUAN_Agent_Error:
256       return mk_error (Invalid_Engine);  /* XXX:  Need something more useful.  */
257
258     case ASSUAN_Bad_Certificate:
259     case ASSUAN_Bad_Certificate_Path:
260     case ASSUAN_Missing_Certificate:
261     case ASSUAN_No_Public_Key:
262     case ASSUAN_No_Secret_Key:
263     case ASSUAN_Invalid_Name:
264     case ASSUAN_Card_Error:     /* XXX: Oh well.  */
265     case ASSUAN_Invalid_Card:   /* XXX: Oh well.  */
266     case ASSUAN_No_PKCS15_App:  /* XXX: Oh well.  */
267     case ASSUAN_Card_Not_Present:       /* XXX: Oh well.  */
268     case ASSUAN_Invalid_Id:     /* XXX: Oh well.  */
269       return mk_error (Invalid_Key);
270
271     case ASSUAN_Bad_Signature:
272       return mk_error (Invalid_Key);  /* XXX: This is wrong.  */
273
274     case ASSUAN_Cert_Revoked:
275     case ASSUAN_No_CRL_For_Cert:
276     case ASSUAN_CRL_Too_Old:
277     case ASSUAN_Not_Trusted:
278       return mk_error (Invalid_Key);  /* XXX Some more details would be good.  */
279
280     default:
281       return mk_error (General_Error);
282     }
283 }
284
285
286 GpgmeError
287 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
288 {
289   GpgmeError err = 0;
290   GpgsmObject gpgsm;
291   char *argv[3];
292   int fds[2];
293   int child_fds[4];
294   char *dft_display = NULL;
295   char *dft_ttyname = NULL;
296   char *dft_ttytype = NULL;
297   char *old_lc = NULL;
298   char *dft_lc = NULL;
299   char *optstr;
300   int fdlist[5];
301   int nfds;
302
303   *r_gpgsm = NULL;
304   gpgsm = xtrycalloc (1, sizeof *gpgsm);
305   if (!gpgsm)
306     {
307       err = mk_error (Out_Of_Core);
308       return err;
309     }
310
311   gpgsm->status_cb.fd = -1;
312   gpgsm->status_cb.tag = 0;
313
314   gpgsm->input_cb.fd = -1;
315   gpgsm->input_cb.tag = 0;
316   gpgsm->input_fd_server = -1;
317   gpgsm->output_cb.fd = -1;
318   gpgsm->output_cb.tag = 0;
319   gpgsm->output_fd_server = -1;
320   gpgsm->message_cb.fd = -1;
321   gpgsm->message_cb.tag = 0;
322   gpgsm->message_fd_server = -1;
323
324   gpgsm->status.fnc = 0;
325   gpgsm->colon.fnc = 0;
326   gpgsm->colon.attic.line = 0;
327   gpgsm->colon.attic.linesize = 0;
328   gpgsm->colon.attic.linelen = 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   xfree (gpgsm->colon.attic.line);
543   xfree (gpgsm->command);
544   xfree (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, GpgStatusHandler 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 = xtrystrdup ("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 = xtrymalloc (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 = xtrymalloc (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 = xtryrealloc (line, newlen);
753           if (! newline)
754             {
755               xfree (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           xfree (line);
770           return err;
771         }
772     }
773   xfree (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 = xtrystrdup ("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 = xtryrealloc (cmd, cmdlen * 2);
846               if (!newcmd)
847                 {
848                   xfree (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 = xtrystrdup ("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 = xtrystrdup ("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 = xtrymalloc (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 = xtrymalloc (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 = xtrystrdup (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 text)
1148 {
1149   GpgmeError err;
1150
1151   if (!gpgsm)
1152     return mk_error (Invalid_Value);
1153
1154   gpgsm->command = xtrystrdup ("VERIFY");
1155   if (!gpgsm->command)
1156     return mk_error (Out_Of_Core);
1157
1158   gpgsm->input_cb.data = sig;
1159   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1160                       map_input_enc (gpgsm->input_cb.data));
1161   if (err)
1162     return err;
1163   if (_gpgme_data_get_mode (text) == GPGME_DATA_MODE_IN)
1164     {
1165       /* Normal or cleartext signature.  */
1166       gpgsm->output_cb.data = text;
1167       err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1168                           0);
1169       _gpgme_io_close (gpgsm->message_cb.fd);
1170     }
1171   else
1172     {
1173       /* Detached signature.  */
1174       gpgsm->message_cb.data = text;
1175       err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1176                           gpgsm->message_fd_server, 0);
1177       _gpgme_io_close (gpgsm->output_cb.fd);
1178     }
1179   if (err)
1180     return err;
1181
1182   return 0;
1183 }
1184
1185
1186 static int
1187 status_cmp (const void *ap, const void *bp)
1188 {
1189   const struct status_table_s *a = ap;
1190   const struct status_table_s *b = bp;
1191
1192   return strcmp (a->name, b->name);
1193 }
1194
1195
1196 static GpgmeStatusCode
1197 parse_status (const char *name)
1198 {
1199   struct status_table_s t, *r;
1200   t.name = name;
1201   r = bsearch (&t, status_table, DIM(status_table) - 1,
1202                sizeof t, status_cmp);
1203   return r ? r->code : -1;
1204 }
1205
1206
1207 static void
1208 gpgsm_status_handler (void *opaque, int fd)
1209 {
1210   AssuanError err;
1211   GpgsmObject gpgsm = opaque;
1212   char *line;
1213   size_t linelen;
1214
1215   do
1216     {
1217       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
1218
1219       if (err
1220           || (linelen >= 2
1221               && line[0] == 'O' && line[1] == 'K'
1222               && (line[2] == '\0' || line[2] == ' '))
1223           || (linelen >= 3
1224               && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
1225               && (line[3] == '\0' || line[3] == ' ')))
1226         {
1227           /* XXX: If an error occured, find out what happened, then
1228              save the error value before running the status handler
1229              (so it takes precedence).  */
1230           if (!err && line[0] == 'E' && line[3] == ' ')
1231             {
1232               err = map_assuan_error (atoi (&line[4]));
1233               if (!err)
1234                 err = mk_error (General_Error);
1235             }
1236           if (err)
1237             {
1238               /* XXX Kludge ahead.  We really, really, really must not
1239                  make use of status.fnc_value.  */
1240               GpgmeCtx ctx = (GpgmeCtx) gpgsm->status.fnc_value;
1241               if (!ctx->error)
1242                 ctx->error = err;
1243             }
1244
1245           if (gpgsm->status.fnc)
1246             gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_EOF, "");
1247
1248           /* XXX: Try our best to terminate the connection.  */
1249           if (err)
1250             assuan_write_line (gpgsm->assuan_ctx, "BYE");
1251
1252           _gpgme_io_close (gpgsm->status_cb.fd);
1253           return;
1254         }
1255
1256       if (linelen > 2
1257           && line[0] == 'D' && line[1] == ' '
1258           && gpgsm->colon.fnc)
1259         {
1260           /* We are using the colon handler even for plain inline data
1261              - strange name for that function but for historic reasons
1262              we keep it.  */
1263           /* FIXME We can't use this for binary data because we
1264              assume this is a string.  For the current usage of colon
1265              output it is correct.  */
1266           unsigned char *src = line + 2;
1267           unsigned char *end = line + linelen;
1268           unsigned char *dst;
1269           unsigned char **aline = &gpgsm->colon.attic.line;
1270           int *alinelen = &gpgsm->colon.attic.linelen;
1271
1272           if (gpgsm->colon.attic.linesize
1273               < *alinelen + linelen + 1)
1274             {
1275               unsigned char *newline = xtryrealloc (*aline,
1276                                                     *alinelen + linelen + 1);
1277               if (!newline)
1278                 {
1279                   _gpgme_io_close (gpgsm->status_cb.fd);
1280                   return;
1281                 }
1282               *aline = newline;
1283               gpgsm->colon.attic.linesize += linelen + 1;
1284             }
1285
1286           dst = *aline + *alinelen;
1287
1288           while (src < end)
1289             {
1290               if (*src == '%' && src + 2 < end)
1291                 {
1292                   /* Handle escaped characters.  */
1293                   ++src;
1294                   *dst = xtoi_2 (src);
1295                   (*alinelen)++;
1296                   src += 2;
1297                 }
1298               else
1299                 {
1300                   *dst = *src++;
1301                   (*alinelen)++;
1302                 }
1303
1304               if (*dst == '\n')
1305                 {
1306                   /* Terminate the pending line, pass it to the colon
1307                      handler and reset it.  */
1308
1309                   if (*alinelen > 1 && *(dst - 1) == '\r')
1310                     dst--;
1311                   *dst = '\0';
1312
1313                   /* FIXME How should we handle the return code? */
1314                   gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
1315                   dst = *aline;
1316                   *alinelen = 0;
1317                 }
1318               else
1319                 dst++;
1320             }
1321         }
1322       else if (linelen > 2
1323           && line[0] == 'S' && line[1] == ' ')
1324         {
1325           char *rest;
1326           GpgmeStatusCode r;
1327
1328           rest = strchr (line + 2, ' ');
1329           if (!rest)
1330             rest = line + linelen; /* set to an empty string */
1331           else
1332             *(rest++) = 0;
1333
1334           r = parse_status (line + 2);
1335
1336           if (r >= 0)
1337             {
1338               if (gpgsm->status.fnc)
1339                 gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
1340             }
1341           else
1342             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
1343         }
1344     }
1345   while (assuan_pending_line (gpgsm->assuan_ctx));
1346 }
1347
1348
1349 void
1350 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
1351                                  GpgStatusHandler fnc, void *fnc_value) 
1352 {
1353   assert (gpgsm);
1354
1355   gpgsm->status.fnc = fnc;
1356   gpgsm->status.fnc_value = fnc_value;
1357 }
1358
1359
1360 void
1361 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
1362                                      GpgColonLineHandler fnc, void *fnc_value) 
1363 {
1364   assert (gpgsm);
1365
1366   gpgsm->colon.fnc = fnc;
1367   gpgsm->colon.fnc_value = fnc_value;
1368 }
1369
1370
1371 static GpgmeError
1372 _gpgme_gpgsm_add_io_cb (GpgsmObject gpgsm, iocb_data_t *iocbd,
1373                         GpgmeIOCb handler)
1374 {
1375   GpgmeError err;
1376
1377   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
1378                               iocbd->fd, iocbd->dir,
1379                               handler, iocbd->data, &iocbd->tag);
1380   if (err)
1381     return err;
1382   if (!iocbd->dir)
1383     /* FIXME Kludge around poll() problem.  */
1384     err = _gpgme_io_set_nonblocking (iocbd->fd);
1385   return err;
1386 }
1387
1388 GpgmeError
1389 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
1390 {
1391   GpgmeError err = 0;
1392   pid_t pid;
1393
1394   if (!gpgsm)
1395     return mk_error (Invalid_Value);
1396
1397   pid = assuan_get_pid (gpgsm->assuan_ctx);
1398
1399   err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->status_cb,
1400                                 gpgsm_status_handler);
1401   if (gpgsm->input_cb.fd != -1)
1402     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->input_cb,
1403                                   _gpgme_data_outbound_handler);
1404   if (!err && gpgsm->output_cb.fd != -1)
1405     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->output_cb,
1406                                   _gpgme_data_inbound_handler);
1407   if (!err && gpgsm->message_cb.fd != -1)
1408     err = _gpgme_gpgsm_add_io_cb (gpgsm, &gpgsm->message_cb,
1409                                   _gpgme_data_outbound_handler);
1410
1411   if (!err)
1412     err = assuan_write_line (gpgsm->assuan_ctx, gpgsm->command);
1413
1414   return err;
1415 }
1416
1417 void
1418 _gpgme_gpgsm_set_io_cbs (GpgsmObject gpgsm, struct GpgmeIOCbs *io_cbs)
1419 {
1420   gpgsm->io_cbs = *io_cbs;
1421 }
1422
1423 void
1424 _gpgme_gpgsm_io_event (GpgsmObject gpgsm, GpgmeEventIO type, void *type_data)
1425 {
1426   if (gpgsm->io_cbs.event)
1427     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1428 }
1429
1430 #else   /* ENABLE_GPGSM */
1431
1432
1433 #include <stddef.h>
1434 #include "util.h"
1435
1436 #include "engine-gpgsm.h"
1437
1438
1439 const char *
1440 _gpgme_gpgsm_get_version (void)
1441 {
1442   return NULL;
1443 }
1444
1445
1446 GpgmeError
1447 _gpgme_gpgsm_check_version (void)
1448 {
1449   return mk_error (Invalid_Engine);
1450 }
1451
1452
1453 GpgmeError
1454 _gpgme_gpgsm_new (GpgsmObject *r_gpgsm)
1455 {
1456   return mk_error (Invalid_Engine);
1457 }
1458
1459
1460 void
1461 _gpgme_gpgsm_release (GpgsmObject gpgsm)
1462 {
1463   return;
1464 }
1465
1466
1467 void
1468 _gpgme_gpgsm_set_status_handler (GpgsmObject gpgsm,
1469                                  GpgStatusHandler fnc, void *fnc_value) 
1470 {
1471   return;
1472 }
1473
1474
1475 GpgmeError
1476 _gpgme_gpgsm_op_decrypt (GpgsmObject gpgsm, GpgmeData ciph, GpgmeData plain)
1477 {
1478   return mk_error (Invalid_Engine);
1479 }
1480
1481
1482 GpgmeError
1483 _gpgme_gpgsm_op_delete (GpgsmObject gpgsm, GpgmeKey key, int allow_secret)
1484 {
1485   return mk_error (Invalid_Engine);
1486 }
1487
1488
1489 GpgmeError
1490 _gpgme_gpgsm_op_encrypt (GpgsmObject gpgsm, GpgmeRecipients recp,
1491                          GpgmeData plain, GpgmeData ciph, int use_armor)
1492 {
1493   return mk_error (Invalid_Engine);
1494 }
1495
1496
1497 GpgmeError
1498 _gpgme_gpgsm_op_export (GpgsmObject gpgsm, GpgmeRecipients recp,
1499                         GpgmeData keydata, int use_armor)
1500 {
1501   return mk_error (Invalid_Engine);
1502 }
1503
1504
1505 GpgmeError
1506 _gpgme_gpgsm_op_genkey (GpgsmObject gpgsm, GpgmeData help_data, int use_armor,
1507                         GpgmeData pubkey, GpgmeData seckey)
1508 {
1509   return mk_error (Invalid_Engine);
1510 }
1511   
1512
1513 GpgmeError
1514 _gpgme_gpgsm_op_import (GpgsmObject gpgsm, GpgmeData keydata)
1515 {
1516   return mk_error (Invalid_Engine);
1517 }
1518
1519
1520 GpgmeError
1521 _gpgme_gpgsm_op_keylist (GpgsmObject gpgsm, const char *pattern,
1522                          int secret_only, int keylist_mode)
1523 {
1524   return mk_error (Invalid_Engine);
1525 }
1526
1527
1528 GpgmeError
1529 _gpgme_gpgsm_op_keylist_ext (GpgsmObject gpgsm, const char *pattern[],
1530                              int secret_only, int reserved, int keylist_mode)
1531 {
1532   return mk_error (Invalid_Engine);
1533 }
1534
1535 GpgmeError
1536 _gpgme_gpgsm_op_sign (GpgsmObject gpgsm, GpgmeData in, GpgmeData out,
1537                       GpgmeSigMode mode, int use_armor,
1538                       int use_textmode, int include_certs,
1539                       GpgmeCtx ctx /* FIXME */)
1540 {
1541   return mk_error (Invalid_Engine);
1542 }
1543
1544
1545 GpgmeError
1546 _gpgme_gpgsm_op_trustlist (GpgsmObject gpgsm, const char *pattern)
1547 {
1548   return mk_error (Invalid_Engine);
1549 }
1550
1551
1552 GpgmeError
1553 _gpgme_gpgsm_op_verify (GpgsmObject gpgsm, GpgmeData sig, GpgmeData text)
1554 {
1555   return mk_error (Invalid_Engine);
1556 }
1557
1558
1559 void
1560 _gpgme_gpgsm_set_colon_line_handler (GpgsmObject gpgsm,
1561                                      GpgColonLineHandler fnc, void *fnc_value) 
1562 {
1563 }
1564
1565
1566 GpgmeError
1567 _gpgme_gpgsm_start (GpgsmObject gpgsm, void *opaque)
1568 {
1569   return mk_error (Invalid_Engine);
1570 }
1571
1572 void
1573 _gpgme_gpgsm_set_io_cbs (GpgsmObject gpgsm, struct GpgmeIOCbs *io_cbs)
1574 {
1575 }
1576
1577 void
1578 _gpgme_gpgsm_io_event (GpgsmObject gpgsm, GpgmeEventIO type, void *type_data)
1579 {
1580 }
1581
1582 #endif  /* ! ENABLE_GPGSM */