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