8351b37b7c18abac41a2995837c24a345bdca6d0
[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  mytid, 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     mytid = GetCurrentThreadId ();
602     th = CreateThread (NULL, 256*1024, async_worker_thread, (void*)mytid,
603                        0, &tid);
604     if (th == INVALID_HANDLE_VALUE)
605       log_error ("failed to launch the async_worker_thread");
606     else
607       CloseHandle (th);
608   }
609
610   init_done = 1; 
611   return 0;
612 }
613
614 #if 0 /* Not used. */
615 /* Dummy window procedure.  */
616 static LRESULT CALLBACK 
617 attach_thread_input_wndw_proc (HWND hwnd, UINT msg, 
618                                WPARAM wparam, LPARAM lparam)
619 {               
620   return DefWindowProc (hwnd, msg, wparam, lparam);
621 }
622
623
624 /* Our helper thread needs to attach its input events to the main
625    message queue.  To do this we need to create a the message queue
626    first by creating an hidden window within this thread.  */
627 static void
628 attach_thread_input (DWORD other_tid)
629 {
630   WNDCLASS wndwclass = {0, attach_thread_input_wndw_proc, 0, 0, glob_hinst,
631                         0, 0, 0, 0, "gpgol-assuan-engine"};
632   HWND hwnd;
633
634   /* First create a window to make sure that a message queue exists
635      for this thread.  */
636   if (!RegisterClass (&wndwclass))
637     {
638       log_error_w32 (-1, "%s:%s: error registering window class",
639                      SRCNAME, __func__);
640       return;
641     }
642   hwnd = CreateWindow ("gpgol-assuan-engine", "gpgol-assuan-engine",
643                        0, 0, 0, 0, 0, NULL, NULL, glob_hinst, NULL);
644   if (!hwnd)
645     {
646       log_error_w32 (-1, "%s:%s: error creating main window",
647                      SRCNAME, __func__);
648       return;
649     }
650
651   /* Now attach it to the main thread.  */
652   if (!AttachThreadInput (GetCurrentThreadId (), other_tid,  TRUE))
653     log_error_w32 (-1, "%s:%s: AttachThreadInput failed",
654                    SRCNAME, __func__);
655   log_debug ("%s:%s: attached thread %lu to %lu", SRCNAME, __func__,
656              GetCurrentThreadId (), other_tid);
657 }
658 #endif /* not used.  */
659
660
661 \f
662 /* Helper for async_worker_thread.  Returns true if the item's handle
663    needs to be put on the wait list.  This is called with the worker
664    mutex hold. */
665 static int
666 worker_start_read (work_item_t item)
667 {
668   int nwritten;
669   DWORD nbytes;
670   int retval = 0;
671
672   /* Read from the handle and write to the callback.  The gpgme
673      callback is expected to never block.  */
674   if (ReadFile (item->hd, item->buffer, sizeof item->buffer,
675                 &nbytes, &item->ov) )
676     {
677       /* (With overlapped, EOF is not indicated by NBYTES==0.)  */
678       if (!nbytes)
679         log_error ("%s:%s: [%s:%p] short read (0 bytes)",
680                    SRCNAME, __func__, item->name, item->hd);
681       else
682         {
683           nwritten = gpgme_data_write (item->data, item->buffer, nbytes);
684           if (nwritten < 0)
685             {
686               log_error ("%s:%s: [%s:%p] writing to callback failed: %s",
687                          SRCNAME, __func__, item->name, item->hd,
688                          strerror (errno));
689               item->got_error = 1;
690             }
691           else if (nwritten < nbytes)
692             {
693               log_error ("%s:%s: [%s:%p] short write to callback (%d of %lu)",
694                          SRCNAME, __func__, item->name, item->hd,
695                          nwritten, nbytes);
696               item->got_error = 1;
697             }
698           else
699             log_debug ("%s:%s: [%s:%p] wrote %d bytes to callback", 
700                        SRCNAME, __func__, item->name, item->hd, nwritten);
701         }
702       retval = 1;
703     }
704   else 
705     {
706       int syserr = GetLastError ();
707
708       if (syserr == ERROR_IO_PENDING)
709         {
710           log_debug ("%s:%s: [%s:%p] io(read) pending",
711                      SRCNAME, __func__, item->name, item->hd);
712           item->io_pending = sizeof item->buffer;
713           retval = 1;
714         }
715       else if (syserr == ERROR_HANDLE_EOF || syserr == ERROR_BROKEN_PIPE)
716         {
717           log_debug ("%s:%s: [%s:%p] EOF%s seen",
718                      SRCNAME, __func__, item->name, item->hd,
719                      syserr == ERROR_BROKEN_PIPE? " (broken pipe)":"");
720           item->got_ready = 1;
721         }
722       else
723         {
724           log_error_w32 (syserr, "%s:%s: [%s:%p] read error",
725                          SRCNAME, __func__, item->name, item->hd);
726           item->got_error = 1;
727         }
728     }
729
730   return retval;
731 }
732
733 /* Result checking helper for async_worker_thread.  This is called with
734    the worker mutex hold.  */
735 static void
736 worker_check_read (work_item_t item, DWORD nbytes)
737 {
738   int nwritten;
739
740   if (!nbytes)
741     log_error ("%s:%s: [%s:%p] short read (0 bytes)",
742                SRCNAME, __func__, item->name, item->hd);
743   else
744     {
745       assert (nbytes > 0);
746       nwritten = gpgme_data_write (item->data, item->buffer, nbytes);
747       if (nwritten < 0)
748         {
749           log_error ("%s:%s: [%s:%p] error writing to callback: %s",
750                      SRCNAME, __func__, item->name, item->hd,strerror (errno));
751           item->got_error = 1;
752         }
753       else if (nwritten < nbytes)
754         {
755           log_error ("%s:%s: [%s:%p] short write to callback (%d of %lu)",
756                      SRCNAME, __func__, item->name, item->hd, nwritten,nbytes);
757           item->got_error = 1;
758         }
759       else
760         log_debug ("%s:%s: [%s:%p] wrote %d bytes to callback",
761                    SRCNAME, __func__, item->name, item->hd, nwritten);
762     }
763 }
764
765
766
767 /* Helper for async_worker_thread.  Returns true if the item's handle
768    needs to be put on the wait list.  This is called with the worker
769    mutex hold.  */
770 static int
771 worker_start_write (work_item_t item)
772 {
773   int nread;
774   DWORD nbytes;
775   int retval = 0;
776
777   /* Read from the callback and the write to the handle.  The gpgme
778      callback is expected to never block.  */
779   nread = gpgme_data_read (item->data, item->buffer, sizeof item->buffer);
780   if (nread < 0)
781     {
782       if (errno == EAGAIN)
783         {
784 /*           log_debug ("%s:%s: [%s:%p] ignoring EAGAIN from callback", */
785 /*                      SRCNAME, __func__, item->name, item->hd); */
786           Sleep (10);
787           retval = 1;
788         }
789       else
790         {
791           log_error ("%s:%s: [%s:%p] error reading from callback: %s",
792                      SRCNAME, __func__, item->name, item->hd,strerror (errno));
793           item->got_error = 1;
794         }
795     }
796   else if (!nread)
797     {
798       log_debug ("%s:%s: [%s:%p] EOF received from callback",
799                  SRCNAME, __func__, item->name, item->hd);
800       item->got_ready = 1;
801       retval = 1;
802     }
803   else 
804     {                  
805       if (WriteFile (item->hd, item->buffer, nread, &nbytes, &item->ov))
806         {
807           if (nbytes < nread)
808             {
809               log_error ("%s:%s: [%s:%p] short write (%lu of %d)", 
810                          SRCNAME, __func__, item->name,item->hd,nbytes, nread);
811               item->got_error = 1;
812             }
813           else
814             log_debug ("%s:%s: [%s:%p] wrote %lu bytes", 
815                        SRCNAME, __func__, item->name, item->hd, nbytes);
816           retval = 1;
817         }
818       else 
819         {
820           int syserr = GetLastError ();
821
822           if (syserr == ERROR_IO_PENDING)
823             {
824               log_debug ("%s:%s: [%s:%p] io(write) pending (%d bytes)",
825                          SRCNAME, __func__, item->name, item->hd, nread);
826               item->io_pending = nread;
827               retval = 1;
828             }
829           else
830             {
831               log_error_w32 (syserr, "%s:%s: [%s:%p] write error",
832                              SRCNAME, __func__, item->name, item->hd);
833               item->got_error = 1;
834             }
835         }
836     }
837
838   return retval;
839 }
840
841
842 /* Result checking helper for async_worker_thread.  This is called with
843    the worker mutex hold.  */
844 static void
845 worker_check_write (work_item_t item, DWORD nbytes)
846 {
847   if (nbytes < item->io_pending)
848     {
849       log_error ("%s:%s: [%s:%p] short write (%lu of %d)",
850                  SRCNAME,__func__, item->name, item->hd, nbytes,
851                  item->io_pending);
852       item->got_error = 1;
853     }
854   else
855     log_debug ("%s:%s: [%s:%p] write finished (%lu bytes)", 
856                SRCNAME, __func__, item->name, item->hd, nbytes);
857 }
858
859
860
861 /* The worker thread which feeds the pipes.  */
862 static DWORD WINAPI
863 async_worker_thread (void *dummy)
864 {
865   work_item_t item;
866   int n;
867   DWORD nbytes;
868   HANDLE hdarray[MAXIMUM_WAIT_OBJECTS];
869   int count, addit, any_ready, hdarraylen;
870   
871 /*   attach_thread_input ( (DWORD)dummy ); */
872   (void)dummy;
873
874   for (;;)
875     {
876       /* Process our queue and fire up async I/O requests.  */
877 /*       log_debug ("%s:%s: processing work queue", SRCNAME, __func__); */
878       EnterCriticalSection (&work_queue_lock);
879       hdarraylen = 0;
880       hdarray[hdarraylen++] = work_queue_event;
881       count = 0;
882       any_ready = 0;
883       for (item = work_queue; item; item = item->next)
884         {
885           item->waiting = 0;
886           if (!item->used)
887             continue;
888           assert (item->hd != INVALID_HANDLE_VALUE);
889           count++;
890           if (item->got_error)
891             {
892               if (!item->delayed_ready)
893                 any_ready = 1;
894               continue; 
895             }
896           assert (item->data);
897           if (hdarraylen == DIM (hdarray))
898             {
899               log_debug ("%s:%s: [%s:%p] wait array full - ignored for now",
900                          SRCNAME, __func__, item->name, item->hd);
901               continue;
902             }
903           
904           if (item->io_pending)
905             addit = 1;
906           else if (item->writing)
907             addit = worker_start_write (item);
908           else 
909             addit = worker_start_read (item);
910
911           if (addit)
912             {
913               hdarray[hdarraylen++] = item->hd;
914               item->waiting = 1; /* Just for the trace output.  */
915             }
916           if (!item->delayed_ready && (item->got_error || item->got_ready))
917             any_ready = 1;
918         }
919       LeaveCriticalSection (&work_queue_lock);
920
921       if (any_ready)
922         log_debug ("%s:%s: %d items in queue; skipping wait", 
923                    SRCNAME, __func__, count);
924       else
925         {
926           /* First process any window messages of this thread.  Do
927              this before wating so that the message queue is cleared
928              before waiting and we don't get stucked due to messages
929              not removed.  We need to process the message queue also
930              after the wait becuase we will only get to here if there
931              is actual ui-server work to be done but some messages
932              might still be in the queue.  */
933 /*           { */
934 /*             MSG msg; */
935
936 /*             while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) */
937 /*               { */
938 /*                 TranslateMessage (&msg); */
939 /*                 DispatchMessage (&msg); */
940 /*               } */
941 /*           } */
942
943           log_debug ("%s:%s: %d items in queue; waiting for %d items:",
944                      SRCNAME, __func__, count, hdarraylen-1);
945           for (item = work_queue; item; item = item->next)
946             {
947               if (item->waiting)
948                 log_debug ("%s:%s: [%s:%p]",
949                            SRCNAME, __func__, item->name, item->hd);
950             }
951           n = WaitForMultipleObjects (hdarraylen, hdarray, FALSE, INFINITE);
952 /*           n = MsgWaitForMultipleObjects (hdarraylen, hdarray, FALSE, */
953 /*                                          INFINITE, QS_ALLEVENTS); */
954           if (n == WAIT_FAILED)
955             {
956               log_error_w32 (-1, "%s:%s: WFMO failed", SRCNAME, __func__);
957               Sleep (1000);
958             }
959           else if (n >= 0 && n < hdarraylen)
960             {
961 /*               log_debug ("%s:%s: WFMO succeeded (res=%d)",SRCNAME,__func__, n); */
962             }
963           else if (n == hdarraylen)
964             ; /* Message event.  */
965           else
966             {
967               log_error ("%s:%s: WFMO returned: %d", SRCNAME, __func__, n);
968               Sleep (1000);
969             }
970
971           /* Try to process the message queue.  */
972 /*           { */
973 /*             MSG msg; */
974             
975 /*             while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) */
976 /*               { */
977 /*                 TranslateMessage (&msg); */
978 /*                 DispatchMessage (&msg); */
979 /*               } */
980 /*           } */
981
982         }
983
984
985       /* Handle completion status.  */
986       EnterCriticalSection (&work_queue_lock);
987 /*       log_debug ("%s:%s: checking completion states", SRCNAME, __func__); */
988       for (item = work_queue; item; item = item->next)
989         {
990           if (!item->io_pending)
991             ;
992           else if (GetOverlappedResult (item->hd, &item->ov, &nbytes, FALSE))
993             {
994               if (item->writing)
995                 worker_check_write (item, nbytes);
996               else
997                 worker_check_read (item, nbytes);
998               item->io_pending = 0;
999             }
1000           else 
1001             {
1002               int syserr = GetLastError ();
1003               if (syserr == ERROR_IO_INCOMPLETE)
1004                 ;
1005               else if (!item->writing && syserr == ERROR_HANDLE_EOF)
1006                 {
1007                   /* Got EOF.  */
1008                   log_debug ("%s:%s: [%s:%p] EOF received",
1009                              SRCNAME, __func__, item->name, item->hd);
1010                   item->io_pending = 0;
1011                   item->got_ready = 1;
1012                 }
1013               else
1014                 {
1015                   log_error_w32 (syserr,
1016                                  "%s:%s: [%s:%p] GetOverlappedResult failed",
1017                                  SRCNAME, __func__, item->name, item->hd);
1018                   item->got_error = 1;
1019                   if (!item->aborting)
1020                     {
1021                       item->aborting = 1;
1022                       if (!CancelIo (item->hd))
1023                         log_error_w32 (-1, "%s:%s: [%s:%p] CancelIo failed",
1024                                        SRCNAME,__func__, item->name, item->hd);
1025                     }
1026                   else 
1027                     item->got_ready = 1;
1028                 }
1029             }
1030         }
1031       LeaveCriticalSection (&work_queue_lock);
1032
1033       Sleep (0);
1034
1035       EnterCriticalSection (&work_queue_lock);
1036 /*       log_debug ("%s:%s: cleaning up work queue", SRCNAME, __func__); */
1037       for (item = work_queue; item; item = item->next)
1038         {
1039           if (item->used && (item->got_ready || item->got_error))
1040             {
1041               if (item->cld)
1042                 {
1043                   if (!item->cld->final_err && item->got_error)
1044                     item->cld->final_err = gpg_error (GPG_ERR_EIO);
1045
1046                   if (!item->cld->final_err)
1047                     {
1048                       /* Check whether there are other work items in
1049                          this group we need to wait for before
1050                          invoking the closure. */
1051                       work_item_t itm2;
1052                       
1053                       for (itm2=work_queue; itm2; itm2 = itm2->next)
1054                         if (itm2->used && itm2 != item 
1055                             && itm2->cmdid == item->cmdid
1056                             && itm2->wait_on_success
1057                             && !(itm2->got_ready || itm2->got_error))
1058                           break;
1059                       if (itm2)
1060                         {
1061                           log_debug ("%s:%s: [%s:%p] delaying closure due to "
1062                                      "[%s/%p]", SRCNAME, __func__,
1063                                      item->name, item->hd, 
1064                                      itm2->name, itm2->hd);
1065                           item->delayed_ready = 1;
1066                           break; 
1067                         }
1068                     }
1069                   item->delayed_ready = 0;
1070                   log_debug ("%s:%s: [%s:%p] invoking closure",
1071                              SRCNAME,__func__, item->name, item->hd);
1072                   
1073                   item->cld->closure (item->cld);
1074                   xfree (item->cld);
1075                   item->cld = NULL;
1076                 }
1077
1078               item->got_ready = 0;
1079               item->finalize (item);
1080               item->used = 0;
1081             }
1082         }
1083
1084       LeaveCriticalSection (&work_queue_lock);
1085     }
1086 }
1087
1088
1089 void
1090 engine_assuan_cancel (void *cancel_data)
1091 {
1092   /* FIXME */
1093 }
1094
1095
1096
1097
1098 /* Standard finalize handler.  Called right before the item is removed
1099    from the queue.  Called while the work_queue_lock is hold.  */
1100 static void
1101 finalize_handler (work_item_t item)
1102 {
1103   log_debug ("%s:%s: [%s:%p] closing handle", 
1104              SRCNAME, __func__, item->name, item->hd);
1105   CloseHandle (item->hd);
1106   item->hd = INVALID_HANDLE_VALUE;
1107 }
1108
1109 /* A finalize handler which does not close the handle.  */
1110 static void
1111 noclose_finalize_handler (work_item_t item)
1112 {
1113   log_debug ("%s:%s: [%s:%p] called", SRCNAME, __func__, item->name, item->hd);
1114   item->hd = INVALID_HANDLE_VALUE;
1115 }
1116
1117
1118 /* Add a data callback and a handle to the work queue.  This should
1119    only be called once per handle.  Caller gives up ownership of
1120    CLD. */
1121 static void
1122 enqueue_callback (const char *name, assuan_context_t ctx, 
1123                   gpgme_data_t data, HANDLE hd,
1124                   int for_write, void (*fin_handler)(work_item_t),
1125                   ULONG cmdid, closure_data_t cld, int wait_on_success)
1126 {
1127   work_item_t item;
1128   int created = 0;
1129
1130   EnterCriticalSection (&work_queue_lock);
1131   for (item = work_queue; item; item = item->next)
1132     if (!item->used)
1133       break;
1134   if (!item)
1135     {
1136       item = xmalloc (sizeof *item);
1137       item->next = work_queue;
1138       work_queue = item;
1139       created = 1;
1140     }
1141   item->used = 1;
1142   item->name = name;
1143   item->cmdid = cmdid;
1144   item->cld = cld;
1145   item->wait_on_success = wait_on_success;
1146   item->data = data;
1147   item->writing = for_write;
1148   item->hd = hd;
1149   item->io_pending = 0;
1150   item->got_ready = 0;
1151   item->delayed_ready = 0;
1152   item->got_error = 0;
1153   item->aborting = 0;
1154   item->finalize = fin_handler;
1155   memset (&item->ov, 0, sizeof item->ov);
1156   log_debug ("%s:%s: [%s:%p] created%s",
1157              SRCNAME, __func__, item->name, item->hd, created?"":" (reusing)");
1158   LeaveCriticalSection (&work_queue_lock);
1159 }
1160
1161
1162 /* Remove all items from the work queue belonging to the command with
1163    the id CMDID.  */
1164 static int
1165 destroy_command (ULONG cmdid)
1166 {
1167   work_item_t item;
1168
1169   EnterCriticalSection (&work_queue_lock);
1170   for (item = work_queue; item; item = item->next)
1171     if (item->used && item->cmdid == cmdid && !item->wait_on_success)
1172       {
1173         log_debug ("%s:%s: [%s:%p] cmdid=%lu registered for destroy",
1174                    SRCNAME, __func__, item->name, item->hd, item->cmdid);
1175         /* First send an I/O cancel in case the the last
1176            GetOverlappedResult returned only a partial result.  This
1177            works because we are always running within the
1178            async_worker_thread.  */
1179 /*         if (!CancelIo (item->hd)) */
1180 /*           log_error_w32 (-1, "%s:%s: [%s:%p] CancelIo failed", */
1181 /*                          SRCNAME, __func__, item->name, item->hd); */
1182         item->got_ready = 1;
1183       }
1184   LeaveCriticalSection (&work_queue_lock);
1185 }
1186
1187
1188 /* Process a status line.  */
1189 static int
1190 status_handler (closure_data_t cld, const char *line)
1191 {
1192   gpg_error_t err;
1193   int retval = 0;
1194
1195   log_debug ("%s:%s: cld %p, line `%s'", SRCNAME, __func__, cld, line);
1196
1197   if (*line == '#' || !*line)
1198     ;
1199   else if (line[0] == 'D' && line[1] == ' ')
1200     {
1201       line += 2;
1202     }
1203   else if (line[0] == 'S' && (!line[1] || line[1] == ' '))
1204     {
1205       for (line += 1; *line == ' '; line++)
1206         ;
1207     }  
1208   else if (line[0] == 'O' && line[1] == 'K' && (!line[2] || line[2] == ' '))
1209     {
1210       for (line += 2; *line == ' '; line++)
1211         ;
1212       cld->final_err = 0;
1213       retval = 1;
1214     }
1215   else if (!strncmp (line, "ERR", 3) && (!line[3] || line[3] == ' '))
1216     {
1217       for (line += 3; *line == ' '; line++)
1218         ;
1219       err = strtoul (line, NULL, 10);
1220       if (!err)
1221         err = gpg_error (GPG_ERR_ASS_INV_RESPONSE);
1222       cld->final_err = err;
1223       retval = 1;
1224     }  
1225   else if (!strncmp (line, "INQUIRE", 7) && (!line[7] || line[7] == ' '))
1226     {
1227       for (line += 7; *line == ' '; line++)
1228         ;
1229       /* We have no inquire handler thus get out of it immediately.  */
1230       err = assuan_write_line (cld->assctx, "END");
1231       if (err)
1232         cld->last_err = err;
1233     }
1234   else if (!strncmp (line, "END", 3) && (!line[3] || line[3] == ' '))
1235     {
1236       for (line += 3; *line == ' '; line++)
1237         ;
1238     }
1239   else
1240     retval = -1; /* Invalid response.  */
1241
1242   return retval;
1243 }
1244
1245
1246 /* This write callback is used by GPGME to push data to our status
1247    line handler.  The function should return the number of bytes
1248    written, and -1 on error.  If an error occurs, ERRNO should be set
1249    to describe the type of the error.  */
1250 static ssize_t
1251 status_in_cb (void *opaque, const void *buffer, size_t size)
1252 {
1253   size_t orig_size = size;
1254   closure_data_t cld = opaque;
1255   status_buffer_t sb;
1256   size_t nleft, nbytes;
1257   char *p;
1258
1259   assert (cld);
1260   if (!size)
1261     return 0;
1262
1263   if (!(sb=cld->status_buffer))
1264     {
1265       cld->status_buffer = sb = xmalloc (sizeof *cld->status_buffer);
1266       sb->eof = 0;
1267       sb->linelen = 0;
1268     }
1269
1270   do
1271     {
1272       assert (sb->linelen < ASSUAN_LINELENGTH);
1273       nleft = ASSUAN_LINELENGTH - sb->linelen;
1274       nbytes = size < nleft? size : nleft;
1275       memcpy (sb->line+sb->linelen, buffer, nbytes);
1276       sb->linelen += nbytes;
1277       size -= nbytes;
1278       while ((p = memchr (sb->line, '\n', sb->linelen)) && !cld->status_ready)
1279         {
1280           *p = 0;
1281           if (p > sb->line && p[-1] == '\r')
1282             p[-1] = 0;
1283           switch (status_handler (cld, sb->line))
1284             {
1285             case 0: 
1286               break;
1287             case 1: /* Ready. */
1288               cld->status_ready = 1;
1289               destroy_command (cld->cmdid);
1290               break;
1291             default:
1292               log_error ("%s:%s: invalid line from server", SRCNAME, __func__);
1293               errno = EINVAL;
1294               return -1;
1295             }
1296           sb->linelen -= (p+1 - sb->line);
1297           memmove (sb->line, p+1, sb->linelen);
1298         }
1299       if (sb->linelen >= ASSUAN_LINELENGTH)
1300         {
1301           log_error ("%s:%s: line from server too long", SRCNAME, __func__);
1302           errno = ERANGE;
1303           return -1;
1304         }
1305     }
1306   while (size);
1307   
1308   return orig_size;
1309 }
1310
1311
1312
1313 /* Start an asynchronous command.  Caller gives up ownership of
1314    CLD.  */
1315 static gpg_error_t
1316 start_command (assuan_context_t ctx, closure_data_t cld,
1317                ULONG cmdid, const char *line)
1318 {
1319   gpg_error_t err;
1320   assuan_fd_t fds[5];
1321   int nfds;
1322
1323   /* Get the fd used by assuan for status channel reads.  This is the
1324      first fd returned by assuan_get_active_fds for read fds.  */
1325   nfds = assuan_get_active_fds (ctx, 0, fds, DIM (fds));
1326   if (nfds < 1)
1327     return gpg_error (GPG_ERR_GENERAL); /* Ooops.  */
1328
1329   cld->cmdid = cmdid;
1330   cld->status_cbs.write = status_in_cb;
1331   cld->assctx = ctx;
1332   /* Fixme: We might want to have reference counting for CLD to cope
1333      with thye problem that the gpgme data object uses CLD which might
1334      get invalidated at any time.  */
1335   err = gpgme_data_new_from_cbs (&cld->status_data, &cld->status_cbs, cld);
1336   if (err)
1337     {
1338       xfree (cld);
1339       return err;
1340     }
1341
1342   enqueue_callback ("status", ctx, cld->status_data, fds[0], 0,
1343                     noclose_finalize_handler, cmdid, cld, 0);
1344   cld = NULL; /* Now belongs to the status work item.  */
1345
1346   /* Process the work queue.  */
1347   if (!SetEvent (work_queue_event))
1348     log_error_w32 (-1, "%s:%s: SetEvent failed", SRCNAME, __func__);
1349   /* Send the command. */
1350   return assuan_write_line (ctx, line);
1351 }
1352
1353
1354 static const char *
1355 get_protocol_name (protocol_t protocol)
1356 {
1357   switch (protocol)
1358     {
1359     case PROTOCOL_OPENPGP: return "OpenPGP"; break;
1360     case PROTOCOL_SMIME:   return "CMS"; break;
1361     default: return NULL;
1362     }
1363 }
1364
1365
1366 /* Callback used to get the protocool status line form a PREP_*
1367    command.  */
1368 static assuan_error_t
1369 prep_foo_status_cb (void *opaque, const char *line)
1370 {
1371   protocol_t *protocol = opaque;
1372
1373   if (!strncmp (line, "PROTOCOL", 8) && (line[8]==' ' || !line[8]))
1374     {
1375       for (line += 8; *line == ' '; line++)
1376         ;
1377       if (!strncmp (line, "OpenPGP", 7) && (line[7]==' '||!line[7]))
1378         *protocol = PROTOCOL_OPENPGP;
1379       else if (!strncmp (line, "CMS", 3) && (line[3]==' '||!line[3]))
1380         *protocol = PROTOCOL_SMIME;
1381     }
1382   return 0;
1383 }
1384
1385
1386
1387 \f
1388 /* Note that this closure is called in the context of the
1389    async_worker_thread.  */
1390 static void
1391 encrypt_closure (closure_data_t cld)
1392 {
1393   engine_private_finished (cld->filter, cld->final_err);
1394 }
1395
1396
1397 /* Encrypt the data from INDATA to the OUTDATA object for all
1398    recpients given in the NULL terminated array RECIPIENTS.  This
1399    function terminates with success and then expects the caller to
1400    wait for the result of the encryption using engine_wait.  FILTER is
1401    used for asynchronous commnication with the engine module.  HWND is
1402    the window handle of the current window and used to maintain the
1403    correct relationship between a popups and the active window.  If
1404    this function returns success, the data objects may only be
1405    destroyed after an engine_wait or engine_cancel.  */
1406 int
1407 op_assuan_encrypt (protocol_t protocol, 
1408                    gpgme_data_t indata, gpgme_data_t outdata,
1409                    engine_filter_t filter, void *hwnd,
1410                    char **recipients, protocol_t *r_used_protocol)
1411 {
1412   gpg_error_t err;
1413   closure_data_t cld;
1414   assuan_context_t ctx;
1415   char line[1024];
1416   HANDLE inpipe[2], outpipe[2];
1417   ULONG cmdid;
1418   pid_t pid;
1419   int i;
1420   char *p;
1421   int detect_protocol;
1422   const char *protocol_name;
1423
1424   detect_protocol = !(protocol_name = get_protocol_name (protocol));
1425   
1426   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1427   if (err)
1428     return err;
1429
1430   if ((err = create_io_pipe (inpipe, pid, 1)))
1431     return err;
1432   if ((err = create_io_pipe (outpipe, pid, 0)))
1433     {
1434       close_pipe (inpipe);
1435       return err;
1436     }
1437
1438   cld = xcalloc (1, sizeof *cld);
1439   cld->closure = encrypt_closure;
1440   cld->filter = filter;
1441
1442   err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
1443   if (err)
1444     goto leave;
1445   for (i=0; recipients && recipients[i]; i++)
1446     {
1447       snprintf (line, sizeof line, "RECIPIENT %s", recipients[i]);
1448       for (p=line; *p; p++)
1449         if (*p == '\n' || *p =='\r' )
1450           *p = ' ';
1451       err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1452       if (err)
1453         goto leave;
1454     }
1455
1456   /* If the protocol has not been given, let the UI server tell us the
1457      protocol to use. */
1458   if (detect_protocol)
1459     {
1460       protocol = PROTOCOL_UNKNOWN;
1461       err = assuan_transact (ctx, "PREP_ENCRYPT", NULL, NULL, NULL, NULL,
1462                              prep_foo_status_cb, &protocol);
1463       if (err)
1464         {
1465           if (gpg_err_code (err) == GPG_ERR_ASS_UNKNOWN_CMD)
1466             err = gpg_error (GPG_ERR_INV_VALUE);
1467           goto leave;
1468         }
1469       if ( !(protocol_name = get_protocol_name (protocol)) )
1470         {
1471           err = gpg_error (GPG_ERR_INV_VALUE);
1472           goto leave;
1473         }
1474     }
1475
1476   *r_used_protocol = protocol;
1477
1478   /* Note: We don't use real descriptor passing but a hack: We
1479      duplicate the handle into the server process and the server then
1480      uses this handle.  Eventually we should put this code into
1481      assuan_sendfd.  */
1482   snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)inpipe[0]);
1483   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1484   if (err)
1485     goto leave;
1486   snprintf (line, sizeof line, "OUTPUT FD=%ld", (unsigned long int)outpipe[1]);
1487   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1488   if (err)
1489     goto leave;
1490
1491   enqueue_callback (" input", ctx, indata, inpipe[1], 1, finalize_handler,
1492                     cmdid, NULL, 0); 
1493   enqueue_callback ("output", ctx, outdata, outpipe[0], 0, finalize_handler, 
1494                     cmdid, NULL, 1 /* Wait on success */); 
1495   snprintf (line, sizeof line, "ENCRYPT --protocol=%s", protocol_name);
1496   err = start_command (ctx, cld, cmdid, line);
1497   cld = NULL; /* Now owned by start_command.  */
1498   if (err)
1499     goto leave;
1500
1501
1502  leave:
1503   if (err)
1504     {
1505       /* Fixme: Cancel stuff in the work_queue. */
1506       close_pipe (inpipe);
1507       close_pipe (outpipe);
1508       xfree (cld);
1509       assuan_disconnect (ctx);
1510     }
1511   else
1512     engine_private_set_cancel (filter, ctx);
1513   return err;
1514 }
1515
1516
1517 \f
1518 /* Note that this closure is called in the context of the
1519    async_worker_thread.  */
1520 static void
1521 sign_closure (closure_data_t cld)
1522 {
1523   engine_private_finished (cld->filter, cld->final_err);
1524 }
1525
1526
1527 /* Created a detached signature for INDATA and write it to OUTDATA.
1528    On termination of the signing command engine_private_finished() is
1529    called with FILTER as the first argument.  */
1530 int 
1531 op_assuan_sign (protocol_t protocol, 
1532                 gpgme_data_t indata, gpgme_data_t outdata,
1533                 engine_filter_t filter, void *hwnd)
1534 {
1535   gpg_error_t err;
1536   closure_data_t cld;
1537   assuan_context_t ctx;
1538   char line[1024];
1539   HANDLE inpipe[2], outpipe[2];
1540   ULONG cmdid;
1541   pid_t pid;
1542   const char *protocol_name;
1543
1544
1545   if (!(protocol_name = get_protocol_name (protocol)))
1546     return gpg_error(GPG_ERR_INV_VALUE);
1547
1548   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1549   if (err)
1550     return err;
1551
1552   if ((err = create_io_pipe (inpipe, pid, 1)))
1553     return err;
1554   if ((err = create_io_pipe (outpipe, pid, 0)))
1555     {
1556       close_pipe (inpipe);
1557       return err;
1558     }
1559
1560   cld = xcalloc (1, sizeof *cld);
1561   cld->closure = sign_closure;
1562   cld->filter = filter;
1563
1564   err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
1565   if (err)
1566     goto leave;
1567
1568   snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)inpipe[0]);
1569   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1570   if (err)
1571     goto leave;
1572   snprintf (line, sizeof line, "OUTPUT FD=%ld", (unsigned long int)outpipe[1]);
1573   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1574   if (err)
1575     goto leave;
1576
1577   /* FIXME: Implement the optinonal SENDER command. */
1578
1579   enqueue_callback (" input", ctx, indata, inpipe[1], 1, finalize_handler,
1580                     cmdid, NULL, 0); 
1581   enqueue_callback ("output", ctx, outdata, outpipe[0], 0, finalize_handler, 
1582                     cmdid, NULL, 1 /* Wait on success */); 
1583
1584   snprintf (line, sizeof line, "SIGN --protocol=%s --detached",
1585             protocol_name);
1586   err = start_command (ctx, cld, cmdid, line);
1587   cld = NULL; /* Now owned by start_command.  */
1588   if (err)
1589     goto leave;
1590
1591
1592  leave:
1593   if (err)
1594     {
1595       /* Fixme: Cancel stuff in the work_queue. */
1596       close_pipe (inpipe);
1597       close_pipe (outpipe);
1598       xfree (cld);
1599       assuan_disconnect (ctx);
1600     }
1601   else
1602     engine_private_set_cancel (filter, ctx);
1603   return err;
1604 }
1605
1606
1607
1608 \f
1609 /* Note that this closure is called in the context of the
1610    async_worker_thread.  */
1611 static void
1612 decrypt_closure (closure_data_t cld)
1613 {
1614   engine_private_finished (cld->filter, cld->final_err);
1615 }
1616
1617
1618 /* Decrypt data from INDATA to OUTDATE.  If WITH_VERIFY is set, the
1619    signature of a PGP/MIME combined message is also verified the same
1620    way as with op_assuan_verify.  */
1621 int 
1622 op_assuan_decrypt (protocol_t protocol,
1623                    gpgme_data_t indata, gpgme_data_t outdata, 
1624                    engine_filter_t filter, void *hwnd,
1625                    int with_verify)
1626 {
1627   gpg_error_t err;
1628   closure_data_t cld;
1629   assuan_context_t ctx;
1630   char line[1024];
1631   HANDLE inpipe[2], outpipe[2];
1632   ULONG cmdid;
1633   pid_t pid;
1634   const char *protocol_name;
1635
1636   if (!(protocol_name = get_protocol_name (protocol)))
1637     return gpg_error(GPG_ERR_INV_VALUE);
1638
1639   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1640   if (err)
1641     return err;
1642
1643   if ((err = create_io_pipe (inpipe, pid, 1)))
1644     return err;
1645   if ((err = create_io_pipe (outpipe, pid, 0)))
1646     {
1647       close_pipe (inpipe);
1648       return err;
1649     }
1650
1651   cld = xcalloc (1, sizeof *cld);
1652   cld->closure = decrypt_closure;
1653   cld->filter = filter;
1654
1655   err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
1656   if (err)
1657     goto leave;
1658
1659   snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)inpipe[0]);
1660   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1661   if (err)
1662     goto leave;
1663   snprintf (line, sizeof line, "OUTPUT FD=%ld", (unsigned long int)outpipe[1]);
1664   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1665   if (err)
1666     goto leave;
1667
1668   enqueue_callback (" input", ctx, indata, inpipe[1], 1, finalize_handler,
1669                     cmdid, NULL, 0); 
1670   enqueue_callback ("output", ctx, outdata, outpipe[0], 0, finalize_handler, 
1671                     cmdid, NULL, 1 /* Wait on success */); 
1672
1673   snprintf (line, sizeof line, "DECRYPT --protocol=%s%s",
1674             protocol_name, with_verify? "":" --no-verify");
1675   err = start_command (ctx, cld, cmdid, line);
1676   cld = NULL; /* Now owned by start_command.  */
1677   if (err)
1678     goto leave;
1679
1680
1681  leave:
1682   if (err)
1683     {
1684       /* Fixme: Cancel stuff in the work_queue. */
1685       close_pipe (inpipe);
1686       close_pipe (outpipe);
1687       xfree (cld);
1688       assuan_disconnect (ctx);
1689     }
1690   else
1691     engine_private_set_cancel (filter, ctx);
1692   return err;
1693 }
1694
1695
1696 \f
1697 /* Note that this closure is called in the context of the
1698    async_worker_thread.  */
1699 static void
1700 verify_closure (closure_data_t cld)
1701 {
1702   gpgme_data_release (cld->sigdata);
1703   cld->sigdata = NULL;
1704   engine_private_finished (cld->filter, cld->final_err);
1705 }
1706
1707
1708 /* Verify a detached message where the data is in the gpgme object
1709    MSGDATA and the signature given as the string SIGNATURE. */
1710 int 
1711 op_assuan_verify (gpgme_protocol_t protocol, 
1712                   gpgme_data_t msgdata, const char *signature,
1713                   engine_filter_t filter, void *hwnd)
1714 {
1715   gpg_error_t err;
1716   closure_data_t cld = NULL;
1717   assuan_context_t ctx;
1718   char line[1024];
1719   HANDLE msgpipe[2], sigpipe[2];
1720   ULONG cmdid;
1721   pid_t pid;
1722   gpgme_data_t sigdata = NULL;
1723   const char *protocol_name;
1724
1725   msgpipe[0] = INVALID_HANDLE_VALUE;
1726   msgpipe[1] = INVALID_HANDLE_VALUE;
1727   sigpipe[0] = INVALID_HANDLE_VALUE;
1728   sigpipe[1] = INVALID_HANDLE_VALUE;
1729
1730   if (!(protocol_name = get_protocol_name (protocol)))
1731     return gpg_error(GPG_ERR_INV_VALUE);
1732
1733   err = gpgme_data_new_from_mem (&sigdata, signature, strlen (signature), 0);
1734   if (err)
1735     goto leave;
1736
1737   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1738   if (err)
1739     goto leave;
1740
1741   if ((err = create_io_pipe (msgpipe, pid, 1)))
1742     goto leave;
1743   if ((err = create_io_pipe (sigpipe, pid, 1)))
1744     goto leave;
1745
1746   cld = xcalloc (1, sizeof *cld);
1747   cld->closure = verify_closure;
1748   cld->filter = filter;
1749   cld->sigdata = sigdata;
1750
1751   err = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
1752   if (err)
1753     goto leave;
1754
1755   snprintf (line, sizeof line, "MESSAGE FD=%ld",(unsigned long int)msgpipe[0]);
1756   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1757   if (err)
1758     goto leave;
1759   snprintf (line, sizeof line, "INPUT FD=%ld", (unsigned long int)sigpipe[0]);
1760   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
1761   if (err)
1762     goto leave;
1763
1764   enqueue_callback ("   msg", ctx, msgdata, msgpipe[1], 1, finalize_handler,
1765                     cmdid, NULL, 0); 
1766   enqueue_callback ("   sig", ctx, sigdata, sigpipe[1], 1, finalize_handler, 
1767                     cmdid, NULL, 0); 
1768
1769   snprintf (line, sizeof line, "VERIFY --protocol=%s",  protocol_name);
1770   err = start_command (ctx, cld, cmdid, line);
1771   cld = NULL;     /* Now owned by start_command.  */
1772   sigdata = NULL; /* Ditto.  */
1773   if (err)
1774     goto leave;
1775
1776
1777  leave:
1778   if (err)
1779     {
1780       /* Fixme: Cancel stuff in the work_queue. */
1781       close_pipe (msgpipe);
1782       close_pipe (sigpipe);
1783       gpgme_data_release (sigdata);
1784       xfree (cld);
1785       assuan_disconnect (ctx);
1786     }
1787   else
1788     engine_private_set_cancel (filter, ctx);
1789   return err;
1790 }
1791
1792
1793 \f
1794 /* Ask the server to fire up the key manager.  */
1795 int 
1796 op_assuan_start_keymanager (void *hwnd)
1797 {
1798   gpg_error_t err;
1799   assuan_context_t ctx;
1800   ULONG cmdid;
1801   pid_t pid;
1802
1803   err = connect_uiserver (&ctx, &pid, &cmdid, hwnd);
1804   if (!err)
1805     {
1806       err = assuan_transact (ctx, "START_KEYMANAGER",
1807                              NULL, NULL, NULL, NULL, NULL, NULL);
1808       assuan_disconnect (ctx);
1809     }
1810   return err;
1811 }