Fix comment.
[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,
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                              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           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     }
1055   if (err)
1056     return err;
1057
1058   gpgsm->output_cb.data = keydata;
1059   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1060                       use_armor ? "--armor" : 0);
1061   if (err)
1062     return err;
1063   _gpgme_io_close (gpgsm->input_cb.fd);
1064   _gpgme_io_close (gpgsm->message_cb.fd);
1065
1066   err = start (gpgsm, cmd);
1067   free (cmd);
1068   return err;
1069 }
1070
1071
1072 static gpgme_error_t
1073 gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1074               gpgme_data_t pubkey, gpgme_data_t seckey)
1075 {
1076   GpgsmObject gpgsm = engine;
1077   gpgme_error_t err;
1078
1079   if (!gpgsm || !pubkey || seckey)
1080     return GPGME_Invalid_Value;
1081
1082   gpgsm->input_cb.data = help_data;
1083   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1084                       map_input_enc (gpgsm->input_cb.data));
1085   if (err)
1086     return err;
1087   gpgsm->output_cb.data = pubkey;
1088   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1089                       use_armor ? "--armor" : 0);
1090   if (err)
1091     return err;
1092   _gpgme_io_close (gpgsm->message_cb.fd);
1093
1094   err = start (gpgsm, "GENKEY");
1095   return err;
1096 }
1097
1098
1099 static gpgme_error_t
1100 gpgsm_import (void *engine, gpgme_data_t keydata)
1101 {
1102   GpgsmObject gpgsm = engine;
1103   gpgme_error_t err;
1104
1105   if (!gpgsm)
1106     return GPGME_Invalid_Value;
1107
1108   gpgsm->input_cb.data = keydata;
1109   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1110                       map_input_enc (gpgsm->input_cb.data));
1111   if (err)
1112     return err;
1113   _gpgme_io_close (gpgsm->output_cb.fd);
1114   _gpgme_io_close (gpgsm->message_cb.fd);
1115
1116   err = start (gpgsm, "IMPORT");
1117   return err;
1118 }
1119
1120
1121 static gpgme_error_t
1122 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1123                int keylist_mode)
1124 {
1125   GpgsmObject gpgsm = engine;
1126   char *line;
1127   gpgme_error_t err;
1128
1129   if (!pattern)
1130     pattern = "";
1131
1132   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1133     return GPGME_Out_Of_Core;
1134   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1135   free (line);
1136   if (err)
1137     return err;
1138
1139   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1140   line = malloc (15 + strlen (pattern) + 1);
1141   if (!line)
1142     return GPGME_Out_Of_Core;
1143   if (secret_only)
1144     {
1145       strcpy (line, "LISTSECRETKEYS ");
1146       strcpy (&line[15], pattern);
1147     }
1148   else
1149     {
1150       strcpy (line, "LISTKEYS ");
1151       strcpy (&line[9], pattern);
1152     }
1153
1154   _gpgme_io_close (gpgsm->input_cb.fd);
1155   _gpgme_io_close (gpgsm->output_cb.fd);
1156   _gpgme_io_close (gpgsm->message_cb.fd);
1157
1158   err = start (gpgsm, line);
1159   free (line);
1160   return err;
1161 }
1162
1163
1164 static gpgme_error_t
1165 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1166                    int reserved, int keylist_mode)
1167 {
1168   GpgsmObject gpgsm = engine;
1169   char *line;
1170   gpgme_error_t err;
1171   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1172   int length = 15 + 1;
1173   char *linep;
1174   
1175   if (reserved)
1176     return GPGME_Invalid_Value;
1177
1178   if (asprintf (&line, "OPTION list-mode=%d", (keylist_mode & 3)) < 0)
1179     return GPGME_Out_Of_Core;
1180   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1181   free (line);
1182   if (err)
1183     return err;
1184
1185   if (pattern && *pattern)
1186     {
1187       const char **pat = pattern;
1188
1189       while (*pat)
1190         {
1191           const char *patlet = *pat;
1192
1193           while (*patlet)
1194             {
1195               length++;
1196               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1197                 length += 2;
1198               patlet++;
1199             }
1200           pat++;
1201           /* This will allocate one byte more than necessary.  */
1202           length++;
1203         }
1204     }
1205   line = malloc (length);
1206   if (!line)
1207     return GPGME_Out_Of_Core;
1208   if (secret_only)
1209     {
1210       strcpy (line, "LISTSECRETKEYS ");
1211       linep = &line[15];
1212     }
1213   else
1214     {
1215       strcpy (line, "LISTKEYS ");
1216       linep = &line[9];
1217     }
1218
1219   if (pattern && *pattern)
1220     {
1221       while (*pattern)
1222         {
1223           const char *patlet = *pattern;
1224
1225           while (*patlet)
1226             {
1227               switch (*patlet)
1228                 {
1229                 case '%':
1230                   *(linep++) = '%';
1231                   *(linep++) = '2';
1232                   *(linep++) = '5';
1233                   break;
1234                 case ' ':
1235                   *(linep++) = '%';
1236                   *(linep++) = '2';
1237                   *(linep++) = '0';
1238                   break;
1239                 case '+':
1240                   *(linep++) = '%';
1241                   *(linep++) = '2';
1242                   *(linep++) = 'B';
1243                   break;
1244                 default:
1245                   *(linep++) = *patlet;
1246                   break;
1247                 }
1248               patlet++;
1249             }
1250           pattern++;
1251         }
1252     }
1253   *linep = '\0';
1254
1255   _gpgme_io_close (gpgsm->input_cb.fd);
1256   _gpgme_io_close (gpgsm->output_cb.fd);
1257   _gpgme_io_close (gpgsm->message_cb.fd);
1258
1259   err = start (gpgsm, line);
1260   free (line);
1261   return err;
1262 }
1263
1264
1265 static gpgme_error_t
1266 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1267             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1268             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1269 {
1270   GpgsmObject gpgsm = engine;
1271   gpgme_error_t err;
1272   char *assuan_cmd;
1273   int i;
1274   gpgme_key_t key;
1275
1276   if (!gpgsm)
1277     return GPGME_Invalid_Value;
1278
1279   if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1280     return GPGME_Out_Of_Core;
1281   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL,NULL);
1282   free (assuan_cmd);
1283   if (err)
1284     return err;
1285
1286   /* We must do a reset becuase we need to reset the list of signers.  Note
1287      that RESET does not reset OPTION commands. */
1288   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1289   if (err)
1290     return err;
1291
1292   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1293     {
1294       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1295       if (s && strlen (s) < 80)
1296         {
1297           char buf[100];
1298
1299           strcpy (stpcpy (buf, "SIGNER "), s);
1300           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1301                                              NULL, NULL);
1302         }
1303       else
1304         err = GPGME_Invalid_Key;
1305       gpgme_key_unref (key);
1306       if (err) 
1307         return err;
1308     }
1309
1310   gpgsm->input_cb.data = in;
1311   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1312                       map_input_enc (gpgsm->input_cb.data));
1313   if (err)
1314     return err;
1315   gpgsm->output_cb.data = out;
1316   err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1317                       use_armor ? "--armor" : 0);
1318   if (err)
1319     return err;
1320   _gpgme_io_close (gpgsm->message_cb.fd);
1321
1322   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1323                ? "SIGN --detached" : "SIGN");
1324   return err;
1325 }
1326
1327
1328 static gpgme_error_t
1329 gpgsm_trustlist (void *engine, const char *pattern)
1330 {
1331   /* FIXME */
1332   return GPGME_Not_Implemented;
1333 }
1334
1335
1336 static gpgme_error_t
1337 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1338               gpgme_data_t plaintext)
1339 {
1340   GpgsmObject gpgsm = engine;
1341   gpgme_error_t err;
1342
1343   if (!gpgsm)
1344     return GPGME_Invalid_Value;
1345
1346   gpgsm->input_cb.data = sig;
1347   err = gpgsm_set_fd (gpgsm->assuan_ctx, "INPUT", gpgsm->input_fd_server,
1348                       map_input_enc (gpgsm->input_cb.data));
1349   if (err)
1350     return err;
1351   if (plaintext)
1352     {
1353       /* Normal or cleartext signature.  */
1354       gpgsm->output_cb.data = plaintext;
1355       err = gpgsm_set_fd (gpgsm->assuan_ctx, "OUTPUT", gpgsm->output_fd_server,
1356                           0);
1357       _gpgme_io_close (gpgsm->message_cb.fd);
1358     }
1359   else
1360     {
1361       /* Detached signature.  */
1362       gpgsm->message_cb.data = signed_text;
1363       err = gpgsm_set_fd (gpgsm->assuan_ctx, "MESSAGE",
1364                           gpgsm->message_fd_server, 0);
1365       _gpgme_io_close (gpgsm->output_cb.fd);
1366     }
1367
1368   if (!err)
1369     err = start (gpgsm, "VERIFY");
1370
1371   return err;
1372 }
1373
1374
1375 static void
1376 gpgsm_set_status_handler (void *engine, EngineStatusHandler fnc,
1377                           void *fnc_value) 
1378 {
1379   GpgsmObject gpgsm = engine;
1380
1381   gpgsm->status.fnc = fnc;
1382   gpgsm->status.fnc_value = fnc_value;
1383 }
1384
1385
1386 static gpgme_error_t
1387 gpgsm_set_colon_line_handler (void *engine, EngineColonLineHandler fnc,
1388                               void *fnc_value) 
1389 {
1390   GpgsmObject gpgsm = engine;
1391
1392   gpgsm->colon.fnc = fnc;
1393   gpgsm->colon.fnc_value = fnc_value;
1394   gpgsm->colon.any = 0;
1395   return 0;
1396 }
1397
1398
1399 static void
1400 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1401 {
1402   GpgsmObject gpgsm = engine;
1403   gpgsm->io_cbs = *io_cbs;
1404 }
1405
1406
1407 static void
1408 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1409 {
1410   GpgsmObject gpgsm = engine;
1411
1412   if (gpgsm->io_cbs.event)
1413     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1414 }
1415
1416
1417 struct engine_ops _gpgme_engine_ops_gpgsm =
1418   {
1419     /* Static functions.  */
1420     _gpgme_get_gpgsm_path,
1421     gpgsm_get_version,
1422     gpgsm_get_req_version,
1423     gpgsm_new,
1424
1425     /* Member functions.  */
1426     gpgsm_release,
1427     gpgsm_set_status_handler,
1428     NULL,               /* set_command_handler */
1429     gpgsm_set_colon_line_handler,
1430     gpgsm_decrypt,
1431     gpgsm_delete,
1432     NULL,               /* edit */
1433     gpgsm_encrypt,
1434     NULL,
1435     gpgsm_export,
1436     gpgsm_genkey,
1437     gpgsm_import,
1438     gpgsm_keylist,
1439     gpgsm_keylist_ext,
1440     gpgsm_sign,
1441     gpgsm_trustlist,
1442     gpgsm_verify,
1443     gpgsm_set_io_cbs,
1444     gpgsm_io_event
1445   };