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