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