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