2009-11-05 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 gpgme_error_t
184 g13_cancel_op (void *engine)
185 {
186   engine_g13_t g13 = engine;
187
188   if (!g13)
189     return gpg_error (GPG_ERR_INV_VALUE);
190
191   if (g13->status_cb.fd != -1)
192     _gpgme_io_close (g13->status_cb.fd);
193
194   return 0;
195 }
196
197
198 static void
199 g13_release (void *engine)
200 {
201   engine_g13_t g13 = engine;
202
203   if (!g13)
204     return;
205
206   g13_cancel (engine);
207
208   free (g13);
209 }
210
211
212 static gpgme_error_t
213 g13_new (void **engine, const char *file_name, const char *home_dir)
214 {
215   gpgme_error_t err = 0;
216   engine_g13_t g13;
217   int argc;
218   char *argv[5];
219   char *dft_display = NULL;
220   char dft_ttyname[64];
221   char *dft_ttytype = NULL;
222   char *optstr;
223
224   g13 = calloc (1, sizeof *g13);
225   if (!g13)
226     return gpg_error_from_errno (errno);
227
228   g13->status_cb.fd = -1;
229   g13->status_cb.dir = 1;
230   g13->status_cb.tag = 0;
231   g13->status_cb.data = g13;
232
233   argc = 0;
234   argv[argc++] = "g13";
235   if (home_dir)
236     {
237       argv[argc++] = "--homedir";
238       argv[argc++] = home_dir;
239     }
240   argv[argc++] = "--server";
241   argv[argc++] = NULL;
242
243   err = assuan_new_ext (&g13->assuan_ctx, GPG_ERR_SOURCE_GPGME,
244                         &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb,
245                         NULL);
246   if (err)
247     goto leave;
248   assuan_ctx_set_system_hooks (g13->assuan_ctx, &_gpgme_assuan_system_hooks);
249
250 #if USE_DESCRIPTOR_PASSING
251   err = assuan_pipe_connect_ext
252     (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
253      argv, NULL, NULL, NULL, 1);
254 #else
255   err = assuan_pipe_connect
256     (g13->assuan_ctx, file_name ? file_name : _gpgme_get_g13_path (),
257      argv, NULL);
258 #endif
259   if (err)
260     goto leave;
261
262   err = _gpgme_getenv ("DISPLAY", &dft_display);
263   if (err)
264     goto leave;
265   if (dft_display)
266     {
267       if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0)
268         {
269           free (dft_display);
270           err = gpg_error_from_errno (errno);
271           goto leave;
272         }
273       free (dft_display);
274
275       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
276                              NULL, NULL, NULL);
277       free (optstr);
278       if (err)
279         goto leave;
280     }
281
282   if (isatty (1))
283     {
284       int rc;
285
286       rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname));
287       if (rc)
288         {
289           err = gpg_error_from_errno (rc);
290           goto leave;
291         }
292       else
293         {
294           if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0)
295             {
296               err = gpg_error_from_errno (errno);
297               goto leave;
298             }
299           err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL, NULL,
300                                  NULL, NULL, NULL);
301           free (optstr);
302           if (err)
303             goto leave;
304
305           err = _gpgme_getenv ("TERM", &dft_ttytype);
306           if (err)
307             goto leave;
308           if (dft_ttytype)
309             {
310               if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0)
311                 {
312                   free (dft_ttytype);
313                   err = gpg_error_from_errno (errno);
314                   goto leave;
315                 }
316               free (dft_ttytype);
317
318               err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
319                                      NULL, NULL, NULL, NULL);
320               free (optstr);
321               if (err)
322                 goto leave;
323             }
324         }
325     }
326
327 #ifdef HAVE_W32_SYSTEM
328   /* Under Windows we need to use AllowSetForegroundWindow.  Tell
329      g13 to tell us when it needs it.  */
330   if (!err)
331     {
332       err = assuan_transact (g13->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 is a new feature of g13.  */
336     }
337 #endif /*HAVE_W32_SYSTEM*/
338
339  leave:
340
341   if (err)
342     g13_release (g13);
343   else
344     *engine = g13;
345
346   return err;
347 }
348
349
350 static gpgme_error_t
351 g13_set_locale (void *engine, int category, const char *value)
352 {
353   engine_g13_t g13 = engine;
354   gpgme_error_t err;
355   char *optstr;
356   char *catstr;
357
358   /* FIXME: If value is NULL, we need to reset the option to default.
359      But we can't do this.  So we error out here.  G13 needs support
360      for this.  */
361   if (category == LC_CTYPE)
362     {
363       catstr = "lc-ctype";
364       if (!value && g13->lc_ctype_set)
365         return gpg_error (GPG_ERR_INV_VALUE);
366       if (value)
367         g13->lc_ctype_set = 1;
368     }
369 #ifdef LC_MESSAGES
370   else if (category == LC_MESSAGES)
371     {
372       catstr = "lc-messages";
373       if (!value && g13->lc_messages_set)
374         return gpg_error (GPG_ERR_INV_VALUE);
375       if (value)
376         g13->lc_messages_set = 1;
377     }
378 #endif /* LC_MESSAGES */
379   else
380     return gpg_error (GPG_ERR_INV_VALUE);
381
382   /* FIXME: Reset value to default.  */
383   if (!value) 
384     return 0;
385
386   if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0)
387     err = gpg_error_from_errno (errno);
388   else
389     {
390       err = assuan_transact (g13->assuan_ctx, optstr, NULL, NULL,
391                              NULL, NULL, NULL, NULL);
392       free (optstr);
393     }
394
395   return err;
396 }
397
398
399 static gpgme_error_t
400 g13_assuan_simple_command (assuan_context_t ctx, char *cmd,
401                              engine_status_handler_t status_fnc,
402                              void *status_fnc_value)
403 {
404   gpg_error_t err;
405   char *line;
406   size_t linelen;
407
408   err = assuan_write_line (ctx, cmd);
409   if (err)
410     return err;
411
412   do
413     {
414       err = assuan_read_line (ctx, &line, &linelen);
415       if (err)
416         return err;
417
418       if (*line == '#' || !linelen)
419         continue;
420
421       if (linelen >= 2
422           && line[0] == 'O' && line[1] == 'K'
423           && (line[2] == '\0' || line[2] == ' '))
424         return 0;
425       else if (linelen >= 4
426           && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
427           && line[3] == ' ')
428         err = atoi (&line[4]);
429       else if (linelen >= 2
430                && line[0] == 'S' && line[1] == ' ')
431         {
432           char *rest;
433
434           rest = strchr (line + 2, ' ');
435           if (!rest)
436             rest = line + linelen; /* set to an empty string */
437           else
438             *(rest++) = 0;
439
440           /* Nothing to do with status lines.  */
441         }
442       else
443         err = gpg_error (GPG_ERR_GENERAL);
444     }
445   while (!err);
446
447   return err;
448 }
449
450
451 static gpgme_error_t
452 status_handler (void *opaque, int fd)
453 {
454   struct io_cb_data *data = (struct io_cb_data *) opaque;
455   engine_g13_t g13 = (engine_g13_t) data->handler_value;
456   gpgme_error_t err = 0;
457   char *line;
458   size_t linelen;
459
460   do
461     {
462       err = assuan_read_line (g13->assuan_ctx, &line, &linelen);
463       if (err)
464         {
465           /* Try our best to terminate the connection friendly.  */
466           /*      assuan_write_line (g13->assuan_ctx, "BYE"); */
467           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
468                   "fd 0x%x: error reading assuan line: %s",
469                   fd, gpg_strerror (err));
470         }
471       else if (linelen >= 3
472                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
473                && (line[3] == '\0' || line[3] == ' '))
474         {
475           if (line[3] == ' ')
476             err = atoi (&line[4]);
477           if (! err)
478             err = gpg_error (GPG_ERR_GENERAL);
479           TRACE2 (DEBUG_CTX, "gpgme:status_handler", g13,
480                   "fd 0x%x: ERR line: %s",
481                   fd, err ? gpg_strerror (err) : "ok");
482           
483           /* In g13, command execution errors are not fatal, as we use
484              a session based protocol.  */
485           if (g13->result_cb)
486             err = g13->result_cb (g13->result_cb_value, err);
487           else
488             err = 0;
489           if (!err)
490             {
491               _gpgme_io_close (g13->status_cb.fd);
492               return 0;
493             }
494         }
495       else if (linelen >= 2
496                && line[0] == 'O' && line[1] == 'K'
497                && (line[2] == '\0' || line[2] == ' '))
498         {
499           TRACE1 (DEBUG_CTX, "gpgme:status_handler", g13,
500                   "fd 0x%x: OK line", fd);
501           if (g13->result_cb)
502             err = g13->result_cb (g13->result_cb_value, 0);
503           else
504             err = 0;
505           if (!err)
506             {
507               _gpgme_io_close (g13->status_cb.fd);
508               return 0;
509             }
510         }
511       else if (linelen > 2
512                && line[0] == 'D' && line[1] == ' ')
513         {
514           /* We are using the colon handler even for plain inline data
515              - strange name for that function but for historic reasons
516              we keep it.  */
517           /* FIXME We can't use this for binary data because we
518              assume this is a string.  For the current usage of colon
519              output it is correct.  */
520           char *src = line + 2;
521           char *end = line + linelen;
522           char *dst = src;
523
524           linelen = 0;
525           while (src < end)
526             {
527               if (*src == '%' && src + 2 < end)
528                 {
529                   /* Handle escaped characters.  */
530                   ++src;
531                   *dst++ = _gpgme_hextobyte (src);
532                   src += 2;
533                 }
534               else
535                 *dst++ = *src++;
536
537               linelen++;
538             }
539
540           src = line + 2;
541           if (linelen && g13->user.data_cb)
542             err = g13->user.data_cb (g13->user.data_cb_value,
543                                        src, linelen);
544           else
545             err = 0;
546
547           TRACE2 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
548                   "fd 0x%x: D inlinedata; status from cb: %s",
549                   fd, (g13->user.data_cb ?
550                        (err? gpg_strerror (err):"ok"):"no callback"));
551
552         }
553       else if (linelen > 2
554                && line[0] == 'S' && line[1] == ' ')
555         {
556           char *src;
557           char *args;
558
559           src = line + 2;
560           while (*src == ' ')
561             src++;
562           
563           args = strchr (line + 2, ' ');
564           if (!args)
565             args = line + linelen; /* set to an empty string */
566           else
567             *(args++) = 0;
568
569           while (*args == ' ')
570             args++;
571
572           if (g13->user.status_cb)
573             err = g13->user.status_cb (g13->user.status_cb_value,
574                                        src, args);
575           else
576             err = 0;
577
578           TRACE3 (DEBUG_CTX, "gpgme:g13_status_handler", g13,
579                   "fd 0x%x: S line (%s) - status from cb: %s",
580                   fd, line+2, (g13->user.status_cb ?
581                                (err? gpg_strerror (err):"ok"):"no callback"));
582         }
583       else if (linelen >= 7
584                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
585                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
586                && line[6] == 'E' 
587                && (line[7] == '\0' || line[7] == ' '))
588         {
589           char *src;
590           char *args;
591           
592           for (src=line+7; *src == ' '; src++)
593             ;
594
595           args = strchr (src, ' ');
596           if (!args)
597             args = line + linelen; /* Let it point to an empty string.  */
598           else
599             *(args++) = 0;
600
601           while (*args == ' ')
602             args++;
603
604           err = default_inq_cb (g13, src, args);
605           if (!err) 
606             {
607               /* Flush and send END.  */
608               err = assuan_send_data (g13->assuan_ctx, NULL, 0);
609             }
610           else if (gpg_err_code (err) == GPG_ERR_ASS_CANCELED)
611             {
612               /* Flush and send CANcel.  */
613               err = assuan_send_data (g13->assuan_ctx, NULL, 1);
614             }
615           assuan_write_line (g13->assuan_ctx, "END");
616         }
617     }
618   while (!err && assuan_pending_line (g13->assuan_ctx));
619   
620   return err;
621 }
622   
623
624 static gpgme_error_t
625 add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
626 {
627   gpgme_error_t err;
628
629   TRACE_BEG2 (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
630               "fd %d, dir %d", iocbd->fd, iocbd->dir);
631   err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
632                               iocbd->fd, iocbd->dir,
633                               handler, iocbd->data, &iocbd->tag);
634   if (err)
635     return TRACE_ERR (err);
636   if (!iocbd->dir)
637     /* FIXME Kludge around poll() problem.  */
638     err = _gpgme_io_set_nonblocking (iocbd->fd);
639   return TRACE_ERR (err);
640 }
641
642
643 static gpgme_error_t
644 start (engine_g13_t g13, const char *command)
645 {
646   gpgme_error_t err;
647   int fdlist[5];
648   int nfds;
649
650   /* We need to know the fd used by assuan for reads.  We do this by
651      using the assumption that the first returned fd from
652      assuan_get_active_fds() is always this one.  */
653   nfds = assuan_get_active_fds (g13->assuan_ctx, 0 /* read fds */,
654                                 fdlist, DIM (fdlist));
655   if (nfds < 1)
656     return gpg_error (GPG_ERR_GENERAL); /* FIXME */
657
658   /* We "duplicate" the file descriptor, so we can close it here (we
659      can't close fdlist[0], as that is closed by libassuan, and
660      closing it here might cause libassuan to close some unrelated FD
661      later).  Alternatively, we could special case status_fd and
662      register/unregister it manually as needed, but this increases
663      code duplication and is more complicated as we can not use the
664      close notifications etc.  A third alternative would be to let
665      Assuan know that we closed the FD, but that complicates the
666      Assuan interface.  */
667
668   g13->status_cb.fd = _gpgme_io_dup (fdlist[0]);
669   if (g13->status_cb.fd < 0)
670     return gpg_error_from_syserror ();
671
672   if (_gpgme_io_set_close_notify (g13->status_cb.fd,
673                                   close_notify_handler, g13))
674     {
675       _gpgme_io_close (g13->status_cb.fd);
676       g13->status_cb.fd = -1;
677       return gpg_error (GPG_ERR_GENERAL);
678     }
679
680   err = add_io_cb (g13, &g13->status_cb, status_handler);
681   if (!err)
682     err = assuan_write_line (g13->assuan_ctx, command);
683
684   if (!err)
685     g13_io_event (g13, GPGME_EVENT_START, NULL);
686
687   return err;
688 }
689
690
691 #if USE_DESCRIPTOR_PASSING
692 static gpgme_error_t
693 g13_reset (void *engine)
694 {
695   engine_g13_t g13 = engine;
696
697   /* We must send a reset because we need to reset the list of
698      signers.  Note that RESET does not reset OPTION commands. */
699   return g13_assuan_simple_command (g13->assuan_ctx, "RESET", NULL, NULL);
700 }
701 #endif
702
703
704 static gpgme_error_t
705 g13_transact (void *engine,
706                 const char *command,
707                 engine_assuan_result_cb_t result_cb,
708                 void *result_cb_value,
709                 gpgme_assuan_data_cb_t data_cb,
710                 void *data_cb_value,
711                 gpgme_assuan_inquire_cb_t inq_cb,
712                 void *inq_cb_value,
713                 gpgme_assuan_status_cb_t status_cb,
714                 void *status_cb_value)
715 {
716   engine_g13_t g13 = engine;
717   gpgme_error_t err;
718
719   if (!g13 || !command || !*command)
720     return gpg_error (GPG_ERR_INV_VALUE);
721
722   g13->result_cb = result_cb;
723   g13->result_cb_value = result_cb_value;
724   g13->user.data_cb = data_cb;
725   g13->user.data_cb_value = data_cb_value;
726   g13->user.inq_cb = inq_cb;
727   g13->user.inq_cb_value = inq_cb_value;
728   g13->user.status_cb = status_cb;
729   g13->user.status_cb_value = status_cb_value;
730
731   err = start (g13, command);
732   return err;
733 }
734
735
736
737 static void
738 g13_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs)
739 {
740   engine_g13_t g13 = engine;
741   g13->io_cbs = *io_cbs;
742 }
743
744
745 static void
746 g13_io_event (void *engine, gpgme_event_io_t type, void *type_data)
747 {
748   engine_g13_t g13 = engine;
749
750   TRACE3 (DEBUG_ENGINE, "gpgme:g13_io_event", g13,
751           "event %p, type %d, type_data %p",
752           g13->io_cbs.event, type, type_data);
753   if (g13->io_cbs.event)
754     (*g13->io_cbs.event) (g13->io_cbs.event_priv, type, type_data);
755 }
756
757
758 struct engine_ops _gpgme_engine_ops_g13 =
759   {
760     /* Static functions.  */
761     _gpgme_get_g13_path,
762     NULL,
763     g13_get_version,
764     g13_get_req_version,
765     g13_new,
766
767     /* Member functions.  */
768     g13_release,
769 #if USE_DESCRIPTOR_PASSING
770     g13_reset,
771 #else
772     NULL,                       /* reset */
773 #endif
774     NULL,               /* set_status_handler */
775     NULL,               /* set_command_handler */
776     NULL,               /* set_colon_line_handler */
777     g13_set_locale,
778     NULL,               /* decrypt */
779     NULL,               /* delete */
780     NULL,               /* edit */
781     NULL,               /* encrypt */
782     NULL,               /* encrypt_sign */
783     NULL,               /* export */
784     NULL,               /* export_ext */
785     NULL,               /* genkey */
786     NULL,               /* import */
787     NULL,               /* keylist */
788     NULL,               /* keylist_ext */
789     NULL,               /* sign */
790     NULL,               /* trustlist */
791     NULL,               /* verify */
792     NULL,               /* getauditlog */
793     g13_transact,
794     NULL,               /* conf_load */
795     NULL,               /* conf_save */
796     g13_set_io_cbs,
797     g13_io_event,
798     g13_cancel,
799     g13_cancel_op,
800   };