2003-02-01 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 "key.h"
39 #include "sema.h"
40
41 #include "assuan.h"
42 #include "status-table.h"
43
44 #include "engine-backend.h"
45
46 \f
47 typedef struct
48 {
49   int fd;       /* FD we talk about.  */
50   int dir;      /* Inbound/Outbound, maybe given implicit?  */
51   void *data;   /* Handler-specific data.  */
52   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
53 } iocb_data_t;
54
55
56 struct gpgsm_object_s
57 {
58   ASSUAN_CONTEXT assuan_ctx;
59
60   iocb_data_t status_cb;
61
62   /* Input, output etc are from the servers perspective.  */
63   iocb_data_t input_cb;
64   int input_fd_server;
65
66   iocb_data_t output_cb;
67   int output_fd_server;
68
69   iocb_data_t message_cb;
70   int message_fd_server;
71
72   struct
73   {
74     EngineStatusHandler fnc;
75     void *fnc_value;
76   } status;
77
78   struct
79   {
80     EngineColonLineHandler fnc;
81     void *fnc_value;
82     struct
83     {
84       unsigned char *line;
85       int linesize;
86       int linelen;
87     } attic;
88     int any; /* any data line seen */
89   } colon; 
90
91   struct GpgmeIOCbs io_cbs;
92 };
93
94 typedef struct gpgsm_object_s *GpgsmObject;
95
96 \f
97 static const char *
98 gpgsm_get_version (void)
99 {
100   static const char *gpgsm_version;
101   DEFINE_STATIC_LOCK (gpgsm_version_lock);
102
103   LOCK (gpgsm_version_lock);
104   if (!gpgsm_version)
105     gpgsm_version = _gpgme_get_program_version (_gpgme_get_gpgsm_path ());
106   UNLOCK (gpgsm_version_lock);
107
108   return gpgsm_version;
109 }
110
111
112 static const char *
113 gpgsm_get_req_version (void)
114 {
115   return NEED_GPGSM_VERSION;
116 }
117
118 \f
119 static void
120 close_notify_handler (int fd, void *opaque)
121 {
122   GpgsmObject gpgsm = opaque;
123
124   assert (fd != -1);
125   if (gpgsm->status_cb.fd == fd)
126     {
127       if (gpgsm->status_cb.tag)
128         (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
129       gpgsm->status_cb.fd = -1;
130     }
131   else if (gpgsm->input_cb.fd == fd)
132     {
133       if (gpgsm->input_cb.tag)
134         (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
135       gpgsm->input_cb.fd = -1;
136     }
137   else if (gpgsm->output_cb.fd == fd)
138     {
139       if (gpgsm->output_cb.tag)
140         (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
141       gpgsm->output_cb.fd = -1;
142     }
143   else if (gpgsm->message_cb.fd == fd)
144     {
145       if (gpgsm->message_cb.tag)
146         (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
147       gpgsm->message_cb.fd = -1;
148     }
149 }
150
151
152 static GpgmeError
153 map_assuan_error (AssuanError err)
154 {
155   switch (err)
156     {
157     case ASSUAN_No_Error:
158       return GPGME_No_Error;
159     case ASSUAN_General_Error:
160       return GPGME_General_Error;
161     case ASSUAN_Out_Of_Core:
162       return GPGME_Out_Of_Core;
163     case ASSUAN_Invalid_Value:
164       return GPGME_Invalid_Value;
165     case ASSUAN_Read_Error:
166       return GPGME_Read_Error;
167     case ASSUAN_Write_Error:
168       return GPGME_Write_Error;
169
170     case ASSUAN_Timeout:
171     case ASSUAN_Problem_Starting_Server:
172     case ASSUAN_Not_A_Server:
173     case ASSUAN_Not_A_Client:
174     case ASSUAN_Nested_Commands:
175     case ASSUAN_Invalid_Response:
176     case ASSUAN_No_Data_Callback:
177     case ASSUAN_No_Inquire_Callback:
178     case ASSUAN_Connect_Failed:
179     case ASSUAN_Accept_Failed:
180       return GPGME_General_Error;
181
182       /* The following error codes are meant as status codes.  */
183     case ASSUAN_Not_Implemented:
184       return GPGME_Not_Implemented;
185     case ASSUAN_Canceled:
186       return GPGME_Canceled;
187     case ASSUAN_Unsupported_Algorithm:
188       return GPGME_Not_Implemented;  /* XXX Argh.  */
189       
190     case ASSUAN_No_Data_Available:
191       return GPGME_EOF;
192       
193       /* These are errors internal to GPGME.  */
194     case ASSUAN_No_Input:
195     case ASSUAN_No_Output:
196     case ASSUAN_Invalid_Command:
197     case ASSUAN_Unknown_Command:
198     case ASSUAN_Syntax_Error:
199     case ASSUAN_Parameter_Error:
200     case ASSUAN_Parameter_Conflict:
201     case ASSUAN_Line_Too_Long:
202     case ASSUAN_Line_Not_Terminated:
203     case ASSUAN_Invalid_Data:
204     case ASSUAN_Unexpected_Command:
205     case ASSUAN_Too_Much_Data:
206     case ASSUAN_Inquire_Unknown:
207     case ASSUAN_Inquire_Error:
208     case ASSUAN_Invalid_Option:
209     case ASSUAN_Invalid_Index:
210     case ASSUAN_Unexpected_Status:
211     case ASSUAN_Unexpected_Data:
212     case ASSUAN_Invalid_Status:
213     case ASSUAN_Not_Confirmed:
214       return GPGME_General_Error;
215
216       /* These are errors in the server.  */
217     case ASSUAN_Server_Fault:
218     case ASSUAN_Server_Resource_Problem:
219     case ASSUAN_Server_IO_Error:
220     case ASSUAN_Server_Bug:
221     case ASSUAN_No_Agent:
222     case ASSUAN_Agent_Error:
223       return GPGME_Invalid_Engine;  /* XXX:  Need something more useful.  */
224
225     case ASSUAN_Bad_Certificate:
226     case ASSUAN_Bad_Certificate_Chain:
227     case ASSUAN_Missing_Certificate:
228     case ASSUAN_No_Public_Key:
229     case ASSUAN_No_Secret_Key:
230     case ASSUAN_Invalid_Name:
231     case ASSUAN_Card_Error:     /* XXX: Oh well.  */
232     case ASSUAN_Invalid_Card:   /* XXX: Oh well.  */
233     case ASSUAN_No_PKCS15_App:  /* XXX: Oh well.  */
234     case ASSUAN_Card_Not_Present:       /* XXX: Oh well.  */
235     case ASSUAN_Invalid_Id:     /* XXX: Oh well.  */
236       return GPGME_Invalid_Key;
237
238     case ASSUAN_Bad_Signature:
239       return GPGME_Invalid_Key;  /* XXX: This is wrong.  */
240
241     case ASSUAN_Cert_Revoked:
242     case ASSUAN_No_CRL_For_Cert:
243     case ASSUAN_CRL_Too_Old:
244     case ASSUAN_Not_Trusted:
245       return GPGME_Invalid_Key;  /* XXX Some more details would be good.  */
246
247     default:
248       return GPGME_General_Error;
249     }
250 }
251
252
253 static void
254 gpgsm_release (void *engine)
255 {
256   GpgsmObject gpgsm = engine;
257
258   if (!gpgsm)
259     return;
260
261   if (gpgsm->status_cb.fd != -1)
262     _gpgme_io_close (gpgsm->status_cb.fd);
263   if (gpgsm->input_cb.fd != -1)
264     _gpgme_io_close (gpgsm->input_cb.fd);
265   if (gpgsm->output_cb.fd != -1)
266     _gpgme_io_close (gpgsm->output_cb.fd);
267   if (gpgsm->message_cb.fd != -1)
268     _gpgme_io_close (gpgsm->message_cb.fd);
269
270   assuan_disconnect (gpgsm->assuan_ctx);
271
272   free (gpgsm->colon.attic.line);
273   free (gpgsm);
274 }
275
276
277 static GpgmeError
278 gpgsm_new (void **engine)
279 {
280   GpgmeError err = 0;
281   GpgsmObject gpgsm;
282   char *argv[3];
283   int fds[2];
284   int child_fds[4];
285   char *dft_display = NULL;
286   char *dft_ttyname = NULL;
287   char *dft_ttytype = NULL;
288   char *old_lc = NULL;
289   char *dft_lc = NULL;
290   char *optstr;
291   int fdlist[5];
292   int nfds;
293
294   gpgsm = calloc (1, sizeof *gpgsm);
295   if (!gpgsm)
296     {
297       err = GPGME_Out_Of_Core;
298       return err;
299     }
300
301   gpgsm->status_cb.fd = -1;
302   gpgsm->status_cb.tag = 0;
303
304   gpgsm->input_cb.fd = -1;
305   gpgsm->input_cb.tag = 0;
306   gpgsm->input_fd_server = -1;
307   gpgsm->output_cb.fd = -1;
308   gpgsm->output_cb.tag = 0;
309   gpgsm->output_fd_server = -1;
310   gpgsm->message_cb.fd = -1;
311   gpgsm->message_cb.tag = 0;
312   gpgsm->message_fd_server = -1;
313
314   gpgsm->status.fnc = 0;
315   gpgsm->colon.fnc = 0;
316   gpgsm->colon.attic.line = 0;
317   gpgsm->colon.attic.linesize = 0;
318   gpgsm->colon.attic.linelen = 0;
319   gpgsm->colon.any = 0;
320
321   gpgsm->io_cbs.add = NULL;
322   gpgsm->io_cbs.add_priv = NULL;
323   gpgsm->io_cbs.remove = NULL;
324   gpgsm->io_cbs.event = NULL;
325   gpgsm->io_cbs.event_priv = NULL;
326
327   if (_gpgme_io_pipe (fds, 0) < 0)
328     {
329       err = GPGME_Pipe_Error;
330       goto leave;
331     }
332   gpgsm->input_cb.fd = fds[1];
333   gpgsm->input_cb.dir = 0;
334   gpgsm->input_fd_server = fds[0];
335
336   if (_gpgme_io_pipe (fds, 1) < 0)
337     {
338       err = GPGME_Pipe_Error;
339       goto leave;
340     }
341   gpgsm->output_cb.fd = fds[0];
342   gpgsm->output_cb.dir = 1;
343   gpgsm->output_fd_server = fds[1];
344
345   if (_gpgme_io_pipe (fds, 0) < 0)
346     {
347       err = GPGME_Pipe_Error;
348       goto leave;
349     }
350   gpgsm->message_cb.fd = fds[1];
351   gpgsm->message_cb.dir = 0;
352   gpgsm->message_fd_server = fds[0];
353
354   child_fds[0] = gpgsm->input_fd_server;
355   child_fds[1] = gpgsm->output_fd_server;
356   child_fds[2] = gpgsm->message_fd_server;
357   child_fds[3] = -1;
358
359   argv[0] = "gpgsm";
360   argv[1] = "--server";
361   argv[2] = NULL;
362
363   err = assuan_pipe_connect (&gpgsm->assuan_ctx,
364                              _gpgme_get_gpgsm_path (), argv, child_fds);
365
366   /* We need to know the fd used by assuan for reads.  We do this by
367      using the assumption that the first returned fd from
368      assuan_get_active_fds() is always this one.  */
369   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
370                                 fdlist, DIM (fdlist));
371   if (nfds < 1)
372     {
373       err = GPGME_General_Error;  /* FIXME */
374       goto leave;
375     }
376   /* We duplicate the file descriptor, so we can close it without
377      disturbing assuan.  Alternatively, we could special case
378      status_fd and register/unregister it manually as needed, but this
379      increases code duplication and is more complicated as we can not
380      use the close notifications etc.  */
381   gpgsm->status_cb.fd = dup (fdlist[0]);
382   if (gpgsm->status_cb.fd < 0)
383     {
384       err = GPGME_General_Error;        /* FIXME */
385       goto leave;
386     }
387   gpgsm->status_cb.dir = 1;
388   gpgsm->status_cb.data = gpgsm;
389
390   dft_display = getenv ("DISPLAY");
391   if (dft_display)
392     {
393       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
394         {
395           err = GPGME_Out_Of_Core;
396           goto leave;
397         }
398       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
399                              NULL, NULL, NULL);
400       free (optstr);
401       if (err)
402         {
403           err = map_assuan_error (err);
404           goto leave;
405         }
406     }
407   dft_ttyname = ttyname (1);
408   if (dft_ttyname)
409     {
410       if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
411         {
412           err = GPGME_Out_Of_Core;
413           goto leave;
414         }
415       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
416                              NULL);
417       free (optstr);
418       if (err)
419         {
420           err = map_assuan_error (err);
421           goto leave;
422         }
423
424       dft_ttytype = getenv ("TERM");
425       if (dft_ttytype)
426         {
427           if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
428             {
429               err = GPGME_Out_Of_Core;
430               goto leave;
431             }
432           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
433                                  NULL);
434           free (optstr);
435           if (err)
436             {
437               err = map_assuan_error (err);
438               goto leave;
439             }
440         }
441
442       old_lc = setlocale (LC_CTYPE, NULL);
443       if (old_lc)
444         {
445           old_lc = strdup (old_lc);
446           if (!old_lc)
447             {
448               err = GPGME_Out_Of_Core;
449               goto leave;
450             }
451         }
452       dft_lc = setlocale (LC_CTYPE, "");
453       if (dft_lc)
454         {
455           if (asprintf (&optstr, "OPTION lc-ctype=%s", dft_lc) < 0)
456             err = GPGME_Out_Of_Core;
457           else
458             {
459               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
460                                      NULL, NULL, NULL, NULL);
461               free (optstr);
462               if (err)
463                 err = map_assuan_error (err);
464             }
465         }
466       if (old_lc)
467         {
468           setlocale (LC_CTYPE, old_lc);
469           free (old_lc);
470         }
471       if (err)
472         goto leave;
473
474
475       old_lc = setlocale (LC_MESSAGES, NULL);
476       if (old_lc)
477         {
478           old_lc = strdup (old_lc);
479           if (!old_lc)
480             {
481               err = GPGME_Out_Of_Core;
482               goto leave;
483             }
484         }
485       dft_lc = setlocale (LC_MESSAGES, "");
486       if (dft_lc)
487         {
488           if (asprintf (&optstr, "OPTION lc-messages=%s", dft_lc) < 0)
489             err = GPGME_Out_Of_Core;
490           else
491             {
492               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
493                                      NULL);
494               free (optstr);
495               if (err)
496                 err = map_assuan_error (err);
497             }
498         }
499       if (old_lc)
500         {
501           setlocale (LC_MESSAGES, old_lc);
502           free (old_lc);
503         }
504       if (err)
505         goto leave;
506     }
507
508   if (!err &&
509       (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
510                                    close_notify_handler, gpgsm)
511        || _gpgme_io_set_close_notify (gpgsm->input_cb.fd,
512                                    close_notify_handler, gpgsm)
513        || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
514                                       close_notify_handler, gpgsm)
515        || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
516                                       close_notify_handler, gpgsm)))
517     {
518       err = GPGME_General_Error;
519       goto leave;
520     }
521       
522  leave:
523   /* Close the server ends of the pipes.  Our ends are closed in
524      gpgsm_release().  */
525   if (gpgsm->input_fd_server != -1)
526     _gpgme_io_close (gpgsm->input_fd_server);
527   if (gpgsm->output_fd_server != -1)
528     _gpgme_io_close (gpgsm->output_fd_server);
529   if (gpgsm->message_fd_server != -1)
530     _gpgme_io_close (gpgsm->message_fd_server);
531
532   if (err)
533     gpgsm_release (gpgsm);
534   else
535     *engine = gpgsm;
536
537   return err;
538 }
539
540
541 /* Forward declaration.  */
542 static GpgmeStatusCode parse_status (const char *name);
543
544 static GpgmeError
545 gpgsm_assuan_simple_command (ASSUAN_CONTEXT ctx, char *cmd, EngineStatusHandler status_fnc,
546                              void *status_fnc_value)
547 {
548   AssuanError err;
549   char *line;
550   size_t linelen;
551
552   err = assuan_write_line (ctx, cmd);
553   if (err)
554     return map_assuan_error (err);
555
556   do
557     {
558       err = assuan_read_line (ctx, &line, &linelen);
559       if (err)
560         return map_assuan_error (err);
561
562       if (*line == '#' || !linelen)
563         continue;
564
565       if (linelen >= 2
566           && line[0] == 'O' && line[1] == 'K'
567           && (line[2] == '\0' || line[2] == ' '))
568         return 0;
569       else if (linelen >= 4
570           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
571           && line[3] == ' ')
572         err = map_assuan_error (atoi (&line[4]));
573       else if (linelen >= 2
574                && line[0] == 'S' && line[1] == ' ')
575         {
576           char *rest;
577           GpgmeStatusCode r;
578
579           rest = strchr (line + 2, ' ');
580           if (!rest)
581             rest = line + linelen; /* set to an empty string */
582           else
583             *(rest++) = 0;
584
585           r = parse_status (line + 2);
586
587           if (r >= 0 && status_fnc)
588             status_fnc (status_fnc_value, r, rest);
589           else
590             err = 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 GpgmeError
603 gpgsm_set_fd (ASSUAN_CONTEXT ctx, const char *which, int fd, const char *opt)
604 {
605   char line[COMMANDLINELEN];
606
607   if (opt)
608     snprintf (line, COMMANDLINELEN, "%s FD=%i %s", which, fd, opt);
609   else
610     snprintf (line, COMMANDLINELEN, "%s FD=%i", which, fd);
611
612   return gpgsm_assuan_simple_command (ctx, line, NULL, NULL);
613 }
614
615
616 static const char *
617 map_input_enc (GpgmeData d)
618 {
619   switch (gpgme_data_get_encoding (d))
620     {
621     case GPGME_DATA_ENCODING_NONE:
622       break;
623     case GPGME_DATA_ENCODING_BINARY:
624       return "--binary";
625     case GPGME_DATA_ENCODING_BASE64:
626       return "--base64";
627     case GPGME_DATA_ENCODING_ARMOR:
628       return "--armor";
629     default:
630       break;
631     }
632   return NULL;
633 }
634
635
636 static 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 GpgmeStatusCode
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 GpgmeError
658 status_handler (void *opaque, int fd)
659 {
660   AssuanError assuan_err;
661   GpgmeError 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           GpgmeStatusCode 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 GpgmeError
807 add_io_cb (GpgsmObject gpgsm, iocb_data_t *iocbd, GpgmeIOCb handler)
808 {
809   GpgmeError 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 GpgmeError
824 start (GpgsmObject gpgsm, const char *command)
825 {
826   GpgmeError 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 GpgmeError
847 gpgsm_decrypt (void *engine, GpgmeData ciph, GpgmeData plain)
848 {
849   GpgsmObject gpgsm = engine;
850   GpgmeError 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 GpgmeError
872 gpgsm_delete (void *engine, GpgmeKey key, int allow_secret)
873 {
874   GpgsmObject gpgsm = engine;
875   GpgmeError err;
876   char *fpr = (char *) gpgme_key_get_string_attr (key, GPGME_ATTR_FPR, NULL, 0);
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 GpgmeError
939 set_recipients (GpgsmObject gpgsm, GpgmeRecipients recp)
940 {
941   GpgmeError err;
942   ASSUAN_CONTEXT ctx = gpgsm->assuan_ctx;
943   char *line;
944   int linelen;
945   struct user_id_s *r;
946   int valid_recipients = 0;
947
948   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
949   line = malloc (10 + 40 + 1);
950   if (!line)
951     return GPGME_Out_Of_Core;
952   strcpy (line, "RECIPIENT ");
953   for (r = recp->list; r; r = r->next)
954     {
955       int newlen = 11 + strlen (r->name);
956       if (linelen < newlen)
957         {
958           char *newline = realloc (line, newlen);
959           if (! newline)
960             {
961               free (line);
962               return GPGME_Out_Of_Core;
963             }
964           line = newline;
965           linelen = newlen;
966         }
967       strcpy (&line[10], r->name);
968       
969       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
970                                          gpgsm->status.fnc_value);
971       if (!err)
972         valid_recipients = 1;
973       else if (err != GPGME_Invalid_Key)
974         {
975           free (line);
976           return err;
977         }
978     }
979   free (line);
980   if (!valid_recipients && gpgsm->status.fnc)
981     gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_NO_RECP, "");
982   return 0;
983 }
984
985
986 static GpgmeError
987 gpgsm_encrypt (void *engine, GpgmeRecipients recp, GpgmeData plain,
988                GpgmeData ciph, int use_armor)
989 {
990   GpgsmObject gpgsm = engine;
991   GpgmeError err;
992
993   if (!gpgsm)
994     return GPGME_Invalid_Value;
995   if (!recp)
996     return GPGME_Not_Implemented;
997
998   gpgsm->input_cb.data = plain;
999   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1000                       map_input_enc (gpgsm->input_cb.data));
1001   if (err)
1002     return err;
1003   gpgsm->output_cb.data = ciph;
1004   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1005                       use_armor ? "--armor" : 0);
1006   if (err)
1007     return err;
1008   _gpgme_io_close (gpgsm->message_cb.fd);
1009
1010   err = set_recipients (gpgsm, recp);
1011
1012   if (!err)
1013     err = start (gpgsm, "ENCRYPT");
1014
1015   return err;
1016 }
1017
1018
1019 static GpgmeError
1020 gpgsm_export (void *engine, GpgmeRecipients recp, GpgmeData keydata,
1021               int use_armor)
1022 {
1023   GpgsmObject gpgsm = engine;
1024   GpgmeError err = 0;
1025   char *cmd = NULL;
1026   int cmdi;
1027   int cmdlen = 32;
1028
1029   if (!gpgsm)
1030     return GPGME_Invalid_Value;
1031
1032   cmd = malloc (cmdlen);
1033   if (!cmd)
1034     return GPGME_Out_Of_Core;
1035   strcpy (cmd, "EXPORT");
1036   cmdi = 6;
1037
1038   if (recp)
1039     {
1040       void *ec;
1041       const char *s;
1042
1043       err = gpgme_recipients_enum_open (recp, &ec);
1044       while (!err && (s = gpgme_recipients_enum_read (recp, &ec)))
1045         {
1046           int slen = strlen (s);
1047           /* New string is old string + ' ' + s + '\0'.  */
1048           if (cmdlen < cmdi + 1 + slen + 1)
1049             {
1050               char *newcmd = realloc (cmd, cmdlen * 2);
1051               if (!newcmd)
1052                 {
1053                   free (cmd);
1054                   return GPGME_Out_Of_Core;
1055                 }
1056               cmd = newcmd;
1057               cmdlen *= 2;
1058             }
1059           cmd[cmdi++] = ' ';
1060           strcpy (cmd + cmdi, s);
1061           cmdi += slen;
1062         }
1063       if (!err)
1064         err = gpgme_recipients_enum_close (recp, &ec);
1065       if (err)
1066         return err;
1067     }
1068
1069   gpgsm->output_cb.data = keydata;
1070   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1071                       use_armor ? "--armor" : 0);
1072   if (err)
1073     return err;
1074   _gpgme_io_close (gpgsm->input_cb.fd);
1075   _gpgme_io_close (gpgsm->message_cb.fd);
1076
1077   err = start (gpgsm, cmd);
1078   free (cmd);
1079   return err;
1080 }
1081
1082
1083 static GpgmeError
1084 gpgsm_genkey (void *engine, GpgmeData help_data, int use_armor,
1085               GpgmeData pubkey, GpgmeData seckey)
1086 {
1087   GpgsmObject gpgsm = engine;
1088   GpgmeError err;
1089
1090   if (!gpgsm || !pubkey || seckey)
1091     return GPGME_Invalid_Value;
1092
1093   gpgsm->input_cb.data = help_data;
1094   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1095                       map_input_enc (gpgsm->input_cb.data));
1096   if (err)
1097     return err;
1098   gpgsm->output_cb.data = pubkey;
1099   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1100                       use_armor ? "--armor" : 0);
1101   if (err)
1102     return err;
1103   _gpgme_io_close (gpgsm->message_cb.fd);
1104
1105   err = start (gpgsm, "GENKEY");
1106   return err;
1107 }
1108
1109
1110 static GpgmeError
1111 gpgsm_import (void *engine, GpgmeData keydata)
1112 {
1113   GpgsmObject gpgsm = engine;
1114   GpgmeError err;
1115
1116   if (!gpgsm)
1117     return GPGME_Invalid_Value;
1118
1119   gpgsm->input_cb.data = keydata;
1120   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1121                       map_input_enc (gpgsm->input_cb.data));
1122   if (err)
1123     return err;
1124   _gpgme_io_close (gpgsm->output_cb.fd);
1125   _gpgme_io_close (gpgsm->message_cb.fd);
1126
1127   err = start (gpgsm, "IMPORT");
1128   return err;
1129 }
1130
1131
1132 static GpgmeError
1133 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1134                int keylist_mode)
1135 {
1136   GpgsmObject gpgsm = engine;
1137   char *line;
1138   GpgmeError err;
1139
1140   if (!pattern)
1141     pattern = "";
1142
1143   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1144     return GPGME_Out_Of_Core;
1145   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1146   free (line);
1147   if (err)
1148     return err;
1149
1150   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1151   line = malloc (15 + strlen (pattern) + 1);
1152   if (!line)
1153     return GPGME_Out_Of_Core;
1154   if (secret_only)
1155     {
1156       strcpy (line, "LISTSECRETKEYS ");
1157       strcpy (&line[15], pattern);
1158     }
1159   else
1160     {
1161       strcpy (line, "LISTKEYS ");
1162       strcpy (&line[9], pattern);
1163     }
1164
1165   _gpgme_io_close (gpgsm->input_cb.fd);
1166   _gpgme_io_close (gpgsm->output_cb.fd);
1167   _gpgme_io_close (gpgsm->message_cb.fd);
1168
1169   err = start (gpgsm, line);
1170   free (line);
1171   return err;
1172 }
1173
1174
1175 static GpgmeError
1176 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1177                    int reserved, int keylist_mode)
1178 {
1179   GpgsmObject gpgsm = engine;
1180   char *line;
1181   GpgmeError err;
1182   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1183   int length = 15 + 1;
1184   char *linep;
1185   
1186   if (reserved)
1187     return GPGME_Invalid_Value;
1188
1189   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1190     return GPGME_Out_Of_Core;
1191   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1192   free (line);
1193   if (err)
1194     return err;
1195
1196   if (pattern && *pattern)
1197     {
1198       const char **pat = pattern;
1199
1200       while (*pat)
1201         {
1202           const char *patlet = *pat;
1203
1204           while (*patlet)
1205             {
1206               length++;
1207               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1208                 length += 2;
1209               patlet++;
1210             }
1211           pat++;
1212           /* This will allocate one byte more than necessary.  */
1213           length++;
1214         }
1215     }
1216   line = malloc (length);
1217   if (!line)
1218     return GPGME_Out_Of_Core;
1219   if (secret_only)
1220     {
1221       strcpy (line, "LISTSECRETKEYS ");
1222       linep = &line[15];
1223     }
1224   else
1225     {
1226       strcpy (line, "LISTKEYS ");
1227       linep = &line[9];
1228     }
1229
1230   if (pattern && *pattern)
1231     {
1232       while (*pattern)
1233         {
1234           const char *patlet = *pattern;
1235
1236           while (*patlet)
1237             {
1238               switch (*patlet)
1239                 {
1240                 case '%':
1241                   *(linep++) = '%';
1242                   *(linep++) = '2';
1243                   *(linep++) = '5';
1244                   break;
1245                 case ' ':
1246                   *(linep++) = '%';
1247                   *(linep++) = '2';
1248                   *(linep++) = '0';
1249                   break;
1250                 case '+':
1251                   *(linep++) = '%';
1252                   *(linep++) = '2';
1253                   *(linep++) = 'B';
1254                   break;
1255                 default:
1256                   *(linep++) = *patlet;
1257                   break;
1258                 }
1259               patlet++;
1260             }
1261           pattern++;
1262         }
1263     }
1264   *linep = '\0';
1265
1266   _gpgme_io_close (gpgsm->input_cb.fd);
1267   _gpgme_io_close (gpgsm->output_cb.fd);
1268   _gpgme_io_close (gpgsm->message_cb.fd);
1269
1270   err = start (gpgsm, line);
1271   free (line);
1272   return err;
1273 }
1274
1275
1276 static GpgmeError
1277 gpgsm_sign (void *engine, GpgmeData in, GpgmeData out, GpgmeSigMode mode,
1278             int use_armor, int use_textmode, int include_certs,
1279             GpgmeCtx ctx /* FIXME */)
1280 {
1281   GpgsmObject gpgsm = engine;
1282   GpgmeError err;
1283   char *assuan_cmd;
1284   int i;
1285   GpgmeKey key;
1286
1287   if (!gpgsm)
1288     return GPGME_Invalid_Value;
1289
1290   if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1291     return GPGME_Out_Of_Core;
1292   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL,NULL);
1293   free (assuan_cmd);
1294   if (err)
1295     return err;
1296
1297   /* We must do a reset becuase we need to reset the list of signers.  Note
1298      that RESET does not reset OPTION commands. */
1299   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1300   if (err)
1301     return err;
1302
1303   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1304     {
1305       const char *s = gpgme_key_get_string_attr (key, GPGME_ATTR_FPR,
1306                                                  NULL, 0);
1307       if (s && strlen (s) < 80)
1308         {
1309           char buf[100];
1310
1311           strcpy (stpcpy (buf, "SIGNER "), s);
1312           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1313                                              NULL, NULL);
1314         }
1315       else
1316         err = GPGME_Invalid_Key;
1317       gpgme_key_unref (key);
1318       if (err) 
1319         return err;
1320     }
1321
1322   gpgsm->input_cb.data = in;
1323   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1324                       map_input_enc (gpgsm->input_cb.data));
1325   if (err)
1326     return err;
1327   gpgsm->output_cb.data = out;
1328   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1329                       use_armor ? "--armor" : 0);
1330   if (err)
1331     return err;
1332   _gpgme_io_close (gpgsm->message_cb.fd);
1333
1334   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1335                ? "SIGN --detached" : "SIGN");
1336   return err;
1337 }
1338
1339
1340 static GpgmeError
1341 gpgsm_trustlist (void *engine, const char *pattern)
1342 {
1343   /* FIXME */
1344   return GPGME_Not_Implemented;
1345 }
1346
1347
1348 static GpgmeError
1349 gpgsm_verify (void *engine, GpgmeData sig, GpgmeData signed_text,
1350               GpgmeData plaintext)
1351 {
1352   GpgsmObject gpgsm = engine;
1353   GpgmeError err;
1354
1355   if (!gpgsm)
1356     return GPGME_Invalid_Value;
1357
1358   gpgsm->input_cb.data = sig;
1359   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1360                       map_input_enc (gpgsm->input_cb.data));
1361   if (err)
1362     return err;
1363   if (plaintext)
1364     {
1365       /* Normal or cleartext signature.  */
1366       gpgsm->output_cb.data = plaintext;
1367       err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1368                           0);
1369       _gpgme_io_close (gpgsm->message_cb.fd);
1370     }
1371   else
1372     {
1373       /* Detached signature.  */
1374       gpgsm->message_cb.data = signed_text;
1375       err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1376                           gpgsm->message_fd_server, 0);
1377       _gpgme_io_close (gpgsm->output_cb.fd);
1378     }
1379
1380   if (!err)
1381     err = start (gpgsm, "VERIFY");
1382
1383   return err;
1384 }
1385
1386
1387 static void
1388 gpgsm_set_status_handler (void *engine, EngineStatusHandler fnc,
1389                           void *fnc_value) 
1390 {
1391   GpgsmObject gpgsm = engine;
1392
1393   gpgsm->status.fnc = fnc;
1394   gpgsm->status.fnc_value = fnc_value;
1395 }
1396
1397
1398 static GpgmeError
1399 gpgsm_set_colon_line_handler (void *engine, EngineColonLineHandler fnc,
1400                               void *fnc_value) 
1401 {
1402   GpgsmObject gpgsm = engine;
1403
1404   gpgsm->colon.fnc = fnc;
1405   gpgsm->colon.fnc_value = fnc_value;
1406   gpgsm->colon.any = 0;
1407   return 0;
1408 }
1409
1410
1411 static void
1412 gpgsm_set_io_cbs (void *engine, struct GpgmeIOCbs *io_cbs)
1413 {
1414   GpgsmObject gpgsm = engine;
1415   gpgsm->io_cbs = *io_cbs;
1416 }
1417
1418
1419 static void
1420 gpgsm_io_event (void *engine, GpgmeEventIO type, void *type_data)
1421 {
1422   GpgsmObject gpgsm = engine;
1423
1424   if (gpgsm->io_cbs.event)
1425     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1426 }
1427
1428
1429 struct engine_ops _gpgme_engine_ops_gpgsm =
1430   {
1431     /* Static functions.  */
1432     _gpgme_get_gpgsm_path,
1433     gpgsm_get_version,
1434     gpgsm_get_req_version,
1435     gpgsm_new,
1436
1437     /* Member functions.  */
1438     gpgsm_release,
1439     gpgsm_set_status_handler,
1440     NULL,               /* set_command_handler */
1441     gpgsm_set_colon_line_handler,
1442     NULL,               /* set_verbosity */
1443     gpgsm_decrypt,
1444     gpgsm_delete,
1445     NULL,               /* edit */
1446     gpgsm_encrypt,
1447     NULL,
1448     gpgsm_export,
1449     gpgsm_genkey,
1450     gpgsm_import,
1451     gpgsm_keylist,
1452     gpgsm_keylist_ext,
1453     gpgsm_sign,
1454     gpgsm_trustlist,
1455     gpgsm_verify,
1456     gpgsm_set_io_cbs,
1457     gpgsm_io_event
1458   };