2003-05-18 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     EngineStatusHandler fnc;
74     void *fnc_value;
75   } status;
76
77   struct
78   {
79     EngineColonLineHandler 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, NULL, NULL,
415                              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, NULL, NULL,
432                                  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, NULL, NULL, NULL,
492                                      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, EngineStatusHandler status_fnc,
545                              void *status_fnc_value)
546 {
547   AssuanError err;
548   char *line;
549   size_t linelen;
550
551   err = assuan_write_line (ctx, cmd);
552   if (err)
553     return map_assuan_error (err);
554
555   do
556     {
557       err = assuan_read_line (ctx, &line, &linelen);
558       if (err)
559         return map_assuan_error (err);
560
561       if (*line == '#' || !linelen)
562         continue;
563
564       if (linelen >= 2
565           && line[0] == 'O' && line[1] == 'K'
566           && (line[2] == '\0' || line[2] == ' '))
567         return 0;
568       else if (linelen >= 4
569           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
570           && line[3] == ' ')
571         err = map_assuan_error (atoi (&line[4]));
572       else if (linelen >= 2
573                && line[0] == 'S' && line[1] == ' ')
574         {
575           char *rest;
576           gpgme_status_code_t r;
577
578           rest = strchr (line + 2, ' ');
579           if (!rest)
580             rest = line + linelen; /* set to an empty string */
581           else
582             *(rest++) = 0;
583
584           r = parse_status (line + 2);
585
586           if (r >= 0 && status_fnc)
587             status_fnc (status_fnc_value, r, rest);
588           else
589             err = GPGME_General_Error;
590         }
591       else
592         err = GPGME_General_Error;
593     }
594   while (!err);
595
596   return err;
597 }
598
599
600 #define COMMANDLINELEN 40
601 static gpgme_error_t
602 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
603 {
604   char line[COMMANDLINELEN];
605
606   if (opt)
607     snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
608   else
609     snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
610
611   return gpgsm_assuan_simple_command (ctx, line, NULL, NULL);
612 }
613
614
615 static const char *
616 map_input_enc (gpgme_data_t d)
617 {
618   switch (gpgme_data_get_encoding (d))
619     {
620     case GPGME_DATA_ENCODING_NONE:
621       break;
622     case GPGME_DATA_ENCODING_BINARY:
623       return "--binary";
624     case GPGME_DATA_ENCODING_BASE64:
625       return "--base64";
626     case GPGME_DATA_ENCODING_ARMOR:
627       return "--armor";
628     default:
629       break;
630     }
631   return NULL;
632 }
633
634
635 static int
636 status_cmp (const void *ap, const void *bp)
637 {
638   const struct status_table_s *a = ap;
639   const struct status_table_s *b = bp;
640
641   return strcmp (a->name, b->name);
642 }
643
644
645 static gpgme_status_code_t
646 parse_status (const char *name)
647 {
648   struct status_table_s t, *r;
649   t.name = name;
650   r = bsearch (&t, status_table, DIM(status_table) - 1,
651                sizeof t, status_cmp);
652   return r ? r->code : -1;
653 }
654
655
656 static gpgme_error_t
657 status_handler (void *opaque, int fd)
658 {
659   AssuanError assuan_err;
660   gpgme_error_t err = 0;
661   GpgsmObject gpgsm = opaque;
662   char *line;
663   size_t linelen;
664
665   do
666     {
667       assuan_err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
668       if (assuan_err)
669         {
670           /* Try our best to terminate the connection friendly.  */
671           assuan_write_line (gpgsm->assuan_ctx, "BYE");
672           err = map_assuan_error (assuan_err);
673         }
674       else if (linelen >= 3
675                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
676                && (line[3] == '\0' || line[3] == ' '))
677         {
678           if (line[3] == ' ')
679             err = map_assuan_error (atoi (&line[4]));
680           else
681             err = GPGME_General_Error;
682         }
683       else if (linelen >= 2
684                && line[0] == 'O' && line[1] == 'K'
685                && (line[2] == '\0' || line[2] == ' '))
686         {
687           if (gpgsm->status.fnc)
688             err = gpgsm->status.fnc (gpgsm->status.fnc_value,
689                                      GPGME_STATUS_EOF, "");
690           
691           if (!err && gpgsm->colon.fnc && gpgsm->colon.any )
692             {
693               /* We must tell a colon function about the EOF. We do
694                  this only when we have seen any data lines.  Note
695                  that this inlined use of colon data lines will
696                  eventually be changed into using a regular data
697                  channel. */
698               gpgsm->colon.any = 0;
699               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
700             }
701           _gpgme_io_close (gpgsm->status_cb.fd);
702           return err;
703         }
704       else if (linelen > 2
705                && line[0] == 'D' && line[1] == ' '
706                && gpgsm->colon.fnc)
707         {
708           /* We are using the colon handler even for plain inline data
709              - strange name for that function but for historic reasons
710              we keep it.  */
711           /* FIXME We can't use this for binary data because we
712              assume this is a string.  For the current usage of colon
713              output it is correct.  */
714           unsigned char *src = line + 2;
715           unsigned char *end = line + linelen;
716           unsigned char *dst;
717           unsigned char **aline = &gpgsm->colon.attic.line;
718           int *alinelen = &gpgsm->colon.attic.linelen;
719
720           if (gpgsm->colon.attic.linesize
721               < *alinelen + linelen + 1)
722             {
723               unsigned char *newline = realloc (*aline,
724                                                 *alinelen + linelen + 1);
725               if (!newline)
726                 err = GPGME_Out_Of_Core;
727               else
728                 {
729                   *aline = newline;
730                   gpgsm->colon.attic.linesize += linelen + 1;
731                 }
732             }
733           if (!err)
734             {
735               dst = *aline + *alinelen;
736
737               while (!err && src < end)
738                 {
739                   if (*src == '%' && src + 2 < end)
740                     {
741                       /* Handle escaped characters.  */
742                       ++src;
743                       *dst = (unsigned char) _gpgme_hextobyte (src);
744                       (*alinelen)++;
745                       src += 2;
746                     }
747                   else
748                     {
749                       *dst = *src++;
750                       (*alinelen)++;
751                     }
752                   
753                   if (*dst == '\n')
754                     {
755                       /* Terminate the pending line, pass it to the colon
756                          handler and reset it.  */
757                       
758                       gpgsm->colon.any = 1;
759                       if (*alinelen > 1 && *(dst - 1) == '\r')
760                         dst--;
761                       *dst = '\0';
762
763                       /* FIXME How should we handle the return code?  */
764                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
765                       if (!err)
766                         {
767                           dst = *aline;
768                           *alinelen = 0;
769                         }
770                     }
771                   else
772                     dst++;
773                 }
774             }
775         }
776       else if (linelen > 2
777                && line[0] == 'S' && line[1] == ' ')
778         {
779           char *rest;
780           gpgme_status_code_t r;
781           
782           rest = strchr (line + 2, ' ');
783           if (!rest)
784             rest = line + linelen; /* set to an empty string */
785           else
786             *(rest++) = 0;
787
788           r = parse_status (line + 2);
789
790           if (r >= 0)
791             {
792               if (gpgsm->status.fnc)
793                 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
794             }
795           else
796             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
797         }
798     }
799   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
800           
801   return err;
802 }
803
804
805 static gpgme_error_t
806 add_io_cb (GpgsmObject gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
807 {
808   gpgme_error_t err;
809
810   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
811                               iocbd->fd, iocbd->dir,
812                               handler, iocbd->data, &iocbd->tag);
813   if (err)
814     return err;
815   if (!iocbd->dir)
816     /* FIXME Kludge around poll() problem.  */
817     err = _gpgme_io_set_nonblocking (iocbd->fd);
818   return err;
819 }
820
821
822 static gpgme_error_t
823 start (GpgsmObject gpgsm, const char *command)
824 {
825   gpgme_error_t err;
826
827   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
828   if (!err && gpgsm->input_cb.fd != -1)
829     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
830   if (!err && gpgsm->output_cb.fd != -1)
831     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
832   if (!err && gpgsm->message_cb.fd != -1)
833     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
834
835   if (!err)
836     err = assuan_write_line (gpgsm->assuan_ctx, command);
837
838   if (!err)
839     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, GPGME_EVENT_START, NULL);
840
841   return err;
842 }
843
844
845 static gpgme_error_t
846 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
847 {
848   GpgsmObject gpgsm = engine;
849   gpgme_error_t err;
850
851   if (!gpgsm)
852     return GPGME_Invalid_Value;
853
854   gpgsm->input_cb.data = ciph;
855   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server, 
856                       map_input_enc (gpgsm->input_cb.data));
857   if (err)
858     return GPGME_General_Error; /* FIXME */
859   gpgsm->output_cb.data = plain;
860   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server, 0);
861   if (err)
862     return GPGME_General_Error; /* FIXME */
863   _gpgme_io_close (gpgsm->message_cb.fd);
864
865   err = start (engine, "DECRYPT");
866   return err;
867 }
868
869
870 static gpgme_error_t
871 gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
872 {
873   GpgsmObject gpgsm = engine;
874   gpgme_error_t err;
875   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
876   char *linep = fpr;
877   char *line;
878   int length = 8;       /* "DELKEYS " */
879
880   if (!fpr)
881     return GPGME_Invalid_Key;
882
883   while (*linep)
884     {
885       length++;
886       if (*linep == '%' || *linep == ' ' || *linep == '+')
887         length += 2;
888       linep++;
889     }
890   length++;
891
892   line = malloc (length);
893   if (!line)
894     return GPGME_Out_Of_Core;
895
896   strcpy (line, "DELKEYS ");
897   linep = &line[8];
898
899   while (*fpr)
900     {
901       switch (*fpr)
902         {
903         case '%':
904           *(linep++) = '%';
905           *(linep++) = '2';
906           *(linep++) = '5';
907           break;
908         case ' ':
909           *(linep++) = '%';
910           *(linep++) = '2';
911           *(linep++) = '0';
912           break;
913         case '+':
914           *(linep++) = '%';
915           *(linep++) = '2';
916           *(linep++) = 'B';
917           break;
918         default:
919           *(linep++) = *fpr;
920           break;
921         }
922       fpr++;
923     }
924   *linep = '\0';
925
926   _gpgme_io_close (gpgsm->output_cb.fd);
927   _gpgme_io_close (gpgsm->input_cb.fd);
928   _gpgme_io_close (gpgsm->message_cb.fd);
929
930   err = start (gpgsm, line);
931   free (line);
932
933   return err;
934 }
935
936
937 static gpgme_error_t
938 set_recipients (GpgsmObject gpgsm, gpgme_recipients_t recp)
939 {
940   gpgme_error_t err;
941   ASSUAN_CONTEXT ctx = gpgsm->assuan_ctx;
942   char *line;
943   int linelen;
944   gpgme_user_id_t uid;
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   for (uid = recp->list; uid; uid = uid->next)
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     }
978   free (line);
979   return invalid_recipients ? GPGME_Invalid_UserID : 0;
980 }
981
982
983 static gpgme_error_t
984 gpgsm_encrypt (void *engine, gpgme_recipients_t recp, gpgme_data_t plain,
985                gpgme_data_t ciph, int use_armor)
986 {
987   GpgsmObject gpgsm = engine;
988   gpgme_error_t err;
989
990   if (!gpgsm)
991     return GPGME_Invalid_Value;
992   if (!recp)
993     return GPGME_Not_Implemented;
994
995   gpgsm->input_cb.data = plain;
996   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
997                       map_input_enc (gpgsm->input_cb.data));
998   if (err)
999     return err;
1000   gpgsm->output_cb.data = ciph;
1001   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1002                       use_armor ? "--armor" : 0);
1003   if (err)
1004     return err;
1005   _gpgme_io_close (gpgsm->message_cb.fd);
1006
1007   err = set_recipients (gpgsm, recp);
1008
1009   if (!err)
1010     err = start (gpgsm, "ENCRYPT");
1011
1012   return err;
1013 }
1014
1015
1016 static gpgme_error_t
1017 gpgsm_export (void *engine, gpgme_recipients_t recp, gpgme_data_t keydata,
1018               int use_armor)
1019 {
1020   GpgsmObject gpgsm = engine;
1021   gpgme_error_t err = 0;
1022   char *cmd = NULL;
1023   int cmdi;
1024   int cmdlen = 32;
1025
1026   if (!gpgsm)
1027     return GPGME_Invalid_Value;
1028
1029   cmd = malloc (cmdlen);
1030   if (!cmd)
1031     return GPGME_Out_Of_Core;
1032   strcpy (cmd, "EXPORT");
1033   cmdi = 6;
1034
1035   if (recp)
1036     {
1037       void *ec;
1038       const char *s;
1039
1040       err = gpgme_recipients_enum_open (recp, &ec);
1041       while (!err && (s = gpgme_recipients_enum_read (recp, &ec)))
1042         {
1043           int slen = strlen (s);
1044           /* New string is old string + ' ' + s + '\0'.  */
1045           if (cmdlen < cmdi + 1 + slen + 1)
1046             {
1047               char *newcmd = realloc (cmd, cmdlen * 2);
1048               if (!newcmd)
1049                 {
1050                   free (cmd);
1051                   return GPGME_Out_Of_Core;
1052                 }
1053               cmd = newcmd;
1054               cmdlen *= 2;
1055             }
1056           cmd[cmdi++] = ' ';
1057           strcpy (cmd + cmdi, s);
1058           cmdi += slen;
1059         }
1060       if (!err)
1061         err = gpgme_recipients_enum_close (recp, &ec);
1062       if (err)
1063         return err;
1064     }
1065
1066   gpgsm->output_cb.data = keydata;
1067   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1068                       use_armor ? "--armor" : 0);
1069   if (err)
1070     return err;
1071   _gpgme_io_close (gpgsm->input_cb.fd);
1072   _gpgme_io_close (gpgsm->message_cb.fd);
1073
1074   err = start (gpgsm, cmd);
1075   free (cmd);
1076   return err;
1077 }
1078
1079
1080 static gpgme_error_t
1081 gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1082               gpgme_data_t pubkey, gpgme_data_t seckey)
1083 {
1084   GpgsmObject gpgsm = engine;
1085   gpgme_error_t err;
1086
1087   if (!gpgsm || !pubkey || seckey)
1088     return GPGME_Invalid_Value;
1089
1090   gpgsm->input_cb.data = help_data;
1091   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1092                       map_input_enc (gpgsm->input_cb.data));
1093   if (err)
1094     return err;
1095   gpgsm->output_cb.data = pubkey;
1096   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1097                       use_armor ? "--armor" : 0);
1098   if (err)
1099     return err;
1100   _gpgme_io_close (gpgsm->message_cb.fd);
1101
1102   err = start (gpgsm, "GENKEY");
1103   return err;
1104 }
1105
1106
1107 static gpgme_error_t
1108 gpgsm_import (void *engine, gpgme_data_t keydata)
1109 {
1110   GpgsmObject gpgsm = engine;
1111   gpgme_error_t err;
1112
1113   if (!gpgsm)
1114     return GPGME_Invalid_Value;
1115
1116   gpgsm->input_cb.data = keydata;
1117   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1118                       map_input_enc (gpgsm->input_cb.data));
1119   if (err)
1120     return err;
1121   _gpgme_io_close (gpgsm->output_cb.fd);
1122   _gpgme_io_close (gpgsm->message_cb.fd);
1123
1124   err = start (gpgsm, "IMPORT");
1125   return err;
1126 }
1127
1128
1129 static gpgme_error_t
1130 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1131                int keylist_mode)
1132 {
1133   GpgsmObject gpgsm = engine;
1134   char *line;
1135   gpgme_error_t err;
1136
1137   if (!pattern)
1138     pattern = "";
1139
1140   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1141     return GPGME_Out_Of_Core;
1142   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1143   free (line);
1144   if (err)
1145     return err;
1146
1147   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1148   line = malloc (15 + strlen (pattern) + 1);
1149   if (!line)
1150     return GPGME_Out_Of_Core;
1151   if (secret_only)
1152     {
1153       strcpy (line, "LISTSECRETKEYS ");
1154       strcpy (&line[15], pattern);
1155     }
1156   else
1157     {
1158       strcpy (line, "LISTKEYS ");
1159       strcpy (&line[9], pattern);
1160     }
1161
1162   _gpgme_io_close (gpgsm->input_cb.fd);
1163   _gpgme_io_close (gpgsm->output_cb.fd);
1164   _gpgme_io_close (gpgsm->message_cb.fd);
1165
1166   err = start (gpgsm, line);
1167   free (line);
1168   return err;
1169 }
1170
1171
1172 static gpgme_error_t
1173 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1174                    int reserved, int keylist_mode)
1175 {
1176   GpgsmObject gpgsm = engine;
1177   char *line;
1178   gpgme_error_t err;
1179   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1180   int length = 15 + 1;
1181   char *linep;
1182   
1183   if (reserved)
1184     return GPGME_Invalid_Value;
1185
1186   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1187     return GPGME_Out_Of_Core;
1188   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1189   free (line);
1190   if (err)
1191     return err;
1192
1193   if (pattern && *pattern)
1194     {
1195       const char **pat = pattern;
1196
1197       while (*pat)
1198         {
1199           const char *patlet = *pat;
1200
1201           while (*patlet)
1202             {
1203               length++;
1204               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1205                 length += 2;
1206               patlet++;
1207             }
1208           pat++;
1209           /* This will allocate one byte more than necessary.  */
1210           length++;
1211         }
1212     }
1213   line = malloc (length);
1214   if (!line)
1215     return GPGME_Out_Of_Core;
1216   if (secret_only)
1217     {
1218       strcpy (line, "LISTSECRETKEYS ");
1219       linep = &line[15];
1220     }
1221   else
1222     {
1223       strcpy (line, "LISTKEYS ");
1224       linep = &line[9];
1225     }
1226
1227   if (pattern && *pattern)
1228     {
1229       while (*pattern)
1230         {
1231           const char *patlet = *pattern;
1232
1233           while (*patlet)
1234             {
1235               switch (*patlet)
1236                 {
1237                 case '%':
1238                   *(linep++) = '%';
1239                   *(linep++) = '2';
1240                   *(linep++) = '5';
1241                   break;
1242                 case ' ':
1243                   *(linep++) = '%';
1244                   *(linep++) = '2';
1245                   *(linep++) = '0';
1246                   break;
1247                 case '+':
1248                   *(linep++) = '%';
1249                   *(linep++) = '2';
1250                   *(linep++) = 'B';
1251                   break;
1252                 default:
1253                   *(linep++) = *patlet;
1254                   break;
1255                 }
1256               patlet++;
1257             }
1258           pattern++;
1259         }
1260     }
1261   *linep = '\0';
1262
1263   _gpgme_io_close (gpgsm->input_cb.fd);
1264   _gpgme_io_close (gpgsm->output_cb.fd);
1265   _gpgme_io_close (gpgsm->message_cb.fd);
1266
1267   err = start (gpgsm, line);
1268   free (line);
1269   return err;
1270 }
1271
1272
1273 static gpgme_error_t
1274 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode,
1275             int use_armor, int use_textmode, int include_certs,
1276             gpgme_ctx_t ctx /* FIXME */)
1277 {
1278   GpgsmObject gpgsm = engine;
1279   gpgme_error_t err;
1280   char *assuan_cmd;
1281   int i;
1282   gpgme_key_t key;
1283
1284   if (!gpgsm)
1285     return GPGME_Invalid_Value;
1286
1287   if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1288     return GPGME_Out_Of_Core;
1289   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL,NULL);
1290   free (assuan_cmd);
1291   if (err)
1292     return err;
1293
1294   /* We must do a reset becuase we need to reset the list of signers.  Note
1295      that RESET does not reset OPTION commands. */
1296   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1297   if (err)
1298     return err;
1299
1300   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1301     {
1302       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1303       if (s && strlen (s) < 80)
1304         {
1305           char buf[100];
1306
1307           strcpy (stpcpy (buf, "SIGNER "), s);
1308           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1309                                              NULL, NULL);
1310         }
1311       else
1312         err = GPGME_Invalid_Key;
1313       gpgme_key_unref (key);
1314       if (err) 
1315         return err;
1316     }
1317
1318   gpgsm->input_cb.data = in;
1319   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1320                       map_input_enc (gpgsm->input_cb.data));
1321   if (err)
1322     return err;
1323   gpgsm->output_cb.data = out;
1324   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1325                       use_armor ? "--armor" : 0);
1326   if (err)
1327     return err;
1328   _gpgme_io_close (gpgsm->message_cb.fd);
1329
1330   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1331                ? "SIGN --detached" : "SIGN");
1332   return err;
1333 }
1334
1335
1336 static gpgme_error_t
1337 gpgsm_trustlist (void *engine, const char *pattern)
1338 {
1339   /* FIXME */
1340   return GPGME_Not_Implemented;
1341 }
1342
1343
1344 static gpgme_error_t
1345 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1346               gpgme_data_t plaintext)
1347 {
1348   GpgsmObject gpgsm = engine;
1349   gpgme_error_t err;
1350
1351   if (!gpgsm)
1352     return GPGME_Invalid_Value;
1353
1354   gpgsm->input_cb.data = sig;
1355   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1356                       map_input_enc (gpgsm->input_cb.data));
1357   if (err)
1358     return err;
1359   if (plaintext)
1360     {
1361       /* Normal or cleartext signature.  */
1362       gpgsm->output_cb.data = plaintext;
1363       err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1364                           0);
1365       _gpgme_io_close (gpgsm->message_cb.fd);
1366     }
1367   else
1368     {
1369       /* Detached signature.  */
1370       gpgsm->message_cb.data = signed_text;
1371       err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1372                           gpgsm->message_fd_server, 0);
1373       _gpgme_io_close (gpgsm->output_cb.fd);
1374     }
1375
1376   if (!err)
1377     err = start (gpgsm, "VERIFY");
1378
1379   return err;
1380 }
1381
1382
1383 static void
1384 gpgsm_set_status_handler (void *engine, EngineStatusHandler fnc,
1385                           void *fnc_value) 
1386 {
1387   GpgsmObject gpgsm = engine;
1388
1389   gpgsm->status.fnc = fnc;
1390   gpgsm->status.fnc_value = fnc_value;
1391 }
1392
1393
1394 static gpgme_error_t
1395 gpgsm_set_colon_line_handler (void *engine, EngineColonLineHandler fnc,
1396                               void *fnc_value) 
1397 {
1398   GpgsmObject gpgsm = engine;
1399
1400   gpgsm->colon.fnc = fnc;
1401   gpgsm->colon.fnc_value = fnc_value;
1402   gpgsm->colon.any = 0;
1403   return 0;
1404 }
1405
1406
1407 static void
1408 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1409 {
1410   GpgsmObject gpgsm = engine;
1411   gpgsm->io_cbs = *io_cbs;
1412 }
1413
1414
1415 static void
1416 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1417 {
1418   GpgsmObject gpgsm = engine;
1419
1420   if (gpgsm->io_cbs.event)
1421     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1422 }
1423
1424
1425 struct engine_ops _gpgme_engine_ops_gpgsm =
1426   {
1427     /* Static functions.  */
1428     _gpgme_get_gpgsm_path,
1429     gpgsm_get_version,
1430     gpgsm_get_req_version,
1431     gpgsm_new,
1432
1433     /* Member functions.  */
1434     gpgsm_release,
1435     gpgsm_set_status_handler,
1436     NULL,               /* set_command_handler */
1437     gpgsm_set_colon_line_handler,
1438     gpgsm_decrypt,
1439     gpgsm_delete,
1440     NULL,               /* edit */
1441     gpgsm_encrypt,
1442     NULL,
1443     gpgsm_export,
1444     gpgsm_genkey,
1445     gpgsm_import,
1446     gpgsm_keylist,
1447     gpgsm_keylist_ext,
1448     gpgsm_sign,
1449     gpgsm_trustlist,
1450     gpgsm_verify,
1451     gpgsm_set_io_cbs,
1452     gpgsm_io_event
1453   };