Asynchronous agent requests on Windows. Actually, I've kept the
[u/mdw/putty] / pageantc.c
index d7dbfc0..3d5e61f 100644 (file)
@@ -6,17 +6,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "puttymem.h"
+#include "putty.h"
 
 #define AGENT_COPYDATA_ID 0x804e50ba   /* random goop */
 #define AGENT_MAX_MSGLEN  8192
 
-#ifdef TESTMODE
-#define debug(x) (printf x)
-#else
-#define debug(x)
-#endif
-
 #define GET_32BIT(cp) \
     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
@@ -33,11 +27,48 @@ int agent_exists(void)
        return TRUE;
 }
 
+struct agent_query_data {
+    COPYDATASTRUCT cds;
+    unsigned char *mapping;
+    HANDLE handle;
+    char *mapname;
+    HWND hwnd;
+    void (*callback)(void *, void *, int);
+    void *callback_ctx;
+};
+
+DWORD WINAPI agent_query_thread(LPVOID param)
+{
+    struct agent_query_data *data = (struct agent_query_data *)param;
+    unsigned char *ret;
+    int id, retlen;
+
+    id = SendMessage(data->hwnd, WM_COPYDATA, (WPARAM) NULL,
+                    (LPARAM) &data->cds);
+    ret = NULL;
+    if (id > 0) {
+       retlen = 4 + GET_32BIT(data->mapping);
+       ret = snewn(retlen, unsigned char);
+       if (ret) {
+           memcpy(ret, data->mapping, retlen);
+       }
+    }
+    if (!ret)
+       retlen = 0;
+    UnmapViewOfFile(data->mapping);
+    CloseHandle(data->handle);
+    sfree(data->mapname);
+
+    agent_schedule_callback(data->callback, data->callback_ctx, ret, retlen);
+
+    return 0;
+}
+
 int agent_query(void *in, int inlen, void **out, int *outlen,
                void (*callback)(void *, void *, int), void *callback_ctx)
 {
     HWND hwnd;
-    char mapname[64];
+    char *mapname;
     HANDLE filemap;
     unsigned char *p, *ret;
     int id, retlen;
@@ -47,10 +78,9 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
     *outlen = 0;
 
     hwnd = FindWindow("Pageant", "Pageant");
-    debug(("hwnd is %p\n", hwnd));
     if (!hwnd)
        return 1;                      /* *out == NULL, so failure */
-    sprintf(mapname, "PageantRequest%08x", (unsigned)GetCurrentThreadId());
+    mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId());
     filemap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
                                0, AGENT_MAX_MSGLEN, mapname);
     if (!filemap)
@@ -60,11 +90,37 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
     cds.dwData = AGENT_COPYDATA_ID;
     cds.cbData = 1 + strlen(mapname);
     cds.lpData = mapname;
+    if (callback != NULL && !(flags & FLAG_SYNCAGENT)) {
+       /*
+        * We need an asynchronous Pageant request. Since I know of
+        * no way to stop SendMessage from blocking the thread it's
+        * called in, I see no option but to start a fresh thread.
+        * When we're done we'll PostMessage the result back to our
+        * main window, so that the callback is done in the primary
+        * thread to avoid concurrency.
+        */
+       struct agent_query_data *data = snew(struct agent_query_data);
+       DWORD threadid;
+       data->mapping = p;
+       data->handle = filemap;
+       data->mapname = mapname;
+       data->callback = callback;
+       data->callback_ctx = callback_ctx;
+       data->cds = cds;               /* structure copy */
+       data->hwnd = hwnd;
+       if (CreateThread(NULL, 0, agent_query_thread, data, 0, &threadid))
+           return 0;
+       sfree(data);
+    }
+
+    /*
+     * The user either passed a null callback (indicating that the
+     * query is required to be synchronous) or CreateThread failed.
+     * Either way, we need a synchronous request.
+     */
     id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) & cds);
-    debug(("return is %d\n", id));
     if (id > 0) {
        retlen = 4 + GET_32BIT(p);
-       debug(("len is %d\n", retlen));
        ret = snewn(retlen, unsigned char);
        if (ret) {
            memcpy(ret, p, retlen);
@@ -74,24 +130,5 @@ int agent_query(void *in, int inlen, void **out, int *outlen,
     }
     UnmapViewOfFile(p);
     CloseHandle(filemap);
-
     return 1;
 }
-
-#ifdef TESTMODE
-
-int main(void)
-{
-    void *msg;
-    int len;
-    int i;
-
-    agent_query("\0\0\0\1\1", 5, &msg, &len);
-    debug(("%d:", len));
-    for (i = 0; i < len; i++)
-       debug((" %02x", ((unsigned char *) msg)[i]));
-    debug(("\n"));
-    return 0;
-}
-
-#endif