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