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