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