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