Merge branch 'master' of ssh+git://playfair.gnupg.org/git/gpgme
[gpgme.git] / src / w32-io.c
index 8e7abd3..05e11ee 100644 (file)
@@ -50,6 +50,7 @@
 #include "sema.h"
 #include "priv-io.h"
 #include "debug.h"
+#include "sys-util.h"
 
 
 /* FIXME: Optimize.  */
@@ -83,6 +84,7 @@ static struct
      duplicates works just fine.  */
   int dup_from;
 } fd_table[MAX_SLAFD];
+DEFINE_STATIC_LOCK (fd_table_lock);
 
 
 /* Returns the FD or -1 on resource limit.  */
@@ -91,6 +93,8 @@ new_fd (void)
 {
   int idx;
 
+  LOCK (fd_table_lock);
+
   for (idx = 0; idx < MAX_SLAFD; idx++)
     if (! fd_table[idx].used)
       break;
@@ -98,14 +102,18 @@ new_fd (void)
   if (idx == MAX_SLAFD)
     {
       gpg_err_set_errno (EIO);
-      return -1;
+      idx = -1;
+    }
+  else
+    {
+      fd_table[idx].used = 1;
+      fd_table[idx].handle = INVALID_HANDLE_VALUE;
+      fd_table[idx].socket = INVALID_SOCKET;
+      fd_table[idx].rvid = 0;
+      fd_table[idx].dup_from = -1;
     }
 
-  fd_table[idx].used = 1;
-  fd_table[idx].handle = INVALID_HANDLE_VALUE;
-  fd_table[idx].socket = INVALID_SOCKET;
-  fd_table[idx].rvid = 0;
-  fd_table[idx].dup_from = -1;
+  UNLOCK (fd_table_lock);
 
   return idx;
 }
@@ -114,14 +122,21 @@ new_fd (void)
 void
 release_fd (int fd)
 {
-  if (fd < 0 || fd >= MAX_SLAFD || !fd_table[fd].used)
+  if (fd < 0 || fd >= MAX_SLAFD)
     return;
 
-  fd_table[fd].used = 0;
-  fd_table[fd].handle = INVALID_HANDLE_VALUE;
-  fd_table[fd].socket = INVALID_SOCKET;
-  fd_table[fd].rvid = 0;
-  fd_table[fd].dup_from = -1;
+  LOCK (fd_table_lock);
+
+  if (fd_table[fd].used)
+    {
+      fd_table[fd].used = 0;
+      fd_table[fd].handle = INVALID_HANDLE_VALUE;
+      fd_table[fd].socket = INVALID_SOCKET;
+      fd_table[fd].rvid = 0;
+      fd_table[fd].dup_from = -1;
+    }
+
+  UNLOCK (fd_table_lock);
 }
 
 
@@ -685,7 +700,7 @@ writer (void *arg)
   for (;;)
     {
       LOCK (ctx->mutex);
-      if (ctx->stop_me)
+      if (ctx->stop_me && !ctx->nbytes)
        {
          UNLOCK (ctx->mutex);
          break;
@@ -702,7 +717,7 @@ writer (void *arg)
          TRACE_LOG ("got data to send");
          LOCK (ctx->mutex);
                }
-      if (ctx->stop_me)
+      if (ctx->stop_me && !ctx->nbytes)
        {
          UNLOCK (ctx->mutex);
          break;
@@ -761,6 +776,9 @@ writer (void *arg)
   TRACE_LOG ("waiting for close");
   WaitForSingleObject (ctx->close_ev, INFINITE);
 
+  if (ctx->nbytes)
+    TRACE_LOG1 ("still %d bytes in buffer at close time", ctx->nbytes);
+
   CloseHandle (ctx->close_ev);
   CloseHandle (ctx->have_data);
   CloseHandle (ctx->is_empty);
@@ -877,6 +895,9 @@ destroy_writer (struct writer_context_s *ctx)
     SetEvent (ctx->have_data);
   UNLOCK (ctx->mutex);
 
+  /* Give the writer a chance to flush the buffer.  */
+  WaitForSingleObject (ctx->is_empty, INFINITE);
+
 #ifdef HAVE_W32CE_SYSTEM
   /* Scenario: We never create a full pipe, but already started
      writing more than the pipe buffer.  Then we need to unblock the
@@ -1605,6 +1626,31 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
     cr_flags |= DETACHED_PROCESS;
   cr_flags |= GetPriorityClass (GetCurrentProcess ());
   spawnhelper = _gpgme_get_w32spawn_path ();
+  if (!spawnhelper)
+    {
+      /* This is a common mistake for new users of gpgme not to include
+         gpgme-w32spawn.exe with their binary. So we want to make
+         this transparent to developers. If users have somehow messed
+         up their installation this should also be properly communicated
+         as otherwise calls to gnupg will result in unsupported protocol
+         errors that do not explain a lot. */
+      char *msg;
+      gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the "
+                            "detected installation directory of GpgME"
+                            "\n\t\"%s\"\n\n"
+                            "Crypto operations will not work.\n\n"
+                            "If you see this it indicates a problem "
+                            "with your installation.\n"
+                            "Please report the problem to your "
+                            "distributor of GpgME.\n\n"
+                            "Developer's Note: The install dir can be "
+                            "manually set with: gpgme_set_global_flag",
+                            _gpgme_get_inst_dir ());
+      MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK);
+      gpgrt_free (msg);
+      gpg_err_set_errno (EIO);
+      return TRACE_SYSRES (-1);
+    }
   if (!CreateProcessA (spawnhelper,
                       arg_string,
                       &sec_attr,     /* process security attributes */
@@ -1618,8 +1664,6 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
     {
       int lasterr = (int)GetLastError ();
       TRACE_LOG1 ("CreateProcess failed: ec=%d", lasterr);
-      if (lasterr == ERROR_INVALID_PARAMETER)
-        TRACE_LOG1 ("(is '%s' correctly installed?)", spawnhelper);
       free (arg_string);
       close (tmp_fd);
       DeleteFileA (tmp_name);
@@ -1680,8 +1724,8 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
     int written;
     size_t len;
 
-    if ((flags & IOSPAWN_FLAG_ALLOW_SET_FG))
-      strcpy (line, "~1 \n");
+    if (flags)
+      snprintf (line, BUFFER_MAX, "~%i \n", flags);
     else
       strcpy (line, "\n");
     for (i = 0; fd_list[i].fd != -1; i++)