Made debugging configurable.
[gpgol.git] / src / engine-assuan.c
1 /* engine-assuan.c - Crypto engine using an Assuan server
2  *      Copyright (C) 2007, 2008 g10 Code GmbH
3  *
4  * This file is part of GpgOL.
5  *
6  * GpgOL is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 
9  * of the License, or (at your option) any later version.
10  *  
11  * GpgOL is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <errno.h>
27 #include <assert.h>
28 #define WIN32_LEAN_AND_MEAN 
29 #define WINVER 0x0500  /* Required for AllowSetForegroundWindow.  */
30 #include <windows.h>
31
32 #include <assuan.h>
33 #include "common.h"
34 #include "engine.h"
35 #include "engine-assuan.h"
36
37
38 #define TRACEPOINT() do { log_debug ("%s:%s:%d: tracepoint\n", \
39                                        SRCNAME, __func__, __LINE__); \
40                         } while (0)
41
42 /* Debug macros.  */
43 #define debug_ioworker        (opt.enable_debug & DBG_IOWORKER)
44 #define debug_ioworker_extra  (opt.enable_debug & DBG_IOWORKER_EXTRA)
45
46
47 /* How many times we will try to connect to a server after we have
48    started him.  */
49 #define FIREUP_RETRIES 10
50
51
52 /* This is the buffer object used for the asynchronous reading of the
53    status channel.  */
54 struct status_buffer_s
55 {
56   int eof;
57   int linelen;  /* Used length of LINE. */
58   char line[ASSUAN_LINELENGTH];
59 };
60 typedef struct status_buffer_s *status_buffer_t;
61
62
63 /* We operate in an asynchronous mode and thus need to run code for
64    final cleanup.  Thus all functions need to implement a closure
65    function and setup an closure_data_t object.  */ 
66 struct closure_data_s;
67 typedef struct closure_data_s *closure_data_t;
68 struct closure_data_s
69 {
70   void (*closure)(closure_data_t);
71   gpg_error_t final_err;         /* Final error code.  */
72   engine_filter_t filter;
73   assuan_context_t assctx;
74   ULONG cmdid;
75   assuan_fd_t status_read_fd;
76   struct gpgme_data_cbs status_cbs;
77   gpgme_data_t status_data;
78   status_buffer_t status_buffer; /* Allocated on demand.  */
79   int status_ready;
80   gpgme_data_t sigdata;   /* Used by verify_closure.  */
81   gpg_error_t last_err;
82 };
83
84
85 /* The object used by our I/O worker thread.  */
86 struct work_item_s;
87 typedef struct work_item_s *work_item_t;
88 struct work_item_s
89 {
90   work_item_t next;
91   int used;          /* If not set this object may be reused.  */
92   int waiting;       /* Helper for async_worker_thread.  */
93
94   const char *name;  /* Description used for debugging.  */
95   ULONG cmdid;       /* Used to group work items of one command.  */
96   closure_data_t cld;/* NULL or the closure.  */
97   int wait_on_success; /* This work item needs to be ready before
98                           invoking a closure for this command.  */
99   gpgme_data_t data; /* The data object we write to or read from.  */
100   int writing;       /* If true we are going to write to HD.  */
101   HANDLE hd;         /* The handle we read from or write to.  */
102   int io_pending;    /* I/O is still pending.  The value is the number
103                         of bytes to be written or the size of the
104                         buffer given to ReadFile. */
105   int got_ready;     /* Operation finished.  */
106   int delayed_ready; /* Ready but delayed to to a missing prerequesite.  */
107   int got_error;     /* An error as been encountered.  */
108   int aborting;      /* Set to true after a CancelIO has been issued.  */
109   void (*finalize)(work_item_t); /* Function called immediately before
110                                     the item is removed from the
111                                     queue.  */
112   OVERLAPPED ov;     /* The overlapped info structure.  */
113   char buffer[128];  /* The buffer used by ReadFile or WriteFile.  */
114 };
115
116
117 /* The queue of all outstandig I/O operations.  Protected by the
118    work_queue_lock.  */
119 static work_item_t work_queue;
120
121 /* The big lock used to protect the work queue.  */
122 static CRITICAL_SECTION work_queue_lock;
123
124 /* An auto-reset event which will be signaled to get the
125    async_worker_thread out of its WFMO and to inspect the work
126    queue.  */
127 static HANDLE work_queue_event;
128
129
130 /*-- prototypes --*/
131 static DWORD WINAPI async_worker_thread (void *dummy);
132
133
134
135 \f
136 /* Return the next command id.  Command Ids are used to group
137    resources of one command. */
138 static ULONG
139 create_command_id (void)
140 {
141   static ULONG command_id;
142   ULONG cmdid;
143
144   while (!(cmdid = InterlockedIncrement (&command_id)))
145     ;
146   return cmdid;
147 }
148
149
150 static void
151 close_pipe (HANDLE apipe[2])
152 {
153   int i;
154
155   for (i=0; i < 2; i++)
156     if (apipe[i] != INVALID_HANDLE_VALUE)
157       {
158         CloseHandle (apipe[i]);
159         apipe[i] = INVALID_HANDLE_VALUE;
160       }
161 }
162
163
164 /* Duplicate HANDLE into the server's process and close HANDLE.  Note
165    that HANDLE is closed even if the function fails.  Returns the
166    duplicated handle on success or INVALID_HANDLE_VALUE on error.  */
167 static HANDLE
168 dup_to_server (HANDLE handle, pid_t serverpid)
169 {
170   HANDLE prochandle, newhandle;
171
172   prochandle = OpenProcess (PROCESS_DUP_HANDLE, FALSE, serverpid);
173   if (!prochandle)
174     {
175       log_error_w32 (-1, "%s:%s: OpenProcess(%lu) failed", 
176                      SRCNAME, __func__, (unsigned long)serverpid);
177       CloseHandle (handle);
178       return INVALID_HANDLE_VALUE;
179     }
180
181   if (!DuplicateHandle (GetCurrentProcess(), handle,
182                         prochandle, &newhandle, 0,
183                         TRUE, DUPLICATE_SAME_ACCESS ))
184     {
185       log_error_w32 (-1, "%s:%s: DuplicateHandle to pid %lu failed", 
186                      SRCNAME, __func__, (unsigned long)serverpid);
187       CloseHandle (prochandle);
188       CloseHandle (handle);
189       return INVALID_HANDLE_VALUE;
190     }
191   CloseHandle (prochandle);
192   CloseHandle (handle);
193   return newhandle;
194 }
195
196
197 /* Create pipe with one end being inheritable and prepared for
198    overlapped I/O.
199
200      FILEDES[0] := read handle. 
201      FILEDES[1] := write handle. 
202
203    SERVERPID is the PID of the server.  FOR_WRITE is seen out of our
204    perspective; if it is set, the read handle is created in the server
205    process and the write handle is overlapped.  If it is not set the
206    write handle is created in the server process and the read handle
207    is overlapped.
208 */
209 static gpg_error_t
210 create_io_pipe (HANDLE filedes[2], pid_t serverpid, int for_write)
211 {
212   static ULONG pipenumber;
213   ULONG pipeno;
214   char pipename[100];
215   HANDLE r, w;
216   SECURITY_ATTRIBUTES sec_attr;
217
218   memset (&sec_attr, 0, sizeof sec_attr );
219   sec_attr.nLength = sizeof sec_attr;
220
221   /* CreatePipe is in reality implemented using a Named Pipe.  We do
222      it the same but use a name which is in our name space.  We allow
223      only one instance, use the standard timeout of 120 seconds and
224      buffers of 4k. */
225   pipeno = InterlockedIncrement (&pipenumber);
226   snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\GpgOL_anon.%08lx.%08lx",
227             (unsigned long)GetCurrentProcessId(), pipeno);
228   sec_attr.bInheritHandle = /*for_write? TRUE :*/FALSE;
229   r = CreateNamedPipe (pipename, (PIPE_ACCESS_INBOUND
230                                   | (for_write? 0:FILE_FLAG_OVERLAPPED)),
231                        PIPE_TYPE_BYTE | PIPE_WAIT,
232                        1, 4096, 4096, 120000, &sec_attr);
233   if (r == INVALID_HANDLE_VALUE)
234     {
235       log_error_w32 (-1, "%s:%s: CreateNamedPipe failed for `%s'",
236                      SRCNAME, __func__, pipename);
237       return gpg_error (GPG_ERR_GENERAL);
238     }
239   if (for_write)
240     {
241       r = dup_to_server (r, serverpid);
242       if (r == INVALID_HANDLE_VALUE)
243         {
244           log_error_w32 (-1, "%s:%s: dup_for_server(r) failed for `%s'",
245                          SRCNAME, __func__, pipename);
246           return gpg_error (GPG_ERR_GENERAL);
247         }
248     }
249
250   /* Now open the other side of the named pipe.  Because we have not
251      called ConnectNamedPipe another process should not be able to
252      open the pipe in the meantime.  This is an educated guess by
253      looking at REACTOS and WINE - they implement an anonymous pipe
254      this way.  */
255   sec_attr.bInheritHandle = /*for_write?*/ FALSE /*: TRUE*/;
256   w = CreateFile (pipename, GENERIC_WRITE, 0, &sec_attr,
257                   OPEN_EXISTING, (FILE_ATTRIBUTE_NORMAL
258                                   | (for_write? FILE_FLAG_OVERLAPPED:0)),
259                   NULL);
260   if (w == INVALID_HANDLE_VALUE)
261     {
262       log_error_w32 (-1, "%s:%s: CreateFile failed for `%s'",
263                      SRCNAME, __func__, pipename);
264       CloseHandle (r);
265       return gpg_error (GPG_ERR_GENERAL);
266     }
267   if (!for_write)
268     {
269       w = dup_to_server (w, serverpid);
270       if (w == INVALID_HANDLE_VALUE)
271         {
272           log_error_w32 (-1, "%s:%s: dup_for_server(w) failed for `%s'",
273                          SRCNAME, __func__, pipename);
274           CloseHandle (r);
275           return gpg_error (GPG_ERR_GENERAL);
276         }
277     }
278
279   filedes[0] = r;
280   filedes[1] = w;
281   if (debug_ioworker)
282     log_debug ("%s:%s: new pipe created: r=%p%s w=%p%s",  SRCNAME, __func__,
283                r, for_write? " (server)":"",
284                w, !for_write?" (server)":"");
285   return 0;
286 }
287
288
289 /* Return the socket name of the UI Server.  */
290 static const char *
291 get_socket_name (void)
292 {
293   static char *name;
294
295   if (!name)
296     {
297       const char *dir = default_homedir ();
298       name = xmalloc (strlen (dir) + 11 + 1);
299       strcpy (stpcpy (name, dir), "\\S.uiserver");
300     }
301
302   return name;
303 }
304
305
306 /* Same as get_socket_name but returns a malloced string with a quoted
307    filename.  */
308 static char *
309 get_quoted_socket_name (void)
310 {
311   const char *sname = get_socket_name ();
312   const char *s;
313   char *buffer, *p;
314   size_t n;
315
316   for (n=2, s=sname; *s; s++, n++)
317     if (*s== '\"')
318       n++;
319   buffer = p = xmalloc (n+1);
320   *p++ = '\"';
321   for (s=sname; *s; s++)
322     {
323       *p++ = *s;
324       if (*s == '\"')
325         *p++ = *s;
326     }
327   *p++ = '\"';
328   *p = 0;
329   return buffer;
330 }
331
332
333 /* Substitute all substrings "$s" in BUFFER by the value of the
334    default socket and replace all "$$" by "$".  Free BUFFER if
335    necessary and return a newly malloced buffer.  */
336 static char *
337 replace_dollar_s (char *buffer)
338 {
339   char *rover, *p;
340
341   for (rover=buffer; (p = strchr (rover, '$')); )
342     {
343       if (p[1] == '$') /* Just an escaped dollar sign. */
344         {
345           memmove (p, p+1, strlen (p+1)+1);
346           rover = p + 1;
347         }
348       else if (p[1] == 's') /* Substitute with socket name.  */
349         {
350           char *value = get_quoted_socket_name ();
351           size_t n = p - buffer;
352           char *newbuf;
353
354           newbuf = xmalloc (strlen (buffer) + strlen (value) + 1);
355           memcpy (newbuf, buffer, n);
356           strcpy (newbuf + n, value);
357           n += strlen (value);
358           strcpy (newbuf + n, p+2);
359           rover = newbuf + n;
360           xfree (buffer);
361           buffer = newbuf;
362           xfree (value);
363         }
364       else
365         rover = p + 1;
366     }
367   return buffer;
368 }
369
370
371
372 /* Return the name of the default UI server.  This name is used to
373    auto start an UI server if an initial connect failed.  */
374 static char *
375 get_uiserver_name (void)
376 {
377   char *name = NULL;
378   char *dir, *uiserver, *p;
379
380   dir = read_w32_registry_string ("HKEY_LOCAL_MACHINE", GNUPG_REGKEY,
381                                   "Install Directory");
382   if (dir)
383     {
384       uiserver = read_w32_registry_string (NULL, GNUPG_REGKEY, 
385                                            "UI Server");
386       if (!uiserver)
387         uiserver = xstrdup ("bin\\kleopatra.exe --uiserver-socket $s");
388           
389       uiserver = replace_dollar_s (uiserver);
390       
391       /* FIXME: Very dirty work-around to make kleopatra find its
392          DLLs.  */
393       if (!strncmp (uiserver, "bin\\kleopatra.exe", 17))
394         chdir (dir);
395
396       name = xmalloc (strlen (dir) + strlen (uiserver) + 2);
397       strcpy (stpcpy (stpcpy (name, dir), "\\"), uiserver);
398       for (p=name; *p; p++)
399         if (*p == '/')
400           *p == '\\';
401       xfree (uiserver);
402       xfree (dir);
403     }
404   
405   return name;
406 }
407
408
409
410 static gpg_error_t
411 send_one_option (assuan_context_t ctx, const char *name, const char *value)
412 {
413   gpg_error_t err;
414   char buffer[1024];
415
416   if (!value || !*value)
417     err = 0;  /* Avoid sending empty strings.  */
418   else 
419     {
420       snprintf (buffer, sizeof buffer, "OPTION %s=%s", name, value);
421       err = assuan_transact (ctx, buffer, NULL, NULL, NULL, NULL, NULL, NULL);
422     }
423
424   return err;
425 }
426
427
428 static int
429 getinfo_pid_cb (void *opaque, const void *buffer, size_t length)
430 {
431   pid_t *pid = opaque;
432   char pidbuf[50];
433
434   /* There is only the pid in the server's response.  */
435   if (length >= sizeof pidbuf)
436     length = sizeof pidbuf -1;
437   if (length)
438     {
439       strncpy (pidbuf, buffer, length);
440       pidbuf[length] = 0;
441       *pid = (pid_t)strtoul (pidbuf, NULL, 10);
442     }
443   return 0;
444 }
445
446
447 /* Send options to the UI server and return the server's PID.  */
448 static gpg_error_t
449 send_options (assuan_context_t ctx, void *hwnd, pid_t *r_pid)
450 {
451   gpg_error_t err = 0;
452   char numbuf[50];
453
454   *r_pid = (pid_t)(-1);
455   err = assuan_transact (ctx, "GETINFO pid", getinfo_pid_cb, r_pid,
456                          NULL, NULL, NULL, NULL);
457   if (!err && *r_pid == (pid_t)(-1))
458     {
459       log_debug ("%s:%s: server did not return a PID", SRCNAME, __func__);
460       err = gpg_error (GPG_ERR_ASSUAN_SERVER_FAULT);
461     }
462
463   if (*r_pid != (pid_t)(-1) && !AllowSetForegroundWindow (*r_pid))
464     log_error_w32 (-1, "AllowSetForegroundWindow(%u) failed", *r_pid);
465
466   if (!err && hwnd)
467     {
468       snprintf (numbuf, sizeof numbuf, "%lx", (unsigned long)hwnd);
469       err = send_one_option (ctx, "window-id", numbuf);
470     }
471
472   return err;
473 }
474
475
476 /* Connect to the UI server and setup the connection.  */
477 static gpg_error_t
478 connect_uiserver (assuan_context_t *r_ctx, pid_t *r_pid, ULONG *r_cmdid,
479                   void *hwnd)
480 {
481   static ULONG retry_counter;
482   ULONG retry_count;
483   gpg_error_t err;
484   assuan_context_t ctx;
485
486   *r_ctx = NULL;
487   *r_pid = (pid_t)(-1);
488   *r_cmdid = 0;
489  retry:
490   err = assuan_socket_connect (&ctx, get_socket_name (), -1);
491   if (err)
492     {
493       /* Let only one thread start an UI server but all allow threads
494          to check for a connection.  Note that this is not really
495          correct as the maximum waiting time decreases with the number
496          of threads.  However, it is unlikely that we have more than 2
497          or 3 threads here - if at all more than one.  */
498       retry_count = InterlockedExchangeAdd (&retry_counter, 1);
499       if (retry_count < FIREUP_RETRIES)
500         {
501           if (!retry_count)
502             {
503               char *uiserver = get_uiserver_name ();
504               if (!uiserver)
505                 {
506                   log_error ("%s:%s: UI server not installed",
507                              SRCNAME, __func__);
508                   InterlockedExchange (&retry_counter, FIREUP_RETRIES);
509                   retry_count = FIREUP_RETRIES;
510                 }
511               else
512                 {
513                   log_debug ("%s:%s: UI server not running, starting `%s'",
514                              SRCNAME, __func__, uiserver);
515                   if (gpgol_spawn_detached (uiserver))
516                     {
517                       /* Error; try again to connect in case the
518                          server has been started in the meantime.
519                          Make sure that we don't get here a second
520                          time.  */
521                       InterlockedExchange (&retry_counter, FIREUP_RETRIES);
522                     }
523                   xfree (uiserver);
524                 }
525             }
526           if (retry_count < FIREUP_RETRIES)
527             {
528               log_debug ("%s:%s: waiting for UI server to come up",
529                          SRCNAME, __func__);
530               Sleep (1000);
531               goto retry;
532             }
533         }
534       else
535         {
536           /* Avoid a retry counter overflow by limiting to the limit.  */
537           InterlockedExchange (&retry_counter, FIREUP_RETRIES);
538         }
539
540       log_error ("%s:%s: error connecting `%s': %s\n", SRCNAME, __func__,
541                  get_socket_name (), gpg_strerror (err));
542     }
543   else if ((err = send_options (ctx, hwnd, r_pid)))
544     {
545       assuan_disconnect (ctx);
546     }
547   else
548     {
549       *r_cmdid = create_command_id ();
550       *r_ctx = ctx;
551     }
552   return err;
553 }
554
555
556
557 \f
558 \f
559 static void
560 cleanup (void)
561 {
562   /* Fixme: We should stop the worker thread.  */
563 }
564
565
566 /* Cleanup static resources. */
567 void
568 op_assuan_deinit (void)
569 {
570   cleanup ();
571 }
572
573
574 /* Initialize this system. */
575 int
576 op_assuan_init (void)
577 {
578   static int init_done;
579   gpgme_error_t err;
580   assuan_context_t ctx;
581   pid_t pid;
582   ULONG cmdid;
583
584   if (init_done)
585     return 0;
586   
587   /* Run a test connection to see whether the UI server is available.  */
588   err = connect_uiserver (&ctx, &pid, &cmdid, NULL);
589   if (!err)
590     {
591       err = assuan_transact (ctx, "NOP", NULL, NULL, NULL, NULL, NULL, NULL);
592       assuan_disconnect (ctx);
593     }
594   if (err)
595     return err;
596   
597   /* Fire up the pipe worker thread. */
598   {
599     HANDLE th;
600     DWORD  mytid, tid;
601
602     InitializeCriticalSection (&work_queue_lock);
603     work_queue_event = CreateEvent (NULL, FALSE, FALSE, NULL);
604     if (!work_queue_event)
605       {
606         log_error_w32 (-1, "%s:%s: CreateEvent failed", SRCNAME, __func__);
607         return gpg_error (GPG_ERR_GENERAL);
608       }
609     mytid = GetCurrentThreadId ();
610     th = CreateThread (NULL, 256*1024, async_worker_thread, (void*)mytid,
611                        0, &tid);
612     if (th == INVALID_HANDLE_VALUE)
613       log_error ("failed to launch the async_worker_thread");
614     else
615       CloseHandle (th);
616   }
617
618   init_done = 1; 
619   return 0;
620 }
621
622 #if 0 /* Not used. */
623 /* Dummy window procedure.  */
624 static LRESULT CALLBACK 
625 attach_thread_input_wndw_proc (HWND hwnd, UINT msg, 
626                                WPARAM wparam, LPARAM lparam)
627 {               
628   return DefWindowProc (hwnd, msg, wparam, lparam);
629 }
630
631
632 /* Our helper thread needs to attach its input events to the main
633    message queue.  To do this we need to create a the message queue
634    first by creating an hidden window within this thread.  */
635 static void
636 attach_thread_input (DWORD other_tid)
637 {
638   WNDCLASS wndwclass = {0, attach_thread_input_wndw_proc, 0, 0, glob_hinst,
639                         0, 0, 0, 0, "gpgol-assuan-engine"};
640   HWND hwnd;
641
642   /* First create a window to make sure that a message queue exists
643      for this thread.  */
644   if (!RegisterClass (&wndwclass))
645     {
646       log_error_w32 (-1, "%s:%s: error registering window class",
647                      SRCNAME, __func__);
648       return;
649     }
650   hwnd = CreateWindow ("gpgol-assuan-engine", "gpgol-assuan-engine",
651                        0, 0, 0, 0, 0, NULL, NULL, glob_hinst, NULL);
652   if (!hwnd)
653     {
654       log_error_w32 (-1, "%s:%s: error creating main window",
655                      SRCNAME, __func__);
656       return;
657     }
658
659   /* Now attach it to the main thread.  */
660   if (!AttachThreadInput (GetCurrentThreadId (), other_tid,  TRUE))
661     log_error_w32 (-1, "%s:%s: AttachThreadInput failed",
662                    SRCNAME, __func__);
663   log_debug ("%s:%s: attached thread %lu to %lu", SRCNAME, __func__,
664              GetCurrentThreadId (), other_tid);
665 }
666 #endif /* not used.  */
667
668
669 \f
670 /* Helper for async_worker_thread.  Returns true if the item's handle
671    needs to be put on the wait list.  This is called with the worker
672    mutex hold. */
673 static int
674 worker_start_read (work_item_t item)
675 {
676   int nwritten;
677   DWORD nbytes;
678   int retval = 0;
679
680   /* Read from the handle and write to the callback.  The gpgme
681      callback is expected to never block.  */
682   if (ReadFile (item->hd, item->buffer, sizeof item->buffer,
683                 &nbytes, &item->ov) )
684     {
685       /* (With overlapped, EOF is not indicated by NBYTES==0.)  */
686       if (!nbytes)
687         log_error ("%s:%s: [%s:%p] short read (0 bytes)",
688                    SRCNAME, __func__, item->name, item->hd);
689       else
690         {
691           nwritten = gpgme_data_write (item->data, item->buffer, nbytes);
692           if (nwritten < 0)
693             {
694               log_error ("%s:%s: [%s:%p] writing to callback failed: %s",
695                          SRCNAME, __func__, item->name, item->hd,
696                          strerror (errno));
697               item->got_error = 1;
698             }
699           else if (nwritten < nbytes)
700             {
701               log_error ("%s:%s: [%s:%p] short write to callback (%d of %lu)",
702                          SRCNAME, __func__, item->name, item->hd,
703                          nwritten, nbytes);
704               item->got_error = 1;
705             }
706           else
707             {
708               if (debug_ioworker)
709                 log_debug ("%s:%s: [%s:%p] wrote %d bytes to callback", 
710                            SRCNAME, __func__, item->name, item->hd, nwritten);
711             }
712         }
713       retval = 1;
714     }
715   else 
716     {
717       int syserr = GetLastError ();
718
719       if (syserr == ERROR_IO_PENDING)
720         {
721           if (debug_ioworker)
722             log_debug ("%s:%s: [%s:%p] io(read) pending",
723                        SRCNAME, __func__, item->name, item->hd);
724           item->io_pending = sizeof item->buffer;
725           retval = 1;
726         }
727       else if (syserr == ERROR_HANDLE_EOF || syserr == ERROR_BROKEN_PIPE)
728         {
729           if (debug_ioworker)
730             log_debug ("%s:%s: [%s:%p] EOF%s seen",
731                        SRCNAME, __func__, item->name, item->hd,
732                        syserr == ERROR_BROKEN_PIPE? " (broken pipe)":"");
733           item->got_ready = 1;
734         }
735       else
736         {
737           log_error_w32 (syserr, "%s:%s: [%s:%p] read error",
738                          SRCNAME, __func__, item->name, item->hd);
739           item->got_error = 1;
740         }
741     }
742
743   return retval;
744 }
745
746 /* Result checking helper for async_worker_thread.  This is called with
747    the worker mutex hold.  */
748 static void
749 worker_check_read (work_item_t item, DWORD nbytes)
750 {
751   int nwritten;
752
753   if (!nbytes)
754     log_error ("%s:%s: [%s:%p] short read (0 bytes)",
755                SRCNAME, __func__, item->name, item->hd);
756   else
757     {
758       assert (nbytes > 0);
759       nwritten = gpgme_data_write (item->data, item->buffer, nbytes);
760       if (nwritten < 0)
761         {
762           log_error ("%s:%s: [%s:%p] error writing to callback: %s",
763                      SRCNAME, __func__, item->name, item->hd,strerror (errno));
764           item->got_error = 1;
765         }
766       else if (nwritten < nbytes)
767         {
768           log_error ("%s:%s: [%s:%p] short write to callback (%d of %lu)",
769                      SRCNAME, __func__, item->name, item->hd, nwritten,nbytes);
770           item->got_error = 1;
771         }
772       else
773         {
774           if (debug_ioworker)
775             log_debug ("%s:%s: [%s:%p] wrote %d bytes to callback",
776                        SRCNAME, __func__, item->name, item->hd, nwritten);
777         }
778     }
779 }
780
781
782
783 /* Helper for async_worker_thread.  Returns true if the item's handle
784    needs to be put on the wait list.  This is called with the worker
785    mutex hold.  */
786 static int
787 worker_start_write (work_item_t item)
788 {
789   int nread;
790   DWORD nbytes;
791   int retval = 0;
792
793   /* Read from the callback and the write to the handle.  The gpgme
794      callback is expected to never block.  */
795   nread = gpgme_data_read (item->data, item->buffer, sizeof item->buffer);
796   if (nread < 0)
797     {
798       if (errno == EAGAIN)
799         {
800 /*           log_debug ("%s:%s: [%s:%p] ignoring EAGAIN from callback", */
801 /*                      SRCNAME, __func__, item->name, item->hd); */
802           Sleep (10);
803           retval = 1;
804         }
805       else
806         {
807           log_error ("%s:%s: [%s:%p] error reading from callback: %s",
808                      SRCNAME, __func__, item->name, item->hd,strerror (errno));
809           item->got_error = 1;
810         }
811     }
812   else if (!nread)
813     {
814       if (debug_ioworker)
815         log_debug ("%s:%s: [%s:%p] EOF received from callback",
816                    SRCNAME, __func__, item->name, item->hd);
817       item->got_ready = 1;
818       retval = 1;
819     }
820   else 
821     {                  
822       if (WriteFile (item->hd, item->buffer, nread, &nbytes, &item->ov))
823         {
824           if (nbytes < nread)
825             {
826               log_error ("%s:%s: [%s:%p] short write (%lu of %d)", 
827                          SRCNAME, __func__, item->name,item->hd,nbytes, nread);
828               item->got_error = 1;
829             }
830           else
831             {
832               if (debug_ioworker)
833                 log_debug ("%s:%s: [%s:%p] wrote %lu bytes", 
834                            SRCNAME, __func__, item->name, item->hd, nbytes);
835             }
836           retval = 1;
837         }
838       else 
839         {
840           int syserr = GetLastError ();
841
842           if (syserr == ERROR_IO_PENDING)
843             {
844               if (debug_ioworker)
845                 log_debug ("%s:%s: [%s:%p] io(write) pending (%d bytes)",
846                            SRCNAME, __func__, item->name, item->hd, nread);
847               item->io_pending = nread;
848               retval = 1;
849             }
850           else
851             {
852               log_error_w32 (syserr, "%s:%s: [%s:%p] write error",
853                              SRCNAME, __func__, item->name, item->hd);
854               item->got_error = 1;
855             }
856         }
857     }
858
859   return retval;
860 }
861
862
863 /* Result checking helper for async_worker_thread.  This is called with
864    the worker mutex hold.  */
865 static void
866 worker_check_write (work_item_t item, DWORD nbytes)
867 {
868   if (nbytes < item->io_pending)
869     {
870       log_error ("%s:%s: [%s:%p] short write (%lu of %d)",
871                  SRCNAME,__func__, item->name, item->hd, nbytes,
872                  item->io_pending);
873       item->got_error = 1;
874     }
875   else
876     {
877       if (debug_ioworker)
878         log_debug ("%s:%s: [%s:%p] write finished (%lu bytes)", 
879                    SRCNAME, __func__, item->name, item->hd, nbytes);
880     }
881 }
882
883
884
885 /* The worker thread which feeds the pipes.  */
886 static DWORD WINAPI
887 async_worker_thread (void *dummy)
888 {
889   work_item_t item;
890   int n;
891   DWORD nbytes;
892   HANDLE hdarray[MAXIMUM_WAIT_OBJECTS];
893   int count, addit, any_ready, hdarraylen;
894   
895 /*   attach_thread_input ( (DWORD)dummy ); */
896   (void)dummy;
897
898   for (;;)
899     {
900       /* Process our queue and fire up async I/O requests.  */
901 /*       log_debug ("%s:%s: processing work queue", SRCNAME, __func__); */
902       EnterCriticalSection (&work_queue_lock);
903       hdarraylen = 0;
904       hdarray[hdarraylen++] = work_queue_event;
905       count = 0;
906       any_ready = 0;
907       for (item = work_queue; item; item = item->next)
908         {
909           item->waiting = 0;
910           if (!item->used)
911             continue;
912           assert (item->hd != INVALID_HANDLE_VALUE);
913           count++;
914           if (item->got_error)
915             {
916               if (!item->delayed_ready)
917                 any_ready = 1;
918               continue; 
919             }
920           assert (item->data);
921           if (hdarraylen == DIM (hdarray))
922             {
923               if (debug_ioworker)
924                 log_debug ("%s:%s: [%s:%p] wait array full - ignored for now",
925                            SRCNAME, __func__, item->name, item->hd);
926               continue;
927             }
928           
929           if (item->io_pending)
930             addit = 1;
931           else if (item->writing)
932             addit = worker_start_write (item);
933           else 
934             addit = worker_start_read (item);
935
936           if (addit)
937             {
938               hdarray[hdarraylen++] = item->hd;
939               item->waiting = 1; /* Just for the trace output.  */
940             }
941           if (!item->delayed_ready && (item->got_error || item->got_ready))
942             any_ready = 1;
943         }
944       LeaveCriticalSection (&work_queue_lock);
945
946       if (any_ready)
947         {
948           if (debug_ioworker_extra)
949             log_debug ("%s:%s: %d items in queue; skipping wait", 
950                        SRCNAME, __func__, count);
951         }
952       else
953         {
954           /* First process any window messages of this thread.  Do
955              this before wating so that the message queue is cleared
956              before waiting and we don't get stucked due to messages
957              not removed.  We need to process the message queue also
958              after the wait becuase we will only get to here if there
959              is actual ui-server work to be done but some messages
960              might still be in the queue.  */
961 /*           { */
962 /*             MSG msg; */
963
964 /*             while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) */
965 /*               { */
966 /*                 TranslateMessage (&msg); */
967 /*                 DispatchMessage (&msg); */
968 /*               } */
969 /*           } */
970
971           if (debug_ioworker_extra)
972             {
973               log_debug ("%s:%s: %d items in queue; waiting for %d items:",
974                          SRCNAME, __func__, count, hdarraylen-1);
975               for (item = work_queue; item; item = item->next)
976                 {
977                   if (item->waiting)
978                     log_debug ("%s:%s: [%s:%p]",
979                                SRCNAME, __func__, item->name, item->hd);
980                 }
981             }
982           n = WaitForMultipleObjects (hdarraylen, hdarray, FALSE, INFINITE);
983 /*           n = MsgWaitForMultipleObjects (hdarraylen, hdarray, FALSE, */
984 /*                                          INFINITE, QS_ALLEVENTS); */
985           if (n == WAIT_FAILED)
986             {
987               log_error_w32 (-1, "%s:%s: WFMO failed", SRCNAME, __func__);
988               Sleep (1000);
989             }
990           else if (n >= 0 && n < hdarraylen)
991             {
992 /*               log_debug ("%s:%s: WFMO succeeded (res=%d)",SRCNAME,__func__, n); */
993             }
994           else if (n == hdarraylen)
995             ; /* Message event.  */
996           else
997             {
998               log_error ("%s:%s: WFMO returned: %d", SRCNAME, __func__, n);
999               Sleep (1000);
1000             }
1001
1002           /* Try to process the message queue.  */
1003 /*           { */
1004 /*             MSG msg; */
1005             
1006 /*             while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) */
1007 /*               { */
1008 /*                 TranslateMessage (&msg); */
1009 /*                 DispatchMessage (&msg); */
1010 /*               } */
1011 /*           } */
1012
1013         }
1014
1015
1016       /* Handle completion status.  */
1017       EnterCriticalSection (&work_queue_lock);
1018       if (debug_ioworker_extra)
1019         log_debug ("%s:%s: checking completion states", SRCNAME, __func__);
1020       for (item = work_queue; item; item = item->next)
1021         {
1022           if (!item->io_pending)
1023             ;
1024           else if (GetOverlappedResult (item->hd, &item->ov, &nbytes, FALSE))
1025             {
1026               if (item->writing)
1027                 worker_check_write (item, nbytes);
1028               else
1029                 worker_check_read (item, nbytes);
1030               item->io_pending = 0;
1031             }
1032           else 
1033             {
1034               int syserr = GetLastError ();
1035               if (syserr == ERROR_IO_INCOMPLETE)
1036                 ;
1037               else if (!item->writing && syserr == ERROR_HANDLE_EOF)
1038                 {
1039                   /* Got EOF.  */
1040                   if (debug_ioworker)
1041                     log_debug ("%s:%s: [%s:%p] EOF received",
1042                                SRCNAME, __func__, item->name, item->hd);
1043                   item->io_pending = 0;
1044                   item->got_ready = 1;
1045                 }
1046               else
1047                 {
1048                   log_error_w32 (syserr,
1049                                  "%s:%s: [%s:%p] GetOverlappedResult failed",
1050                                  SRCNAME, __func__, item->name, item->hd);
1051                   item->got_error = 1;
1052                   if (!item->aborting)
1053                     {
1054                       item->aborting = 1;
1055                       if (!CancelIo (item->hd))
1056                         log_error_w32 (-1, "%s:%s: [%s:%p] CancelIo failed",
1057                                        SRCNAME,__func__, item->name, item->hd);
1058                     }
1059                   else 
1060                     item->got_ready = 1;
1061                 }
1062             }
1063         }
1064       LeaveCriticalSection (&work_queue_lock);
1065
1066       Sleep (0);
1067
1068       EnterCriticalSection (&work_queue_lock);
1069       if (debug_ioworker_extra)
1070         log_debug ("%s:%s: cleaning up work queue", SRCNAME, __func__); 
1071       for (item = work_queue; item; item = item->next)
1072         {
1073           if (item->used && (item->got_ready || item->got_error))
1074             {
1075               if (item->cld)
1076                 {
1077                   if (!item->cld->final_err && item->got_error)
1078                     item->cld->final_err = gpg_error (GPG_ERR_EIO);
1079
1080                   if (!item->cld->final_err)
1081                     {
1082                       /* Check whether there are other work items in
1083                          this group we need to wait for before
1084                          invoking the closure. */
1085                       work_item_t itm2;
1086                       
1087                       for (itm2=work_queue; itm2; itm2 = itm2->next)
1088                         if (itm2->used && itm2 != item 
1089                             && itm2->cmdid == item->cmdid
1090                             && itm2->wait_on_success
1091                             && !(itm2->got_ready || itm2->got_error))
1092                           break;
1093                       if (itm2)
1094                         {
1095                           if (debug_ioworker)
1096                             log_debug ("%s:%s: [%s:%p] delaying closure "
1097                                        "due to [%s/%p]", SRCNAME, __func__,
1098                                        item->name, item->hd, 
1099                                        itm2->name, itm2->hd);
1100                           item->delayed_ready = 1;
1101                           break; 
1102                         }
1103                     }
1104                   item->delayed_ready = 0;
1105                   if (debug_ioworker)
1106                     log_debug ("%s:%s: [%s:%p] invoking closure",
1107                                SRCNAME,__func__, item->name, item->hd);
1108                   
1109                   item->cld->closure (item->cld);
1110                   xfree (item->cld);
1111                   item->cld = NULL;
1112                 }
1113
1114               item->got_ready = 0;
1115               item->finalize (item);
1116               item->used = 0;
1117             }
1118         }
1119
1120       LeaveCriticalSection (&work_queue_lock);
1121     }
1122 }
1123
1124
1125 void
1126 engine_assuan_cancel (void *cancel_data)
1127 {
1128   /* FIXME */
1129 }
1130
1131
1132
1133
1134 /* Standard finalize handler.  Called right before the item is removed
1135    from the queue.  Called while the work_queue_lock is hold.  */
1136 static void
1137 finalize_handler (work_item_t item)
1138 {
1139   if (debug_ioworker)
1140     log_debug ("%s:%s: [%s:%p] closing handle", 
1141                SRCNAME, __func__, item->name, item->hd);
1142   CloseHandle (item->hd);
1143   item->hd = INVALID_HANDLE_VALUE;
1144 }
1145
1146 /* A finalize handler which does not close the handle.  */
1147 static void
1148 noclose_finalize_handler (work_item_t item)
1149 {
1150   if (debug_ioworker)
1151     log_debug ("%s:%s: [%s:%p] called",
1152                SRCNAME, __func__, item->name, item->hd);
1153   item->hd = INVALID_HANDLE_VALUE;
1154 }
1155
1156
1157 /* Add a data callback and a handle to the work queue.  This should
1158    only be called once per handle.  Caller gives up ownership of
1159    CLD. */
1160 static void
1161 enqueue_callback (const char *name, assuan_context_t ctx, 
1162                   gpgme_data_t data, HANDLE hd,
1163                   int for_write, void (*fin_handler)(work_item_t),
1164                   ULONG cmdid, closure_data_t cld, int wait_on_success)
1165 {
1166   work_item_t item;
1167   int created = 0;
1168
1169   EnterCriticalSection (&work_queue_lock);
1170   for (item = work_queue; item; item = item->next)
1171     if (!item->used)
1172       break;
1173   if (!item)
1174     {
1175       item = xmalloc (sizeof *item);
1176       item->next = work_queue;
1177       work_queue = item;
1178       created = 1;
1179     }
1180   item->used = 1;
1181   item->name = name;
1182   item->cmdid = cmdid;
1183   item->cld = cld;
1184   item->wait_on_success = wait_on_success;
1185   item->data = data;
1186   item->writing = for_write;
1187   item->hd = hd;
1188   item->io_pending = 0;
1189   item->got_ready = 0;
1190   item->delayed_ready = 0;
1191   item->got_error = 0;
1192   item->aborting = 0;
1193   item->finalize = fin_handler;
1194   memset (&item->ov, 0, sizeof item->ov);
1195   if (debug_ioworker)
1196     log_debug ("%s:%s: [%s:%p] created%s",
1197                SRCNAME, __func__, item->name, item->hd,
1198                created?"":" (reusing)");
1199   LeaveCriticalSection (&work_queue_lock);
1200 }
1201
1202
1203 /* Remove all items from the work queue belonging to the command with
1204    the id CMDID.  */
1205 static int
1206 destroy_command (ULONG cmdid)
1207 {
1208   work_item_t item;
1209
1210   EnterCriticalSection (&work_queue_lock);
1211   for (item = work_queue; item; item = item->next)
1212     if (item->used && item->cmdid == cmdid && !item->wait_on_success)
1213       {
1214         if (debug_ioworker)
1215           log_debug ("%s:%s: [%s:%p] cmdid=%lu registered for destroy",
1216                      SRCNAME, __func__, item->name, item->hd, item->cmdid);
1217         /* First send an I/O cancel in case the the last
1218            GetOverlappedResult returned only a partial result.  This
1219            works because we are always running within the
1220            async_worker_thread.  */
1221 /*         if (!CancelIo (item->hd)) */
1222 /*           log_error_w32 (-1, "%s:%s: [%s:%p] CancelIo failed", */
1223 /*                          SRCNAME, __func__, item->name, item->hd); */
1224         item->got_ready = 1;
1225       }
1226   LeaveCriticalSection (&work_queue_lock);
1227 }
1228
1229
1230 /* Process a status line.  */
1231 static int
1232 status_handler (closure_data_t cld, const char *line)
1233 {
1234   gpg_error_t err;
1235   int retval = 0;
1236
1237   if (debug_ioworker)
1238     log_debug ("%s:%s: cld %p, line `%s'", SRCNAME, __func__, cld, line);
1239
1240   if (*line == '#' || !*line)
1241     ;
1242   else if (line[0] == 'D' && line[1] == ' ')
1243     {
1244       line += 2;
1245     }
1246   else if (line[0] == 'S' && (!line[1] || line[1] == ' '))
1247     {
1248       for (line += 1; *line == ' '; line++)
1249         ;
1250     }  
1251   else if (line[0] == 'O' && line[1] == 'K' && (!line[2] || line[2] == ' '))
1252     {
1253       for (line += 2; *line == ' '; line++)
1254         ;
1255       cld->final_err = 0;
1256       retval = 1;
1257     }
1258   else if (!strncmp (line, "ERR", 3) && (!line[3] || line[3] == ' '))
1259     {
1260       for (line += 3; *line == ' '; line++)
1261         ;
1262       err = strtoul (line, NULL, 10);
1263       if (!err)
1264         err = gpg_error (GPG_ERR_ASS_INV_RESPONSE);
1265       cld->final_err = err;
1266       retval = 1;
1267     }  
1268   else if (!strncmp (line, "INQUIRE", 7) && (!line[7] || line[7] == ' '))
1269     {
1270       for (line += 7; *line == ' '; line++)
1271         ;
1272       /* We have no inquire handler thus get out of it immediately.  */
1273       err = assuan_write_line (cld->assctx, "END");
1274       if (err)
1275         cld->last_err = err;
1276     }
1277   else if (!strncmp (line, "END", 3) && (!line[3] || line[3] == ' '))
1278     {
1279       for (line += 3; *line == ' '; line++)
1280         ;
1281     }
1282   else
1283     retval = -1; /* Invalid response.  */
1284
1285   return retval;
1286 }
1287
1288
1289 /* This write callback is used by GPGME to push data to our status
1290    line handler.  The function should return the number of bytes
1291    written, and -1 on error.  If an error occurs, ERRNO should be set
1292    to describe the type of the error.  */
1293 static ssize_t
1294 status_in_cb (void *opaque, const void *buffer, size_t size)
1295 {
1296   size_t orig_size = size;
1297   closure_data_t cld = opaque;
1298   status_buffer_t sb;
1299   size_t nleft, nbytes;
1300   char *p;
1301
1302   assert (cld);
1303   if (!size)
1304     return 0;
1305
1306   if (!(sb=cld->status_buffer))
1307     {
1308       cld->status_buffer = sb = xmalloc (sizeof *cld->status_buffer);
1309       sb->eof = 0;
1310       sb->linelen = 0;
1311     }
1312
1313   do
1314     {
1315       assert (sb->linelen < ASSUAN_LINELENGTH);
1316       nleft = ASSUAN_LINELENGTH - sb->linelen;
1317       nbytes = size < nleft? size : nleft;
1318       memcpy (sb->line+sb->linelen, buffer, nbytes);
1319       sb->linelen += nbytes;
1320       size -= nbytes;
1321       while ((p = memchr (sb->line, '\n', sb->linelen)) && !cld->status_ready)
1322         {
1323           *p = 0;
1324           if (p > sb->line && p[-1] == '\r')
1325             p[-1] = 0;
1326           switch (status_handler (cld, sb->line))
1327             {
1328             case 0: 
1329               break;
1330             case 1: /* Ready. */
1331               cld->status_ready = 1;
1332               destroy_command (cld->cmdid);
1333               break;
1334             default:
1335               log_error ("%s:%s: invalid line from server", SRCNAME, __func__);
1336               errno = EINVAL;
1337               return -1;
1338             }
1339           sb->linelen -= (p+1 - sb->line);
1340           memmove (sb->line, p+1, sb->linelen);
1341         }
1342       if (sb->linelen >= ASSUAN_LINELENGTH)
1343         {
1344           log_error ("%s:%s: line from server too long", SRCNAME, __func__);
1345           errno = ERANGE;
1346           return -1;
1347         }
1348     }
1349   while (size);
1350   
1351   return orig_size;
1352 }
1353
1354
1355
1356 /* Start an asynchronous command.  Caller gives up ownership of
1357    CLD.  */
1358 static gpg_error_t
1359 start_command (assuan_context_t ctx, closure_data_t cld,
1360                ULONG cmdid, const char *line)
1361 {
1362   gpg_error_t err;
1363   assuan_fd_t fds[5];
1364   int nfds;
1365
1366   /* Get the fd used by assuan for status channel reads.  This is the
1367      first fd returned by assuan_get_active_fds for read fds.  */
1368   nfds = assuan_get_active_fds (ctx, 0, fds, DIM (fds));
1369   if (nfds < 1)
1370     return gpg_error (GPG_ERR_GENERAL); /* Ooops.  */
1371
1372   cld->cmdid = cmdid;
1373   cld->status_cbs.write = status_in_cb;
1374   cld->assctx = ctx;
1375   /* Fixme: We might want to have reference counting for CLD to cope
1376      with thye problem that the gpgme data object uses CLD which might
1377      get invalidated at any time.  */
1378   err = gpgme_data_new_from_cbs (&cld->status_data, &cld->status_cbs, cld);
1379   if (err)
1380     {
1381       xfree (cld);
1382       return err;
1383     }
1384
1385   enqueue_callback ("status", ctx, cld->status_data, fds[0], 0,
1386                     noclose_finalize_handler, cmdid, cld, 0);
1387   cld = NULL; /* Now belongs to the status work item.  */
1388
1389   /* Process the work queue.  */
1390   if (!SetEvent (work_queue_event))
1391     log_error_w32 (-1, "%s:%s: SetEvent failed", SRCNAME, __func__);
1392   /* Send the command. */
1393   return assuan_write_line (ctx, line);
1394 }
1395
1396
1397 static const char *
1398 get_protocol_name (protocol_t protocol)
1399 {
1400   switch (protocol)
1401     {
1402     case PROTOCOL_OPENPGP: return "OpenPGP"; break;
1403     case PROTOCOL_SMIME:   return "CMS"; break;
1404     default: return NULL;
1405     }
1406 }
1407
1408
1409 /* Callback used to get the protocool status line form a PREP_*
1410    command.  */
1411 static assuan_error_t
1412 prep_foo_status_cb (void *opaque, const char *line)
1413 {
1414   protocol_t *protocol = opaque;
1415
1416   if (!strncmp (line, "PROTOCOL", 8) && (line[8]==' ' || !line[8]))
1417     {
1418       for (line += 8; *line == ' '; line++)
1419         ;
1420       if (!strncmp (line, "OpenPGP", 7) && (line[7]==' '||!line[7]))
1421         *protocol = PROTOCOL_OPENPGP;
1422       else if (!strncmp (line, "CMS", 3) && (line[3]==' '||!line[3]))
1423         *protocol = PROTOCOL_SMIME;
1424     }
1425   return 0;
1426 }
1427
1428
1429
1430 \f
1431 /* Note that this closure is called in the context of the
1432    async_worker_thread.  */
1433 static void
1434 encrypt_closure (closure_data_t cld)
1435 {
1436   engine_private_finished (cld->filter, cld->final_err);
1437 }
1438
1439
1440 /* Encrypt the data from INDATA to the OUTDATA object for all
1441    recpients given in the NULL terminated array RECIPIENTS.  This
1442    function terminates with success and then expects the caller to
1443    wait for the result of the encryption using engine_wait.  FILTER is
1444    used for asynchronous commnication with the engine module.  HWND is
1445    the window handle of the current window and used to maintain the
1446    correct relationship between a popups and the active window.  If
1447    this function returns success, the data objects may only be
1448    destroyed after an engine_wait or engine_cancel.  */
1449 int
1450 op_assuan_encrypt (protocol_t protocol, 
1451                    gpgme_data_t indata, gpgme_data_t outdata,
1452                    engine_filter_t filter, void *hwnd,
1453                    char **recipients, protocol_t *r_used_protocol)
1454 {
1455   gpg_error_t err;
1456   closure_data_t cld;
1457   assuan_context_t ctx;
1458   char line[1024];
1459   HANDLE inpipe[2], outpipe[2];
1460   ULONG cmdid;
1461   pid_t pid;
1462   int i;
1463   char *p;
1464   int detect_protocol;
1465   const char *protocol_name;
1466
1467   detect_protocol = !(protocol_name = get_protocol_name (protocol));
1468   
1469   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1470   if (err)
1471     return err;
1472
1473   if ((err = create_io_pipe (inpipe, pid, 1)))
1474     return err;
1475   if ((err = create_io_pipe (outpipe, pid, 0)))
1476     {
1477       close_pipe (inpipe);
1478       return err;
1479     }
1480
1481   cld = xcalloc (1, sizeof *cld);
1482   cld->closure = encrypt_closure;
1483   cld->filter = filter;
1484
1485   err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
1486   if (err)
1487     goto leave;
1488   for (i=0; recipients && recipients[i]; i++)
1489     {
1490       snprintf (line, sizeof line, "RECIPIENT %s", recipients[i]);
1491       for (p=line; *p; p++)
1492         if (*p == '\n' || *p =='\r' )
1493           *p = ' ';
1494       err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1495       if (err)
1496         goto leave;
1497     }
1498
1499   /* If the protocol has not been given, let the UI server tell us the
1500      protocol to use. */
1501   if (detect_protocol)
1502     {
1503       protocol = PROTOCOL_UNKNOWN;
1504       err = assuan_transact (ctx, "PREP_ENCRYPT", NULL, NULL, NULL, NULL,
1505                              prep_foo_status_cb, &protocol);
1506       if (err)
1507         {
1508           if (gpg_err_code (err) == GPG_ERR_ASS_UNKNOWN_CMD)
1509             err = gpg_error (GPG_ERR_INV_VALUE);
1510           goto leave;
1511         }
1512       if ( !(protocol_name = get_protocol_name (protocol)) )
1513         {
1514           err = gpg_error (GPG_ERR_INV_VALUE);
1515           goto leave;
1516         }
1517     }
1518
1519   *r_used_protocol = protocol;
1520
1521   /* Note: We don't use real descriptor passing but a hack: We
1522      duplicate the handle into the server process and the server then
1523      uses this handle.  Eventually we should put this code into
1524      assuan_sendfd.  */
1525   snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)inpipe[0]);
1526   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1527   if (err)
1528     goto leave;
1529   snprintf (line, sizeof line, "OUTPUT FD=%ld", (unsigned long int)outpipe[1]);
1530   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1531   if (err)
1532     goto leave;
1533
1534   enqueue_callback (" input", ctx, indata, inpipe[1], 1, finalize_handler,
1535                     cmdid, NULL, 0); 
1536   enqueue_callback ("output", ctx, outdata, outpipe[0], 0, finalize_handler, 
1537                     cmdid, NULL, 1 /* Wait on success */); 
1538   snprintf (line, sizeof line, "ENCRYPT --protocol=%s", protocol_name);
1539   err = start_command (ctx, cld, cmdid, line);
1540   cld = NULL; /* Now owned by start_command.  */
1541   if (err)
1542     goto leave;
1543
1544
1545  leave:
1546   if (err)
1547     {
1548       /* Fixme: Cancel stuff in the work_queue. */
1549       close_pipe (inpipe);
1550       close_pipe (outpipe);
1551       xfree (cld);
1552       assuan_disconnect (ctx);
1553     }
1554   else
1555     engine_private_set_cancel (filter, ctx);
1556   return err;
1557 }
1558
1559
1560 \f
1561 /* Note that this closure is called in the context of the
1562    async_worker_thread.  */
1563 static void
1564 sign_closure (closure_data_t cld)
1565 {
1566   engine_private_finished (cld->filter, cld->final_err);
1567 }
1568
1569
1570 /* Created a detached signature for INDATA and write it to OUTDATA.
1571    On termination of the signing command engine_private_finished() is
1572    called with FILTER as the first argument.  */
1573 int 
1574 op_assuan_sign (protocol_t protocol, 
1575                 gpgme_data_t indata, gpgme_data_t outdata,
1576                 engine_filter_t filter, void *hwnd)
1577 {
1578   gpg_error_t err;
1579   closure_data_t cld;
1580   assuan_context_t ctx;
1581   char line[1024];
1582   HANDLE inpipe[2], outpipe[2];
1583   ULONG cmdid;
1584   pid_t pid;
1585   const char *protocol_name;
1586
1587
1588   if (!(protocol_name = get_protocol_name (protocol)))
1589     return gpg_error(GPG_ERR_INV_VALUE);
1590
1591   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1592   if (err)
1593     return err;
1594
1595   if ((err = create_io_pipe (inpipe, pid, 1)))
1596     return err;
1597   if ((err = create_io_pipe (outpipe, pid, 0)))
1598     {
1599       close_pipe (inpipe);
1600       return err;
1601     }
1602
1603   cld = xcalloc (1, sizeof *cld);
1604   cld->closure = sign_closure;
1605   cld->filter = filter;
1606
1607   err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
1608   if (err)
1609     goto leave;
1610
1611   snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)inpipe[0]);
1612   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1613   if (err)
1614     goto leave;
1615   snprintf (line, sizeof line, "OUTPUT FD=%ld", (unsigned long int)outpipe[1]);
1616   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1617   if (err)
1618     goto leave;
1619
1620   /* FIXME: Implement the optinonal SENDER command. */
1621
1622   enqueue_callback (" input", ctx, indata, inpipe[1], 1, finalize_handler,
1623                     cmdid, NULL, 0); 
1624   enqueue_callback ("output", ctx, outdata, outpipe[0], 0, finalize_handler, 
1625                     cmdid, NULL, 1 /* Wait on success */); 
1626
1627   snprintf (line, sizeof line, "SIGN --protocol=%s --detached",
1628             protocol_name);
1629   err = start_command (ctx, cld, cmdid, line);
1630   cld = NULL; /* Now owned by start_command.  */
1631   if (err)
1632     goto leave;
1633
1634
1635  leave:
1636   if (err)
1637     {
1638       /* Fixme: Cancel stuff in the work_queue. */
1639       close_pipe (inpipe);
1640       close_pipe (outpipe);
1641       xfree (cld);
1642       assuan_disconnect (ctx);
1643     }
1644   else
1645     engine_private_set_cancel (filter, ctx);
1646   return err;
1647 }
1648
1649
1650
1651 \f
1652 /* Note that this closure is called in the context of the
1653    async_worker_thread.  */
1654 static void
1655 decrypt_closure (closure_data_t cld)
1656 {
1657   engine_private_finished (cld->filter, cld->final_err);
1658 }
1659
1660
1661 /* Decrypt data from INDATA to OUTDATE.  If WITH_VERIFY is set, the
1662    signature of a PGP/MIME combined message is also verified the same
1663    way as with op_assuan_verify.  */
1664 int 
1665 op_assuan_decrypt (protocol_t protocol,
1666                    gpgme_data_t indata, gpgme_data_t outdata, 
1667                    engine_filter_t filter, void *hwnd,
1668                    int with_verify)
1669 {
1670   gpg_error_t err;
1671   closure_data_t cld;
1672   assuan_context_t ctx;
1673   char line[1024];
1674   HANDLE inpipe[2], outpipe[2];
1675   ULONG cmdid;
1676   pid_t pid;
1677   const char *protocol_name;
1678
1679   if (!(protocol_name = get_protocol_name (protocol)))
1680     return gpg_error(GPG_ERR_INV_VALUE);
1681
1682   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1683   if (err)
1684     return err;
1685
1686   if ((err = create_io_pipe (inpipe, pid, 1)))
1687     return err;
1688   if ((err = create_io_pipe (outpipe, pid, 0)))
1689     {
1690       close_pipe (inpipe);
1691       return err;
1692     }
1693
1694   cld = xcalloc (1, sizeof *cld);
1695   cld->closure = decrypt_closure;
1696   cld->filter = filter;
1697
1698   err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
1699   if (err)
1700     goto leave;
1701
1702   snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)inpipe[0]);
1703   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1704   if (err)
1705     goto leave;
1706   snprintf (line, sizeof line, "OUTPUT FD=%ld", (unsigned long int)outpipe[1]);
1707   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1708   if (err)
1709     goto leave;
1710
1711   enqueue_callback (" input", ctx, indata, inpipe[1], 1, finalize_handler,
1712                     cmdid, NULL, 0); 
1713   enqueue_callback ("output", ctx, outdata, outpipe[0], 0, finalize_handler, 
1714                     cmdid, NULL, 1 /* Wait on success */); 
1715
1716   snprintf (line, sizeof line, "DECRYPT --protocol=%s%s",
1717             protocol_name, with_verify? "":" --no-verify");
1718   err = start_command (ctx, cld, cmdid, line);
1719   cld = NULL; /* Now owned by start_command.  */
1720   if (err)
1721     goto leave;
1722
1723
1724  leave:
1725   if (err)
1726     {
1727       /* Fixme: Cancel stuff in the work_queue. */
1728       close_pipe (inpipe);
1729       close_pipe (outpipe);
1730       xfree (cld);
1731       assuan_disconnect (ctx);
1732     }
1733   else
1734     engine_private_set_cancel (filter, ctx);
1735   return err;
1736 }
1737
1738
1739 \f
1740 /* Note that this closure is called in the context of the
1741    async_worker_thread.  */
1742 static void
1743 verify_closure (closure_data_t cld)
1744 {
1745   gpgme_data_release (cld->sigdata);
1746   cld->sigdata = NULL;
1747   engine_private_finished (cld->filter, cld->final_err);
1748 }
1749
1750
1751 /* Verify a detached message where the data is in the gpgme object
1752    MSGDATA and the signature given as the string SIGNATURE. */
1753 int 
1754 op_assuan_verify (gpgme_protocol_t protocol, 
1755                   gpgme_data_t msgdata, const char *signature,
1756                   engine_filter_t filter, void *hwnd)
1757 {
1758   gpg_error_t err;
1759   closure_data_t cld = NULL;
1760   assuan_context_t ctx;
1761   char line[1024];
1762   HANDLE msgpipe[2], sigpipe[2];
1763   ULONG cmdid;
1764   pid_t pid;
1765   gpgme_data_t sigdata = NULL;
1766   const char *protocol_name;
1767
1768   msgpipe[0] = INVALID_HANDLE_VALUE;
1769   msgpipe[1] = INVALID_HANDLE_VALUE;
1770   sigpipe[0] = INVALID_HANDLE_VALUE;
1771   sigpipe[1] = INVALID_HANDLE_VALUE;
1772
1773   if (!(protocol_name = get_protocol_name (protocol)))
1774     return gpg_error(GPG_ERR_INV_VALUE);
1775
1776   err = gpgme_data_new_from_mem (&sigdata, signature, strlen (signature), 0);
1777   if (err)
1778     goto leave;
1779
1780   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1781   if (err)
1782     goto leave;
1783
1784   if ((err = create_io_pipe (msgpipe, pid, 1)))
1785     goto leave;
1786   if ((err = create_io_pipe (sigpipe, pid, 1)))
1787     goto leave;
1788
1789   cld = xcalloc (1, sizeof *cld);
1790   cld->closure = verify_closure;
1791   cld->filter = filter;
1792   cld->sigdata = sigdata;
1793
1794   err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
1795   if (err)
1796     goto leave;
1797
1798   snprintf (line, sizeof line, "MESSAGE FD=%ld",(unsigned long int)msgpipe[0]);
1799   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1800   if (err)
1801     goto leave;
1802   snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)sigpipe[0]);
1803   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1804   if (err)
1805     goto leave;
1806
1807   enqueue_callback ("   msg", ctx, msgdata, msgpipe[1], 1, finalize_handler,
1808                     cmdid, NULL, 0); 
1809   enqueue_callback ("   sig", ctx, sigdata, sigpipe[1], 1, finalize_handler, 
1810                     cmdid, NULL, 0); 
1811
1812   snprintf (line, sizeof line, "VERIFY --protocol=%s",  protocol_name);
1813   err = start_command (ctx, cld, cmdid, line);
1814   cld = NULL;     /* Now owned by start_command.  */
1815   sigdata = NULL; /* Ditto.  */
1816   if (err)
1817     goto leave;
1818
1819
1820  leave:
1821   if (err)
1822     {
1823       /* Fixme: Cancel stuff in the work_queue. */
1824       close_pipe (msgpipe);
1825       close_pipe (sigpipe);
1826       gpgme_data_release (sigdata);
1827       xfree (cld);
1828       assuan_disconnect (ctx);
1829     }
1830   else
1831     engine_private_set_cancel (filter, ctx);
1832   return err;
1833 }
1834
1835
1836 \f
1837 /* Ask the server to fire up the key manager.  */
1838 int 
1839 op_assuan_start_keymanager (void *hwnd)
1840 {
1841   gpg_error_t err;
1842   assuan_context_t ctx;
1843   ULONG cmdid;
1844   pid_t pid;
1845
1846   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1847   if (!err)
1848     {
1849       err = assuan_transact (ctx, "START_KEYMANAGER",
1850                              NULL, NULL, NULL, NULL, NULL, NULL);
1851       assuan_disconnect (ctx);
1852     }
1853   return err;
1854 }