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