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