2007-09-27 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / w32-qt-io.cpp
1 /* w32-glib-io.c - W32 Glib I/O functions
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2004, 2005, 2007 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 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <assert.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <windows.h>
36 #include <io.h>
37
38 #include "kdpipeiodevice.h"
39
40 extern "C"
41 {
42 #include "util.h"
43 #include "priv-io.h"
44 #include "sema.h"
45 #include "debug.h"
46 }
47
48 #ifndef O_BINARY
49 #ifdef _O_BINARY
50 #define O_BINARY        _O_BINARY
51 #else
52 #define O_BINARY        0
53 #endif
54 #endif
55
56 using _gpgme_::KDPipeIODevice;
57
58 \f
59 /* This file is an ugly hack to get GPGME working with Qt on Windows
60    targets.  On Windows, you can not select() on file descriptors.
61 \f
62    The only way to check if there is something to read is to read
63    something.  This means that GPGME can not let Qt check for data
64    without letting Qt also handle the data on Windows targets.
65
66    The ugly consequence is that we need to work on QIODevices in
67    GPGME, creating a Qt dependency.  Also, we need to export an
68    interface for the application to get at GPGME's QIODevices.  There
69    is no good way to abstract all this with callbacks, because the
70    whole thing is also interconnected with the creation of pipes and
71    child processes.
72
73    The following rule applies only to this I/O backend:
74
75    * ALL operations must use the user defined event loop.  GPGME can
76    not anymore provide its own event loop.  This is mostly a sanity
77    requirement: Although we have in theory all information we need to
78    make the GPGME W32 code for select still work, it would be a big
79    complication and require changes throughout GPGME.
80
81    Eventually, we probably have to bite the bullet and make some
82    really nice callback interfaces to let the user control all this at
83    a per-context level.  */
84
85 \f
86 #define MAX_SLAFD 256
87
88 QIODevice *iodevice_table[MAX_SLAFD];
89
90
91 static QIODevice *
92 find_channel (int fd, int create)
93 {
94   if (fd < 0 || fd >= MAX_SLAFD)
95     return NULL;
96
97   if (create && !iodevice_table[fd])
98     iodevice_table[fd] = new KDPipeIODevice
99       (fd, QIODevice::ReadOnly|QIODevice::Unbuffered);
100
101   return iodevice_table[fd];
102 }
103
104
105 /* Write the printable version of FD to the buffer BUF of length
106    BUFLEN.  The printable version is the representation on the command
107    line that the child process expects.  */
108 int
109 _gpgme_io_fd2str (char *buf, int buflen, int fd)
110 {
111   return snprintf (buf, buflen, "%ld", (long) _get_osfhandle (fd));
112 }
113
114 \f
115 void
116 _gpgme_io_subsystem_init (void)
117 {
118 }
119
120 \f
121 static struct
122 {
123   _gpgme_close_notify_handler_t handler;
124   void *value;
125 } notify_table[MAX_SLAFD];
126
127
128 int
129 _gpgme_io_read (int fd, void *buffer, size_t count)
130 {
131   int saved_errno = 0;
132   qint64 nread;
133   QIODevice *chan;
134   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
135               "buffer=%p, count=%u", buffer, count);
136
137   chan = find_channel (fd, 0);
138   if (!chan)
139     {
140       TRACE_LOG ("no channel registered");
141       errno = EINVAL;
142       return TRACE_SYSRES (-1);
143     }
144   TRACE_LOG1 ("channel %p", chan);
145
146   nread = chan->read ((char *) buffer, count);
147   if (nread < 0)
148     {
149       TRACE_LOG1 ("err %s", qPrintable (chan->errorString ()));
150       saved_errno = EIO;
151       nread = -1;
152     }
153
154   TRACE_LOGBUF ((char *) buffer, nread);
155
156   errno = saved_errno;
157   return TRACE_SYSRES (nread);
158 }
159
160
161 int
162 _gpgme_io_write (int fd, const void *buffer, size_t count)
163 {
164   qint64 nwritten;
165   QIODevice *chan;
166   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
167               "buffer=%p, count=%u", buffer, count);
168   TRACE_LOGBUF ((char *) buffer, count);
169
170   chan = find_channel (fd, 0);
171   if (!chan)
172     {
173       TRACE_LOG ("fd %d: no channel registered");
174       errno = EINVAL;
175       return -1;
176     }
177
178   nwritten = chan->write ((char *) buffer, count);
179
180   if (nwritten < 0)
181     {
182       nwritten = -1;
183       errno = EIO;
184       return TRACE_SYSRES(-1);
185     }
186   errno = 0;
187   return TRACE_SYSRES (nwritten);
188 }
189
190
191 int
192 _gpgme_io_pipe (int filedes[2], int inherit_idx)
193 {
194   QIODevice *chan;
195   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
196               "inherit_idx=%i (GPGME uses it for %s)",
197               inherit_idx, inherit_idx ? "reading" : "writing");
198
199 #define PIPEBUF_SIZE  4096
200   if (_pipe (filedes, PIPEBUF_SIZE, O_NOINHERIT | O_BINARY) == -1)
201     return TRACE_SYSRES (-1);
202
203   /* Make one end inheritable. */
204   if (inherit_idx == 0)
205     {
206       int new_read;
207
208       new_read = _dup (filedes[0]);
209       _close (filedes[0]);
210       filedes[0] = new_read;
211
212       if (new_read < 0)
213         {
214           _close (filedes[1]);
215           return TRACE_SYSRES (-1);
216         }
217     }
218   else if (inherit_idx == 1)
219     {
220       int new_write;
221
222       new_write = _dup (filedes[1]);
223       _close (filedes[1]);
224       filedes[1] = new_write;
225
226       if (new_write < 0)
227         {
228           _close (filedes[0]);
229           return TRACE_SYSRES (-1);
230         }
231     }
232
233   /* Now we have a pipe with the right end inheritable.  The other end
234      should have a giochannel.  */
235   chan = find_channel (filedes[1 - inherit_idx], 1);
236   if (!chan)
237     {
238       int saved_errno = errno;
239       _close (filedes[0]);
240       _close (filedes[1]);
241       errno = saved_errno;
242       return TRACE_SYSRES (-1);
243     }
244
245   return TRACE_SUC5 ("read=0x%x/%p, write=0x%x/%p, channel=%p",
246           filedes[0], (HANDLE) _get_osfhandle (filedes[0]),
247           filedes[1], (HANDLE) _get_osfhandle (filedes[1]),
248           chan);
249 }
250
251
252 int
253 _gpgme_io_close (int fd)
254 {
255   QIODevice *chan;
256   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
257
258   if (fd < 0 || fd >= MAX_SLAFD)
259     {
260       errno = EBADF;
261       return TRACE_SYSRES (-1);
262     }
263
264   /* First call the notify handler.  */
265   if (notify_table[fd].handler)
266     {
267       notify_table[fd].handler (fd, notify_table[fd].value);
268       notify_table[fd].handler = NULL;
269       notify_table[fd].value = NULL;
270     }
271
272   /* Then do the close.  */    
273   chan = iodevice_table[fd];
274   if (chan)
275     {
276       chan->close();
277       delete chan;
278       iodevice_table[fd] = NULL;
279     }
280   else
281     _close (fd);
282
283   return 0;
284 }
285
286
287 int
288 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
289                             void *value)
290 {
291   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
292               "close_handler=%p/%p", handler, value);
293
294   assert (fd != -1);
295
296   if (fd < 0 || fd >= (int) DIM (notify_table))
297     {
298       errno = EINVAL;
299       return TRACE_SYSRES (-1);
300     }
301   notify_table[fd].handler = handler;
302   notify_table[fd].value = value;
303   return TRACE_SYSRES (0);
304 }
305
306
307 int
308 _gpgme_io_set_nonblocking (int fd)
309 {
310   /* Qt always uses non-blocking IO, except for files, maybe, but who
311      uses that?  */
312   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
313
314   return TRACE_SYSRES (0);
315 }
316
317
318 static char *
319 build_commandline (char **argv)
320 {
321   int i;
322   int n = 0;
323   char *buf;
324   char *p;
325   
326   /* We have to quote some things because under Windows the program
327      parses the commandline and does some unquoting.  We enclose the
328      whole argument in double-quotes, and escape literal double-quotes
329      as well as backslashes with a backslash.  We end up with a
330      trailing space at the end of the line, but that is harmless.  */
331   for (i = 0; argv[i]; i++)
332     {
333       p = argv[i];
334       /* The leading double-quote.  */
335       n++;
336       while (*p)
337         {
338           /* An extra one for each literal that must be escaped.  */
339           if (*p == '\\' || *p == '"')
340             n++;
341           n++;
342           p++;
343         }
344       /* The trailing double-quote and the delimiter.  */
345       n += 2;
346     }
347   /* And a trailing zero.  */
348   n++;
349
350   buf = p = (char *) malloc (n);
351   if (!buf)
352     return NULL;
353   for (i = 0; argv[i]; i++)
354     {
355       char *argvp = argv[i];
356
357       *(p++) = '"';
358       while (*argvp)
359         {
360           if (*argvp == '\\' || *argvp == '"')
361             *(p++) = '\\';
362           *(p++) = *(argvp++);
363         }
364       *(p++) = '"';
365       *(p++) = ' ';
366     }
367   *(p++) = 0;
368
369   return buf;
370 }
371
372
373 int
374 _gpgme_io_spawn (const char *path, char **argv,
375                  struct spawn_fd_item_s *fd_child_list,
376                  struct spawn_fd_item_s *fd_parent_list)
377 {
378   SECURITY_ATTRIBUTES sec_attr;
379   PROCESS_INFORMATION pi =
380     {
381       NULL,      /* returns process handle */
382       0,         /* returns primary thread handle */
383       0,         /* returns pid */
384       0         /* returns tid */
385     };
386   STARTUPINFO si;
387   char *envblock = NULL;
388   int cr_flags = CREATE_DEFAULT_ERROR_MODE
389     | GetPriorityClass (GetCurrentProcess ());
390   int i;
391   char *arg_string;
392   int duped_stdin = 0;
393   int duped_stderr = 0;
394   HANDLE hnul = INVALID_HANDLE_VALUE;
395   /* FIXME.  */
396   int debug_me = 0;
397   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
398               "path=%s", path);
399   i = 0;
400   while (argv[i])
401     {
402       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
403       i++;
404     }
405   
406   memset (&sec_attr, 0, sizeof sec_attr);
407   sec_attr.nLength = sizeof sec_attr;
408   sec_attr.bInheritHandle = FALSE;
409   
410   arg_string = build_commandline (argv);
411   if (!arg_string)
412     return TRACE_SYSRES (-1);
413   
414   memset (&si, 0, sizeof si);
415   si.cb = sizeof (si);
416   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
417   si.wShowWindow = debug_me? SW_SHOW : SW_HIDE;
418   si.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
419   si.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
420   si.hStdError = GetStdHandle (STD_ERROR_HANDLE);
421   
422   for (i = 0; fd_child_list[i].fd != -1; i++)
423     {
424       if (fd_child_list[i].dup_to == 0)
425         {
426           si.hStdInput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
427           TRACE_LOG2 ("using 0x%x/%p for stdin", fd_child_list[i].fd,
428                       _get_osfhandle (fd_child_list[i].fd));
429           duped_stdin = 1;
430         }
431       else if (fd_child_list[i].dup_to == 1)
432         {
433           si.hStdOutput = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
434           TRACE_LOG2 ("using 0x%x/%p for stdout", fd_child_list[i].fd,
435                       _get_osfhandle (fd_child_list[i].fd));
436         }
437       else if (fd_child_list[i].dup_to == 2)
438         {
439           si.hStdError = (HANDLE) _get_osfhandle (fd_child_list[i].fd);
440           TRACE_LOG2 ("using 0x%x/%p for stderr", fd_child_list[i].fd,
441                       _get_osfhandle (fd_child_list[i].fd));
442           duped_stderr = 1;
443         }
444     }
445   
446   if (!duped_stdin || !duped_stderr)
447     {
448       SECURITY_ATTRIBUTES sa;
449       
450       memset (&sa, 0, sizeof sa);
451       sa.nLength = sizeof sa;
452       sa.bInheritHandle = TRUE;
453       hnul = CreateFile ("nul",
454                          GENERIC_READ|GENERIC_WRITE,
455                          FILE_SHARE_READ|FILE_SHARE_WRITE,
456                          &sa,
457                          OPEN_EXISTING,
458                          FILE_ATTRIBUTE_NORMAL,
459                          NULL);
460       if (hnul == INVALID_HANDLE_VALUE)
461         {
462           TRACE_LOG1 ("CreateFile (\"nul\") failed: ec=%d",
463                       (int) GetLastError ());
464           free (arg_string);
465           /* FIXME: Should translate the error code.  */
466           errno = EIO;
467           return TRACE_SYSRES (-1);
468         }
469       /* Make sure that the process has a connected stdin.  */
470       if (!duped_stdin)
471         {
472           si.hStdInput = hnul;
473           TRACE_LOG1 ("using 0x%x for dummy stdin", (int) hnul);
474         }
475       /* We normally don't want all the normal output.  */
476       if (!duped_stderr)
477         {
478           si.hStdError = hnul;
479           TRACE_LOG1 ("using %d for dummy stderr", (int)hnul);
480         }
481     }
482   
483   cr_flags |= CREATE_SUSPENDED;
484   cr_flags |= DETACHED_PROCESS;
485   if (!CreateProcessA (path,
486                        arg_string,
487                        &sec_attr,     /* process security attributes */
488                        &sec_attr,     /* thread security attributes */
489                        TRUE,          /* inherit handles */
490                        cr_flags,      /* creation flags */
491                        envblock,      /* environment */
492                        NULL,          /* use current drive/directory */
493                        &si,           /* startup information */
494                        &pi))          /* returns process information */
495     {
496       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
497       free (arg_string);
498       /* FIXME: Should translate the error code.  */
499       errno = EIO;
500       return TRACE_SYSRES (-1);
501     }
502   
503   /* Close the /dev/nul handle if used.  */
504   if (hnul != INVALID_HANDLE_VALUE)
505     {
506       if (!CloseHandle (hnul))
507         TRACE_LOG1 ("CloseHandle (hnul) failed: ec=%d (ignored)",
508                     (int) GetLastError ());
509     }
510   
511   /* Close the other ends of the pipes.  */
512   for (i = 0; fd_parent_list[i].fd != -1; i++)
513     _gpgme_io_close (fd_parent_list[i].fd);
514   
515   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
516               "dwProcessID=%d, dwThreadId=%d",
517               pi.hProcess, pi.hThread, 
518               (int) pi.dwProcessId, (int) pi.dwThreadId);
519   
520   if (ResumeThread (pi.hThread) < 0)
521     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
522   
523   if (!CloseHandle (pi.hThread))
524     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
525                 (int) GetLastError ());
526
527   TRACE_SUC1 ("process=%p", pi.hProcess);
528
529   /* We don't need to wait for the process. */
530   CloseHandle (pi.hProcess);
531
532   return TRACE_SYSRES (0);
533 }
534
535
536 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
537    nothing to select, > 0 = number of signaled fds.  */
538 int
539 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
540 {
541   int i;
542   int count;
543   /* Use a 1s timeout.  */
544
545   void *dbg_help = NULL;
546   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
547               "nfds=%u, nonblock=%u", nfds, nonblock);
548
549   /* We only implement the special case of nonblock == true.  */
550   assert (nonblock);
551
552   count = 0;
553
554   TRACE_SEQ (dbg_help, "select on [ ");
555   for (i = 0; i < nfds; i++)
556     {
557       if (fds[i].fd == -1)
558         {
559           fds[i].signaled = 0;
560         }
561       else if (fds[i].frozen)
562         {
563           TRACE_ADD1 (dbg_help, "f0x%x ", fds[i].fd);
564           fds[i].signaled = 0;
565         }
566       else if (fds[i].for_read )
567         {
568           const QIODevice * const chan = find_channel (fds[i].fd, 0);
569           assert (chan);
570           fds[i].signaled = chan->bytesAvailable() > 0 ? 1 : 0 ;
571           TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
572           count++;
573         }
574       else if (fds[i].for_write)
575         {
576           const QIODevice * const chan = find_channel (fds[i].fd, 0);
577           assert (chan);
578           fds[i].signaled = chan->bytesToWrite() > 0 ? 0 : 1 ;
579           TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
580           count++;
581         }
582     }
583   TRACE_END (dbg_help, "]"); 
584
585   return TRACE_SYSRES (count);
586 }
587
588
589 int
590 _gpgme_io_dup (int fd)
591 {
592   return _dup (fd);
593 }
594
595 \f
596 /* Look up the qiodevice for file descriptor FD.  */
597 extern "C"
598 void *
599 gpgme_get_fdptr (int fd)
600 {
601   return find_channel (fd, 0);
602 }
603
604
605 /* Obsolete compatibility interface.  */
606 extern "C"
607 void *
608 gpgme_get_giochannel (int fd)
609 {
610   return NULL;
611 }
612