json: Remove the -noinstall flag used during development.
[gpgme.git] / src / 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, 2009,
4                  2010 g10 Code GmbH
5
6    This file is part of GPGME.
7
8    GPGME is free software; you can redistribute it and/or modify it
9    under the terms of the GNU Lesser General Public License as
10    published by the Free Software Foundation; either version 2.1 of
11    the License, or (at your option) any later version.
12
13    GPGME is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public
19    License along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21    02111-1307, USA.  */
22
23 #if HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <string.h>
29 #ifdef HAVE_SYS_TYPES_H
30 # include <sys/types.h>
31 #endif
32 #include <assert.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif
36 #ifdef HAVE_LOCALE_H
37 #include <locale.h>
38 #endif
39 #include <fcntl.h> /* FIXME */
40 #include <errno.h>
41
42 #include "gpgme.h"
43 #include "util.h"
44 #include "ops.h"
45 #include "wait.h"
46 #include "priv-io.h"
47 #include "sema.h"
48 #include "data.h"
49
50 #include "assuan.h"
51 #include "debug.h"
52
53 #include "engine-backend.h"
54
55 \f
56 typedef struct
57 {
58   int fd;       /* FD we talk about.  */
59   int server_fd;/* Server FD for this connection.  */
60   int dir;      /* Inbound/Outbound, maybe given implicit?  */
61   void *data;   /* Handler-specific data.  */
62   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
63   char server_fd_str[15]; /* Same as SERVER_FD but as a string.  We
64                              need this because _gpgme_io_fd2str can't
65                              be used on a closed descriptor.  */
66 } iocb_data_t;
67
68
69 struct engine_gpgsm
70 {
71   assuan_context_t assuan_ctx;
72
73   int lc_ctype_set;
74   int lc_messages_set;
75
76   iocb_data_t status_cb;
77
78   /* Input, output etc are from the servers perspective.  */
79   iocb_data_t input_cb;
80   gpgme_data_t input_helper_data;  /* Input helper data object.  */
81   void *input_helper_memory;       /* Input helper memory block.  */
82
83   iocb_data_t output_cb;
84
85   iocb_data_t message_cb;
86
87   struct
88   {
89     engine_status_handler_t fnc;
90     void *fnc_value;
91     gpgme_status_cb_t mon_cb;
92     void *mon_cb_value;
93   } status;
94
95   struct
96   {
97     engine_colon_line_handler_t fnc;
98     void *fnc_value;
99     struct
100     {
101       char *line;
102       int linesize;
103       int linelen;
104     } attic;
105     int any; /* any data line seen */
106   } colon;
107
108   gpgme_data_t inline_data;  /* Used to collect D lines.  */
109
110   char request_origin[10];
111
112   struct gpgme_io_cbs io_cbs;
113 };
114
115 typedef struct engine_gpgsm *engine_gpgsm_t;
116
117
118 static void gpgsm_io_event (void *engine,
119                             gpgme_event_io_t type, void *type_data);
120
121
122 \f
123 static char *
124 gpgsm_get_version (const char *file_name)
125 {
126   return _gpgme_get_program_version (file_name ? file_name
127                                      : _gpgme_get_default_gpgsm_name ());
128 }
129
130
131 static const char *
132 gpgsm_get_req_version (void)
133 {
134   return "2.0.4";
135 }
136
137 \f
138 static void
139 close_notify_handler (int fd, void *opaque)
140 {
141   engine_gpgsm_t gpgsm = opaque;
142
143   assert (fd != -1);
144   if (gpgsm->status_cb.fd == fd)
145     {
146       if (gpgsm->status_cb.tag)
147         (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag);
148       gpgsm->status_cb.fd = -1;
149       gpgsm->status_cb.tag = NULL;
150     }
151   else if (gpgsm->input_cb.fd == fd)
152     {
153       if (gpgsm->input_cb.tag)
154         (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag);
155       gpgsm->input_cb.fd = -1;
156       gpgsm->input_cb.tag = NULL;
157       if (gpgsm->input_helper_data)
158         {
159           gpgme_data_release (gpgsm->input_helper_data);
160           gpgsm->input_helper_data = NULL;
161         }
162       if (gpgsm->input_helper_memory)
163         {
164           free (gpgsm->input_helper_memory);
165           gpgsm->input_helper_memory = NULL;
166         }
167     }
168   else if (gpgsm->output_cb.fd == fd)
169     {
170       if (gpgsm->output_cb.tag)
171         (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag);
172       gpgsm->output_cb.fd = -1;
173       gpgsm->output_cb.tag = NULL;
174     }
175   else if (gpgsm->message_cb.fd == fd)
176     {
177       if (gpgsm->message_cb.tag)
178         (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag);
179       gpgsm->message_cb.fd = -1;
180       gpgsm->message_cb.tag = NULL;
181     }
182 }
183
184
185 /* This is the default inquiry callback.  We use it to handle the
186    Pinentry notifications.  */
187 static gpgme_error_t
188 default_inq_cb (engine_gpgsm_t gpgsm, const char *line)
189 {
190   (void)gpgsm;
191
192   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
193     {
194       _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10));
195     }
196
197   return 0;
198 }
199
200
201 static gpgme_error_t
202 gpgsm_cancel (void *engine)
203 {
204   engine_gpgsm_t gpgsm = engine;
205
206   if (!gpgsm)
207     return gpg_error (GPG_ERR_INV_VALUE);
208
209   if (gpgsm->status_cb.fd != -1)
210     _gpgme_io_close (gpgsm->status_cb.fd);
211   if (gpgsm->input_cb.fd != -1)
212     _gpgme_io_close (gpgsm->input_cb.fd);
213   if (gpgsm->output_cb.fd != -1)
214     _gpgme_io_close (gpgsm->output_cb.fd);
215   if (gpgsm->message_cb.fd != -1)
216     _gpgme_io_close (gpgsm->message_cb.fd);
217
218   if (gpgsm->assuan_ctx)
219     {
220       assuan_release (gpgsm->assuan_ctx);
221       gpgsm->assuan_ctx = NULL;
222     }
223
224   return 0;
225 }
226
227
228 static void
229 gpgsm_release (void *engine)
230 {
231   engine_gpgsm_t gpgsm = engine;
232
233   if (!gpgsm)
234     return;
235
236   gpgsm_cancel (engine);
237
238   free (gpgsm->colon.attic.line);
239   free (gpgsm);
240 }
241
242
243 static gpgme_error_t
244 gpgsm_new (void **engine, const char *file_name, const char *home_dir,
245            const char *version)
246 {
247   gpgme_error_t err = 0;
248   engine_gpgsm_t gpgsm;
249   const char *pgmname;
250   const char *argv[5];
251   int argc;
252 #if !USE_DESCRIPTOR_PASSING
253   int fds[2];
254   int child_fds[4];
255 #endif
256   char *dft_display = NULL;
257   char dft_ttyname[64];
258   char *env_tty = NULL;
259   char *dft_ttytype = NULL;
260   char *optstr;
261
262   (void)version; /* Not yet used.  */
263
264   gpgsm = calloc (1, sizeof *gpgsm);
265   if (!gpgsm)
266     return gpg_error_from_syserror ();
267
268   gpgsm->status_cb.fd = -1;
269   gpgsm->status_cb.dir = 1;
270   gpgsm->status_cb.tag = 0;
271   gpgsm->status_cb.data = gpgsm;
272
273   gpgsm->input_cb.fd = -1;
274   gpgsm->input_cb.dir = 0;
275   gpgsm->input_cb.tag = 0;
276   gpgsm->input_cb.server_fd = -1;
277   *gpgsm->input_cb.server_fd_str = 0;
278   gpgsm->output_cb.fd = -1;
279   gpgsm->output_cb.dir = 1;
280   gpgsm->output_cb.tag = 0;
281   gpgsm->output_cb.server_fd = -1;
282   *gpgsm->output_cb.server_fd_str = 0;
283   gpgsm->message_cb.fd = -1;
284   gpgsm->message_cb.dir = 0;
285   gpgsm->message_cb.tag = 0;
286   gpgsm->message_cb.server_fd = -1;
287   *gpgsm->message_cb.server_fd_str = 0;
288
289   gpgsm->status.fnc = 0;
290   gpgsm->colon.fnc = 0;
291   gpgsm->colon.attic.line = 0;
292   gpgsm->colon.attic.linesize = 0;
293   gpgsm->colon.attic.linelen = 0;
294   gpgsm->colon.any = 0;
295
296   gpgsm->inline_data = NULL;
297
298   gpgsm->io_cbs.add = NULL;
299   gpgsm->io_cbs.add_priv = NULL;
300   gpgsm->io_cbs.remove = NULL;
301   gpgsm->io_cbs.event = NULL;
302   gpgsm->io_cbs.event_priv = NULL;
303
304 #if !USE_DESCRIPTOR_PASSING
305   if (_gpgme_io_pipe (fds, 0) < 0)
306     {
307       err = gpg_error_from_syserror ();
308       goto leave;
309     }
310   gpgsm->input_cb.fd = fds[1];
311   gpgsm->input_cb.server_fd = fds[0];
312
313   if (_gpgme_io_pipe (fds, 1) < 0)
314     {
315       err = gpg_error_from_syserror ();
316       goto leave;
317     }
318   gpgsm->output_cb.fd = fds[0];
319   gpgsm->output_cb.server_fd = fds[1];
320
321   if (_gpgme_io_pipe (fds, 0) < 0)
322     {
323       err = gpg_error_from_syserror ();
324       goto leave;
325     }
326   gpgsm->message_cb.fd = fds[1];
327   gpgsm->message_cb.server_fd = fds[0];
328
329   child_fds[0] = gpgsm->input_cb.server_fd;
330   child_fds[1] = gpgsm->output_cb.server_fd;
331   child_fds[2] = gpgsm->message_cb.server_fd;
332   child_fds[3] = -1;
333 #endif
334
335   pgmname = file_name ? file_name : _gpgme_get_default_gpgsm_name ();
336
337   argc = 0;
338   argv[argc++] = _gpgme_get_basename (pgmname);
339   if (home_dir)
340     {
341       argv[argc++] = "--homedir";
342       argv[argc++] = home_dir;
343     }
344   argv[argc++] = "--server";
345   argv[argc++] = NULL;
346
347   err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME,
348                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
349                         NULL);
350   if (err)
351     goto leave;
352   assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks);
353
354 #if USE_DESCRIPTOR_PASSING
355   err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
356                              NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING);
357 #else
358   {
359     assuan_fd_t achild_fds[4];
360     int i;
361
362     /* For now... */
363     for (i = 0; i < 4; i++)
364       achild_fds[i] = (assuan_fd_t) child_fds[i];
365
366     err = assuan_pipe_connect (gpgsm->assuan_ctx, pgmname, argv,
367                                achild_fds, NULL, NULL, 0);
368
369     /* For now... */
370     for (i = 0; i < 4; i++)
371       child_fds[i] = (int) achild_fds[i];
372   }
373
374   /* On Windows, handles are inserted in the spawned process with
375      DuplicateHandle, and child_fds contains the server-local names
376      for the inserted handles when assuan_pipe_connect returns.  */
377   if (!err)
378     {
379       /* Note: We don't use _gpgme_io_fd2str here.  On W32 the
380          returned handles are real W32 system handles, not whatever
381          GPGME uses internally (which may be a system handle, a C
382          library handle or a GLib/Qt channel.  Confusing, yes, but
383          remember these are server-local names, so they are not part
384          of GPGME at all.  */
385       snprintf (gpgsm->input_cb.server_fd_str,
386                 sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]);
387       snprintf (gpgsm->output_cb.server_fd_str,
388                 sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]);
389       snprintf (gpgsm->message_cb.server_fd_str,
390                 sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]);
391     }
392 #endif
393   if (err)
394     goto leave;
395
396   err = _gpgme_getenv ("DISPLAY", &dft_display);
397   if (err)
398     goto leave;
399   if (dft_display)
400     {
401       if (gpgrt_asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
402         {
403           free (dft_display);
404           err = gpg_error_from_syserror ();
405           goto leave;
406         }
407       free (dft_display);
408
409       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
410                              NULL, NULL, NULL);
411       gpgrt_free (optstr);
412       if (err)
413         goto leave;
414     }
415
416   err = _gpgme_getenv ("GPG_TTY", &env_tty);
417   if (isatty (1) || env_tty || err)
418     {
419       int rc = 0;
420
421       if (err)
422         goto leave;
423       else if (env_tty)
424         {
425           snprintf (dft_ttyname, sizeof (dft_ttyname), "%s", env_tty);
426           free (env_tty);
427         }
428       else
429         rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
430
431       /* Even though isatty() returns 1, ttyname_r() may fail in many
432          ways, e.g., when /dev/pts is not accessible under chroot.  */
433       if (!rc)
434         {
435           if (gpgrt_asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
436             {
437               err = gpg_error_from_syserror ();
438               goto leave;
439             }
440           err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL,
441                                  NULL, NULL, NULL);
442           gpgrt_free (optstr);
443           if (err)
444             goto leave;
445
446           err = _gpgme_getenv ("TERM", &dft_ttytype);
447           if (err)
448             goto leave;
449           if (dft_ttytype)
450             {
451               if (gpgrt_asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype)< 0)
452                 {
453                   free (dft_ttytype);
454                   err = gpg_error_from_syserror ();
455                   goto leave;
456                 }
457               free (dft_ttytype);
458
459               err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
460                                      NULL, NULL, NULL, NULL);
461               gpgrt_free (optstr);
462               if (err)
463                 goto leave;
464             }
465         }
466     }
467
468   /* Ask gpgsm to enable the audit log support.  */
469   if (!err)
470     {
471       err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1",
472                              NULL, NULL, NULL, NULL, NULL, NULL);
473       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
474         err = 0; /* This is an optional feature of gpgsm.  */
475     }
476
477
478 #ifdef HAVE_W32_SYSTEM
479   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
480      gpgsm to tell us when it needs it.  */
481   if (!err)
482     {
483       err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify",
484                              NULL, NULL, NULL, NULL, NULL, NULL);
485       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
486         err = 0; /* This is a new feature of gpgsm.  */
487     }
488 #endif /*HAVE_W32_SYSTEM*/
489
490 #if !USE_DESCRIPTOR_PASSING
491   if (!err
492       && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd,
493                                       close_notify_handler, gpgsm)
494           || _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
495                                          close_notify_handler, gpgsm)
496           || _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
497                                          close_notify_handler, gpgsm)))
498     {
499       err = gpg_error (GPG_ERR_GENERAL);
500       goto leave;
501     }
502 #endif
503
504  leave:
505   /* Close the server ends of the pipes (because of this, we must use
506      the stored server_fd_str in the function start).  Our ends are
507      closed in gpgsm_release().  */
508 #if !USE_DESCRIPTOR_PASSING
509   if (gpgsm->input_cb.server_fd != -1)
510     _gpgme_io_close (gpgsm->input_cb.server_fd);
511   if (gpgsm->output_cb.server_fd != -1)
512     _gpgme_io_close (gpgsm->output_cb.server_fd);
513   if (gpgsm->message_cb.server_fd != -1)
514     _gpgme_io_close (gpgsm->message_cb.server_fd);
515 #endif
516
517   if (err)
518     gpgsm_release (gpgsm);
519   else
520     *engine = gpgsm;
521
522   return err;
523 }
524
525
526 /* Copy flags from CTX into the engine object.  */
527 static void
528 gpgsm_set_engine_flags (void *engine, const gpgme_ctx_t ctx)
529 {
530   engine_gpgsm_t gpgsm = engine;
531
532   if (ctx->request_origin)
533     {
534       if (strlen (ctx->request_origin) + 1 > sizeof gpgsm->request_origin)
535         strcpy (gpgsm->request_origin, "xxx"); /* Too long  - force error */
536       else
537         strcpy (gpgsm->request_origin, ctx->request_origin);
538     }
539   else
540     *gpgsm->request_origin = 0;
541 }
542
543
544 static gpgme_error_t
545 gpgsm_set_locale (void *engine, int category, const char *value)
546 {
547   engine_gpgsm_t gpgsm = engine;
548   gpgme_error_t err;
549   char *optstr;
550   const char *catstr;
551
552   /* FIXME: If value is NULL, we need to reset the option to default.
553      But we can't do this.  So we error out here.  GPGSM needs support
554      for this.  */
555   if (0)
556     ;
557 #ifdef LC_CTYPE
558   else if (category == LC_CTYPE)
559     {
560       catstr = "lc-ctype";
561       if (!value && gpgsm->lc_ctype_set)
562         return gpg_error (GPG_ERR_INV_VALUE);
563       if (value)
564         gpgsm->lc_ctype_set = 1;
565     }
566 #endif
567 #ifdef LC_MESSAGES
568   else if (category == LC_MESSAGES)
569     {
570       catstr = "lc-messages";
571       if (!value && gpgsm->lc_messages_set)
572         return gpg_error (GPG_ERR_INV_VALUE);
573       if (value)
574         gpgsm->lc_messages_set = 1;
575     }
576 #endif /* LC_MESSAGES */
577   else
578     return gpg_error (GPG_ERR_INV_VALUE);
579
580   /* FIXME: Reset value to default.  */
581   if (!value)
582     return 0;
583
584   if (gpgrt_asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
585     err = gpg_error_from_syserror ();
586   else
587     {
588       err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL,
589                              NULL, NULL, NULL, NULL);
590       gpgrt_free (optstr);
591     }
592
593   return err;
594 }
595
596
597 static gpgme_error_t
598 gpgsm_assuan_simple_command (engine_gpgsm_t gpgsm, const char *cmd,
599                              engine_status_handler_t status_fnc,
600                              void *status_fnc_value)
601 {
602   assuan_context_t ctx = gpgsm->assuan_ctx;
603   gpg_error_t err, cb_err;
604   char *line;
605   size_t linelen;
606
607   err = assuan_write_line (ctx, cmd);
608   if (err)
609     return err;
610
611   cb_err = 0;
612   do
613     {
614       err = assuan_read_line (ctx, &line, &linelen);
615       if (err)
616         break;
617
618       if (*line == '#' || !linelen)
619         continue;
620
621       if (linelen >= 2
622           && line[0] == 'O' && line[1] == 'K'
623           && (line[2] == '\0' || line[2] == ' '))
624         break;
625       else if (linelen >= 4
626           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
627           && line[3] == ' ')
628         {
629           /* We prefer a callback generated error because that one is
630              more related to gpgme and thus probably more important
631              than the error returned by the engine.  */
632           err = cb_err? cb_err : atoi (&line[4]);
633           cb_err = 0;
634         }
635       else if (linelen >= 2
636                && line[0] == 'S' && line[1] == ' ')
637         {
638           /* After an error from a status callback we skip all further
639              status lines.  */
640           if (!cb_err)
641             {
642               char *rest;
643               gpgme_status_code_t r;
644
645               rest = strchr (line + 2, ' ');
646               if (!rest)
647                 rest = line + linelen; /* set to an empty string */
648               else
649                 *(rest++) = 0;
650
651               r = _gpgme_parse_status (line + 2);
652               if (gpgsm->status.mon_cb && r != GPGME_STATUS_PROGRESS)
653                 {
654                   /* Note that we call the monitor even if we do
655                    * not know the status code (r < 0).  */
656                   cb_err = gpgsm->status.mon_cb (gpgsm->status.mon_cb_value,
657                                                  line + 2, rest);
658                 }
659
660               if (r >= 0 && status_fnc && !cb_err)
661                 cb_err = status_fnc (status_fnc_value, r, rest);
662             }
663         }
664       else
665         {
666           /* Invalid line or INQUIRY.  We can't do anything else than
667              to stop.  As with ERR we prefer a status callback
668              generated error code, though.  */
669           err = cb_err ? cb_err : gpg_error (GPG_ERR_GENERAL);
670           cb_err = 0;
671         }
672     }
673   while (!err);
674
675   /* We only want the first error from the status handler, thus we
676    * take the one saved in CB_ERR. */
677   if (!err && cb_err)
678     err = cb_err;
679
680   return err;
681 }
682
683
684 typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t;
685
686 static void
687 gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type)
688 {
689 #if !USE_DESCRIPTOR_PASSING
690   switch (fd_type)
691     {
692     case INPUT_FD:
693       _gpgme_io_close (gpgsm->input_cb.fd);
694       break;
695     case OUTPUT_FD:
696       _gpgme_io_close (gpgsm->output_cb.fd);
697       break;
698     case MESSAGE_FD:
699       _gpgme_io_close (gpgsm->message_cb.fd);
700       break;
701     }
702 #else
703   (void)gpgsm;
704   (void)fd_type;
705 #endif
706 }
707
708 #define COMMANDLINELEN 40
709 static gpgme_error_t
710 gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt)
711 {
712   gpg_error_t err = 0;
713   char line[COMMANDLINELEN];
714   const char *which;
715   iocb_data_t *iocb_data;
716 #if USE_DESCRIPTOR_PASSING
717   int dir;
718 #endif
719
720   switch (fd_type)
721     {
722     case INPUT_FD:
723       which = "INPUT";
724       iocb_data = &gpgsm->input_cb;
725       break;
726
727     case OUTPUT_FD:
728       which = "OUTPUT";
729       iocb_data = &gpgsm->output_cb;
730       break;
731
732     case MESSAGE_FD:
733       which = "MESSAGE";
734       iocb_data = &gpgsm->message_cb;
735       break;
736
737     default:
738       return gpg_error (GPG_ERR_INV_VALUE);
739     }
740
741 #if USE_DESCRIPTOR_PASSING
742   dir = iocb_data->dir;
743   /* We try to short-cut the communication by giving GPGSM direct
744      access to the file descriptor, rather than using a pipe.  */
745   iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data);
746   if (iocb_data->server_fd < 0)
747     {
748       int fds[2];
749
750       if (_gpgme_io_pipe (fds, dir) < 0)
751         return gpg_error_from_syserror ();
752
753       iocb_data->fd = dir ? fds[0] : fds[1];
754       iocb_data->server_fd = dir ? fds[1] : fds[0];
755
756       if (_gpgme_io_set_close_notify (iocb_data->fd,
757                                       close_notify_handler, gpgsm))
758         {
759           err = gpg_error (GPG_ERR_GENERAL);
760           goto leave_set_fd;
761         }
762     }
763
764   err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd);
765   if (err)
766     goto leave_set_fd;
767
768   _gpgme_io_close (iocb_data->server_fd);
769   iocb_data->server_fd = -1;
770
771   if (opt)
772     snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt);
773   else
774     snprintf (line, COMMANDLINELEN, "%s FD", which);
775 #else
776   if (opt)
777     snprintf (line, COMMANDLINELEN, "%s FD=%s %s",
778               which, iocb_data->server_fd_str, opt);
779   else
780     snprintf (line, COMMANDLINELEN, "%s FD=%s",
781               which, iocb_data->server_fd_str);
782 #endif
783
784   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
785
786 #if USE_DESCRIPTOR_PASSING
787  leave_set_fd:
788   if (err)
789     {
790       _gpgme_io_close (iocb_data->fd);
791       iocb_data->fd = -1;
792       if (iocb_data->server_fd != -1)
793         {
794           _gpgme_io_close (iocb_data->server_fd);
795           iocb_data->server_fd = -1;
796         }
797     }
798 #endif
799
800   return err;
801 }
802
803
804 static const char *
805 map_data_enc (gpgme_data_t d)
806 {
807   switch (gpgme_data_get_encoding (d))
808     {
809     case GPGME_DATA_ENCODING_NONE:
810       break;
811     case GPGME_DATA_ENCODING_BINARY:
812       return "--binary";
813     case GPGME_DATA_ENCODING_BASE64:
814       return "--base64";
815     case GPGME_DATA_ENCODING_ARMOR:
816       return "--armor";
817     default:
818       break;
819     }
820   return NULL;
821 }
822
823
824 static gpgme_error_t
825 status_handler (void *opaque, int fd)
826 {
827   struct io_cb_data *data = (struct io_cb_data *) opaque;
828   engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value;
829   gpgme_error_t err = 0;
830   char *line;
831   size_t linelen;
832
833   do
834     {
835       err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen);
836       if (err)
837         {
838           /* Try our best to terminate the connection friendly.  */
839           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
840           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
841                   "fd 0x%x: error from assuan (%d) getting status line : %s",
842                   fd, err, gpg_strerror (err));
843         }
844       else if (linelen >= 3
845                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
846                && (line[3] == '\0' || line[3] == ' '))
847         {
848           if (line[3] == ' ')
849             err = atoi (&line[4]);
850           if (! err)
851             err = gpg_error (GPG_ERR_GENERAL);
852           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
853                   "fd 0x%x: ERR line - mapped to: %s",
854                   fd, err ? gpg_strerror (err) : "ok");
855           /* Try our best to terminate the connection friendly.  */
856           /*      assuan_write_line (gpgsm->assuan_ctx, "BYE"); */
857         }
858       else if (linelen >= 2
859                && line[0] == 'O' && line[1] == 'K'
860                && (line[2] == '\0' || line[2] == ' '))
861         {
862           if (gpgsm->status.fnc)
863             {
864               char emptystring[1] = {0};
865               err = gpgsm->status.fnc (gpgsm->status.fnc_value,
866                                        GPGME_STATUS_EOF, emptystring);
867               if (gpg_err_code (err) == GPG_ERR_FALSE)
868                 err = 0; /* Drop special error code.  */
869             }
870
871           if (!err && gpgsm->colon.fnc && gpgsm->colon.any)
872             {
873               /* We must tell a colon function about the EOF. We do
874                  this only when we have seen any data lines.  Note
875                  that this inlined use of colon data lines will
876                  eventually be changed into using a regular data
877                  channel. */
878               gpgsm->colon.any = 0;
879               err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL);
880             }
881           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
882                   "fd 0x%x: OK line - final status: %s",
883                   fd, err ? gpg_strerror (err) : "ok");
884           _gpgme_io_close (gpgsm->status_cb.fd);
885           return err;
886         }
887       else if (linelen > 2
888                && line[0] == 'D' && line[1] == ' '
889                && gpgsm->colon.fnc)
890         {
891           /* We are using the colon handler even for plain inline data
892              - strange name for that function but for historic reasons
893              we keep it.  */
894           /* FIXME We can't use this for binary data because we
895              assume this is a string.  For the current usage of colon
896              output it is correct.  */
897           char *src = line + 2;
898           char *end = line + linelen;
899           char *dst;
900           char **aline = &gpgsm->colon.attic.line;
901           int *alinelen = &gpgsm->colon.attic.linelen;
902
903           if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1)
904             {
905               char *newline = realloc (*aline, *alinelen + linelen + 1);
906               if (!newline)
907                 err = gpg_error_from_syserror ();
908               else
909                 {
910                   *aline = newline;
911                   gpgsm->colon.attic.linesize = *alinelen + linelen + 1;
912                 }
913             }
914           if (!err)
915             {
916               dst = *aline + *alinelen;
917
918               while (!err && src < end)
919                 {
920                   if (*src == '%' && src + 2 < end)
921                     {
922                       /* Handle escaped characters.  */
923                       ++src;
924                       *dst = _gpgme_hextobyte (src);
925                       (*alinelen)++;
926                       src += 2;
927                     }
928                   else
929                     {
930                       *dst = *src++;
931                       (*alinelen)++;
932                     }
933
934                   if (*dst == '\n')
935                     {
936                       /* Terminate the pending line, pass it to the colon
937                          handler and reset it.  */
938
939                       gpgsm->colon.any = 1;
940                       if (*alinelen > 1 && *(dst - 1) == '\r')
941                         dst--;
942                       *dst = '\0';
943
944                       /* FIXME How should we handle the return code?  */
945                       err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline);
946                       if (!err)
947                         {
948                           dst = *aline;
949                           *alinelen = 0;
950                         }
951                     }
952                   else
953                     dst++;
954                 }
955             }
956           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
957                   "fd 0x%x: D line; final status: %s",
958                   fd, err? gpg_strerror (err):"ok");
959         }
960       else if (linelen > 2
961                && line[0] == 'D' && line[1] == ' '
962                && gpgsm->inline_data)
963         {
964           char *src = line + 2;
965           char *end = line + linelen;
966           char *dst = src;
967           gpgme_ssize_t nwritten;
968
969           linelen = 0;
970           while (src < end)
971             {
972               if (*src == '%' && src + 2 < end)
973                 {
974                   /* Handle escaped characters.  */
975                   ++src;
976                   *dst++ = _gpgme_hextobyte (src);
977                   src += 2;
978                 }
979               else
980                 *dst++ = *src++;
981
982               linelen++;
983             }
984
985           src = line + 2;
986           while (linelen > 0)
987             {
988               nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen);
989               if (!nwritten || (nwritten < 0 && errno != EINTR)
990                   || nwritten > linelen)
991                 {
992                   err = gpg_error_from_syserror ();
993                   break;
994                 }
995               src += nwritten;
996               linelen -= nwritten;
997             }
998
999           TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
1000                   "fd 0x%x: D inlinedata; final status: %s",
1001                   fd, err? gpg_strerror (err):"ok");
1002         }
1003       else if (linelen > 2
1004                && line[0] == 'S' && line[1] == ' ')
1005         {
1006           char *rest;
1007           gpgme_status_code_t r;
1008
1009           rest = strchr (line + 2, ' ');
1010           if (!rest)
1011             rest = line + linelen; /* set to an empty string */
1012           else
1013             *(rest++) = 0;
1014
1015           r = _gpgme_parse_status (line + 2);
1016
1017           if (r >= 0)
1018             {
1019               if (gpgsm->status.fnc)
1020                 {
1021                   err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest);
1022                   if (gpg_err_code (err) == GPG_ERR_FALSE)
1023                     err = 0; /* Drop special error code.  */
1024                 }
1025             }
1026           else
1027             fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest);
1028           TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm,
1029                   "fd 0x%x: S line (%s) - final status: %s",
1030                   fd, line+2, err? gpg_strerror (err):"ok");
1031         }
1032       else if (linelen >= 7
1033                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
1034                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
1035                && line[6] == 'E'
1036                && (line[7] == '\0' || line[7] == ' '))
1037         {
1038           char *keyword = line+7;
1039
1040           while (*keyword == ' ')
1041             keyword++;;
1042           default_inq_cb (gpgsm, keyword);
1043           assuan_write_line (gpgsm->assuan_ctx, "END");
1044         }
1045
1046     }
1047   while (!err && assuan_pending_line (gpgsm->assuan_ctx));
1048
1049   return err;
1050 }
1051
1052
1053 static gpgme_error_t
1054 add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
1055 {
1056   gpgme_error_t err;
1057
1058   TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
1059               "fd %d, dir %d", iocbd->fd, iocbd->dir);
1060   err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
1061                               iocbd->fd, iocbd->dir,
1062                               handler, iocbd->data, &iocbd->tag);
1063   if (err)
1064     return TRACE_ERR (err);
1065   if (!iocbd->dir)
1066     /* FIXME Kludge around poll() problem.  */
1067     err = _gpgme_io_set_nonblocking (iocbd->fd);
1068   return TRACE_ERR (err);
1069 }
1070
1071
1072 static gpgme_error_t
1073 start (engine_gpgsm_t gpgsm, const char *command)
1074 {
1075   gpgme_error_t err;
1076   assuan_fd_t afdlist[5];
1077   int fdlist[5];
1078   int nfds;
1079   int i;
1080
1081   if (*gpgsm->request_origin)
1082     {
1083       char *cmd;
1084
1085       cmd = _gpgme_strconcat ("OPTION request-origin=",
1086                               gpgsm->request_origin, NULL);
1087       if (!cmd)
1088         return gpg_error_from_syserror ();
1089       err = gpgsm_assuan_simple_command (gpgsm, cmd, NULL, NULL);
1090       free (cmd);
1091       if (err && gpg_err_code (err) != GPG_ERR_UNKNOWN_OPTION)
1092         return err;
1093     }
1094
1095   /* We need to know the fd used by assuan for reads.  We do this by
1096      using the assumption that the first returned fd from
1097      assuan_get_active_fds() is always this one.  */
1098   nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */,
1099                                 afdlist, DIM (afdlist));
1100   if (nfds < 1)
1101     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1102   /* For now... */
1103   for (i = 0; i < nfds; i++)
1104     fdlist[i] = (int) afdlist[i];
1105
1106   /* We "duplicate" the file descriptor, so we can close it here (we
1107      can't close fdlist[0], as that is closed by libassuan, and
1108      closing it here might cause libassuan to close some unrelated FD
1109      later).  Alternatively, we could special case status_fd and
1110      register/unregister it manually as needed, but this increases
1111      code duplication and is more complicated as we can not use the
1112      close notifications etc.  A third alternative would be to let
1113      Assuan know that we closed the FD, but that complicates the
1114      Assuan interface.  */
1115
1116   gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]);
1117   if (gpgsm->status_cb.fd < 0)
1118     return gpg_error_from_syserror ();
1119
1120   if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd,
1121                                   close_notify_handler, gpgsm))
1122     {
1123       _gpgme_io_close (gpgsm->status_cb.fd);
1124       gpgsm->status_cb.fd = -1;
1125       return gpg_error (GPG_ERR_GENERAL);
1126     }
1127
1128   err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler);
1129   if (!err && gpgsm->input_cb.fd != -1)
1130     err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler);
1131   if (!err && gpgsm->output_cb.fd != -1)
1132     err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler);
1133   if (!err && gpgsm->message_cb.fd != -1)
1134     err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler);
1135
1136   if (!err)
1137     err = assuan_write_line (gpgsm->assuan_ctx, command);
1138
1139   if (!err)
1140     gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL);
1141
1142   return err;
1143 }
1144
1145
1146 #if USE_DESCRIPTOR_PASSING
1147 static gpgme_error_t
1148 gpgsm_reset (void *engine)
1149 {
1150   engine_gpgsm_t gpgsm = engine;
1151
1152   /* IF we have an active connection we must send a reset because we
1153      need to reset the list of signers.  Note that RESET does not
1154      reset OPTION commands. */
1155   return (gpgsm->assuan_ctx
1156           ? gpgsm_assuan_simple_command (gpgsm, "RESET", NULL, NULL)
1157           : 0);
1158 }
1159 #endif
1160
1161
1162
1163 static gpgme_error_t
1164 gpgsm_decrypt (void *engine,
1165                gpgme_decrypt_flags_t flags,
1166                gpgme_data_t ciph, gpgme_data_t plain,
1167                int export_session_key, const char *override_session_key,
1168                int auto_key_retrieve)
1169 {
1170   engine_gpgsm_t gpgsm = engine;
1171   gpgme_error_t err;
1172
1173   (void)flags;
1174
1175   /* gpgsm is not capable of exporting session keys right now, so we
1176    * will ignore this if requested. */
1177   (void)export_session_key;
1178   (void)override_session_key;
1179
1180   /* --auto-key-retrieve is also not supported.  */
1181   (void)auto_key_retrieve;
1182
1183   if (!gpgsm)
1184     return gpg_error (GPG_ERR_INV_VALUE);
1185
1186   gpgsm->input_cb.data = ciph;
1187   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1188   if (err)
1189     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1190   gpgsm->output_cb.data = plain;
1191   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
1192   if (err)
1193     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
1194   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1195   gpgsm->inline_data = NULL;
1196
1197   err = start (engine, "DECRYPT");
1198   return err;
1199 }
1200
1201
1202 static gpgme_error_t
1203 gpgsm_delete (void *engine, gpgme_key_t key, unsigned int flags)
1204 {
1205   engine_gpgsm_t gpgsm = engine;
1206   gpgme_error_t err;
1207   char *fpr = key->subkeys ? key->subkeys->fpr : NULL;
1208   char *linep = fpr;
1209   char *line;
1210   int length = 8;       /* "DELKEYS " */
1211
1212   (void)flags;
1213
1214   if (!fpr)
1215     return gpg_error (GPG_ERR_INV_VALUE);
1216
1217   while (*linep)
1218     {
1219       length++;
1220       if (*linep == '%' || *linep == ' ' || *linep == '+')
1221         length += 2;
1222       linep++;
1223     }
1224   length++;
1225
1226   line = malloc (length);
1227   if (!line)
1228     return gpg_error_from_syserror ();
1229
1230   strcpy (line, "DELKEYS ");
1231   linep = &line[8];
1232
1233   while (*fpr)
1234     {
1235       switch (*fpr)
1236         {
1237         case '%':
1238           *(linep++) = '%';
1239           *(linep++) = '2';
1240           *(linep++) = '5';
1241           break;
1242         case ' ':
1243           *(linep++) = '%';
1244           *(linep++) = '2';
1245           *(linep++) = '0';
1246           break;
1247         case '+':
1248           *(linep++) = '%';
1249           *(linep++) = '2';
1250           *(linep++) = 'B';
1251           break;
1252         default:
1253           *(linep++) = *fpr;
1254           break;
1255         }
1256       fpr++;
1257     }
1258   *linep = '\0';
1259
1260   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1261   gpgsm_clear_fd (gpgsm, INPUT_FD);
1262   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1263   gpgsm->inline_data = NULL;
1264
1265   err = start (gpgsm, line);
1266   free (line);
1267
1268   return err;
1269 }
1270
1271
1272 static gpgme_error_t
1273 set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[])
1274 {
1275   gpgme_error_t err = 0;
1276   char *line;
1277   int linelen;
1278   int invalid_recipients = 0;
1279   int i;
1280
1281   linelen = 10 + 40 + 1;        /* "RECIPIENT " + guess + '\0'.  */
1282   line = malloc (10 + 40 + 1);
1283   if (!line)
1284     return gpg_error_from_syserror ();
1285   strcpy (line, "RECIPIENT ");
1286   for (i =0; !err && recp[i]; i++)
1287     {
1288       char *fpr;
1289       int newlen;
1290
1291       if (!recp[i]->subkeys || !recp[i]->subkeys->fpr)
1292         {
1293           invalid_recipients++;
1294           continue;
1295         }
1296       fpr = recp[i]->subkeys->fpr;
1297
1298       newlen = 11 + strlen (fpr);
1299       if (linelen < newlen)
1300         {
1301           char *newline = realloc (line, newlen);
1302           if (! newline)
1303             {
1304               int saved_err = gpg_error_from_syserror ();
1305               free (line);
1306               return saved_err;
1307             }
1308           line = newline;
1309           linelen = newlen;
1310         }
1311       strcpy (&line[10], fpr);
1312
1313       err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
1314                                          gpgsm->status.fnc_value);
1315       /* FIXME: This requires more work.  */
1316       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1317         invalid_recipients++;
1318       else if (err)
1319         {
1320           free (line);
1321           return err;
1322         }
1323     }
1324   free (line);
1325   return gpg_error (invalid_recipients
1326                     ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR);
1327 }
1328
1329
1330 /* Take recipients from the LF delimited STRING and send RECIPIENT
1331  * commands to gpgsm.  */
1332 static gpgme_error_t
1333 set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string)
1334 {
1335   gpg_error_t err = 0;
1336   char *line = NULL;
1337   int no_pubkey = 0;
1338   const char *s;
1339   int n;
1340
1341   for (;;)
1342     {
1343       while (*string == ' ' || *string == '\t')
1344         string++;
1345       if (!*string)
1346         break;
1347
1348       s = strchr (string, '\n');
1349       if (s)
1350         n = s - string;
1351       else
1352         n = strlen (string);
1353       while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
1354         n--;
1355
1356       gpgrt_free (line);
1357       if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
1358         {
1359           err = gpg_error_from_syserror ();
1360           break;
1361         }
1362       string += n + !!s;
1363
1364       err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
1365                                          gpgsm->status.fnc_value);
1366
1367       /* Fixme: Improve error reporting.  */
1368       if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1369         no_pubkey++;
1370       else if (err)
1371         break;
1372     }
1373   gpgrt_free (line);
1374   return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
1375 }
1376
1377
1378 static gpgme_error_t
1379 gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
1380                gpgme_encrypt_flags_t flags,
1381                gpgme_data_t plain, gpgme_data_t ciph, int use_armor)
1382 {
1383   engine_gpgsm_t gpgsm = engine;
1384   gpgme_error_t err;
1385
1386   if (!gpgsm)
1387     return gpg_error (GPG_ERR_INV_VALUE);
1388   if (!recp)
1389     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1390
1391   if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
1392     {
1393       err = gpgsm_assuan_simple_command (gpgsm,
1394                                          "OPTION no-encrypt-to", NULL, NULL);
1395       if (err)
1396         return err;
1397     }
1398
1399   gpgsm->input_cb.data = plain;
1400   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1401   if (err)
1402     return err;
1403   gpgsm->output_cb.data = ciph;
1404   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1405                       : map_data_enc (gpgsm->output_cb.data));
1406   if (err)
1407     return err;
1408   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1409   gpgsm->inline_data = NULL;
1410
1411   if (!recp && recpstring)
1412     err = set_recipients_from_string (gpgsm, recpstring);
1413   else
1414     err = set_recipients (gpgsm, recp);
1415
1416   if (!err)
1417     err = start (gpgsm, "ENCRYPT");
1418
1419   return err;
1420 }
1421
1422
1423 static gpgme_error_t
1424 gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode,
1425               gpgme_data_t keydata, int use_armor)
1426 {
1427   engine_gpgsm_t gpgsm = engine;
1428   gpgme_error_t err = 0;
1429   char *cmd;
1430
1431   if (!gpgsm)
1432     return gpg_error (GPG_ERR_INV_VALUE);
1433
1434   if (!pattern)
1435     pattern = "";
1436
1437   cmd = malloc (7 + 9 + 9 + strlen (pattern) + 1);
1438   if (!cmd)
1439     return gpg_error_from_syserror ();
1440
1441   strcpy (cmd, "EXPORT ");
1442   if ((mode & GPGME_EXPORT_MODE_SECRET))
1443     {
1444       strcat (cmd, "--secret ");
1445       if ((mode & GPGME_EXPORT_MODE_RAW))
1446         strcat (cmd, "--raw ");
1447       else if ((mode & GPGME_EXPORT_MODE_PKCS12))
1448         strcat (cmd, "--pkcs12 ");
1449     }
1450   strcat (cmd, pattern);
1451
1452   gpgsm->output_cb.data = keydata;
1453   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1454                       : map_data_enc (gpgsm->output_cb.data));
1455   if (err)
1456     return err;
1457   gpgsm_clear_fd (gpgsm, INPUT_FD);
1458   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1459   gpgsm->inline_data = NULL;
1460
1461   err = start (gpgsm, cmd);
1462   free (cmd);
1463   return err;
1464 }
1465
1466
1467 static gpgme_error_t
1468 gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode,
1469                   gpgme_data_t keydata, int use_armor)
1470 {
1471   engine_gpgsm_t gpgsm = engine;
1472   gpgme_error_t err = 0;
1473   char *line;
1474   /* Length is "EXPORT " + "--secret " + "--pkcs12 " + p + '\0'.  */
1475   int length = 7 + 9 + 9 + 1;
1476   char *linep;
1477
1478   if (!gpgsm)
1479     return gpg_error (GPG_ERR_INV_VALUE);
1480
1481   if (pattern && *pattern)
1482     {
1483       const char **pat = pattern;
1484
1485       while (*pat)
1486         {
1487           const char *patlet = *pat;
1488
1489           while (*patlet)
1490             {
1491               length++;
1492               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1493                 length += 2;
1494               patlet++;
1495             }
1496           pat++;
1497           length++;
1498         }
1499     }
1500   line = malloc (length);
1501   if (!line)
1502     return gpg_error_from_syserror ();
1503
1504   strcpy (line, "EXPORT ");
1505   if ((mode & GPGME_EXPORT_MODE_SECRET))
1506     {
1507       strcat (line, "--secret ");
1508       if ((mode & GPGME_EXPORT_MODE_RAW))
1509         strcat (line, "--raw ");
1510       else if ((mode & GPGME_EXPORT_MODE_PKCS12))
1511         strcat (line, "--pkcs12 ");
1512     }
1513   linep = &line[strlen (line)];
1514
1515   if (pattern && *pattern)
1516     {
1517       while (*pattern)
1518         {
1519           const char *patlet = *pattern;
1520
1521           while (*patlet)
1522             {
1523               switch (*patlet)
1524                 {
1525                 case '%':
1526                   *(linep++) = '%';
1527                   *(linep++) = '2';
1528                   *(linep++) = '5';
1529                   break;
1530                 case ' ':
1531                   *(linep++) = '%';
1532                   *(linep++) = '2';
1533                   *(linep++) = '0';
1534                   break;
1535                 case '+':
1536                   *(linep++) = '%';
1537                   *(linep++) = '2';
1538                   *(linep++) = 'B';
1539                   break;
1540                 default:
1541                   *(linep++) = *patlet;
1542                   break;
1543                 }
1544               patlet++;
1545             }
1546           pattern++;
1547           if (*pattern)
1548             *linep++ = ' ';
1549         }
1550     }
1551   *linep = '\0';
1552
1553   gpgsm->output_cb.data = keydata;
1554   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1555                       : map_data_enc (gpgsm->output_cb.data));
1556   if (err)
1557     return err;
1558   gpgsm_clear_fd (gpgsm, INPUT_FD);
1559   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1560   gpgsm->inline_data = NULL;
1561
1562   err = start (gpgsm, line);
1563   free (line);
1564   return err;
1565 }
1566
1567
1568 static gpgme_error_t
1569 gpgsm_genkey (void *engine,
1570               const char *userid, const char *algo,
1571               unsigned long reserved, unsigned long expires,
1572               gpgme_key_t key, unsigned int flags,
1573               gpgme_data_t help_data, unsigned int extraflags,
1574               gpgme_data_t pubkey, gpgme_data_t seckey)
1575 {
1576   engine_gpgsm_t gpgsm = engine;
1577   gpgme_error_t err;
1578
1579   (void)reserved;
1580
1581   if (!gpgsm)
1582     return gpg_error (GPG_ERR_INV_VALUE);
1583
1584   if (help_data)
1585     {
1586       if (!pubkey || seckey)
1587         return gpg_error (GPG_ERR_INV_VALUE);
1588
1589       gpgsm->input_cb.data = help_data;
1590       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1591       if (err)
1592         return err;
1593       gpgsm->output_cb.data = pubkey;
1594       err = gpgsm_set_fd (gpgsm, OUTPUT_FD,
1595                           (extraflags & GENKEY_EXTRAFLAG_ARMOR)? "--armor"
1596                           : map_data_enc (gpgsm->output_cb.data));
1597       if (err)
1598         return err;
1599       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1600       gpgsm->inline_data = NULL;
1601
1602       err = start (gpgsm, "GENKEY");
1603       return err;
1604     }
1605
1606   (void)userid;
1607   (void)algo;
1608   (void)expires;
1609   (void)key;
1610   (void)flags;
1611
1612   /* The new interface has not yet been implemented,  */
1613   return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1614 }
1615
1616
1617 static gpgme_error_t
1618 gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray)
1619 {
1620   engine_gpgsm_t gpgsm = engine;
1621   gpgme_error_t err;
1622   gpgme_data_encoding_t dataenc;
1623   int idx;
1624
1625   if (!gpgsm)
1626     return gpg_error (GPG_ERR_INV_VALUE);
1627
1628   if (keydata && keyarray)
1629     return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed.  */
1630
1631   dataenc = gpgme_data_get_encoding (keydata);
1632
1633   if (keyarray)
1634     {
1635       size_t buflen;
1636       char *buffer, *p;
1637
1638       /* Fist check whether the engine already features the
1639          --re-import option.  */
1640       err = gpgsm_assuan_simple_command
1641         (gpgsm, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL);
1642       if (err)
1643         return gpg_error (GPG_ERR_NOT_SUPPORTED);
1644
1645       /* Create an internal data object with a list of all
1646          fingerprints.  The data object and its memory (to avoid an
1647          extra copy by gpgme_data_new_from_mem) are stored in two
1648          variables which are released by the close_notify_handler.  */
1649       for (idx=0, buflen=0; keyarray[idx]; idx++)
1650         {
1651           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1652               && keyarray[idx]->subkeys
1653               && keyarray[idx]->subkeys->fpr
1654               && *keyarray[idx]->subkeys->fpr)
1655             buflen += strlen (keyarray[idx]->subkeys->fpr) + 1;
1656         }
1657       /* Allocate a bufer with extra space for the trailing Nul
1658          introduced by the use of stpcpy.  */
1659       buffer = malloc (buflen+1);
1660       if (!buffer)
1661         return gpg_error_from_syserror ();
1662       for (idx=0, p = buffer; keyarray[idx]; idx++)
1663         {
1664           if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS
1665               && keyarray[idx]->subkeys
1666               && keyarray[idx]->subkeys->fpr
1667               && *keyarray[idx]->subkeys->fpr)
1668             p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n");
1669         }
1670
1671       err = gpgme_data_new_from_mem (&gpgsm->input_helper_data,
1672                                      buffer, buflen, 0);
1673       if (err)
1674         {
1675           free (buffer);
1676           return err;
1677         }
1678       gpgsm->input_helper_memory = buffer;
1679
1680       gpgsm->input_cb.data = gpgsm->input_helper_data;
1681       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1682       if (err)
1683         {
1684           gpgme_data_release (gpgsm->input_helper_data);
1685           gpgsm->input_helper_data = NULL;
1686           free (gpgsm->input_helper_memory);
1687           gpgsm->input_helper_memory = NULL;
1688           return err;
1689         }
1690       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1691       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1692       gpgsm->inline_data = NULL;
1693
1694       return start (gpgsm, "IMPORT --re-import");
1695     }
1696   else if (dataenc == GPGME_DATA_ENCODING_URL
1697            || dataenc == GPGME_DATA_ENCODING_URL0
1698            || dataenc == GPGME_DATA_ENCODING_URLESC)
1699     {
1700       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1701     }
1702   else
1703     {
1704       gpgsm->input_cb.data = keydata;
1705       err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1706       if (err)
1707         return err;
1708       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1709       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1710       gpgsm->inline_data = NULL;
1711
1712       return start (gpgsm, "IMPORT");
1713     }
1714 }
1715
1716
1717 static gpgme_error_t
1718 gpgsm_keylist (void *engine, const char *pattern, int secret_only,
1719                gpgme_keylist_mode_t mode, int engine_flags)
1720 {
1721   engine_gpgsm_t gpgsm = engine;
1722   char *line;
1723   gpgme_error_t err;
1724   int list_mode = 0;
1725
1726   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1727     list_mode |= 1;
1728   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1729     list_mode |= 2;
1730
1731   if (!pattern)
1732     pattern = "";
1733
1734   /* Hack to make sure that the agent is started.  Only if the agent
1735      has been started an application may connect to the agent via
1736      GPGME_PROTOCOL_ASSUAN - for example to look for smartcards.  We
1737      do this only if a secret key listing has been requested.  In
1738      general this is not needed because a secret key listing starts
1739      the agent.  However on a fresh installation no public keys are
1740      available and thus there is no need for gpgsm to ask the agent
1741      whether a secret key exists for the public key.  */
1742   if (secret_only || (mode & GPGME_KEYLIST_MODE_WITH_SECRET))
1743     gpgsm_assuan_simple_command (gpgsm, "GETINFO agent-check", NULL, NULL);
1744
1745   /* Always send list-mode option because RESET does not reset it.  */
1746   if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1747     return gpg_error_from_syserror ();
1748   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
1749   gpgrt_free (line);
1750   if (err)
1751     return err;
1752
1753
1754   /* Always send key validation because RESET does not reset it.  */
1755
1756   /* Use the validation mode if requested.  We don't check for an error
1757      yet because this is a pretty fresh gpgsm features. */
1758   gpgsm_assuan_simple_command (gpgsm,
1759                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1760                                "OPTION with-validation=1":
1761                                "OPTION with-validation=0" ,
1762                                NULL, NULL);
1763   /* Include the ephemeral keys if requested.  We don't check for an error
1764      yet because this is a pretty fresh gpgsm features. */
1765   gpgsm_assuan_simple_command (gpgsm,
1766                                (mode & GPGME_KEYLIST_MODE_EPHEMERAL)?
1767                                "OPTION with-ephemeral-keys=1":
1768                                "OPTION with-ephemeral-keys=0" ,
1769                                NULL, NULL);
1770   gpgsm_assuan_simple_command (gpgsm,
1771                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1772                                "OPTION with-secret=1":
1773                                "OPTION with-secret=0" ,
1774                                NULL, NULL);
1775   gpgsm_assuan_simple_command (gpgsm,
1776                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1777                                "OPTION offline=1":
1778                                "OPTION offline=0" ,
1779                                NULL, NULL);
1780
1781
1782   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1783   line = malloc (15 + strlen (pattern) + 1);
1784   if (!line)
1785     return gpg_error_from_syserror ();
1786   if (secret_only)
1787     {
1788       strcpy (line, "LISTSECRETKEYS ");
1789       strcpy (&line[15], pattern);
1790     }
1791   else
1792     {
1793       strcpy (line, "LISTKEYS ");
1794       strcpy (&line[9], pattern);
1795     }
1796
1797   gpgsm_clear_fd (gpgsm, INPUT_FD);
1798   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1799   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1800   gpgsm->inline_data = NULL;
1801
1802   err = start (gpgsm, line);
1803   free (line);
1804   return err;
1805 }
1806
1807
1808 static gpgme_error_t
1809 gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only,
1810                    int reserved, gpgme_keylist_mode_t mode, int engine_flags)
1811 {
1812   engine_gpgsm_t gpgsm = engine;
1813   char *line;
1814   gpgme_error_t err;
1815   /* Length is "LISTSECRETKEYS " + p + '\0'.  */
1816   int length = 15 + 1;
1817   char *linep;
1818   int any_pattern = 0;
1819   int list_mode = 0;
1820
1821   if (reserved)
1822     return gpg_error (GPG_ERR_INV_VALUE);
1823
1824   if (mode & GPGME_KEYLIST_MODE_LOCAL)
1825     list_mode |= 1;
1826   if (mode & GPGME_KEYLIST_MODE_EXTERN)
1827     list_mode |= 2;
1828
1829   /* Always send list-mode option because RESET does not reset it.  */
1830   if (gpgrt_asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0)
1831     return gpg_error_from_syserror ();
1832   err = gpgsm_assuan_simple_command (gpgsm, line, NULL, NULL);
1833   gpgrt_free (line);
1834   if (err)
1835     return err;
1836
1837   /* Always send key validation because RESET does not reset it.  */
1838   /* Use the validation mode if required.  We don't check for an error
1839      yet because this is a pretty fresh gpgsm features. */
1840   gpgsm_assuan_simple_command (gpgsm,
1841                                (mode & GPGME_KEYLIST_MODE_VALIDATE)?
1842                                "OPTION with-validation=1":
1843                                "OPTION with-validation=0" ,
1844                                NULL, NULL);
1845   gpgsm_assuan_simple_command (gpgsm,
1846                                (mode & GPGME_KEYLIST_MODE_WITH_SECRET)?
1847                                "OPTION with-secret=1":
1848                                "OPTION with-secret=0" ,
1849                                NULL, NULL);
1850   gpgsm_assuan_simple_command (gpgsm,
1851                                (engine_flags & GPGME_ENGINE_FLAG_OFFLINE)?
1852                                "OPTION offline=1":
1853                                "OPTION offline=0" ,
1854                                NULL, NULL);
1855
1856   if (pattern && *pattern)
1857     {
1858       const char **pat = pattern;
1859
1860       while (*pat)
1861         {
1862           const char *patlet = *pat;
1863
1864           while (*patlet)
1865             {
1866               length++;
1867               if (*patlet == '%' || *patlet == ' ' || *patlet == '+')
1868                 length += 2;
1869               patlet++;
1870             }
1871           pat++;
1872           length++;
1873         }
1874     }
1875   line = malloc (length);
1876   if (!line)
1877     return gpg_error_from_syserror ();
1878   if (secret_only)
1879     {
1880       strcpy (line, "LISTSECRETKEYS ");
1881       linep = &line[15];
1882     }
1883   else
1884     {
1885       strcpy (line, "LISTKEYS ");
1886       linep = &line[9];
1887     }
1888
1889   if (pattern && *pattern)
1890     {
1891       while (*pattern)
1892         {
1893           const char *patlet = *pattern;
1894
1895           while (*patlet)
1896             {
1897               switch (*patlet)
1898                 {
1899                 case '%':
1900                   *(linep++) = '%';
1901                   *(linep++) = '2';
1902                   *(linep++) = '5';
1903                   break;
1904                 case ' ':
1905                   *(linep++) = '%';
1906                   *(linep++) = '2';
1907                   *(linep++) = '0';
1908                   break;
1909                 case '+':
1910                   *(linep++) = '%';
1911                   *(linep++) = '2';
1912                   *(linep++) = 'B';
1913                   break;
1914                 default:
1915                   *(linep++) = *patlet;
1916                   break;
1917                 }
1918               patlet++;
1919             }
1920           any_pattern = 1;
1921           *linep++ = ' ';
1922           pattern++;
1923         }
1924     }
1925   if (any_pattern)
1926     linep--;
1927   *linep = '\0';
1928
1929   gpgsm_clear_fd (gpgsm, INPUT_FD);
1930   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
1931   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
1932   gpgsm->inline_data = NULL;
1933
1934   err = start (gpgsm, line);
1935   free (line);
1936   return err;
1937 }
1938
1939
1940 static gpgme_error_t
1941 gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out,
1942             gpgme_sig_mode_t mode, int use_armor, int use_textmode,
1943             int include_certs, gpgme_ctx_t ctx /* FIXME */)
1944 {
1945   engine_gpgsm_t gpgsm = engine;
1946   gpgme_error_t err;
1947   char *assuan_cmd;
1948   int i;
1949   gpgme_key_t key;
1950
1951   (void)use_textmode;
1952
1953   if (!gpgsm)
1954     return gpg_error (GPG_ERR_INV_VALUE);
1955
1956   /* FIXME: This does not work as RESET does not reset it so we can't
1957      revert back to default.  */
1958   if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT)
1959     {
1960       /* FIXME: Make sure that if we run multiple operations, that we
1961          can reset any previously set value in case the default is
1962          requested.  */
1963
1964       if (gpgrt_asprintf (&assuan_cmd,
1965                           "OPTION include-certs %i", include_certs) < 0)
1966         return gpg_error_from_syserror ();
1967       err = gpgsm_assuan_simple_command (gpgsm, assuan_cmd, NULL, NULL);
1968       gpgrt_free (assuan_cmd);
1969       if (err)
1970         return err;
1971     }
1972
1973   for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++)
1974     {
1975       const char *s = key->subkeys ? key->subkeys->fpr : NULL;
1976       if (s && strlen (s) < 80)
1977         {
1978           char buf[100];
1979
1980           strcpy (stpcpy (buf, "SIGNER "), s);
1981           err = gpgsm_assuan_simple_command (gpgsm, buf,
1982                                              gpgsm->status.fnc,
1983                                              gpgsm->status.fnc_value);
1984         }
1985       else
1986         err = gpg_error (GPG_ERR_INV_VALUE);
1987       gpgme_key_unref (key);
1988       if (err)
1989         return err;
1990     }
1991
1992   gpgsm->input_cb.data = in;
1993   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
1994   if (err)
1995     return err;
1996   gpgsm->output_cb.data = out;
1997   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor"
1998                       : map_data_enc (gpgsm->output_cb.data));
1999   if (err)
2000     return err;
2001   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2002   gpgsm->inline_data = NULL;
2003
2004   err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH
2005                ? "SIGN --detached" : "SIGN");
2006   return err;
2007 }
2008
2009
2010 static gpgme_error_t
2011 gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text,
2012               gpgme_data_t plaintext, gpgme_ctx_t ctx)
2013 {
2014   engine_gpgsm_t gpgsm = engine;
2015   gpgme_error_t err;
2016
2017   (void)ctx;
2018
2019   if (!gpgsm)
2020     return gpg_error (GPG_ERR_INV_VALUE);
2021
2022   gpgsm->input_cb.data = sig;
2023   err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data));
2024   if (err)
2025     return err;
2026   if (plaintext)
2027     {
2028       /* Normal or cleartext signature.  */
2029       gpgsm->output_cb.data = plaintext;
2030       err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
2031       gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2032     }
2033   else
2034     {
2035       /* Detached signature.  */
2036       gpgsm->message_cb.data = signed_text;
2037       err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0);
2038       gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2039     }
2040   gpgsm->inline_data = NULL;
2041
2042   if (!err)
2043     err = start (gpgsm, "VERIFY");
2044
2045   return err;
2046 }
2047
2048
2049 /* Send the GETAUDITLOG command.  The result is saved to a gpgme data
2050    object.  */
2051 static gpgme_error_t
2052 gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags)
2053 {
2054   engine_gpgsm_t gpgsm = engine;
2055   gpgme_error_t err = 0;
2056
2057   if (!gpgsm || !output)
2058     return gpg_error (GPG_ERR_INV_VALUE);
2059
2060 #if USE_DESCRIPTOR_PASSING
2061   gpgsm->output_cb.data = output;
2062   err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0);
2063   if (err)
2064     return err;
2065
2066   gpgsm_clear_fd (gpgsm, INPUT_FD);
2067   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2068   gpgsm->inline_data = NULL;
2069 # define CMD  "GETAUDITLOG"
2070 #else
2071   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2072   gpgsm_clear_fd (gpgsm, INPUT_FD);
2073   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2074   gpgsm->inline_data = output;
2075 # define CMD  "GETAUDITLOG --data"
2076 #endif
2077
2078   err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD);
2079
2080   return err;
2081 }
2082
2083
2084 /* This sets a status callback for monitoring status lines before they
2085  * are passed to a caller set handler.  */
2086 static void
2087 gpgsm_set_status_cb (void *engine, gpgme_status_cb_t cb, void *cb_value)
2088 {
2089   engine_gpgsm_t gpgsm = engine;
2090
2091   gpgsm->status.mon_cb = cb;
2092   gpgsm->status.mon_cb_value = cb_value;
2093 }
2094
2095
2096 static void
2097 gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc,
2098                           void *fnc_value)
2099 {
2100   engine_gpgsm_t gpgsm = engine;
2101
2102   gpgsm->status.fnc = fnc;
2103   gpgsm->status.fnc_value = fnc_value;
2104 }
2105
2106
2107 static gpgme_error_t
2108 gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc,
2109                               void *fnc_value)
2110 {
2111   engine_gpgsm_t gpgsm = engine;
2112
2113   gpgsm->colon.fnc = fnc;
2114   gpgsm->colon.fnc_value = fnc_value;
2115   gpgsm->colon.any = 0;
2116   return 0;
2117 }
2118
2119
2120 static void
2121 gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
2122 {
2123   engine_gpgsm_t gpgsm = engine;
2124   gpgsm->io_cbs = *io_cbs;
2125 }
2126
2127
2128 static void
2129 gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data)
2130 {
2131   engine_gpgsm_t gpgsm = engine;
2132
2133   TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm,
2134           "event %p, type %d, type_data %p",
2135           gpgsm->io_cbs.event, type, type_data);
2136   if (gpgsm->io_cbs.event)
2137     (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data);
2138 }
2139
2140
2141 static gpgme_error_t
2142 gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags)
2143 {
2144   engine_gpgsm_t gpgsm = engine;
2145   gpgme_error_t err;
2146   char *line;
2147
2148   (void)flags;
2149
2150   if (!key || !key->subkeys || !key->subkeys->fpr)
2151     return gpg_error (GPG_ERR_INV_CERT_OBJ);
2152
2153   if (gpgrt_asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0)
2154     return gpg_error_from_syserror ();
2155
2156   gpgsm_clear_fd (gpgsm, OUTPUT_FD);
2157   gpgsm_clear_fd (gpgsm, INPUT_FD);
2158   gpgsm_clear_fd (gpgsm, MESSAGE_FD);
2159   gpgsm->inline_data = NULL;
2160
2161   err = start (gpgsm, line);
2162   gpgrt_free (line);
2163
2164   return err;
2165 }
2166
2167
2168
2169 struct engine_ops _gpgme_engine_ops_gpgsm =
2170   {
2171     /* Static functions.  */
2172     _gpgme_get_default_gpgsm_name,
2173     NULL,
2174     gpgsm_get_version,
2175     gpgsm_get_req_version,
2176     gpgsm_new,
2177
2178     /* Member functions.  */
2179     gpgsm_release,
2180 #if USE_DESCRIPTOR_PASSING
2181     gpgsm_reset,
2182 #else
2183     NULL,                       /* reset */
2184 #endif
2185     gpgsm_set_status_cb,
2186     gpgsm_set_status_handler,
2187     NULL,               /* set_command_handler */
2188     gpgsm_set_colon_line_handler,
2189     gpgsm_set_locale,
2190     NULL,               /* set_protocol */
2191     gpgsm_set_engine_flags,
2192     gpgsm_decrypt,
2193     gpgsm_delete,       /* decrypt_verify */
2194     NULL,               /* edit */
2195     gpgsm_encrypt,
2196     NULL,               /* encrypt_sign */
2197     gpgsm_export,
2198     gpgsm_export_ext,
2199     gpgsm_genkey,
2200     gpgsm_import,
2201     gpgsm_keylist,
2202     gpgsm_keylist_ext,
2203     NULL,               /* keylist_data */
2204     NULL,               /* keysign */
2205     NULL,               /* tofu_policy */
2206     gpgsm_sign,
2207     NULL,               /* trustlist */
2208     gpgsm_verify,
2209     gpgsm_getauditlog,
2210     NULL,               /* opassuan_transact */
2211     NULL,               /* conf_load */
2212     NULL,               /* conf_save */
2213     NULL,               /* conf_dir */
2214     NULL,               /* query_swdb */
2215     gpgsm_set_io_cbs,
2216     gpgsm_io_event,
2217     gpgsm_cancel,
2218     NULL,               /* cancel_op */
2219     gpgsm_passwd,
2220     NULL,               /* set_pinentry_mode */
2221     NULL                /* opspawn */
2222   };