core: Pass the engine's version string to the engine's new function.
[gpgme.git] / src / engine-assuan.c
1 /* engine-assuan.c - Low-level Assuan protocol engine
2  * Copyright (C) 2009 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /*
21    Note: This engine requires a modern Assuan server which uses
22    gpg-error codes.  In particular there is no backward compatible
23    mapping of old Assuan error codes implemented.
24 */
25
26
27 #if HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <stdlib.h>
32 #include <string.h>
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36 #include <assert.h>
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40 #ifdef HAVE_LOCALE_H
41 #include <locale.h>
42 #endif
43 #include <errno.h>
44
45 #include "gpgme.h"
46 #include "util.h"
47 #include "ops.h"
48 #include "wait.h"
49 #include "priv-io.h"
50 #include "sema.h"
51
52 #include "assuan.h"
53 #include "debug.h"
54
55 #include "engine-backend.h"
56
57 \f
58 typedef struct
59 {
60   int fd;       /* FD we talk about.  */
61   int server_fd;/* Server FD for this connection.  */
62   int dir;      /* Inbound/Outbound, maybe given implicit?  */
63   void *data;   /* Handler-specific data.  */
64   void *tag;    /* ID from the user for gpgme_remove_io_callback.  */
65 } iocb_data_t;
66
67 /* Engine instance data.  */
68 struct engine_llass
69 {
70   assuan_context_t assuan_ctx;
71
72   int lc_ctype_set;
73   int lc_messages_set;
74
75   iocb_data_t status_cb;
76
77   struct gpgme_io_cbs io_cbs;
78
79   /* Hack for old opassuan.c interface, see there the result struct.  */
80   gpg_error_t last_op_err;
81
82   /* User provided callbacks.  */
83   struct {
84     gpgme_assuan_data_cb_t data_cb;
85     void *data_cb_value;
86
87     gpgme_assuan_inquire_cb_t inq_cb;
88     void *inq_cb_value;
89
90     gpgme_assuan_status_cb_t status_cb;
91     void *status_cb_value;
92   } user;
93
94   /* Option flags.  */
95   struct {
96     int gpg_agent:1;  /* Assume this is a gpg-agent connection.  */
97   } opt;
98
99 };
100 typedef struct engine_llass *engine_llass_t;
101
102
103 gpg_error_t _gpgme_engine_assuan_last_op_err (void *engine)
104 {
105   engine_llass_t llass = engine;
106   return llass->last_op_err;
107 }
108
109
110 /* Prototypes.  */
111 static void llass_io_event (void *engine,
112                             gpgme_event_io_t type, void *type_data);
113
114
115
116
117 \f
118 /* return the default home directory.  */
119 static const char *
120 llass_get_home_dir (void)
121 {
122   /* For this engine the home directory is not a filename but a string
123      used to convey options.  The exclamation mark is a marker to show
124      that this is not a directory name. Options are strings delimited
125      by a space.  The only option defined for now is GPG_AGENT to
126      enable GPG_AGENT specific commands to send to the server at
127      connection startup.  */
128   return "!GPG_AGENT";
129 }
130
131 static char *
132 llass_get_version (const char *file_name)
133 {
134   return strdup ("1.0");
135 }
136
137
138 static const char *
139 llass_get_req_version (void)
140 {
141   return "1.0";
142 }
143
144 \f
145 static void
146 close_notify_handler (int fd, void *opaque)
147 {
148   engine_llass_t llass = opaque;
149
150   assert (fd != -1);
151   if (llass->status_cb.fd == fd)
152     {
153       if (llass->status_cb.tag)
154         llass->io_cbs.remove (llass->status_cb.tag);
155       llass->status_cb.fd = -1;
156       llass->status_cb.tag = NULL;
157     }
158 }
159
160
161
162 static gpgme_error_t
163 llass_cancel (void *engine)
164 {
165   engine_llass_t llass = engine;
166
167   if (!llass)
168     return gpg_error (GPG_ERR_INV_VALUE);
169
170   if (llass->status_cb.fd != -1)
171     _gpgme_io_close (llass->status_cb.fd);
172
173   if (llass->assuan_ctx)
174     {
175       assuan_release (llass->assuan_ctx);
176       llass->assuan_ctx = NULL;
177     }
178
179   return 0;
180 }
181
182
183 static gpgme_error_t
184 llass_cancel_op (void *engine)
185 {
186   engine_llass_t llass = engine;
187
188   if (!llass)
189     return gpg_error (GPG_ERR_INV_VALUE);
190
191   if (llass->status_cb.fd != -1)
192     _gpgme_io_close (llass->status_cb.fd);
193
194   return 0;
195 }
196
197
198 static void
199 llass_release (void *engine)
200 {
201   engine_llass_t llass = engine;
202
203   if (!llass)
204     return;
205
206   llass_cancel (engine);
207
208   free (llass);
209 }
210
211
212 /* Create a new instance. If HOME_DIR is NULL standard options for use
213    with gpg-agent are issued.  */
214 static gpgme_error_t
215 llass_new (void **engine, const char *file_name, const char *home_dir,
216            const char *version)
217 {
218   gpgme_error_t err = 0;
219   engine_llass_t llass;
220   char *optstr;
221
222   (void)version; /* Not yet used.  */
223
224   llass = calloc (1, sizeof *llass);
225   if (!llass)
226     return gpg_error_from_syserror ();
227
228   llass->status_cb.fd = -1;
229   llass->status_cb.dir = 1;
230   llass->status_cb.tag = 0;
231   llass->status_cb.data = llass;
232
233   /* Parse_options.  */
234   if (home_dir && *home_dir == '!')
235     {
236       home_dir++;
237       /* Very simple parser only working for the one option we support.  */
238       /* Note that wk promised to write a regression test if this
239          parser will be extended.  */
240       if (!strncmp (home_dir, "GPG_AGENT", 9)
241           && (!home_dir[9] || home_dir[9] == ' '))
242         llass->opt.gpg_agent = 1;
243     }
244
245   err = assuan_new_ext (&llass->assuan_ctx, GPG_ERR_SOURCE_GPGME,
246                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
247                         NULL);
248   if (err)
249     goto leave;
250   assuan_ctx_set_system_hooks (llass->assuan_ctx, &_gpgme_assuan_system_hooks);
251
252   err = assuan_socket_connect (llass->assuan_ctx, file_name, 0, 0);
253   if (err)
254     goto leave;
255
256   if (llass->opt.gpg_agent)
257     {
258       char *dft_display = NULL;
259
260       err = _gpgme_getenv ("DISPLAY", &dft_display);
261       if (err)
262         goto leave;
263       if (dft_display)
264         {
265           if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
266             {
267               err = gpg_error_from_syserror ();
268               free (dft_display);
269               goto leave;
270             }
271           free (dft_display);
272
273           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
274                                  NULL, NULL, NULL);
275           free (optstr);
276           if (err)
277             goto leave;
278         }
279     }
280
281   if (llass->opt.gpg_agent && isatty (1))
282     {
283       int rc;
284       char dft_ttyname[64];
285       char *dft_ttytype = NULL;
286
287       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
288
289       /* Even though isatty() returns 1, ttyname_r() may fail in many
290          ways, e.g., when /dev/pts is not accessible under chroot.  */
291       if (!rc)
292         {
293           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
294             {
295               err = gpg_error_from_syserror ();
296               goto leave;
297             }
298           err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL, NULL,
299                                  NULL, NULL, NULL);
300           free (optstr);
301           if (err)
302             goto leave;
303
304           err = _gpgme_getenv ("TERM", &dft_ttytype);
305           if (err)
306             goto leave;
307           if (dft_ttytype)
308             {
309               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
310                 {
311                   err = gpg_error_from_syserror ();
312                   free (dft_ttytype);
313                   goto leave;
314                 }
315               free (dft_ttytype);
316
317               err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
318                                      NULL, NULL, NULL, NULL);
319               free (optstr);
320               if (err)
321                 goto leave;
322             }
323         }
324     }
325
326
327 #ifdef HAVE_W32_SYSTEM
328   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
329      llass to tell us when it needs it.  */
330   if (!err && llass->opt.gpg_agent)
331     {
332       err = assuan_transact (llass->assuan_ctx, "OPTION allow-pinentry-notify",
333                              NULL, NULL, NULL, NULL, NULL, NULL);
334       if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION)
335         err = 0; /* This work only with recent gpg-agents.  */
336     }
337 #endif /*HAVE_W32_SYSTEM*/
338
339
340  leave:
341   /* Close the server ends of the pipes (because of this, we must use
342      the stored server_fd_str in the function start).  Our ends are
343      closed in llass_release().  */
344
345   if (err)
346     llass_release (llass);
347   else
348     *engine = llass;
349
350   return err;
351 }
352
353
354 static gpgme_error_t
355 llass_set_locale (void *engine, int category, const char *value)
356 {
357   gpgme_error_t err;
358   engine_llass_t llass = engine;
359   char *optstr;
360   char *catstr;
361
362   if (!llass->opt.gpg_agent)
363     return 0;
364
365   /* FIXME: If value is NULL, we need to reset the option to default.
366      But we can't do this.  So we error out here.  gpg-agent needs
367      support for this.  */
368   if (0)
369     ;
370 #ifdef LC_CTYPE
371   else if (category == LC_CTYPE)
372     {
373       catstr = "lc-ctype";
374       if (!value && llass->lc_ctype_set)
375         return gpg_error (GPG_ERR_INV_VALUE);
376       if (value)
377         llass->lc_ctype_set = 1;
378     }
379 #endif
380 #ifdef LC_MESSAGES
381   else if (category == LC_MESSAGES)
382     {
383       catstr = "lc-messages";
384       if (!value && llass->lc_messages_set)
385         return gpg_error (GPG_ERR_INV_VALUE);
386       if (value)
387         llass->lc_messages_set = 1;
388     }
389 #endif /* LC_MESSAGES */
390   else
391     return gpg_error (GPG_ERR_INV_VALUE);
392
393   /* FIXME: Reset value to default.  */
394   if (!value)
395     return 0;
396
397   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
398     err = gpg_error_from_syserror ();
399   else
400     {
401       err = assuan_transact (llass->assuan_ctx, optstr, NULL, NULL,
402                              NULL, NULL, NULL, NULL);
403       free (optstr);
404     }
405   return err;
406 }
407
408
409 /* This is the inquiry callback.  It handles stuff which ee need to
410    handle here and passes everything on to the user callback.  */
411 static gpgme_error_t
412 inquire_cb (engine_llass_t llass, const char *keyword, const char *args)
413 {
414   gpg_error_t err;
415
416   if (llass->opt.gpg_agent && !strcmp (keyword, "PINENTRY_LAUNCHED"))
417     {
418       _gpgme_allow_set_foreground_window ((pid_t)strtoul (args, NULL, 10));
419     }
420
421   if (llass->user.inq_cb)
422     {
423       gpgme_data_t data = NULL;
424
425       err = llass->user.inq_cb (llass->user.inq_cb_value,
426                                 keyword, args, &data);
427       if (!err && data)
428         {
429           /* FIXME: Returning data is not yet implemented.  However we
430              need to allow the caller to cleanup his data object.
431              Thus we run the callback in finish mode immediately.  */
432           err = llass->user.inq_cb (llass->user.inq_cb_value,
433                                     NULL, NULL, &data);
434         }
435     }
436   else
437     err = 0;
438
439   return err;
440 }
441
442
443 static gpgme_error_t
444 llass_status_handler (void *opaque, int fd)
445 {
446   struct io_cb_data *data = (struct io_cb_data *) opaque;
447   engine_llass_t llass = (engine_llass_t) data->handler_value;
448   gpgme_error_t err = 0;
449   char *line;
450   size_t linelen;
451
452   do
453     {
454       err = assuan_read_line (llass->assuan_ctx, &line, &linelen);
455       if (err)
456         {
457           /* Reading a full line may not be possible when
458              communicating over a socket in nonblocking mode.  In this
459              case, we are done for now.  */
460           if (gpg_err_code (err) == GPG_ERR_EAGAIN)
461             {
462               TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
463                       "fd 0x%x: EAGAIN reading assuan line (ignored)", fd);
464               err = 0;
465               continue;
466             }
467
468           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
469                   "fd 0x%x: error reading assuan line: %s",
470                   fd, gpg_strerror (err));
471         }
472       else if (linelen >= 2 && line[0] == 'D' && line[1] == ' ')
473         {
474           char *src = line + 2;
475           char *end = line + linelen;
476           char *dst = src;
477
478           linelen = 0;
479           while (src < end)
480             {
481               if (*src == '%' && src + 2 < end)
482                 {
483                   /* Handle escaped characters.  */
484                   ++src;
485                   *dst++ = _gpgme_hextobyte (src);
486                   src += 2;
487                 }
488               else
489                 *dst++ = *src++;
490
491               linelen++;
492             }
493
494           src = line + 2;
495           if (linelen && llass->user.data_cb)
496             err = llass->user.data_cb (llass->user.data_cb_value,
497                                        src, linelen);
498
499           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
500                   "fd 0x%x: D inlinedata; status from cb: %s",
501                   fd, (llass->user.data_cb ?
502                        (err? gpg_strerror (err):"ok"):"no callback"));
503         }
504       else if (linelen >= 3
505                && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
506                && (line[3] == '\0' || line[3] == ' '))
507         {
508           /* END received.  Tell the data callback.  */
509           if (llass->user.data_cb)
510             err = llass->user.data_cb (llass->user.data_cb_value, NULL, 0);
511
512           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
513                   "fd 0x%x: END line; status from cb: %s",
514                   fd, (llass->user.data_cb ?
515                        (err? gpg_strerror (err):"ok"):"no callback"));
516         }
517       else if (linelen > 2 && line[0] == 'S' && line[1] == ' ')
518         {
519           char *args;
520           char *src;
521
522           for (src=line+2; *src == ' '; src++)
523             ;
524
525           args = strchr (src, ' ');
526           if (!args)
527             args = line + linelen; /* Let it point to an empty string.  */
528           else
529             *(args++) = 0;
530
531           while (*args == ' ')
532             args++;
533
534           if (llass->user.status_cb)
535             err = llass->user.status_cb (llass->user.status_cb_value,
536                                          src, args);
537
538           TRACE3 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
539                   "fd 0x%x: S line (%s) - status from cb: %s",
540                   fd, line+2, (llass->user.status_cb ?
541                                (err? gpg_strerror (err):"ok"):"no callback"));
542         }
543       else if (linelen >= 7
544                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
545                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
546                && line[6] == 'E'
547                && (line[7] == '\0' || line[7] == ' '))
548         {
549           char *src;
550           char *args;
551
552           for (src=line+7; *src == ' '; src++)
553             ;
554
555           args = strchr (src, ' ');
556           if (!args)
557             args = line + linelen; /* Let it point to an empty string.  */
558           else
559             *(args++) = 0;
560
561           while (*args == ' ')
562             args++;
563
564           err = inquire_cb (llass, src, args);
565           if (!err)
566             {
567               /* Flush and send END.  */
568               err = assuan_send_data (llass->assuan_ctx, NULL, 0);
569             }
570           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
571             {
572               /* Flush and send CANcel.  */
573               err = assuan_send_data (llass->assuan_ctx, NULL, 1);
574             }
575         }
576       else if (linelen >= 3
577                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
578                && (line[3] == '\0' || line[3] == ' '))
579         {
580           if (line[3] == ' ')
581             err = atoi (line+4);
582           else
583             err = gpg_error (GPG_ERR_GENERAL);
584           TRACE2 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
585                   "fd 0x%x: ERR line: %s",
586                   fd, err ? gpg_strerror (err) : "ok");
587
588           /* Command execution errors are not fatal, as we use
589              a session based protocol.  */
590           data->op_err = err;
591           llass->last_op_err = err;
592
593           /* The caller will do the rest (namely, call cancel_op,
594              which closes status_fd).  */
595           return 0;
596         }
597       else if (linelen >= 2
598                && line[0] == 'O' && line[1] == 'K'
599                && (line[2] == '\0' || line[2] == ' '))
600         {
601           TRACE1 (DEBUG_CTX, "gpgme:llass_status_handler", llass,
602                   "fd 0x%x: OK line", fd);
603
604           llass->last_op_err = 0;
605
606           _gpgme_io_close (llass->status_cb.fd);
607           return 0;
608         }
609       else
610         {
611           /* Comment line or invalid line.  */
612         }
613
614     }
615   while (!err && assuan_pending_line (llass->assuan_ctx));
616
617   return err;
618 }
619
620
621 static gpgme_error_t
622 add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
623 {
624   gpgme_error_t err;
625
626   TRACE_BEG2 (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
627               "fd %d, dir %d", iocbd->fd, iocbd->dir);
628   err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
629                               iocbd->fd, iocbd->dir,
630                               handler, iocbd->data, &iocbd->tag);
631   if (err)
632     return TRACE_ERR (err);
633   if (!iocbd->dir)
634     /* FIXME Kludge around poll() problem.  */
635     err = _gpgme_io_set_nonblocking (iocbd->fd);
636   return TRACE_ERR (err);
637 }
638
639
640 static gpgme_error_t
641 start (engine_llass_t llass, const char *command)
642 {
643   gpgme_error_t err;
644   assuan_fd_t afdlist[5];
645   int fdlist[5];
646   int nfds;
647   int i;
648
649   /* We need to know the fd used by assuan for reads.  We do this by
650      using the assumption that the first returned fd from
651      assuan_get_active_fds() is always this one.  */
652   nfds = assuan_get_active_fds (llass->assuan_ctx, 0 /* read fds */,
653                                 afdlist, DIM (afdlist));
654   if (nfds < 1)
655     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
656   /* For now... */
657   for (i = 0; i < nfds; i++)
658     fdlist[i] = (int) afdlist[i];
659
660   /* We "duplicate" the file descriptor, so we can close it here (we
661      can't close fdlist[0], as that is closed by libassuan, and
662      closing it here might cause libassuan to close some unrelated FD
663      later).  Alternatively, we could special case status_fd and
664      register/unregister it manually as needed, but this increases
665      code duplication and is more complicated as we can not use the
666      close notifications etc.  A third alternative would be to let
667      Assuan know that we closed the FD, but that complicates the
668      Assuan interface.  */
669
670   llass->status_cb.fd = _gpgme_io_dup (fdlist[0]);
671   if (llass->status_cb.fd < 0)
672     return gpg_error_from_syserror ();
673
674   if (_gpgme_io_set_close_notify (llass->status_cb.fd,
675                                   close_notify_handler, llass))
676     {
677       _gpgme_io_close (llass->status_cb.fd);
678       llass->status_cb.fd = -1;
679       return gpg_error (GPG_ERR_GENERAL);
680     }
681
682   err = add_io_cb (llass, &llass->status_cb, llass_status_handler);
683   if (!err)
684     err = assuan_write_line (llass->assuan_ctx, command);
685
686   /* FIXME: If *command == '#' no answer is expected.  */
687
688   if (!err)
689     llass_io_event (llass, GPGME_EVENT_START, NULL);
690
691   return err;
692 }
693
694
695
696 static gpgme_error_t
697 llass_transact (void *engine,
698                 const char *command,
699                 gpgme_assuan_data_cb_t data_cb,
700                 void *data_cb_value,
701                 gpgme_assuan_inquire_cb_t inq_cb,
702                 void *inq_cb_value,
703                 gpgme_assuan_status_cb_t status_cb,
704                 void *status_cb_value)
705 {
706   engine_llass_t llass = engine;
707   gpgme_error_t err;
708
709   if (!llass || !command || !*command)
710     return gpg_error (GPG_ERR_INV_VALUE);
711
712   llass->user.data_cb = data_cb;
713   llass->user.data_cb_value = data_cb_value;
714   llass->user.inq_cb = inq_cb;
715   llass->user.inq_cb_value = inq_cb_value;
716   llass->user.status_cb = status_cb;
717   llass->user.status_cb_value = status_cb_value;
718
719   err = start (llass, command);
720   return err;
721 }
722
723
724
725 static void
726 llass_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
727 {
728   engine_llass_t llass = engine;
729   llass->io_cbs = *io_cbs;
730 }
731
732
733 static void
734 llass_io_event (void *engine, gpgme_event_io_t type, void *type_data)
735 {
736   engine_llass_t llass = engine;
737
738   TRACE3 (DEBUG_ENGINE, "gpgme:llass_io_event", llass,
739           "event %p, type %d, type_data %p",
740           llass->io_cbs.event, type, type_data);
741   if (llass->io_cbs.event)
742     (*llass->io_cbs.event) (llass->io_cbs.event_priv, type, type_data);
743 }
744
745
746 struct engine_ops _gpgme_engine_ops_assuan =
747   {
748     /* Static functions.  */
749     _gpgme_get_default_agent_socket,
750     llass_get_home_dir,
751     llass_get_version,
752     llass_get_req_version,
753     llass_new,
754
755     /* Member functions.  */
756     llass_release,
757     NULL,               /* reset */
758     NULL,               /* set_status_cb */
759     NULL,               /* set_status_handler */
760     NULL,               /* set_command_handler */
761     NULL,               /* set_colon_line_handler */
762     llass_set_locale,
763     NULL,               /* set_protocol */
764     NULL,               /* decrypt */
765     NULL,               /* decrypt_verify */
766     NULL,               /* delete */
767     NULL,               /* edit */
768     NULL,               /* encrypt */
769     NULL,               /* encrypt_sign */
770     NULL,               /* export */
771     NULL,               /* export_ext */
772     NULL,               /* genkey */
773     NULL,               /* import */
774     NULL,               /* keylist */
775     NULL,               /* keylist_ext */
776     NULL,               /* sign */
777     NULL,               /* trustlist */
778     NULL,               /* verify */
779     NULL,               /* getauditlog */
780     llass_transact,     /* opassuan_transact */
781     NULL,               /* conf_load */
782     NULL,               /* conf_save */
783     llass_set_io_cbs,
784     llass_io_event,
785     llass_cancel,
786     llass_cancel_op,
787     NULL,               /* passwd */
788     NULL,               /* set_pinentry_mode */
789     NULL                /* opspawn */
790   };