Chnages to supporta pinnetry notification
[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, 2005, 2007 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 Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (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    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <assert.h>
30 #include <unistd.h>
31 #include <locale.h>
32 #include <fcntl.h> /* FIXME */
33 #include <errno.h>
34
35 #include "gpgme.h"
36 #include "util.h"
37 #include "ops.h"
38 #include "wait.h"
39 #include "priv-io.h"
40 #include "sema.h"
41
42 #include "assuan.h"
43 #include "status-table.h"
44 #include "debug.h"
45
46 #include "engine-backend.h"
47
48 \f
49 typedef struct
50 {
51   int fd;       /* FD we talk about.  */
52   int server_fd;/* Server FD for this connection.  */
53   int dir;      /* Inbound/Outbound, maybe given implicit?  */
54   void *data;   /* Handler-specific data.  */
55   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
56   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
57                              need this because _gpgme_io_fd2str can't
58                              be used on a closed descriptor.  */
59 } iocb_data_t;
60
61
62 struct engine_gpgsm
63 {
64   assuan_context_t assuan_ctx;
65
66   int lc_ctype_set;
67   int lc_messages_set;
68
69   iocb_data_t status_cb;
70
71   /* Input, output etc are from the servers perspective.  */
72   iocb_data_t input_cb;
73
74   iocb_data_t output_cb;
75
76   iocb_data_t message_cb;
77
78   struct
79   {
80     engine_status_handler_t fnc;
81     void *fnc_value;
82   } status;
83
84   struct
85   {
86     engine_colon_line_handler_t fnc;
87     void *fnc_value;
88     struct
89     {
90       char *line;
91       int linesize;
92       int linelen;
93     } attic;
94     int any; /* any data line seen */
95   } colon; 
96
97   gpgme_data_t inline_data;  /* Used to collect D lines.  */
98
99   struct gpgme_io_cbs io_cbs;
100 };
101
102 typedef struct engine_gpgsm *engine_gpgsm_t;
103
104
105 static void gpgsm_io_event (void *engine, 
106                             gpgme_event_io_t type, void *type_data);
107
108
109 \f
110 static char *
111 gpgsm_get_version (const char *file_name)
112 {
113   return _gpgme_get_program_version (file_name ? file_name
114                                      : _gpgme_get_gpgsm_path ());
115 }
116
117
118 static const char *
119 gpgsm_get_req_version (void)
120 {
121   return NEED_GPGSM_VERSION;
122 }
123
124 \f
125 static void
126 close_notify_handler (int fd, void *opaque)
127 {
128   engine_gpgsm_t gpgsm = opaque;
129
130   assert (fd != -1);
131   if (gpgsm->status_cb.fd == fd)
132     {
133       if (gpgsm->status_cb.tag)
134         (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
135       gpgsm->status_cb.fd = -1;
136       gpgsm->status_cb.tag = NULL;
137     }
138   else if (gpgsm->input_cb.fd == fd)
139     {
140       if (gpgsm->input_cb.tag)
141         (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
142       gpgsm->input_cb.fd = -1;
143       gpgsm->input_cb.tag = NULL;
144     }
145   else if (gpgsm->output_cb.fd == fd)
146     {
147       if (gpgsm->output_cb.tag)
148         (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
149       gpgsm->output_cb.fd = -1;
150       gpgsm->output_cb.tag = NULL;
151     }
152   else if (gpgsm->message_cb.fd == fd)
153     {
154       if (gpgsm->message_cb.tag)
155         (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
156       gpgsm->message_cb.fd = -1;
157       gpgsm->message_cb.tag = NULL;
158     }
159 }
160
161
162 static gpgme_error_t
163 map_assuan_error (gpg_error_t err)
164 {
165   if (!err)
166     return 0;
167
168   if (err == -1)
169     return gpg_error (GPG_ERR_INV_ENGINE);
170
171   /* New code will use gpg_error_t values.  */
172   if (gpg_err_source (err))
173     return (gpgme_error_t) err;
174
175   /* Legacy code will use old values.  */
176   switch (err)
177     {
178     case ASSUAN_No_Error:
179       return gpg_error (GPG_ERR_NO_ERROR);
180     case ASSUAN_General_Error:
181       return gpg_error (GPG_ERR_GENERAL);
182     case ASSUAN_Out_Of_Core:
183       return gpg_error (GPG_ERR_ENOMEM);
184     case ASSUAN_Invalid_Value:
185       return gpg_error (GPG_ERR_INV_VALUE);
186     case ASSUAN_Timeout:
187       return gpg_error (GPG_ERR_ETIMEDOUT);
188     case ASSUAN_Read_Error:
189       return gpg_error (GPG_ERR_GENERAL);
190     case ASSUAN_Write_Error:
191       return gpg_error (GPG_ERR_GENERAL);
192
193     case ASSUAN_Problem_Starting_Server:
194     case ASSUAN_Not_A_Server:
195     case ASSUAN_Not_A_Client:
196     case ASSUAN_Nested_Commands:
197     case ASSUAN_No_Data_Callback:
198     case ASSUAN_No_Inquire_Callback:
199     case ASSUAN_Connect_Failed:
200     case ASSUAN_Accept_Failed:
201     case ASSUAN_Invalid_Command:
202     case ASSUAN_Unknown_Command:
203     case ASSUAN_Syntax_Error:
204     case ASSUAN_Parameter_Error:
205     case ASSUAN_Parameter_Conflict:
206     case ASSUAN_No_Input:
207     case ASSUAN_No_Output:
208     case ASSUAN_No_Data_Available:
209     case ASSUAN_Too_Much_Data:
210     case ASSUAN_Inquire_Unknown:
211     case ASSUAN_Inquire_Error:
212     case ASSUAN_Invalid_Option:
213     case ASSUAN_Unexpected_Status:
214     case ASSUAN_Unexpected_Data:
215     case ASSUAN_Invalid_Status:
216       return gpg_error (GPG_ERR_ASSUAN);
217
218     case ASSUAN_Invalid_Response:
219       return gpg_error (GPG_ERR_INV_RESPONSE);
220
221     case ASSUAN_Not_Implemented:
222       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
223     case ASSUAN_Line_Too_Long:
224       return gpg_error (GPG_ERR_LINE_TOO_LONG);
225     case ASSUAN_Line_Not_Terminated:
226       return gpg_error (GPG_ERR_INCOMPLETE_LINE);
227     case ASSUAN_Canceled:
228       return gpg_error (GPG_ERR_CANCELED);
229
230     case ASSUAN_Unsupported_Algorithm:
231       return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
232     case ASSUAN_Server_Resource_Problem:
233       return gpg_error (GPG_ERR_RESOURCE_LIMIT);
234     case ASSUAN_Server_IO_Error:
235       return gpg_error (GPG_ERR_GENERAL);
236     case ASSUAN_Server_Bug:
237       return gpg_error (GPG_ERR_BUG);
238     case ASSUAN_Invalid_Data:
239       return gpg_error (GPG_ERR_INV_DATA);
240     case ASSUAN_Invalid_Index:
241       return gpg_error (GPG_ERR_INV_INDEX);
242     case ASSUAN_Not_Confirmed:
243       return gpg_error (GPG_ERR_NOT_CONFIRMED);
244     case ASSUAN_Bad_Certificate:
245       return gpg_error (GPG_ERR_BAD_CERT);
246     case ASSUAN_Bad_Certificate_Chain:
247       return gpg_error (GPG_ERR_BAD_CERT_CHAIN);
248     case ASSUAN_Missing_Certificate:
249       return gpg_error (GPG_ERR_MISSING_CERT);
250     case ASSUAN_Bad_Signature:
251       return gpg_error (GPG_ERR_BAD_SIGNATURE);
252     case ASSUAN_No_Agent:
253       return gpg_error (GPG_ERR_NO_AGENT);
254     case ASSUAN_Agent_Error:
255       return gpg_error (GPG_ERR_AGENT);
256     case ASSUAN_No_Public_Key:
257       return gpg_error (GPG_ERR_NO_PUBKEY);
258     case ASSUAN_No_Secret_Key:
259       return gpg_error (GPG_ERR_NO_SECKEY);
260     case ASSUAN_Invalid_Name:
261       return gpg_error (GPG_ERR_INV_NAME);
262       
263     case ASSUAN_Cert_Revoked:
264       return gpg_error (GPG_ERR_CERT_REVOKED);
265     case ASSUAN_No_CRL_For_Cert:
266       return gpg_error (GPG_ERR_NO_CRL_KNOWN);
267     case ASSUAN_CRL_Too_Old:
268       return gpg_error (GPG_ERR_CRL_TOO_OLD);
269     case ASSUAN_Not_Trusted:
270       return gpg_error (GPG_ERR_NOT_TRUSTED);
271
272     case ASSUAN_Card_Error:
273       return gpg_error (GPG_ERR_CARD);
274     case ASSUAN_Invalid_Card:
275       return gpg_error (GPG_ERR_INV_CARD);
276     case ASSUAN_No_PKCS15_App:
277       return gpg_error (GPG_ERR_NO_PKCS15_APP);
278     case ASSUAN_Card_Not_Present:
279       return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
280     case ASSUAN_Invalid_Id:
281       return gpg_error (GPG_ERR_INV_ID);
282     default:
283       return gpg_error (GPG_ERR_GENERAL);
284     }
285 }
286
287
288 /* This is the default inquiry callback.  We use it to handle the
289    Pinentry notifications.  */
290 static gpgme_error_t
291 default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
292 {
293   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
294     {
295       _gpgme_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
296     }
297
298   return 0;
299 }
300
301
302 static gpgme_error_t
303 gpgsm_cancel (void *engine)
304 {
305   engine_gpgsm_t gpgsm = engine;
306
307   if (!gpgsm)
308     return gpg_error (GPG_ERR_INV_VALUE);
309
310   if (gpgsm->status_cb.fd != -1)
311     _gpgme_io_close (gpgsm->status_cb.fd);
312   if (gpgsm->input_cb.fd != -1)
313     _gpgme_io_close (gpgsm->input_cb.fd);
314   if (gpgsm->output_cb.fd != -1)
315     _gpgme_io_close (gpgsm->output_cb.fd);
316   if (gpgsm->message_cb.fd != -1)
317     _gpgme_io_close (gpgsm->message_cb.fd);
318
319   if (gpgsm->assuan_ctx)
320     {
321       assuan_disconnect (gpgsm->assuan_ctx);
322       gpgsm->assuan_ctx = NULL;
323     }
324
325   return 0;
326 }
327
328
329 static void
330 gpgsm_release (void *engine)
331 {
332   engine_gpgsm_t gpgsm = engine;
333
334   if (!gpgsm)
335     return;
336
337   gpgsm_cancel (engine);
338
339   free (gpgsm->colon.attic.line);
340   free (gpgsm);
341 }
342
343
344 static gpgme_error_t
345 gpgsm_new (void **engine, const char *file_name, const char *home_dir)
346 {
347   gpgme_error_t err = 0;
348   engine_gpgsm_t gpgsm;
349   const char *argv[5];
350   int argc;
351 #if !USE_DESCRIPTOR_PASSING
352   int fds[2];
353   int child_fds[4];
354 #endif
355   char *dft_display = NULL;
356   char dft_ttyname[64];
357   char *dft_ttytype = NULL;
358   char *optstr;
359
360   gpgsm = calloc (1, sizeof *gpgsm);
361   if (!gpgsm)
362     return gpg_error_from_errno (errno);
363
364   gpgsm->status_cb.fd = -1;
365   gpgsm->status_cb.dir = 1;
366   gpgsm->status_cb.tag = 0;
367   gpgsm->status_cb.data = gpgsm;
368
369   gpgsm->input_cb.fd = -1;
370   gpgsm->input_cb.dir = 0;
371   gpgsm->input_cb.tag = 0;
372   gpgsm->input_cb.server_fd = -1;
373   *gpgsm->input_cb.server_fd_str = 0;
374   gpgsm->output_cb.fd = -1;
375   gpgsm->output_cb.dir = 1;
376   gpgsm->output_cb.tag = 0;
377   gpgsm->output_cb.server_fd = -1;
378   *gpgsm->output_cb.server_fd_str = 0;
379   gpgsm->message_cb.fd = -1;
380   gpgsm->message_cb.dir = 0;
381   gpgsm->message_cb.tag = 0;
382   gpgsm->message_cb.server_fd = -1;
383   *gpgsm->message_cb.server_fd_str = 0;
384
385   gpgsm->status.fnc = 0;
386   gpgsm->colon.fnc = 0;
387   gpgsm->colon.attic.line = 0;
388   gpgsm->colon.attic.linesize = 0;
389   gpgsm->colon.attic.linelen = 0;
390   gpgsm->colon.any = 0;
391
392   gpgsm->inline_data = NULL;
393
394   gpgsm->io_cbs.add = NULL;
395   gpgsm->io_cbs.add_priv = NULL;
396   gpgsm->io_cbs.remove = NULL;
397   gpgsm->io_cbs.event = NULL;
398   gpgsm->io_cbs.event_priv = NULL;
399
400 #if !USE_DESCRIPTOR_PASSING
401   if (_gpgme_io_pipe (fds, 0) < 0)
402     {
403       err = gpg_error_from_errno (errno);
404       goto leave;
405     }
406   gpgsm->input_cb.fd = fds[1];
407   gpgsm->input_cb.server_fd = fds[0];
408   _gpgme_io_fd2str (gpgsm->input_cb.server_fd_str, 
409                     sizeof gpgsm->input_cb.server_fd_str,
410                     gpgsm->input_cb.server_fd);
411
412   if (_gpgme_io_pipe (fds, 1) < 0)
413     {
414       err = gpg_error_from_errno (errno);
415       goto leave;
416     }
417   gpgsm->output_cb.fd = fds[0];
418   gpgsm->output_cb.server_fd = fds[1];
419   _gpgme_io_fd2str (gpgsm->output_cb.server_fd_str, 
420                     sizeof gpgsm->output_cb.server_fd_str,
421                     gpgsm->output_cb.server_fd);
422
423   if (_gpgme_io_pipe (fds, 0) < 0)
424     {
425       err = gpg_error_from_errno (errno);
426       goto leave;
427     }
428   gpgsm->message_cb.fd = fds[1];
429   gpgsm->message_cb.server_fd = fds[0];
430   _gpgme_io_fd2str (gpgsm->message_cb.server_fd_str, 
431                     sizeof gpgsm->message_cb.server_fd_str,
432                     gpgsm->message_cb.server_fd);
433
434   child_fds[0] = gpgsm->input_cb.server_fd;
435   child_fds[1] = gpgsm->output_cb.server_fd;
436   child_fds[2] = gpgsm->message_cb.server_fd;
437   child_fds[3] = -1;
438 #endif
439
440   argc = 0;
441   argv[argc++] = "gpgsm";
442   if (home_dir)
443     {
444       argv[argc++] = "--homedir";
445       argv[argc++] = home_dir;
446     }
447   argv[argc++] = "--server";
448   argv[argc++] = NULL;
449
450 #if USE_DESCRIPTOR_PASSING
451   err = assuan_pipe_connect_ext
452     (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
453      argv, NULL, NULL, NULL, 1);
454 #else
455   err = assuan_pipe_connect
456     (&gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (),
457      argv, child_fds);
458 #endif
459   if (err)
460     goto leave;
461
462   err = _gpgme_getenv ("DISPLAY", &dft_display);
463   if (err)
464     goto leave;
465   if (dft_display)
466     {
467       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
468         {
469           free (dft_display);
470           err = gpg_error_from_errno (errno);
471           goto leave;
472         }
473       free (dft_display);
474
475       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
476                              NULL, NULL, NULL);
477       free (optstr);
478       if (err)
479         {
480           err = map_assuan_error (err);
481           goto leave;
482         }
483     }
484
485   if (isatty (1))
486     {
487       int rc;
488
489       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
490       if (rc)
491         {
492           err = gpg_error_from_errno (rc);
493           goto leave;
494         }
495       else
496         {
497           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
498             {
499               err = gpg_error_from_errno (errno);
500               goto leave;
501             }
502           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
503                                  NULL, NULL, NULL);
504           free (optstr);
505           if (err)
506             {
507               err = map_assuan_error (err);
508               goto leave;
509             }
510
511           err = _gpgme_getenv ("TERM", &dft_ttytype);
512           if (err)
513             goto leave;
514           if (dft_ttytype)
515             {
516               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
517                 {
518                   free (dft_ttytype);
519                   err = gpg_error_from_errno (errno);
520                   goto leave;
521                 }
522               free (dft_ttytype);
523
524               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
525                                      NULL, NULL, NULL, NULL);
526               free (optstr);
527               if (err)
528                 {
529                   err = map_assuan_error (err);
530                   goto leave;
531                 }
532             }
533         }
534     }
535
536   /* Ask gpgsm to enable the audit log support.  */
537   if (!err)
538     {
539       err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
540                              NULL, NULL, NULL, NULL, NULL, NULL);
541       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
542         err = 0; /* This is an optional feature of gpgsm.  */
543     }
544
545
546 #ifdef HAVE_W32_SYSTEM
547   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
548      gpgsm to tell us when it needs it.  */
549   if (!err)
550     {
551       err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
552                              NULL, NULL, NULL, NULL, NULL, NULL);
553       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
554         err = 0; /* This is a new feature of gpgsm.  */
555     }
556 #endif /*HAVE_W32_SYSTEM*/
557
558 #if !USE_DESCRIPTOR_PASSING
559   if (!err
560       && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
561                                       close_notify_handler, gpgsm)
562           || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
563                                          close_notify_handler, gpgsm)
564           || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
565                                          close_notify_handler, gpgsm)))
566     {
567       err = gpg_error (GPG_ERR_GENERAL);
568       goto leave;
569     }
570 #endif
571
572  leave:
573   /* Close the server ends of the pipes (because of this, we must use
574      the stored server_fd_str in the function start).  Our ends are
575      closed in gpgsm_release().  */
576 #if !USE_DESCRIPTOR_PASSING
577   if (gpgsm->input_cb.server_fd != -1)
578     _gpgme_io_close (gpgsm->input_cb.server_fd);
579   if (gpgsm->output_cb.server_fd != -1)
580     _gpgme_io_close (gpgsm->output_cb.server_fd);
581   if (gpgsm->message_cb.server_fd != -1)
582     _gpgme_io_close (gpgsm->message_cb.server_fd);
583 #endif
584
585   if (err)
586     gpgsm_release (gpgsm);
587   else
588     *engine = gpgsm;
589
590   return err;
591 }
592
593
594 static gpgme_error_t
595 gpgsm_set_locale (void *engine, int category, const char *value)
596 {
597   engine_gpgsm_t gpgsm = engine;
598   gpgme_error_t err;
599   char *optstr;
600   char *catstr;
601
602   /* FIXME: If value is NULL, we need to reset the option to default.
603      But we can't do this.  So we error out here.  GPGSM needs support
604      for this.  */
605   if (category == LC_CTYPE)
606     {
607       catstr = "lc-ctype";
608       if (!value && gpgsm->lc_ctype_set)
609         return gpg_error (GPG_ERR_INV_VALUE);
610       if (value)
611         gpgsm->lc_ctype_set = 1;
612     }
613 #ifdef LC_MESSAGES
614   else if (category == LC_MESSAGES)
615     {
616       catstr = "lc-messages";
617       if (!value && gpgsm->lc_messages_set)
618         return gpg_error (GPG_ERR_INV_VALUE);
619       if (value)
620         gpgsm->lc_messages_set = 1;
621     }
622 #endif /* LC_MESSAGES */
623   else
624     return gpg_error (GPG_ERR_INV_VALUE);
625
626   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
627     err = gpg_error_from_errno (errno);
628   else
629     {
630       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
631                              NULL, NULL, NULL, NULL);
632       free (optstr);
633       if (err)
634         err = map_assuan_error (err);
635     }
636   return err;
637 }
638
639
640 /* Forward declaration.  */
641 static gpgme_status_code_t parse_status (const char *name);
642
643 static gpgme_error_t
644 gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd,
645                              engine_status_handler_t status_fnc,
646                              void *status_fnc_value)
647 {
648   gpg_error_t err;
649   char *line;
650   size_t linelen;
651
652   err = assuan_write_line (ctx, cmd);
653   if (err)
654     return map_assuan_error (err);
655
656   do
657     {
658       err = assuan_read_line (ctx, &line, &linelen);
659       if (err)
660         return map_assuan_error (err);
661
662       if (*line == '#' || !linelen)
663         continue;
664
665       if (linelen >= 2
666           && line[0] == 'O' && line[1] == 'K'
667           && (line[2] == '\0' || line[2] == ' '))
668         return 0;
669       else if (linelen >= 4
670           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
671           && line[3] == ' ')
672         err = map_assuan_error (atoi (&line[4]));
673       else if (linelen >= 2
674                && line[0] == 'S' && line[1] == ' ')
675         {
676           char *rest;
677           gpgme_status_code_t r;
678
679           rest = strchr (line + 2, ' ');
680           if (!rest)
681             rest = line + linelen; /* set to an empty string */
682           else
683             *(rest++) = 0;
684
685           r = parse_status (line + 2);
686
687           if (r >= 0 && status_fnc)
688             err = status_fnc (status_fnc_value, r, rest);
689           else
690             err = gpg_error (GPG_ERR_GENERAL);
691         }
692       else
693         err = gpg_error (GPG_ERR_GENERAL);
694     }
695   while (!err);
696
697   return err;
698 }
699
700
701 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
702
703 static void
704 gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
705 {
706 #if !USE_DESCRIPTOR_PASSING
707   switch (fd_type)
708     {
709     case INPUT_FD:
710       _gpgme_io_close (gpgsm->input_cb.fd);
711       break;
712     case OUTPUT_FD:
713       _gpgme_io_close (gpgsm->output_cb.fd);
714       break;
715     case MESSAGE_FD:
716       _gpgme_io_close (gpgsm->message_cb.fd);
717       break;
718     }
719 #endif
720 }
721
722 #define COMMANDLINELEN 40
723 static gpgme_error_t
724 gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
725 {
726   gpg_error_t err = 0;
727   char line[COMMANDLINELEN];
728   char *which;
729   iocb_data_t *iocb_data;
730   int dir;
731
732   switch (fd_type)
733     {
734     case INPUT_FD:
735       which = "INPUT";
736       iocb_data = &gpgsm->input_cb;
737       break;
738
739     case OUTPUT_FD:
740       which = "OUTPUT";
741       iocb_data = &gpgsm->output_cb;
742       break;
743
744     case MESSAGE_FD:
745       which = "MESSAGE";
746       iocb_data = &gpgsm->message_cb;
747       break;
748
749     default:
750       return gpg_error (GPG_ERR_INV_VALUE);
751     }
752
753   dir = iocb_data->dir;
754
755 #if USE_DESCRIPTOR_PASSING
756   /* We try to short-cut the communication by giving GPGSM direct
757      access to the file descriptor, rather than using a pipe.  */
758   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
759   if (iocb_data->server_fd < 0)
760     {
761       int fds[2];
762
763       if (_gpgme_io_pipe (fds, 0) < 0)
764         return gpg_error_from_errno (errno);
765
766       iocb_data->fd = dir ? fds[0] : fds[1];
767       iocb_data->server_fd = dir ? fds[1] : fds[0];
768
769       if (_gpgme_io_set_close_notify (iocb_data->fd,
770                                       close_notify_handler, gpgsm))
771         {
772           err = gpg_error (GPG_ERR_GENERAL);
773           goto leave_set_fd;
774         }
775     }
776
777   err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
778   if (err)
779     goto leave_set_fd;
780
781   _gpgme_io_close (iocb_data->server_fd);
782   iocb_data->server_fd = -1;
783
784   if (opt)
785     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
786   else
787     snprintf (line, COMMANDLINELEN, "%s FD", which);
788 #else
789   if (opt)
790     snprintf (line, COMMANDLINELEN, "%s FD=%s %s", 
791               which, iocb_data->server_fd_str, opt);
792   else
793     snprintf (line, COMMANDLINELEN, "%s FD=%s", 
794               which, iocb_data->server_fd_str);
795 #endif
796
797   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
798
799 #if USE_DESCRIPTOR_PASSING
800  leave_set_fd:
801   if (err)
802     {
803       _gpgme_io_close (iocb_data->fd);
804       iocb_data->fd = -1;
805       if (iocb_data->server_fd != -1)
806         {
807           _gpgme_io_close (iocb_data->server_fd);
808           iocb_data->server_fd = -1;
809         }
810     }
811 #endif
812
813   return err;
814 }
815
816
817 static const char *
818 map_data_enc (gpgme_data_t d)
819 {
820   switch (gpgme_data_get_encoding (d))
821     {
822     case GPGME_DATA_ENCODING_NONE:
823       break;
824     case GPGME_DATA_ENCODING_BINARY:
825       return "--binary";
826     case GPGME_DATA_ENCODING_BASE64:
827       return "--base64";
828     case GPGME_DATA_ENCODING_ARMOR:
829       return "--armor";
830     default:
831       break;
832     }
833   return NULL;
834 }
835
836
837 static int
838 status_cmp (const void *ap, const void *bp)
839 {
840   const struct status_table_s *a = ap;
841   const struct status_table_s *b = bp;
842
843   return strcmp (a->name, b->name);
844 }
845
846
847 static gpgme_status_code_t
848 parse_status (const char *name)
849 {
850   struct status_table_s t, *r;
851   t.name = name;
852   r = bsearch (&t, status_table, DIM(status_table) - 1,
853                sizeof t, status_cmp);
854   return r ? r->code : -1;
855 }
856
857
858 static gpgme_error_t
859 status_handler (void *opaque, int fd)
860 {
861   gpg_error_t assuan_err;
862   gpgme_error_t err = 0;
863   engine_gpgsm_t gpgsm = opaque;
864   char *line;
865   size_t linelen;
866
867   do
868     {
869       assuan_err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
870       if (assuan_err)
871         {
872           /* Try our best to terminate the connection friendly.  */
873           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
874           err = map_assuan_error (assuan_err);
875           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
876                   "fd 0x%x: error from assuan (%d) getting status line : %s",
877                   fd, assuan_err, gpg_strerror (err));
878         }
879       else if (linelen >= 3
880                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
881                && (line[3] == '\0' || line[3] == ' '))
882         {
883           if (line[3] == ' ')
884             err = map_assuan_error (atoi (&line[4]));
885           else
886             err = gpg_error (GPG_ERR_GENERAL);
887           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
888                   "fd 0x%x: ERR line - mapped to: %s",
889                   fd, err ? gpg_strerror (err) : "ok");
890           /* Try our best to terminate the connection friendly.  */
891           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
892         }
893       else if (linelen >= 2
894                && line[0] == 'O' && line[1] == 'K'
895                && (line[2] == '\0' || line[2] == ' '))
896         {
897           if (gpgsm->status.fnc)
898             err = gpgsm->status.fnc (gpgsm->status.fnc_value,
899                                      GPGME_STATUS_EOF, "");
900           
901           if (!err && gpgsm->colon.fnc && gpgsm->colon.any )
902             {
903               /* We must tell a colon function about the EOF. We do
904                  this only when we have seen any data lines.  Note
905                  that this inlined use of colon data lines will
906                  eventually be changed into using a regular data
907                  channel. */
908               gpgsm->colon.any = 0;
909               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
910             }
911           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
912                   "fd 0x%x: OK line - final status: %s",
913                   fd, err ? gpg_strerror (err) : "ok");
914           _gpgme_io_close (gpgsm->status_cb.fd);
915           return err;
916         }
917       else if (linelen > 2
918                && line[0] == 'D' && line[1] == ' '
919                && gpgsm->colon.fnc)
920         {
921           /* We are using the colon handler even for plain inline data
922              - strange name for that function but for historic reasons
923              we keep it.  */
924           /* FIXME We can't use this for binary data because we
925              assume this is a string.  For the current usage of colon
926              output it is correct.  */
927           char *src = line + 2;
928           char *end = line + linelen;
929           char *dst;
930           char **aline = &gpgsm->colon.attic.line;
931           int *alinelen = &gpgsm->colon.attic.linelen;
932
933           if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
934             {
935               char *newline = realloc (*aline, *alinelen + linelen + 1);
936               if (!newline)
937                 err = gpg_error_from_errno (errno);
938               else
939                 {
940                   *aline = newline;
941                   gpgsm->colon.attic.linesize += linelen + 1;
942                 }
943             }
944           if (!err)
945             {
946               dst = *aline + *alinelen;
947
948               while (!err && src < end)
949                 {
950                   if (*src == '%' && src + 2 < end)
951                     {
952                       /* Handle escaped characters.  */
953                       ++src;
954                       *dst = _gpgme_hextobyte (src);
955                       (*alinelen)++;
956                       src += 2;
957                     }
958                   else
959                     {
960                       *dst = *src++;
961                       (*alinelen)++;
962                     }
963                   
964                   if (*dst == '\n')
965                     {
966                       /* Terminate the pending line, pass it to the colon
967                          handler and reset it.  */
968                       
969                       gpgsm->colon.any = 1;
970                       if (*alinelen > 1 && *(dst - 1) == '\r')
971                         dst--;
972                       *dst = '\0';
973
974                       /* FIXME How should we handle the return code?  */
975                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
976                       if (!err)
977                         {
978                           dst = *aline;
979                           *alinelen = 0;
980                         }
981                     }
982                   else
983                     dst++;
984                 }
985             }
986           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
987                   "fd 0x%x: D line; final status: %s",
988                   fd, err? gpg_strerror (err):"ok");
989         }
990       else if (linelen > 2
991                && line[0] == 'D' && line[1] == ' '
992                && gpgsm->inline_data)
993         {
994           char *src = line + 2;
995           char *end = line + linelen;
996           char *dst = src;
997           ssize_t nwritten;
998
999           linelen = 0;
1000           while (src < end)
1001             {
1002               if (*src == '%' && src + 2 < end)
1003                 {
1004                   /* Handle escaped characters.  */
1005                   ++src;
1006                   *dst++ = _gpgme_hextobyte (src);
1007                   src += 2;
1008                 }
1009               else
1010                 *dst++ = *src++;
1011               
1012               linelen++;
1013             }
1014           
1015           src = line + 2;
1016           while (linelen > 0)
1017             {
1018               nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
1019               if (!nwritten || (nwritten < 0 && errno != EINTR)
1020                   || nwritten > linelen)
1021                 {
1022                   err = gpg_error_from_errno (errno);
1023                   break;
1024                 }
1025               src += nwritten;
1026               linelen -= nwritten;
1027             }
1028
1029           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
1030                   "fd 0x%x: D inlinedata; final status: %s",
1031                   fd, err? gpg_strerror (err):"ok");
1032         }
1033       else if (linelen > 2
1034                && line[0] == 'S' && line[1] == ' ')
1035         {
1036           char *rest;
1037           gpgme_status_code_t r;
1038           
1039           rest = strchr (line + 2, ' ');
1040           if (!rest)
1041             rest = line + linelen; /* set to an empty string */
1042           else
1043             *(rest++) = 0;
1044
1045           r = parse_status (line + 2);
1046
1047           if (r >= 0)
1048             {
1049               if (gpgsm->status.fnc)
1050                 err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
1051             }
1052           else
1053             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
1054           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
1055                   "fd 0x%x: S line (%s) - final status: %s",
1056                   fd, line+2, err? gpg_strerror (err):"ok");
1057         }
1058       else if (linelen >= 7
1059                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
1060                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
1061                && line[6] == 'E' 
1062                && (line[7] == '\0' || line[7] == ' '))
1063         {
1064           char *keyword = line+7;
1065
1066           while (*keyword == ' ')
1067             keyword++;;
1068           default_inq_cb (gpgsm, keyword);
1069           assuan_write_line (gpgsm->assuan_ctx, "END");
1070         }
1071
1072     }
1073   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
1074           
1075   return err;
1076 }
1077
1078
1079 static gpgme_error_t
1080 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
1081 {
1082   gpgme_error_t err;
1083
1084   TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
1085               "fd %d, dir %d", iocbd->fd, iocbd->dir);
1086   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
1087                               iocbd->fd, iocbd->dir,
1088                               handler, iocbd->data, &iocbd->tag);
1089   if (err)
1090     return TRACE_ERR (err);
1091   if (!iocbd->dir)
1092     /* FIXME Kludge around poll() problem.  */
1093     err = _gpgme_io_set_nonblocking (iocbd->fd);
1094   return TRACE_ERR (err);
1095 }
1096
1097
1098 static gpgme_error_t
1099 start (engine_gpgsm_t gpgsm, const char *command)
1100 {
1101   gpgme_error_t err;
1102   int fdlist[5];
1103   int nfds;
1104
1105   /* We need to know the fd used by assuan for reads.  We do this by
1106      using the assumption that the first returned fd from
1107      assuan_get_active_fds() is always this one.  */
1108   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
1109                                 fdlist, DIM (fdlist));
1110   if (nfds < 1)
1111     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1112
1113   /* We "duplicate" the file descriptor, so we can close it here (we
1114      can't close fdlist[0], as that is closed by libassuan, and
1115      closing it here might cause libassuan to close some unrelated FD
1116      later).  Alternatively, we could special case status_fd and
1117      register/unregister it manually as needed, but this increases
1118      code duplication and is more complicated as we can not use the
1119      close notifications etc.  A third alternative would be to let
1120      Assuan know that we closed the FD, but that complicates the
1121      Assuan interface.  */
1122
1123   gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
1124   if (gpgsm->status_cb.fd < 0)
1125     return gpg_error_from_syserror ();
1126
1127   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
1128                                   close_notify_handler, gpgsm))
1129     {
1130       _gpgme_io_close (gpgsm->status_cb.fd);
1131       gpgsm->status_cb.fd = -1;
1132       return gpg_error (GPG_ERR_GENERAL);
1133     }
1134
1135   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1136   if (!err && gpgsm->input_cb.fd != -1)
1137     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1138   if (!err && gpgsm->output_cb.fd != -1)
1139     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1140   if (!err && gpgsm->message_cb.fd != -1)
1141     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1142
1143   if (!err)
1144     err = map_assuan_error (assuan_write_line (gpgsm->assuan_ctx, command));
1145
1146   if (!err)
1147     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
1148
1149   return err;
1150 }
1151
1152
1153 #if USE_DESCRIPTOR_PASSING
1154 static gpgme_error_t
1155 gpgsm_reset (void *engine)
1156 {
1157   engine_gpgsm_t gpgsm = engine;
1158
1159   /* We must send a reset because we need to reset the list of
1160      signers.  Note that RESET does not reset OPTION commands. */
1161   return gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL);
1162 }
1163 #endif
1164
1165
1166 static gpgme_error_t
1167 gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain)
1168 {
1169   engine_gpgsm_t gpgsm = engine;
1170   gpgme_error_t err;
1171
1172   if (!gpgsm)
1173     return gpg_error (GPG_ERR_INV_VALUE);
1174
1175   gpgsm->input_cb.data = ciph;
1176   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1177   if (err)
1178     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1179   gpgsm->output_cb.data = plain;
1180   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1181   if (err)
1182     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1183   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1184   gpgsm->inline_data = NULL;
1185
1186   err = start (engine, "DECRYPT");
1187   return err;
1188 }
1189
1190
1191 static gpgme_error_t
1192 gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret)
1193 {
1194   engine_gpgsm_t gpgsm = engine;
1195   gpgme_error_t err;
1196   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1197   char *linep = fpr;
1198   char *line;
1199   int length = 8;       /* "DELKEYS " */
1200
1201   if (!fpr)
1202     return gpg_error (GPG_ERR_INV_VALUE);
1203
1204   while (*linep)
1205     {
1206       length++;
1207       if (*linep == '%' || *linep == ' ' || *linep == '+')
1208         length += 2;
1209       linep++;
1210     }
1211   length++;
1212
1213   line = malloc (length);
1214   if (!line)
1215     return gpg_error_from_errno (errno);
1216
1217   strcpy (line, "DELKEYS ");
1218   linep = &line[8];
1219
1220   while (*fpr)
1221     {
1222       switch (*fpr)
1223         {
1224         case '%':
1225           *(linep++) = '%';
1226           *(linep++) = '2';
1227           *(linep++) = '5';
1228           break;
1229         case ' ':
1230           *(linep++) = '%';
1231           *(linep++) = '2';
1232           *(linep++) = '0';
1233           break;
1234         case '+':
1235           *(linep++) = '%';
1236           *(linep++) = '2';
1237           *(linep++) = 'B';
1238           break;
1239         default:
1240           *(linep++) = *fpr;
1241           break;
1242         }
1243       fpr++;
1244     }
1245   *linep = '\0';
1246
1247   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1248   gpgsm_clear_fd (gpgsm, INPUT_FD);
1249   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1250   gpgsm->inline_data = NULL;
1251
1252   err = start (gpgsm, line);
1253   free (line);
1254
1255   return err;
1256 }
1257
1258
1259 static gpgme_error_t
1260 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1261 {
1262   gpgme_error_t err = 0;
1263   assuan_context_t ctx = gpgsm->assuan_ctx;
1264   char *line;
1265   int linelen;
1266   int invalid_recipients = 0;
1267   int i = 0;
1268
1269   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1270   line = malloc (10 + 40 + 1);
1271   if (!line)
1272     return gpg_error_from_errno (errno);
1273   strcpy (line, "RECIPIENT ");
1274   while (!err && recp[i])
1275     {
1276       char *fpr;
1277       int newlen;
1278
1279       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1280         {
1281           invalid_recipients++;
1282           continue;
1283         }
1284       fpr = recp[i]->subkeys->fpr;
1285
1286       newlen = 11 + strlen (fpr);
1287       if (linelen < newlen)
1288         {
1289           char *newline = realloc (line, newlen);
1290           if (! newline)
1291             {
1292               int saved_errno = errno;
1293               free (line);
1294               return gpg_error_from_errno (saved_errno);
1295             }
1296           line = newline;
1297           linelen = newlen;
1298         }
1299       strcpy (&line[10], fpr);
1300
1301       err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc,
1302                                          gpgsm->status.fnc_value);
1303       /* FIXME: This requires more work.  */
1304       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1305         invalid_recipients++;
1306       else if (err)
1307         {
1308           free (line);
1309           return err;
1310         }
1311       i++;
1312     }
1313   free (line);
1314   return gpg_error (invalid_recipients
1315                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1316 }
1317
1318
1319 static gpgme_error_t
1320 gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags,
1321                gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1322 {
1323   engine_gpgsm_t gpgsm = engine;
1324   gpgme_error_t err;
1325
1326   if (!gpgsm)
1327     return gpg_error (GPG_ERR_INV_VALUE);
1328   if (!recp)
1329     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1330
1331   gpgsm->input_cb.data = plain;
1332   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1333   if (err)
1334     return err;
1335   gpgsm->output_cb.data = ciph;
1336   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1337                       : map_data_enc (gpgsm->output_cb.data));
1338   if (err)
1339     return err;
1340   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1341   gpgsm->inline_data = NULL;
1342
1343   err = set_recipients (gpgsm, recp);
1344
1345   if (!err)
1346     err = start (gpgsm, "ENCRYPT");
1347
1348   return err;
1349 }
1350
1351
1352 static gpgme_error_t
1353 gpgsm_export (void *engine, const char *pattern, unsigned int reserved,
1354               gpgme_data_t keydata, int use_armor)
1355 {
1356   engine_gpgsm_t gpgsm = engine;
1357   gpgme_error_t err = 0;
1358   char *cmd;
1359
1360   if (!gpgsm || reserved)
1361     return gpg_error (GPG_ERR_INV_VALUE);
1362
1363   if (!pattern)
1364     pattern = "";
1365
1366   cmd = malloc (7 + strlen (pattern) + 1);
1367   if (!cmd)
1368     return gpg_error_from_errno (errno);
1369   strcpy (cmd, "EXPORT ");
1370   strcpy (&cmd[7], pattern);
1371
1372   gpgsm->output_cb.data = keydata;
1373   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1374                       : map_data_enc (gpgsm->output_cb.data));
1375   if (err)
1376     return err;
1377   gpgsm_clear_fd (gpgsm, INPUT_FD);
1378   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1379   gpgsm->inline_data = NULL;
1380
1381   err = start (gpgsm, cmd);
1382   free (cmd);
1383   return err;
1384 }
1385
1386
1387 static gpgme_error_t
1388 gpgsm_export_ext (void *engine, const char *pattern[], unsigned int reserved,
1389                   gpgme_data_t keydata, int use_armor)
1390 {
1391   engine_gpgsm_t gpgsm = engine;
1392   gpgme_error_t err = 0;
1393   char *line;
1394   /* Length is "EXPORT " + p + '\0'.  */
1395   int length = 7 + 1;
1396   char *linep;
1397
1398   if (!gpgsm || reserved)
1399     return gpg_error (GPG_ERR_INV_VALUE);
1400
1401   if (pattern && *pattern)
1402     {
1403       const char **pat = pattern;
1404
1405       while (*pat)
1406         {
1407           const char *patlet = *pat;
1408
1409           while (*patlet)
1410             {
1411               length++;
1412               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1413                 length += 2;
1414               patlet++;
1415             }
1416           pat++;
1417           length++;
1418         }
1419     }
1420   line = malloc (length);
1421   if (!line)
1422     return gpg_error_from_errno (errno);
1423
1424   strcpy (line, "EXPORT ");
1425   linep = &line[7];
1426
1427   if (pattern && *pattern)
1428     {
1429       while (*pattern)
1430         {
1431           const char *patlet = *pattern;
1432
1433           while (*patlet)
1434             {
1435               switch (*patlet)
1436                 {
1437                 case '%':
1438                   *(linep++) = '%';
1439                   *(linep++) = '2';
1440                   *(linep++) = '5';
1441                   break;
1442                 case ' ':
1443                   *(linep++) = '%';
1444                   *(linep++) = '2';
1445                   *(linep++) = '0';
1446                   break;
1447                 case '+':
1448                   *(linep++) = '%';
1449                   *(linep++) = '2';
1450                   *(linep++) = 'B';
1451                   break;
1452                 default:
1453                   *(linep++) = *patlet;
1454                   break;
1455                 }
1456               patlet++;
1457             }
1458           pattern++;
1459           if (*pattern)
1460             *linep++ = ' ';
1461         }
1462     }
1463   *linep = '\0';
1464
1465   gpgsm->output_cb.data = keydata;
1466   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1467                       : map_data_enc (gpgsm->output_cb.data));
1468   if (err)
1469     return err;
1470   gpgsm_clear_fd (gpgsm, INPUT_FD);
1471   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1472   gpgsm->inline_data = NULL;
1473
1474   err = start (gpgsm, line);
1475   free (line);
1476   return err;
1477 }
1478
1479
1480 static gpgme_error_t
1481 gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor,
1482               gpgme_data_t pubkey, gpgme_data_t seckey)
1483 {
1484   engine_gpgsm_t gpgsm = engine;
1485   gpgme_error_t err;
1486
1487   if (!gpgsm || !pubkey || seckey)
1488     return gpg_error (GPG_ERR_INV_VALUE);
1489
1490   gpgsm->input_cb.data = help_data;
1491   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1492   if (err)
1493     return err;
1494   gpgsm->output_cb.data = pubkey;
1495   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1496                       : map_data_enc (gpgsm->output_cb.data));
1497   if (err)
1498     return err;
1499   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1500   gpgsm->inline_data = NULL;
1501
1502   err = start (gpgsm, "GENKEY");
1503   return err;
1504 }
1505
1506
1507 static gpgme_error_t
1508 gpgsm_import (void *engine, gpgme_data_t keydata)
1509 {
1510   engine_gpgsm_t gpgsm = engine;
1511   gpgme_error_t err;
1512
1513   if (!gpgsm)
1514     return gpg_error (GPG_ERR_INV_VALUE);
1515
1516   gpgsm->input_cb.data = keydata;
1517   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1518   if (err)
1519     return err;
1520   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1521   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1522   gpgsm->inline_data = NULL;
1523
1524   err = start (gpgsm, "IMPORT");
1525   return err;
1526 }
1527
1528
1529 static gpgme_error_t
1530 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1531                gpgme_keylist_mode_t mode)
1532 {
1533   engine_gpgsm_t gpgsm = engine;
1534   char *line;
1535   gpgme_error_t err;
1536   int list_mode = 0;
1537
1538   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1539     list_mode |= 1;
1540   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1541     list_mode |= 2;
1542
1543   if (!pattern)
1544     pattern = "";
1545
1546   /* Always send list-mode option because RESET does not reset it.  */
1547   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1548     return gpg_error_from_errno (errno);
1549   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1550   free (line);
1551   if (err)
1552     return err;
1553
1554
1555   /* Always send key validation because RESET does not reset it.  */
1556
1557   /* Use the validation mode if required.  We don't check for an error
1558      yet because this is a pretty fresh gpgsm features. */
1559   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1560                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1561                                "OPTION with-validation=1":
1562                                "OPTION with-validation=0" ,
1563                                NULL, NULL);
1564
1565
1566   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1567   line = malloc (15 + strlen (pattern) + 1);
1568   if (!line)
1569     return gpg_error_from_errno (errno);
1570   if (secret_only)
1571     {
1572       strcpy (line, "LISTSECRETKEYS ");
1573       strcpy (&line[15], pattern);
1574     }
1575   else
1576     {
1577       strcpy (line, "LISTKEYS ");
1578       strcpy (&line[9], pattern);
1579     }
1580
1581   gpgsm_clear_fd (gpgsm, INPUT_FD);
1582   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1583   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1584   gpgsm->inline_data = NULL;
1585
1586   err = start (gpgsm, line);
1587   free (line);
1588   return err;
1589 }
1590
1591
1592 static gpgme_error_t
1593 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1594                    int reserved, gpgme_keylist_mode_t mode)
1595 {
1596   engine_gpgsm_t gpgsm = engine;
1597   char *line;
1598   gpgme_error_t err;
1599   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1600   int length = 15 + 1;
1601   char *linep;
1602   int any_pattern = 0;
1603   int list_mode = 0;
1604
1605   if (reserved)
1606     return gpg_error (GPG_ERR_INV_VALUE);
1607
1608   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1609     list_mode |= 1;
1610   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1611     list_mode |= 2;
1612
1613   /* Always send list-mode option because RESET does not reset it.  */
1614   if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1615     return gpg_error_from_errno (errno);
1616   err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL);
1617   free (line);
1618   if (err)
1619     return err;
1620
1621   /* Always send key validation because RESET does not reset it.  */
1622   /* Use the validation mode if required.  We don't check for an error
1623      yet because this is a pretty fresh gpgsm features. */
1624   gpgsm_assuan_simple_command (gpgsm->assuan_ctx, 
1625                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1626                                "OPTION with-validation=1":
1627                                "OPTION with-validation=0" ,
1628                                NULL, NULL);
1629
1630
1631   if (pattern && *pattern)
1632     {
1633       const char **pat = pattern;
1634
1635       while (*pat)
1636         {
1637           const char *patlet = *pat;
1638
1639           while (*patlet)
1640             {
1641               length++;
1642               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1643                 length += 2;
1644               patlet++;
1645             }
1646           pat++;
1647           length++;
1648         }
1649     }
1650   line = malloc (length);
1651   if (!line)
1652     return gpg_error_from_errno (errno);
1653   if (secret_only)
1654     {
1655       strcpy (line, "LISTSECRETKEYS ");
1656       linep = &line[15];
1657     }
1658   else
1659     {
1660       strcpy (line, "LISTKEYS ");
1661       linep = &line[9];
1662     }
1663
1664   if (pattern && *pattern)
1665     {
1666       while (*pattern)
1667         {
1668           const char *patlet = *pattern;
1669
1670           while (*patlet)
1671             {
1672               switch (*patlet)
1673                 {
1674                 case '%':
1675                   *(linep++) = '%';
1676                   *(linep++) = '2';
1677                   *(linep++) = '5';
1678                   break;
1679                 case ' ':
1680                   *(linep++) = '%';
1681                   *(linep++) = '2';
1682                   *(linep++) = '0';
1683                   break;
1684                 case '+':
1685                   *(linep++) = '%';
1686                   *(linep++) = '2';
1687                   *(linep++) = 'B';
1688                   break;
1689                 default:
1690                   *(linep++) = *patlet;
1691                   break;
1692                 }
1693               patlet++;
1694             }
1695           any_pattern = 1;
1696           *linep++ = ' ';
1697           pattern++;
1698         }
1699     }
1700   if (any_pattern)
1701     linep--;
1702   *linep = '\0';
1703
1704   gpgsm_clear_fd (gpgsm, INPUT_FD);
1705   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1706   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1707   gpgsm->inline_data = NULL;
1708
1709   err = start (gpgsm, line);
1710   free (line);
1711   return err;
1712 }
1713
1714
1715 static gpgme_error_t
1716 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1717             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1718             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1719 {
1720   engine_gpgsm_t gpgsm = engine;
1721   gpgme_error_t err;
1722   char *assuan_cmd;
1723   int i;
1724   gpgme_key_t key;
1725
1726   if (!gpgsm)
1727     return gpg_error (GPG_ERR_INV_VALUE);
1728
1729   /* FIXME: This does not work as RESET does not reset it so we can't
1730      revert back to default.  */
1731   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1732     {
1733       /* FIXME: Make sure that if we run multiple operations, that we
1734          can reset any previously set value in case the default is
1735          requested.  */
1736
1737       if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0)
1738         return gpg_error_from_errno (errno);
1739       err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd,
1740                                          NULL, NULL);
1741       free (assuan_cmd);
1742       if (err)
1743         return err;
1744     }
1745
1746   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1747     {
1748       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1749       if (s && strlen (s) < 80)
1750         {
1751           char buf[100];
1752
1753           strcpy (stpcpy (buf, "SIGNER "), s);
1754           err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf,
1755                                              NULL, NULL);
1756         }
1757       else
1758         err = gpg_error (GPG_ERR_INV_VALUE);
1759       gpgme_key_unref (key);
1760       if (err) 
1761         return err;
1762     }
1763
1764   gpgsm->input_cb.data = in;
1765   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1766   if (err)
1767     return err;
1768   gpgsm->output_cb.data = out;
1769   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1770                       : map_data_enc (gpgsm->output_cb.data));
1771   if (err)
1772     return err;
1773   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1774   gpgsm->inline_data = NULL;
1775
1776   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
1777                ? "SIGN --detached" : "SIGN");
1778   return err;
1779 }
1780
1781
1782 static gpgme_error_t
1783 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
1784               gpgme_data_t plaintext)
1785 {
1786   engine_gpgsm_t gpgsm = engine;
1787   gpgme_error_t err;
1788
1789   if (!gpgsm)
1790     return gpg_error (GPG_ERR_INV_VALUE);
1791
1792   gpgsm->input_cb.data = sig;
1793   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1794   if (err)
1795     return err;
1796   if (plaintext)
1797     {
1798       /* Normal or cleartext signature.  */
1799       gpgsm->output_cb.data = plaintext;
1800       err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1801       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1802     }
1803   else
1804     {
1805       /* Detached signature.  */
1806       gpgsm->message_cb.data = signed_text;
1807       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
1808       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1809     }
1810   gpgsm->inline_data = NULL;
1811
1812   if (!err)
1813     err = start (gpgsm, "VERIFY");
1814
1815   return err;
1816 }
1817
1818
1819 /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
1820    object.  */
1821 static gpgme_error_t
1822 gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
1823 {
1824   engine_gpgsm_t gpgsm = engine;
1825   gpgme_error_t err = 0;
1826
1827   if (!gpgsm || !output)
1828     return gpg_error (GPG_ERR_INV_VALUE);
1829
1830 #if USE_DESCRIPTOR_PASSING
1831   gpgsm->output_cb.data = output;
1832   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1833   if (err)
1834     return err;
1835
1836   gpgsm_clear_fd (gpgsm, INPUT_FD);
1837   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1838   gpgsm->inline_data = NULL;
1839 # define CMD  "GETAUDITLOG"
1840 #else
1841   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1842   gpgsm_clear_fd (gpgsm, INPUT_FD);
1843   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1844   gpgsm->inline_data = output;
1845 # define CMD  "GETAUDITLOG --data"
1846 #endif
1847
1848   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
1849
1850   return err;
1851 }
1852
1853
1854
1855 static void
1856 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
1857                           void *fnc_value) 
1858 {
1859   engine_gpgsm_t gpgsm = engine;
1860
1861   gpgsm->status.fnc = fnc;
1862   gpgsm->status.fnc_value = fnc_value;
1863 }
1864
1865
1866 static gpgme_error_t
1867 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
1868                               void *fnc_value) 
1869 {
1870   engine_gpgsm_t gpgsm = engine;
1871
1872   gpgsm->colon.fnc = fnc;
1873   gpgsm->colon.fnc_value = fnc_value;
1874   gpgsm->colon.any = 0;
1875   return 0;
1876 }
1877
1878
1879 static void
1880 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
1881 {
1882   engine_gpgsm_t gpgsm = engine;
1883   gpgsm->io_cbs = *io_cbs;
1884 }
1885
1886
1887 static void
1888 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
1889 {
1890   engine_gpgsm_t gpgsm = engine;
1891
1892   TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
1893           "event %p, type %d, type_data %p",
1894           gpgsm->io_cbs.event, type, type_data);
1895   if (gpgsm->io_cbs.event)
1896     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
1897 }
1898
1899
1900 struct engine_ops _gpgme_engine_ops_gpgsm =
1901   {
1902     /* Static functions.  */
1903     _gpgme_get_gpgsm_path,
1904     gpgsm_get_version,
1905     gpgsm_get_req_version,
1906     gpgsm_new,
1907
1908     /* Member functions.  */
1909     gpgsm_release,
1910 #if USE_DESCRIPTOR_PASSING
1911     gpgsm_reset,
1912 #else
1913     NULL,                       /* reset */
1914 #endif
1915     gpgsm_set_status_handler,
1916     NULL,               /* set_command_handler */
1917     gpgsm_set_colon_line_handler,
1918     gpgsm_set_locale,
1919     gpgsm_decrypt,
1920     gpgsm_delete,
1921     NULL,               /* edit */
1922     gpgsm_encrypt,
1923     NULL,               /* encrypt_sign */
1924     gpgsm_export,
1925     gpgsm_export_ext,
1926     gpgsm_genkey,
1927     gpgsm_import,
1928     gpgsm_keylist,
1929     gpgsm_keylist_ext,
1930     gpgsm_sign,
1931     NULL,               /* trustlist */
1932     gpgsm_verify,
1933     gpgsm_getauditlog,
1934     NULL,               /* conf_load */
1935     NULL,               /* conf_save */
1936     gpgsm_set_io_cbs,
1937     gpgsm_io_event,
1938     gpgsm_cancel
1939   };