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