Improved W32 SetForegroundWindow hacks.
[gpgme.git] / src / w32-io.c
1 /* w32-io.c - W32 API I/O functions.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 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 <sys/time.h>
33 #include <sys/types.h>
34 #include <windows.h>
35 #include <io.h>
36
37 #include "util.h"
38 #include "sema.h"
39 #include "priv-io.h"
40 #include "debug.h"
41
42
43 /* We assume that a HANDLE can be represented by an int which should
44    be true for all i386 systems (HANDLE is defined as void *) and
45    these are the only systems for which Windows is available.  Further
46    we assume that -1 denotes an invalid handle.  */
47
48 #define fd_to_handle(a)  ((HANDLE)(a))
49 #define handle_to_fd(a)  ((int)(a))
50 #define pid_to_handle(a) ((HANDLE)(a))
51 #define handle_to_pid(a) ((int)(a))
52
53 #define READBUF_SIZE 4096
54 #define WRITEBUF_SIZE 4096
55 #define PIPEBUF_SIZE  4096
56 #define MAX_READERS 40
57 #define MAX_WRITERS 40
58
59 static struct
60 {
61   int inuse;
62   int fd;
63   _gpgme_close_notify_handler_t handler;
64   void *value;
65 } notify_table[256];
66 DEFINE_STATIC_LOCK (notify_table_lock);
67
68
69 struct reader_context_s
70 {
71   HANDLE file_hd;
72   HANDLE thread_hd;     
73   int refcount;
74
75   DECLARE_LOCK (mutex);
76
77   int stop_me;
78   int eof;
79   int eof_shortcut;
80   int error;
81   int error_code;
82   
83   /* This is manually reset.  */
84   HANDLE have_data_ev;
85   /* This is automatically reset.  */
86   HANDLE have_space_ev;
87   HANDLE stopped;
88   size_t readpos, writepos;
89   char buffer[READBUF_SIZE];
90 };
91
92
93 static struct
94 {
95   volatile int used;
96   int fd;
97   struct reader_context_s *context;
98 } reader_table[MAX_READERS];
99 static int reader_table_size= MAX_READERS;
100 DEFINE_STATIC_LOCK (reader_table_lock);
101
102
103 struct writer_context_s
104 {
105   HANDLE file_hd;
106   HANDLE thread_hd;     
107   int refcount;
108
109   DECLARE_LOCK (mutex);
110   
111   int stop_me;
112   int error;
113   int error_code;
114
115   /* This is manually reset.  */
116   HANDLE have_data;
117   HANDLE is_empty;
118   HANDLE stopped;
119   size_t nbytes; 
120   char buffer[WRITEBUF_SIZE];
121 };
122
123
124 static struct
125 {
126   volatile int used;
127   int fd;
128   struct writer_context_s *context;
129 } writer_table[MAX_WRITERS];
130 static int writer_table_size= MAX_WRITERS;
131 DEFINE_STATIC_LOCK (writer_table_lock);
132
133
134 static int
135 get_desired_thread_priority (void)
136 {
137   int value;
138
139   if (!_gpgme_get_conf_int ("IOThreadPriority", &value))
140     {
141       value = THREAD_PRIORITY_HIGHEST;
142       TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
143               "%d (default)", value);
144     }
145   else
146     {
147       TRACE1 (DEBUG_SYSIO, "gpgme:get_desired_thread_priority", 0,
148               "%d (configured)", value);
149     }
150   return value;
151 }
152
153
154 static HANDLE
155 set_synchronize (HANDLE hd)
156 {
157   HANDLE new_hd;
158
159   /* For NT we have to set the sync flag.  It seems that the only way
160      to do it is by duplicating the handle.  Tsss...  */
161   if (!DuplicateHandle (GetCurrentProcess (), hd,
162                         GetCurrentProcess (), &new_hd,
163                         EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, 0))
164     {
165       TRACE1 (DEBUG_SYSIO, "gpgme:set_synchronize", hd,
166               "DuplicateHandle failed: ec=%d", (int) GetLastError ());
167       /* FIXME: Should translate the error code.  */
168       errno = EIO;
169       return INVALID_HANDLE_VALUE;
170     }
171
172   CloseHandle (hd);
173   return new_hd;
174 }
175
176
177 static DWORD CALLBACK 
178 reader (void *arg)
179 {
180   struct reader_context_s *ctx = arg;
181   int nbytes;
182   DWORD nread;
183   TRACE_BEG1 (DEBUG_SYSIO, "gpgme:reader", ctx->file_hd,
184               "thread=%p", ctx->thread_hd);
185
186   for (;;)
187     {
188       LOCK (ctx->mutex);
189       /* Leave a 1 byte gap so that we can see whether it is empty or
190          full.  */
191       if ((ctx->writepos + 1) % READBUF_SIZE == ctx->readpos)
192         { 
193           /* Wait for space.  */
194           if (!ResetEvent (ctx->have_space_ev))
195             TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
196           UNLOCK (ctx->mutex);
197           TRACE_LOG ("waiting for space");
198           WaitForSingleObject (ctx->have_space_ev, INFINITE);
199           TRACE_LOG ("got space");
200           LOCK (ctx->mutex);
201         }
202       if (ctx->stop_me)
203         {
204           UNLOCK (ctx->mutex);
205           break;
206         }
207       nbytes = (ctx->readpos + READBUF_SIZE
208                 - ctx->writepos - 1) % READBUF_SIZE;
209       if (nbytes > READBUF_SIZE - ctx->writepos)
210         nbytes = READBUF_SIZE - ctx->writepos;
211       UNLOCK (ctx->mutex);
212       
213       TRACE_LOG1 ("reading %d bytes", nbytes);
214       if (!ReadFile (ctx->file_hd,
215                      ctx->buffer + ctx->writepos, nbytes, &nread, NULL))
216         {
217           ctx->error_code = (int) GetLastError ();
218           if (ctx->error_code == ERROR_BROKEN_PIPE)
219             {
220               ctx->eof = 1;
221               TRACE_LOG ("got EOF (broken pipe)");
222             }
223           else
224             {
225               ctx->error = 1;
226               TRACE_LOG1 ("read error: ec=%d", ctx->error_code);
227             }
228           break;
229         }
230       if (!nread)
231         {
232           ctx->eof = 1;
233           TRACE_LOG ("got eof");
234           break;
235         }
236       TRACE_LOG1 ("got %u bytes", nread);
237       
238       LOCK (ctx->mutex);
239       if (ctx->stop_me)
240         {
241           UNLOCK (ctx->mutex);
242           break;
243         }
244       ctx->writepos = (ctx->writepos + nread) % READBUF_SIZE;
245       if (!SetEvent (ctx->have_data_ev))
246         TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
247                     (int) GetLastError ());
248       UNLOCK (ctx->mutex);
249     }
250   /* Indicate that we have an error or EOF.  */
251   if (!SetEvent (ctx->have_data_ev))
252         TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d", ctx->have_data_ev,
253                     (int) GetLastError ());
254   SetEvent (ctx->stopped);
255   
256   return TRACE_SUC ();
257 }
258
259
260 static struct reader_context_s *
261 create_reader (HANDLE fd)
262 {
263   struct reader_context_s *ctx;
264   SECURITY_ATTRIBUTES sec_attr;
265   DWORD tid;
266
267   TRACE_BEG (DEBUG_SYSIO, "gpgme:create_reader", fd);
268
269   memset (&sec_attr, 0, sizeof sec_attr);
270   sec_attr.nLength = sizeof sec_attr;
271   sec_attr.bInheritHandle = FALSE;
272   
273   ctx = calloc (1, sizeof *ctx);
274   if (!ctx)
275     {
276       TRACE_SYSERR (errno);
277       return NULL;
278     }
279
280   ctx->file_hd = fd;
281   ctx->refcount = 1;
282   ctx->have_data_ev = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
283   if (ctx->have_data_ev)
284     ctx->have_space_ev = CreateEvent (&sec_attr, FALSE, TRUE, NULL);
285   if (ctx->have_space_ev)
286     ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
287   if (!ctx->have_data_ev || !ctx->have_space_ev || !ctx->stopped)
288     {
289       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
290       if (ctx->have_data_ev)
291         CloseHandle (ctx->have_data_ev);
292       if (ctx->have_space_ev)
293         CloseHandle (ctx->have_space_ev);
294       if (ctx->stopped)
295         CloseHandle (ctx->stopped);
296       free (ctx);
297       /* FIXME: Translate the error code.  */
298       TRACE_SYSERR (EIO);
299       return NULL;
300     }
301
302   ctx->have_data_ev = set_synchronize (ctx->have_data_ev);
303   INIT_LOCK (ctx->mutex);
304
305   ctx->thread_hd = CreateThread (&sec_attr, 0, reader, ctx, 0, &tid);
306   if (!ctx->thread_hd)
307     {
308       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
309       DESTROY_LOCK (ctx->mutex);
310       if (ctx->have_data_ev)
311         CloseHandle (ctx->have_data_ev);
312       if (ctx->have_space_ev)
313         CloseHandle (ctx->have_space_ev);
314       if (ctx->stopped)
315         CloseHandle (ctx->stopped);
316       free (ctx);
317       TRACE_SYSERR (EIO);
318       return NULL;
319     }    
320   else
321     {
322       /* We set the priority of the thread higher because we know that
323          it only runs for a short time.  This greatly helps to
324          increase the performance of the I/O.  */
325       SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
326     }
327
328   TRACE_SUC ();
329   return ctx;
330 }
331
332
333 static void
334 destroy_reader (struct reader_context_s *ctx)
335 {
336   LOCK (ctx->mutex);
337   ctx->refcount--;
338   if (ctx->refcount != 0)
339     {
340       UNLOCK (ctx->mutex);
341       return;
342     }
343   ctx->stop_me = 1;
344   if (ctx->have_space_ev) 
345     SetEvent (ctx->have_space_ev);
346   UNLOCK (ctx->mutex);
347
348   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
349           "waiting for termination of thread %p", ctx->thread_hd);
350   WaitForSingleObject (ctx->stopped, INFINITE);
351   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_reader", ctx->file_hd,
352           "thread %p has terminated", ctx->thread_hd);
353     
354   if (ctx->stopped)
355     CloseHandle (ctx->stopped);
356   if (ctx->have_data_ev)
357     CloseHandle (ctx->have_data_ev);
358   if (ctx->have_space_ev)
359     CloseHandle (ctx->have_space_ev);
360   CloseHandle (ctx->thread_hd);
361   DESTROY_LOCK (ctx->mutex);
362   free (ctx);
363 }
364
365
366 /* Find a reader context or create a new one.  Note that the reader
367    context will last until a _gpgme_io_close.  */
368 static struct reader_context_s *
369 find_reader (int fd, int start_it)
370 {
371   struct reader_context_s *rd = NULL;
372   int i;
373
374   LOCK (reader_table_lock);
375   for (i = 0; i < reader_table_size; i++)
376     if (reader_table[i].used && reader_table[i].fd == fd)
377       rd = reader_table[i].context;
378
379   if (rd || !start_it)
380     {
381       UNLOCK (reader_table_lock);
382       return rd;
383     }
384
385   for (i = 0; i < reader_table_size; i++)
386     if (!reader_table[i].used)
387       break;
388
389   if (i != reader_table_size)
390     {
391       rd = create_reader (fd_to_handle (fd));
392       reader_table[i].fd = fd;
393       reader_table[i].context = rd;
394       reader_table[i].used = 1;
395     }
396
397   UNLOCK (reader_table_lock);
398   return rd;
399 }
400
401
402 static void
403 kill_reader (int fd)
404 {
405   int i;
406
407   LOCK (reader_table_lock);
408   for (i = 0; i < reader_table_size; i++)
409     {
410       if (reader_table[i].used && reader_table[i].fd == fd)
411         {
412           destroy_reader (reader_table[i].context);
413           reader_table[i].context = NULL;
414           reader_table[i].used = 0;
415           break;
416         }
417     }
418   UNLOCK (reader_table_lock);
419 }
420
421
422 int
423 _gpgme_io_read (int fd, void *buffer, size_t count)
424 {
425   int nread;
426   struct reader_context_s *ctx;
427   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_read", fd,
428               "buffer=%p, count=%u", buffer, count);
429   
430   ctx = find_reader (fd, 1);
431   if (!ctx)
432     {
433       errno = EBADF;
434       return TRACE_SYSRES (-1);
435     }
436   if (ctx->eof_shortcut)
437     return TRACE_SYSRES (0);
438
439   LOCK (ctx->mutex);
440   if (ctx->readpos == ctx->writepos && !ctx->error)
441     {
442       /* No data available.  */
443       UNLOCK (ctx->mutex);
444       TRACE_LOG1 ("waiting for data from thread %p", ctx->thread_hd);
445       WaitForSingleObject (ctx->have_data_ev, INFINITE);
446       TRACE_LOG1 ("data from thread %p available", ctx->thread_hd);
447       LOCK (ctx->mutex);
448     }
449   
450   if (ctx->readpos == ctx->writepos || ctx->error)
451     {
452       UNLOCK (ctx->mutex);
453       ctx->eof_shortcut = 1;
454       if (ctx->eof)
455         return TRACE_SYSRES (0);
456       if (!ctx->error)
457         {
458           TRACE_LOG ("EOF but ctx->eof flag not set");
459           return 0;
460         }
461       errno = ctx->error_code;
462       return TRACE_SYSRES (-1);
463     }
464   
465   nread = ctx->readpos < ctx->writepos
466     ? ctx->writepos - ctx->readpos
467     : READBUF_SIZE - ctx->readpos;
468   if (nread > count)
469     nread = count;
470   memcpy (buffer, ctx->buffer + ctx->readpos, nread);
471   ctx->readpos = (ctx->readpos + nread) % READBUF_SIZE;
472   if (ctx->readpos == ctx->writepos && !ctx->eof)
473     {
474       if (!ResetEvent (ctx->have_data_ev))
475         {
476           TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
477           UNLOCK (ctx->mutex);
478           /* FIXME: Should translate the error code.  */
479           errno = EIO;
480           return TRACE_SYSRES (-1);
481         }
482     }
483   if (!SetEvent (ctx->have_space_ev))
484     {
485       TRACE_LOG2 ("SetEvent (0x%x) failed: ec=%d",
486                   ctx->have_space_ev, (int) GetLastError ());
487       UNLOCK (ctx->mutex);
488       /* FIXME: Should translate the error code.  */
489       errno = EIO;
490       return TRACE_SYSRES (-1);
491     }
492   UNLOCK (ctx->mutex);
493   
494   TRACE_LOGBUF (buffer, nread);
495   return TRACE_SYSRES (nread);
496 }
497
498
499 /* The writer does use a simple buffering strategy so that we are
500    informed about write errors as soon as possible (i. e. with the the
501    next call to the write function.  */
502 static DWORD CALLBACK 
503 writer (void *arg)
504 {
505   struct writer_context_s *ctx = arg;
506   DWORD nwritten;
507   TRACE_BEG1 (DEBUG_SYSIO, "gpgme:writer", ctx->file_hd,
508               "thread=%p", ctx->thread_hd);
509
510   for (;;)
511     {
512       LOCK (ctx->mutex);
513       if (ctx->stop_me)
514         {
515           UNLOCK (ctx->mutex);
516           break;
517         }
518       if (!ctx->nbytes)
519         { 
520           if (!SetEvent (ctx->is_empty))
521             TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
522           if (!ResetEvent (ctx->have_data))
523             TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
524           UNLOCK (ctx->mutex);
525           TRACE_LOG ("idle");
526           WaitForSingleObject (ctx->have_data, INFINITE);
527           TRACE_LOG ("got data to send");
528           LOCK (ctx->mutex);
529         }
530       if (ctx->stop_me)
531         {
532           UNLOCK (ctx->mutex);
533           break;
534         }
535       UNLOCK (ctx->mutex);
536       
537       TRACE_LOG1 ("writing %d bytes", ctx->nbytes);
538       /* Note that CTX->nbytes is not zero at this point, because
539          _gpgme_io_write always writes at least 1 byte before waking
540          us up, unless CTX->stop_me is true, which we catch above.  */
541       if (!WriteFile (ctx->file_hd, ctx->buffer,
542                       ctx->nbytes, &nwritten, NULL))
543         {
544           ctx->error_code = (int) GetLastError ();
545           ctx->error = 1;
546           TRACE_LOG1 ("write error: ec=%d", ctx->error_code);
547           break;
548         }
549       TRACE_LOG1 ("wrote %d bytes", (int) nwritten);
550       
551       LOCK (ctx->mutex);
552       ctx->nbytes -= nwritten;
553       UNLOCK (ctx->mutex);
554     }
555   /* Indicate that we have an error.  */
556   if (!SetEvent (ctx->is_empty))
557     TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
558   SetEvent (ctx->stopped);
559
560   return TRACE_SUC ();
561 }
562
563
564 static struct writer_context_s *
565 create_writer (HANDLE fd)
566 {
567   struct writer_context_s *ctx;
568   SECURITY_ATTRIBUTES sec_attr;
569   DWORD tid;
570
571   TRACE_BEG (DEBUG_SYSIO, "gpgme:create_writer", fd);
572
573   memset (&sec_attr, 0, sizeof sec_attr);
574   sec_attr.nLength = sizeof sec_attr;
575   sec_attr.bInheritHandle = FALSE;
576
577   ctx = calloc (1, sizeof *ctx);
578   if (!ctx)
579     {
580       TRACE_SYSERR (errno);
581       return NULL;
582     }
583   
584   ctx->file_hd = fd;
585   ctx->refcount = 1;
586   ctx->have_data = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
587   if (ctx->have_data)
588     ctx->is_empty  = CreateEvent (&sec_attr, TRUE, TRUE, NULL);
589   if (ctx->is_empty)
590     ctx->stopped = CreateEvent (&sec_attr, TRUE, FALSE, NULL);
591   if (!ctx->have_data || !ctx->is_empty || !ctx->stopped)
592     {
593       TRACE_LOG1 ("CreateEvent failed: ec=%d", (int) GetLastError ());
594       if (ctx->have_data)
595         CloseHandle (ctx->have_data);
596       if (ctx->is_empty)
597         CloseHandle (ctx->is_empty);
598       if (ctx->stopped)
599         CloseHandle (ctx->stopped);
600       free (ctx);
601       /* FIXME: Translate the error code.  */
602       TRACE_SYSERR (EIO);
603       return NULL;
604     }
605
606   ctx->is_empty = set_synchronize (ctx->is_empty);
607   INIT_LOCK (ctx->mutex);
608
609   ctx->thread_hd = CreateThread (&sec_attr, 0, writer, ctx, 0, &tid );
610   if (!ctx->thread_hd)
611     {
612       TRACE_LOG1 ("CreateThread failed: ec=%d", (int) GetLastError ());
613       DESTROY_LOCK (ctx->mutex);
614       if (ctx->have_data)
615         CloseHandle (ctx->have_data);
616       if (ctx->is_empty)
617         CloseHandle (ctx->is_empty);
618       if (ctx->stopped)
619         CloseHandle (ctx->stopped);
620       free (ctx);
621       TRACE_SYSERR (EIO);
622       return NULL;
623     }    
624   else
625     {
626       /* We set the priority of the thread higher because we know
627          that it only runs for a short time.  This greatly helps to
628          increase the performance of the I/O.  */
629       SetThreadPriority (ctx->thread_hd, get_desired_thread_priority ());
630     }
631
632   TRACE_SUC ();
633   return ctx;
634 }
635
636 static void
637 destroy_writer (struct writer_context_s *ctx)
638 {
639   LOCK (ctx->mutex);
640   ctx->refcount--;
641   if (ctx->refcount != 0)
642     {
643       UNLOCK (ctx->mutex);
644       return;
645     }
646   ctx->stop_me = 1;
647   if (ctx->have_data) 
648     SetEvent (ctx->have_data);
649   UNLOCK (ctx->mutex);
650   
651   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
652           "waiting for termination of thread %p", ctx->thread_hd);
653   WaitForSingleObject (ctx->stopped, INFINITE);
654   TRACE1 (DEBUG_SYSIO, "gpgme:destroy_writer", ctx->file_hd,
655           "thread %p has terminated", ctx->thread_hd);
656   
657   if (ctx->stopped)
658     CloseHandle (ctx->stopped);
659   if (ctx->have_data)
660     CloseHandle (ctx->have_data);
661   if (ctx->is_empty)
662     CloseHandle (ctx->is_empty);
663   CloseHandle (ctx->thread_hd);
664   DESTROY_LOCK (ctx->mutex);
665   free (ctx);
666 }
667
668
669 /* Find a writer context or create a new one.  Note that the writer
670    context will last until a _gpgme_io_close.  */
671 static struct writer_context_s *
672 find_writer (int fd, int start_it)
673 {
674   struct writer_context_s *wt = NULL;
675   int i;
676
677   LOCK (writer_table_lock);
678   for (i = 0; i < writer_table_size; i++)
679     if (writer_table[i].used && writer_table[i].fd == fd)
680       wt = writer_table[i].context;
681
682   if (wt || !start_it)
683     {
684       UNLOCK (writer_table_lock);
685       return wt;
686     }
687
688   for (i = 0; i < writer_table_size; i++)
689     if (!writer_table[i].used)
690       break;
691
692   if (i != writer_table_size)
693     {
694       wt = create_writer (fd_to_handle (fd));
695       writer_table[i].fd = fd;
696       writer_table[i].context = wt; 
697       writer_table[i].used = 1;
698     }
699
700   UNLOCK (writer_table_lock);
701   return wt;
702 }
703
704
705 static void
706 kill_writer (int fd)
707 {
708   int i;
709
710   LOCK (writer_table_lock);
711   for (i = 0; i < writer_table_size; i++)
712     {
713       if (writer_table[i].used && writer_table[i].fd == fd)
714         {
715           destroy_writer (writer_table[i].context);
716           writer_table[i].context = NULL;
717           writer_table[i].used = 0;
718           break;
719         }
720     }
721   UNLOCK (writer_table_lock);
722 }
723
724
725 int
726 _gpgme_io_write (int fd, const void *buffer, size_t count)
727 {
728   struct writer_context_s *ctx;
729   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_write", fd,
730               "buffer=%p, count=%u", buffer, count);
731   TRACE_LOGBUF (buffer, count);
732
733   if (count == 0)
734     return TRACE_SYSRES (0);
735
736   ctx = find_writer (fd, 1);
737   if (!ctx)
738     return TRACE_SYSRES (-1);
739
740   LOCK (ctx->mutex);
741   if (!ctx->error && ctx->nbytes)
742     {
743       /* Bytes are pending for send.  */
744
745       /* Reset the is_empty event.  Better safe than sorry.  */
746       if (!ResetEvent (ctx->is_empty))
747         {
748           TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
749           UNLOCK (ctx->mutex);
750           /* FIXME: Should translate the error code.  */
751           errno = EIO;
752           return TRACE_SYSRES (-1);
753         }
754       UNLOCK (ctx->mutex);
755       TRACE_LOG1 ("waiting for empty buffer in thread %p", ctx->thread_hd);
756       WaitForSingleObject (ctx->is_empty, INFINITE);
757       TRACE_LOG1 ("thread %p buffer is empty", ctx->thread_hd);
758       LOCK (ctx->mutex);
759     }
760
761   if (ctx->error)
762     {
763       UNLOCK (ctx->mutex);
764       if (ctx->error_code == ERROR_NO_DATA)
765         errno = EPIPE;
766       else
767         errno = EIO;
768       return TRACE_SYSRES (-1);
769     }
770
771   /* If no error occured, the number of bytes in the buffer must be
772      zero.  */
773   assert (!ctx->nbytes);
774
775   if (count > WRITEBUF_SIZE)
776     count = WRITEBUF_SIZE;
777   memcpy (ctx->buffer, buffer, count);
778   ctx->nbytes = count;
779
780   /* We have to reset the is_empty event early, because it is also
781      used by the select() implementation to probe the channel.  */
782   if (!ResetEvent (ctx->is_empty))
783     {
784       TRACE_LOG1 ("ResetEvent failed: ec=%d", (int) GetLastError ());
785       UNLOCK (ctx->mutex);
786       /* FIXME: Should translate the error code.  */
787       errno = EIO;
788       return TRACE_SYSRES (-1);
789     }
790   if (!SetEvent (ctx->have_data))
791     {
792       TRACE_LOG1 ("SetEvent failed: ec=%d", (int) GetLastError ());
793       UNLOCK (ctx->mutex);
794       /* FIXME: Should translate the error code.  */
795       errno = EIO;
796       return TRACE_SYSRES (-1);
797     }
798   UNLOCK (ctx->mutex);
799
800   return TRACE_SYSRES ((int) count);
801 }
802
803
804 int
805 _gpgme_io_pipe (int filedes[2], int inherit_idx)
806 {
807   HANDLE rh;
808   HANDLE wh;
809   SECURITY_ATTRIBUTES sec_attr;
810   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
811               "inherit_idx=%i (GPGME uses it for %s)",
812               inherit_idx, inherit_idx ? "reading" : "writing");
813
814   memset (&sec_attr, 0, sizeof (sec_attr));
815   sec_attr.nLength = sizeof (sec_attr);
816   sec_attr.bInheritHandle = FALSE;
817   
818   if (!CreatePipe (&rh, &wh, &sec_attr, PIPEBUF_SIZE))
819     {
820       TRACE_LOG1 ("CreatePipe failed: ec=%d", (int) GetLastError ());
821       /* FIXME: Should translate the error code.  */
822       errno = EIO;
823       return TRACE_SYSRES (-1);
824     }
825
826   /* Make one end inheritable.  */
827   if (inherit_idx == 0)
828     {
829       HANDLE hd;
830       if (!DuplicateHandle (GetCurrentProcess(), rh,
831                             GetCurrentProcess(), &hd, 0,
832                             TRUE, DUPLICATE_SAME_ACCESS))
833         {
834           TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
835                       (int) GetLastError ());
836           CloseHandle (rh);
837           CloseHandle (wh);
838           /* FIXME: Should translate the error code.  */
839           errno = EIO;
840           return TRACE_SYSRES (-1);
841         }
842       CloseHandle (rh);
843       rh = hd;
844     }
845   else if (inherit_idx == 1)
846     {
847       HANDLE hd;
848       if (!DuplicateHandle( GetCurrentProcess(), wh,
849                             GetCurrentProcess(), &hd, 0,
850                             TRUE, DUPLICATE_SAME_ACCESS))
851         {
852           TRACE_LOG1 ("DuplicateHandle failed: ec=%d",
853                       (int) GetLastError ());
854           CloseHandle (rh);
855           CloseHandle (wh);
856           /* FIXME: Should translate the error code.  */
857           errno = EIO;
858           return TRACE_SYSRES (-1);
859         }
860       CloseHandle (wh);
861       wh = hd;
862     }
863   
864   filedes[0] = handle_to_fd (rh);
865   filedes[1] = handle_to_fd (wh);
866   return TRACE_SUC2 ("read=%p, write=%p", rh, wh);
867 }
868
869
870 int
871 _gpgme_io_close (int fd)
872 {
873   int i;
874   _gpgme_close_notify_handler_t handler = NULL;
875   void *value = NULL;
876   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd);
877
878   if (fd == -1)
879     {
880       errno = EBADF;
881       return TRACE_SYSRES (-1);
882     }
883
884   kill_reader (fd);
885   kill_writer (fd);
886   LOCK (notify_table_lock);
887   for (i = 0; i < DIM (notify_table); i++)
888     {
889       if (notify_table[i].inuse && notify_table[i].fd == fd)
890         {
891           handler = notify_table[i].handler;
892           value   = notify_table[i].value;
893           notify_table[i].handler = NULL;
894           notify_table[i].value = NULL;
895           notify_table[i].inuse = 0;
896           break;
897         }
898     }
899   UNLOCK (notify_table_lock);
900   if (handler)
901     handler (fd, value);
902
903   if (!CloseHandle (fd_to_handle (fd)))
904     { 
905       TRACE_LOG1 ("CloseHandle failed: ec=%d", (int) GetLastError ());
906       /* FIXME: Should translate the error code.  */
907       errno = EIO;
908       return TRACE_SYSRES (-1);
909     }
910
911   return TRACE_SYSRES (0);
912 }
913
914
915 int
916 _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
917                             void *value)
918 {
919   int i;
920   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
921               "close_handler=%p/%p", handler, value);
922
923   assert (fd != -1);
924
925   LOCK (notify_table_lock);
926   for (i=0; i < DIM (notify_table); i++)
927     if (notify_table[i].inuse && notify_table[i].fd == fd)
928       break;
929   if (i == DIM (notify_table))
930     for (i = 0; i < DIM (notify_table); i++)
931       if (!notify_table[i].inuse)
932         break;
933   if (i == DIM (notify_table))
934     {
935       UNLOCK (notify_table_lock);
936       errno = EINVAL;
937       return TRACE_SYSRES (-1);
938     }
939   notify_table[i].fd = fd;
940   notify_table[i].handler = handler;
941   notify_table[i].value = value;
942   notify_table[i].inuse = 1;
943   UNLOCK (notify_table_lock);
944   return TRACE_SYSRES (0);
945 }
946
947
948 int
949 _gpgme_io_set_nonblocking (int fd)
950 {
951   TRACE (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd);
952   return 0;
953 }
954
955
956 static char *
957 build_commandline (char **argv)
958 {
959   int i;
960   int n = 0;
961   char *buf;
962   char *p;
963   
964   /* We have to quote some things because under Windows the program
965      parses the commandline and does some unquoting.  We enclose the
966      whole argument in double-quotes, and escape literal double-quotes
967      as well as backslashes with a backslash.  We end up with a
968      trailing space at the end of the line, but that is harmless.  */
969   for (i = 0; argv[i]; i++)
970     {
971       p = argv[i];
972       /* The leading double-quote.  */
973       n++;
974       while (*p)
975         {
976           /* An extra one for each literal that must be escaped.  */
977           if (*p == '\\' || *p == '"')
978             n++;
979           n++;
980           p++;
981         }
982       /* The trailing double-quote and the delimiter.  */
983       n += 2;
984     }
985   /* And a trailing zero.  */
986   n++;
987
988   buf = p = malloc (n);
989   if (!buf)
990     return NULL;
991   for (i = 0; argv[i]; i++)
992     {
993       char *argvp = argv[i];
994
995       *(p++) = '"';
996       while (*argvp)
997         {
998           if (*argvp == '\\' || *argvp == '"')
999             *(p++) = '\\';
1000           *(p++) = *(argvp++);
1001         }
1002       *(p++) = '"';
1003       *(p++) = ' ';
1004     }
1005   *(p++) = 0;
1006
1007   return buf;
1008 }
1009
1010
1011 int
1012 _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
1013                  struct spawn_fd_item_s *fd_list, pid_t *r_pid)
1014 {
1015   SECURITY_ATTRIBUTES sec_attr;
1016   PROCESS_INFORMATION pi =
1017     {
1018       NULL,      /* returns process handle */
1019       0,         /* returns primary thread handle */
1020       0,         /* returns pid */
1021       0          /* returns tid */
1022     };
1023   STARTUPINFO si;
1024   int cr_flags = (CREATE_DEFAULT_ERROR_MODE
1025                   | GetPriorityClass (GetCurrentProcess ()));
1026   int i;
1027   char **args;
1028   char *arg_string;
1029   /* FIXME.  */
1030   int debug_me = 0;
1031   int tmp_fd;
1032   char *tmp_name;
1033
1034   TRACE_BEG1 (DEBUG_SYSIO, "_gpgme_io_spawn", path,
1035               "path=%s", path);
1036   i = 0;
1037   while (argv[i])
1038     {
1039       TRACE_LOG2 ("argv[%2i] = %s", i, argv[i]);
1040       i++;
1041     }
1042
1043   /* We do not inherit any handles by default, and just insert those
1044      handles we want the child to have afterwards.  But some handle
1045      values occur on the command line, and we need to move
1046      stdin/out/err to the right location.  So we use a wrapper program
1047      which gets the information from a temporary file.  */
1048   if (_gpgme_mkstemp (&tmp_fd, &tmp_name) < 0)
1049     {
1050       TRACE_LOG1 ("_gpgme_mkstemp failed: %s", strerror (errno));
1051       return TRACE_SYSRES (-1);
1052     }
1053   TRACE_LOG1 ("tmp_name = %s", tmp_name);
1054
1055   args = calloc (2 + i + 1, sizeof (*args));
1056   args[0] = (char *) _gpgme_get_w32spawn_path ();
1057   args[1] = tmp_name;
1058   args[2] = path;
1059   memcpy (&args[3], &argv[1], i * sizeof (*args));
1060
1061   memset (&sec_attr, 0, sizeof sec_attr);
1062   sec_attr.nLength = sizeof sec_attr;
1063   sec_attr.bInheritHandle = FALSE;
1064  
1065   arg_string = build_commandline (args);
1066   free (args);
1067   if (!arg_string)
1068     {
1069       close (tmp_fd);
1070       DeleteFile (tmp_name);
1071       return TRACE_SYSRES (-1);
1072     }
1073
1074   memset (&si, 0, sizeof si);
1075   si.cb = sizeof (si);
1076   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
1077   si.wShowWindow = debug_me ? SW_SHOW : SW_HIDE;
1078   si.hStdInput = INVALID_HANDLE_VALUE;
1079   si.hStdOutput = INVALID_HANDLE_VALUE;
1080   si.hStdError = INVALID_HANDLE_VALUE;
1081
1082   cr_flags |= CREATE_SUSPENDED; 
1083   cr_flags |= DETACHED_PROCESS;
1084   if (!CreateProcessA (_gpgme_get_w32spawn_path (),
1085                        arg_string,
1086                        &sec_attr,     /* process security attributes */
1087                        &sec_attr,     /* thread security attributes */
1088                        FALSE,         /* inherit handles */
1089                        cr_flags,      /* creation flags */
1090                        NULL,          /* environment */
1091                        NULL,          /* use current drive/directory */
1092                        &si,           /* startup information */
1093                        &pi))          /* returns process information */
1094     {
1095       TRACE_LOG1 ("CreateProcess failed: ec=%d", (int) GetLastError ());
1096       free (arg_string);
1097       close (tmp_fd);
1098       DeleteFile (tmp_name);
1099
1100       /* FIXME: Should translate the error code.  */
1101       errno = EIO;
1102       return TRACE_SYSRES (-1);
1103     }
1104
1105   free (arg_string);
1106
1107   if (flags & IOSPAWN_FLAG_ALLOW_SET_FG)
1108     _gpgme_allow_set_foreground_window ((pid_t)pi.dwProcessId);
1109
1110   /* Insert the inherited handles.  */
1111   for (i = 0; fd_list[i].fd != -1; i++)
1112     {
1113       HANDLE hd;
1114
1115       /* Make it inheritable for the wrapper process.  */
1116       if (!DuplicateHandle (GetCurrentProcess(), fd_to_handle (fd_list[i].fd),
1117                             pi.hProcess, &hd, 0, TRUE, DUPLICATE_SAME_ACCESS))
1118         {
1119           TRACE_LOG1 ("DuplicateHandle failed: ec=%d", (int) GetLastError ());
1120           TerminateProcess (pi.hProcess, 0);
1121           /* Just in case TerminateProcess didn't work, let the
1122              process fail on its own.  */
1123           ResumeThread (pi.hThread);
1124           CloseHandle (pi.hThread);
1125           CloseHandle (pi.hProcess);
1126
1127           close (tmp_fd);
1128           DeleteFile (tmp_name);
1129
1130           /* FIXME: Should translate the error code.  */
1131           errno = EIO;
1132           return TRACE_SYSRES (-1);
1133         }
1134       /* Return the child name of this handle.  */
1135       fd_list[i].peer_name = handle_to_fd (hd);
1136     }
1137   
1138   /* Write the handle translation information to the temporary
1139      file.  */
1140   {
1141     /* Hold roughly MAX_TRANS quadruplets of 64 bit numbers in hex
1142        notation: "0xFEDCBA9876543210" with an extra white space after
1143        every quadruplet.  10*(19*4 + 1) - 1 = 769.  This plans ahead
1144        for a time when a HANDLE is 64 bit.  */
1145 #define BUFFER_MAX 810
1146     char line[BUFFER_MAX + 1];
1147     int res;
1148     int written;
1149     size_t len;
1150
1151     if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
1152       strcpy (line, "~1 \n");
1153     else
1154       strcpy (line, "\n");
1155     for (i = 0; fd_list[i].fd != -1; i++)
1156       {
1157         /* Strip the newline.  */
1158         len = strlen (line) - 1;
1159         
1160         /* Format is: Local name, stdin/stdout/stderr, peer name, argv idx.  */
1161         snprintf (&line[len], BUFFER_MAX - len, "0x%x %d 0x%x %d  \n",
1162                   fd_list[i].fd, fd_list[i].dup_to,
1163                   fd_list[i].peer_name, fd_list[i].arg_loc);
1164         /* Rather safe than sorry.  */
1165         line[BUFFER_MAX - 1] = '\n';
1166         line[BUFFER_MAX] = '\0';
1167       }
1168     len = strlen (line);
1169     written = 0;
1170     do
1171       {
1172         res = write (tmp_fd, &line[written], len - written);
1173         if (res > 0)
1174           written += res;
1175       }
1176     while (res > 0 || (res < 0 && errno == EAGAIN));
1177   }
1178   close (tmp_fd);
1179   /* The temporary file is deleted by the gpgme-w32spawn process
1180      (hopefully).  */
1181
1182   TRACE_LOG4 ("CreateProcess ready: hProcess=%p, hThread=%p, "
1183               "dwProcessID=%d, dwThreadId=%d",
1184               pi.hProcess, pi.hThread, 
1185               (int) pi.dwProcessId, (int) pi.dwThreadId);
1186   
1187   if (r_pid)
1188     *r_pid = (pid_t)pi.dwProcessId;
1189
1190   
1191   if (ResumeThread (pi.hThread) < 0)
1192     TRACE_LOG1 ("ResumeThread failed: ec=%d", (int) GetLastError ());
1193   
1194   if (!CloseHandle (pi.hThread))
1195     TRACE_LOG1 ("CloseHandle of thread failed: ec=%d",
1196                 (int) GetLastError ());
1197
1198   TRACE_LOG1 ("process=%p", pi.hProcess);
1199
1200   /* We don't need to wait for the process.  */
1201   if (!CloseHandle (pi.hProcess))
1202     TRACE_LOG1 ("CloseHandle of process failed: ec=%d",
1203                 (int) GetLastError ());
1204
1205   for (i = 0; fd_list[i].fd != -1; i++)
1206     _gpgme_io_close (fd_list[i].fd);
1207
1208   for (i = 0; fd_list[i].fd != -1; i++)
1209     if (fd_list[i].dup_to == -1)
1210       TRACE_LOG3 ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd,
1211                   fd_list[i].peer_name);
1212     else
1213       TRACE_LOG4 ("fd[%i] = 0x%x -> 0x%x (std%s)", i, fd_list[i].fd,
1214                   fd_list[i].peer_name, (fd_list[i].dup_to == 0) ? "in" :
1215                   ((fd_list[i].dup_to == 1) ? "out" : "err"));
1216
1217   return TRACE_SYSRES (0);
1218 }
1219
1220
1221 /* Select on the list of fds.  Returns: -1 = error, 0 = timeout or
1222    nothing to select, > 0 = number of signaled fds.  */
1223 int
1224 _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
1225 {
1226   HANDLE waitbuf[MAXIMUM_WAIT_OBJECTS];
1227   int waitidx[MAXIMUM_WAIT_OBJECTS];
1228   int code;
1229   int nwait;
1230   int i;
1231   int any;
1232   int count;
1233   void *dbg_help;
1234   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_select", fds,
1235               "nfds=%u, nonblock=%u", nfds, nonblock);
1236
1237  restart:
1238   TRACE_SEQ (dbg_help, "select on [ ");
1239   any = 0;
1240   nwait = 0;
1241   count = 0;
1242   for (i=0; i < nfds; i++)
1243     {
1244       if (fds[i].fd == -1)
1245         continue;
1246       fds[i].signaled = 0;
1247       if (fds[i].for_read || fds[i].for_write)
1248         {
1249           if (fds[i].for_read)
1250             {
1251               struct reader_context_s *ctx = find_reader (fds[i].fd,1);
1252               
1253               if (!ctx)
1254                 TRACE_LOG1 ("error: no reader for FD 0x%x (ignored)",
1255                             fds[i].fd);
1256               else
1257                 {
1258                   if (nwait >= DIM (waitbuf))
1259                     {
1260                       TRACE_END (dbg_help, "oops ]");
1261                       TRACE_LOG ("Too many objects for WFMO!");
1262                       /* FIXME: Should translate the error code.  */
1263                       errno = EIO;
1264                       return TRACE_SYSRES (-1);
1265                     }
1266                   waitidx[nwait] = i;
1267                   waitbuf[nwait++] = ctx->have_data_ev;
1268                 }
1269               TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
1270               any = 1;
1271             }
1272           else if (fds[i].for_write)
1273             {
1274               struct writer_context_s *ctx = find_writer (fds[i].fd,1);
1275               
1276               if (!ctx)
1277                 TRACE_LOG1 ("error: no writer for FD 0x%x (ignored)",
1278                             fds[i].fd);
1279               else
1280                 {
1281                   if (nwait >= DIM (waitbuf))
1282                     {
1283                       TRACE_END (dbg_help, "oops ]");
1284                       TRACE_LOG ("Too many objects for WFMO!");
1285                       /* FIXME: Should translate the error code.  */
1286                       errno = EIO;
1287                       return TRACE_SYSRES (-1);
1288                     }
1289                   waitidx[nwait] = i;
1290                   waitbuf[nwait++] = ctx->is_empty;
1291                 }
1292               TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
1293               any = 1;
1294             }
1295         }
1296     }
1297   TRACE_END (dbg_help, "]");
1298   if (!any) 
1299     return TRACE_SYSRES (0);
1300
1301   code = WaitForMultipleObjects (nwait, waitbuf, 0, nonblock ? 0 : 1000);
1302   if (code >= WAIT_OBJECT_0 && code < WAIT_OBJECT_0 + nwait)
1303     {
1304       /* This WFMO is a really silly function: It does return either
1305          the index of the signaled object or if 2 objects have been
1306          signalled at the same time, the index of the object with the
1307          lowest object is returned - so and how do we find out how
1308          many objects have been signaled???.  The only solution I can
1309          imagine is to test each object starting with the returned
1310          index individually - how dull.  */
1311       any = 0;
1312       for (i = code - WAIT_OBJECT_0; i < nwait; i++)
1313         {
1314           if (WaitForSingleObject (waitbuf[i], 0) == WAIT_OBJECT_0)
1315             {
1316               assert (waitidx[i] >=0 && waitidx[i] < nfds);
1317               fds[waitidx[i]].signaled = 1;
1318               any = 1;
1319               count++;
1320             }
1321         }
1322       if (!any)
1323         {
1324           TRACE_LOG ("no signaled objects found after WFMO");
1325           count = -1;
1326         }
1327     }
1328   else if (code == WAIT_TIMEOUT)
1329     TRACE_LOG ("WFMO timed out");
1330   else if (code == WAIT_FAILED)
1331     {
1332       int le = (int) GetLastError ();
1333       if (le == ERROR_INVALID_HANDLE)
1334         {
1335           int k;
1336           int j = handle_to_fd (waitbuf[i]);
1337           
1338           TRACE_LOG1 ("WFMO invalid handle %d removed", j);
1339           for (k = 0 ; k < nfds; k++)
1340             {
1341               if (fds[k].fd == j)
1342                 {
1343                   fds[k].for_read = fds[k].for_write = 0;
1344                   goto restart;
1345                 }
1346             }
1347           TRACE_LOG (" oops, or not???");
1348         }
1349       TRACE_LOG1 ("WFMO failed: %d", le);
1350       count = -1;
1351     }
1352   else
1353     {
1354       TRACE_LOG1 ("WFMO returned %d", code);
1355       count = -1;
1356     }
1357   
1358   if (count > 0)
1359     {
1360       TRACE_SEQ (dbg_help, "select OK [ ");
1361       for (i = 0; i < nfds; i++)
1362         {
1363           if (fds[i].fd == -1)
1364             continue;
1365           if ((fds[i].for_read || fds[i].for_write) && fds[i].signaled)
1366             TRACE_ADD2 (dbg_help, "%c0x%x ",
1367                         fds[i].for_read ? 'r' : 'w', fds[i].fd);
1368         }
1369       TRACE_END (dbg_help, "]");
1370     }
1371
1372   if (count < 0)
1373     {
1374       /* FIXME: Should determine a proper error code.  */
1375       errno = EIO;
1376     }
1377   
1378   return TRACE_SYSRES (count);
1379 }
1380
1381
1382 void
1383 _gpgme_io_subsystem_init (void)
1384 {
1385   /* Nothing to do.  */
1386 }
1387
1388
1389 /* Write the printable version of FD to the buffer BUF of length
1390    BUFLEN.  The printable version is the representation on the command
1391    line that the child process expects.  */
1392 int
1393 _gpgme_io_fd2str (char *buf, int buflen, int fd)
1394 {
1395   return snprintf (buf, buflen, "%d", fd);
1396 }
1397
1398
1399 int
1400 _gpgme_io_dup (int fd)
1401 {
1402   HANDLE handle = fd_to_handle (fd);
1403   HANDLE new_handle = fd_to_handle (fd);
1404   int i;
1405   struct reader_context_s *rd_ctx;
1406   struct writer_context_s *wt_ctx;
1407
1408   TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_dup", fd);
1409
1410   if (!DuplicateHandle (GetCurrentProcess(), handle,
1411                         GetCurrentProcess(), &new_handle,
1412                         0, FALSE, DUPLICATE_SAME_ACCESS))
1413     {
1414       TRACE_LOG1 ("DuplicateHandle failed: ec=%d\n", (int) GetLastError ());
1415       /* FIXME: Translate error code.  */
1416       errno = EIO;
1417       return TRACE_SYSRES (-1);
1418     }
1419
1420   rd_ctx = find_reader (fd, 1);
1421   if (rd_ctx)
1422     {
1423       /* No need for locking, as the only races are against the reader
1424          thread itself, which doesn't touch refcount.  */
1425       rd_ctx->refcount++;
1426
1427       LOCK (reader_table_lock);
1428       for (i = 0; i < reader_table_size; i++)
1429         if (!reader_table[i].used)
1430           break;
1431       /* FIXME.  */
1432       assert (i != reader_table_size);
1433       reader_table[i].fd = handle_to_fd (new_handle);
1434       reader_table[i].context = rd_ctx;
1435       reader_table[i].used = 1;
1436       UNLOCK (reader_table_lock);
1437     }
1438
1439   wt_ctx = find_writer (fd, 1);
1440   if (wt_ctx)
1441     {
1442       /* No need for locking, as the only races are against the writer
1443          thread itself, which doesn't touch refcount.  */
1444       wt_ctx->refcount++;
1445
1446       LOCK (writer_table_lock);
1447       for (i = 0; i < writer_table_size; i++)
1448         if (!writer_table[i].used)
1449           break;
1450       /* FIXME.  */
1451       assert (i != writer_table_size);
1452       writer_table[i].fd = handle_to_fd (new_handle);
1453       writer_table[i].context = wt_ctx;
1454       writer_table[i].used = 1;
1455       UNLOCK (writer_table_lock);
1456     }
1457
1458   return TRACE_SYSRES (handle_to_fd (new_handle));
1459 }
1460
1461 \f
1462 /* The following interface is only useful for GPGME Glib and Qt.  */
1463
1464 /* Compatibility interface, obsolete.  */
1465 void *
1466 gpgme_get_giochannel (int fd)
1467 {
1468   return NULL;
1469 }
1470
1471
1472 /* Look up the giochannel or qiodevice for file descriptor FD.  */
1473 void *
1474 gpgme_get_fdptr (int fd)
1475 {
1476   return NULL;
1477 }
1478
1479 \f
1480 static int
1481 wsa2errno (int err)
1482 {
1483   switch (err)
1484     {
1485     case WSAENOTSOCK:
1486       return EINVAL;
1487     case WSAEWOULDBLOCK:
1488       return EAGAIN;
1489     case ERROR_BROKEN_PIPE:
1490       return EPIPE;
1491     case WSANOTINITIALISED:
1492       return ENOSYS;
1493     default:
1494       return EIO;
1495     }
1496 }
1497
1498
1499 int
1500 _gpgme_io_socket (int domain, int type, int proto)
1501 {
1502   int res;
1503
1504   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_socket", domain,
1505               "type=%i, protp=%i", type, proto);
1506
1507   res = socket (domain, type, proto);
1508   if (res == INVALID_SOCKET)
1509     {
1510       errno = wsa2errno (WSAGetLastError ());
1511       return TRACE_SYSRES (-1);
1512     }
1513
1514   TRACE_SUC1 ("socket=0x%x", res);
1515   
1516   return res;
1517 }
1518
1519
1520 int
1521 _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
1522 {
1523   int res;
1524
1525   TRACE_BEG2 (DEBUG_SYSIO, "_gpgme_io_connect", fd,
1526               "addr=%p, addrlen=%i", addr, addrlen);
1527
1528   res = connect (fd, addr, addrlen);
1529   if (!res)
1530     {
1531       errno = wsa2errno (WSAGetLastError ());
1532       return TRACE_SYSRES (-1);
1533     }
1534
1535   return TRACE_SUC ();
1536 }