4631eadea33b81878ad519f25b623996492dfbe3
[gnupg.git] / agent / call-pinentry.c
1 /* call-pinentry.c - fork of the pinentry to query stuff from the user
2  * Copyright (C) 2001, 2002, 2004 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <assert.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #ifndef HAVE_W32_SYSTEM
30 #include <sys/wait.h>
31 #endif
32 #include <pth.h>
33 #include <assuan.h>
34
35 #include "agent.h"
36 #include "i18n.h"
37
38 #ifdef _POSIX_OPEN_MAX
39 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
40 #else
41 #define MAX_OPEN_FDS 20
42 #endif
43
44
45 /* Because access to the pinentry must be serialized (it is and shall
46    be a global mutual dialog) we should better timeout further
47    requests after some time.  2 minutes seem to be a reasonable
48    time. */
49 #define LOCK_TIMEOUT  (1*60)
50
51 /* The assuan context of the current pinentry. */
52 static assuan_context_t entry_ctx;
53
54 /* The control variable of the connection owning the current pinentry.
55    This is only valid if ENTRY_CTX is not NULL.  Note, that we care
56    only about the value of the pointer and that it should never be
57    dereferenced.  */
58 static ctrl_t entry_owner;
59
60 /* A mutex used to serialize access to the pinentry. */
61 static pth_mutex_t entry_lock;
62
63 /* The thread ID of the popup working thread. */
64 static pth_t  popup_tid;
65
66 /* A flag used in communication between the popup working thread and
67    its stop function. */
68 static int popup_finished;
69
70
71
72 /* Data to be passed to our callbacks, */
73 struct entry_parm_s
74 {
75   int lines;
76   size_t size;
77   unsigned char *buffer;
78 };
79
80
81
82 \f
83 /* This function must be called once to initialize this module.  This
84    has to be done before a second thread is spawned.  We can't do the
85    static initialization because Pth emulation code might not be able
86    to do a static init; in particular, it is not possible for W32. */
87 void
88 initialize_module_call_pinentry (void)
89 {
90   static int initialized;
91
92   if (!initialized)
93     {
94       if (pth_mutex_init (&entry_lock))
95         initialized = 1;
96     }
97 }
98
99
100
101 static void
102 dump_mutex_state (pth_mutex_t *m)
103 {
104 #ifdef _W32_PTH_H
105   log_printf ("unknown under W32");
106 #else
107   if (!(m->mx_state & PTH_MUTEX_INITIALIZED))
108     log_printf ("not_initialized");
109   else if (!(m->mx_state & PTH_MUTEX_LOCKED))
110     log_printf ("not_locked");
111   else
112     log_printf ("locked tid=0x%lx count=%lu", (long)m->mx_owner, m->mx_count);
113 #endif
114 }
115
116
117 /* This function may be called to print infromation pertaining to the
118    current state of this module to the log. */
119 void
120 agent_query_dump_state (void)
121 {
122   log_info ("agent_query_dump_state: entry_lock=");
123   dump_mutex_state (&entry_lock);
124   log_printf ("\n");
125   log_info ("agent_query_dump_state: entry_ctx=%p pid=%ld popup_tid=%p\n",
126             entry_ctx, (long)assuan_get_pid (entry_ctx), popup_tid);
127 }
128
129 /* Called to make sure that a popup window owned by the current
130    connection gets closed. */
131 void
132 agent_reset_query (ctrl_t ctrl)
133 {
134   if (entry_ctx && popup_tid && entry_owner == ctrl)
135     {
136       agent_popup_message_stop (ctrl);
137     }
138 }
139
140
141 /* Unlock the pinentry so that another thread can start one and
142    disconnect that pinentry - we do this after the unlock so that a
143    stalled pinentry does not block other threads.  Fixme: We should
144    have a timeout in Assuan for the disconnect operation. */
145 static int 
146 unlock_pinentry (int rc)
147 {
148   assuan_context_t ctx = entry_ctx;
149
150   entry_ctx = NULL;
151   if (!pth_mutex_release (&entry_lock))
152     {
153       log_error ("failed to release the entry lock\n");
154       if (!rc)
155         rc = gpg_error (GPG_ERR_INTERNAL);
156     }
157   assuan_disconnect (ctx);
158   return rc;
159 }
160
161
162 /* To make sure we leave no secrets in our image after forking of the
163    pinentry, we use this callback. */
164 static void
165 atfork_cb (void *opaque, int where)
166 {
167   if (!where)
168     gcry_control (GCRYCTL_TERM_SECMEM);
169 }
170
171
172 /* Fork off the pin entry if this has not already been done.  Note,
173    that this function must always be used to aquire the lock for the
174    pinentry - we will serialize _all_ pinentry calls.
175  */
176 static int
177 start_pinentry (ctrl_t ctrl)
178 {
179   int rc;
180   const char *pgmname;
181   assuan_context_t ctx;
182   const char *argv[5];
183   int no_close_list[3];
184   int i;
185   pth_event_t evt;
186   const char *tmpstr;
187
188   evt = pth_event (PTH_EVENT_TIME, pth_timeout (LOCK_TIMEOUT, 0));
189   if (!pth_mutex_acquire (&entry_lock, 0, evt))
190     {
191       if (pth_event_occurred (evt))
192         rc = gpg_error (GPG_ERR_TIMEOUT);
193       else
194         rc = gpg_error (GPG_ERR_INTERNAL);
195       pth_event_free (evt, PTH_FREE_THIS);
196       log_error (_("failed to acquire the pinentry lock: %s\n"),
197                  gpg_strerror (rc));
198       return rc;
199     }
200   pth_event_free (evt, PTH_FREE_THIS);
201
202   entry_owner = ctrl;
203
204   if (entry_ctx)
205     return 0; 
206
207   if (opt.verbose)
208     log_info ("starting a new PIN Entry\n");
209
210 #ifdef HAVE_W32_SYSTEM      
211   fflush (stdout);
212   fflush (stderr);
213 #endif
214   if (fflush (NULL))
215     {
216       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
217       log_error ("error flushing pending output: %s\n", strerror (errno));
218       /* At least Windows XP fails here with EBADF.  According to docs
219          and Wine an fflush(NULL) is the same as _flushall.  However
220          the Wime implementaion does not flush stdin,stdout and stderr
221          - see above.  Lets try to ignore the error. */
222 #ifndef HAVE_W32_SYSTEM
223       return unlock_pinentry (tmperr);
224 #endif
225     }
226
227   if (!opt.pinentry_program || !*opt.pinentry_program)
228     opt.pinentry_program = gnupg_module_name (GNUPG_MODULE_NAME_PINENTRY);
229     pgmname = opt.pinentry_program;
230   if ( !(pgmname = strrchr (opt.pinentry_program, '/')))
231     pgmname = opt.pinentry_program;
232   else
233     pgmname++;
234
235   /* OS X needs the entire file name in argv[0], so that it can locate
236      the resource bundle.  For other systems we stick to the usual
237      convention of supplying only the name of the program.  */
238 #ifdef __APPLE__
239   argv[0] = opt.pinentry_program;
240 #else /*!__APPLE__*/
241   argv[0] = pgmname;
242 #endif /*__APPLE__*/
243
244   if (ctrl->display && !opt.keep_display)
245     {
246       argv[1] = "--display";
247       argv[2] = ctrl->display;
248       argv[3] = NULL;
249     }
250   else
251     argv[1] = NULL;
252   
253   i=0;
254   if (!opt.running_detached)
255     {
256       if (log_get_fd () != -1)
257         no_close_list[i++] = log_get_fd ();
258       no_close_list[i++] = fileno (stderr);
259     }
260   no_close_list[i] = -1;
261
262   /* Connect to the pinentry and perform initial handshaking */
263   rc = assuan_pipe_connect_ext (&ctx, opt.pinentry_program, argv,
264                                 no_close_list, atfork_cb, NULL, 0);
265   if (rc)
266     {
267       log_error ("can't connect to the PIN entry module: %s\n",
268                  gpg_strerror (rc));
269       return unlock_pinentry (gpg_error (GPG_ERR_NO_PIN_ENTRY));
270     }
271   entry_ctx = ctx;
272
273   if (DBG_ASSUAN)
274     log_debug ("connection to PIN entry established\n");
275
276   rc = assuan_transact (entry_ctx, 
277                         opt.no_grab? "OPTION no-grab":"OPTION grab",
278                         NULL, NULL, NULL, NULL, NULL, NULL);
279   if (rc)
280     return unlock_pinentry (rc);
281   if (ctrl->ttyname)
282     {
283       char *optstr;
284       if (asprintf (&optstr, "OPTION ttyname=%s", ctrl->ttyname) < 0 )
285         return unlock_pinentry (out_of_core ());
286       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
287                             NULL);
288       free (optstr);
289       if (rc)
290         return unlock_pinentry (rc);
291     }
292   if (ctrl->ttytype)
293     {
294       char *optstr;
295       if (asprintf (&optstr, "OPTION ttytype=%s", ctrl->ttytype) < 0 )
296         return unlock_pinentry (out_of_core ());
297       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
298                             NULL);
299       if (rc)
300         return unlock_pinentry (rc);
301     }
302   if (ctrl->lc_ctype)
303     {
304       char *optstr;
305       if (asprintf (&optstr, "OPTION lc-ctype=%s", ctrl->lc_ctype) < 0 )
306         return unlock_pinentry (out_of_core ());
307       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
308                             NULL);
309       if (rc)
310         return unlock_pinentry (rc);
311     }
312   if (ctrl->lc_messages)
313     {
314       char *optstr;
315       if (asprintf (&optstr, "OPTION lc-messages=%s", ctrl->lc_messages) < 0 )
316         return unlock_pinentry (out_of_core ());
317       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
318                             NULL);
319       if (rc)
320         return unlock_pinentry (rc);
321     }
322
323   
324   /* Tell the pinentry the name of a file it shall touch after having
325      messed with the tty.  This is optional and only supported by
326      newer pinentries and thus we do no error checking. */
327   tmpstr = opt.pinentry_touch_file;
328   if (tmpstr && !strcmp (tmpstr, "/dev/null"))
329     tmpstr = NULL;
330   else if (!tmpstr)
331     tmpstr = get_agent_socket_name ();
332   if (tmpstr)
333     {
334       char *optstr;
335       
336       if (asprintf (&optstr, "OPTION touch-file=%s", tmpstr ) < 0 )
337         ;
338       else
339         {
340           assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
341                            NULL);
342           free (optstr);
343         }
344     }
345
346   return 0;
347 }
348
349 /* Returns True is the pinentry is currently active. If WAITSECONDS is
350    greater than zero the function will wait for this many seconds
351    before returning.  */
352 int
353 pinentry_active_p (ctrl_t ctrl, int waitseconds)
354 {
355   if (waitseconds > 0)
356     {
357       pth_event_t evt;
358       int rc;
359
360       evt = pth_event (PTH_EVENT_TIME, pth_timeout (waitseconds, 0));
361       if (!pth_mutex_acquire (&entry_lock, 0, evt))
362         {
363           if (pth_event_occurred (evt))
364             rc = gpg_error (GPG_ERR_TIMEOUT);
365           else
366             rc = gpg_error (GPG_ERR_INTERNAL);
367           pth_event_free (evt, PTH_FREE_THIS);
368           return rc;
369         }
370       pth_event_free (evt, PTH_FREE_THIS);
371     }
372   else
373     {
374       if (!pth_mutex_acquire (&entry_lock, 1, NULL))
375         return gpg_error (GPG_ERR_LOCKED);
376     }
377
378   if (!pth_mutex_release (&entry_lock))
379     log_error ("failed to release the entry lock at %d\n", __LINE__);
380   return 0;
381 }
382
383
384 static int
385 getpin_cb (void *opaque, const void *buffer, size_t length)
386 {
387   struct entry_parm_s *parm = opaque;
388
389   if (!buffer)
390     return 0;
391
392   /* we expect the pin to fit on one line */
393   if (parm->lines || length >= parm->size)
394     return gpg_error (GPG_ERR_ASS_TOO_MUCH_DATA);
395
396   /* fixme: we should make sure that the assuan buffer is allocated in
397      secure memory or read the response byte by byte */
398   memcpy (parm->buffer, buffer, length);
399   parm->buffer[length] = 0;
400   parm->lines++;
401   return 0;
402 }
403
404
405 static int
406 all_digitsp( const char *s)
407 {
408   for (; *s && *s >= '0' && *s <= '9'; s++)
409     ;
410   return !*s;
411 }  
412
413
414 \f
415 /* Call the Entry and ask for the PIN.  We do check for a valid PIN
416    number here and repeat it as long as we have invalid formed
417    numbers. */
418 int
419 agent_askpin (ctrl_t ctrl,
420               const char *desc_text, const char *prompt_text,
421               const char *initial_errtext,
422               struct pin_entry_info_s *pininfo)
423 {
424   int rc;
425   char line[ASSUAN_LINELENGTH];
426   struct entry_parm_s parm;
427   const char *errtext = NULL;
428   int is_pin = 0;
429
430   if (opt.batch)
431     return 0; /* fixme: we should return BAD PIN */
432
433   if (!pininfo || pininfo->max_length < 1)
434     return gpg_error (GPG_ERR_INV_VALUE);
435   if (!desc_text && pininfo->min_digits)
436     desc_text = _("Please enter your PIN, so that the secret key "
437                   "can be unlocked for this session");
438   else if (!desc_text)
439     desc_text = _("Please enter your passphrase, so that the secret key "
440                   "can be unlocked for this session");
441
442   if (prompt_text)
443     is_pin = !!strstr (prompt_text, "PIN");
444   else
445     is_pin = desc_text && strstr (desc_text, "PIN");
446
447   rc = start_pinentry (ctrl);
448   if (rc)
449     return rc;
450
451   snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
452   line[DIM(line)-1] = 0;
453   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
454   if (rc)
455     return unlock_pinentry (rc);
456
457   snprintf (line, DIM(line)-1, "SETPROMPT %s",
458             prompt_text? prompt_text : is_pin? "PIN:" : "Passphrase:");
459   line[DIM(line)-1] = 0;
460   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
461   if (rc)
462     return unlock_pinentry (rc);
463
464
465   if (initial_errtext)
466     { 
467       snprintf (line, DIM(line)-1, "SETERROR %s", initial_errtext);
468       line[DIM(line)-1] = 0;
469       rc = assuan_transact (entry_ctx, line,
470                             NULL, NULL, NULL, NULL, NULL, NULL);
471       if (rc)
472         return unlock_pinentry (rc);
473     }
474
475   for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++)
476     {
477       memset (&parm, 0, sizeof parm);
478       parm.size = pininfo->max_length;
479       parm.buffer = (unsigned char*)pininfo->pin;
480
481       if (errtext)
482         { 
483           /* TRANLATORS: The string is appended to an error message in
484              the pinentry.  The %s is the actual error message, the
485              two %d give the current and maximum number of tries. */
486           snprintf (line, DIM(line)-1, _("SETERROR %s (try %d of %d)"),
487                     errtext, pininfo->failed_tries+1, pininfo->max_tries);
488           line[DIM(line)-1] = 0;
489           rc = assuan_transact (entry_ctx, line,
490                                 NULL, NULL, NULL, NULL, NULL, NULL);
491           if (rc)
492             return unlock_pinentry (rc);
493           errtext = NULL;
494         }
495       
496       rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
497                             NULL, NULL, NULL, NULL);
498       /* Most pinentries out in the wild return the old Assuan error code
499          for canceled which gets translated to an assuan Cancel error and
500          not to the code for a user cancel.  Fix this here. */
501       if (rc && gpg_err_source (rc)
502           && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
503         rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
504
505       if (gpg_err_code (rc) == GPG_ERR_ASS_TOO_MUCH_DATA)
506         errtext = is_pin? _("PIN too long")
507                         : _("Passphrase too long");
508       else if (rc)
509         return unlock_pinentry (rc);
510
511       if (!errtext && pininfo->min_digits)
512         {
513           /* do some basic checks on the entered PIN. */
514           if (!all_digitsp (pininfo->pin))
515             errtext = _("Invalid characters in PIN");
516           else if (pininfo->max_digits
517                    && strlen (pininfo->pin) > pininfo->max_digits)
518             errtext = _("PIN too long");
519           else if (strlen (pininfo->pin) < pininfo->min_digits)
520             errtext = _("PIN too short");
521         }
522
523       if (!errtext && pininfo->check_cb)
524         {
525           /* More checks by utilizing the optional callback. */
526           pininfo->cb_errtext = NULL;
527           rc = pininfo->check_cb (pininfo);
528           if (rc == -1 && pininfo->cb_errtext)
529             errtext = pininfo->cb_errtext;
530           else if (gpg_err_code (rc) == GPG_ERR_BAD_PASSPHRASE
531                    || gpg_err_code (rc) == GPG_ERR_BAD_PIN)
532             errtext = (is_pin? _("Bad PIN")
533                        : _("Bad Passphrase"));
534           else if (rc)
535             return unlock_pinentry (rc);
536         }
537
538       if (!errtext)
539         return unlock_pinentry (0); /* okay, got a PIN or passphrase */
540     }
541
542   return unlock_pinentry (gpg_error (pininfo->min_digits? GPG_ERR_BAD_PIN
543                           : GPG_ERR_BAD_PASSPHRASE));
544 }
545
546
547 \f
548 /* Ask for the passphrase using the supplied arguments.  The returned
549    passphrase needs to be freed by the caller. */
550 int 
551 agent_get_passphrase (ctrl_t ctrl,
552                       char **retpass, const char *desc, const char *prompt,
553                       const char *errtext)
554 {
555
556   int rc;
557   char line[ASSUAN_LINELENGTH];
558   struct entry_parm_s parm;
559
560   *retpass = NULL;
561   if (opt.batch)
562     return gpg_error (GPG_ERR_BAD_PASSPHRASE); 
563
564   rc = start_pinentry (ctrl);
565   if (rc)
566     return rc;
567
568   if (!prompt)
569     prompt = desc && strstr (desc, "PIN")? "PIN": _("Passphrase");
570
571
572   if (desc)
573     snprintf (line, DIM(line)-1, "SETDESC %s", desc);
574   else
575     snprintf (line, DIM(line)-1, "RESET");
576   line[DIM(line)-1] = 0;
577   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
578   if (rc)
579     return unlock_pinentry (rc);
580
581   snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt);
582   line[DIM(line)-1] = 0;
583   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
584   if (rc)
585     return unlock_pinentry (rc);
586
587   if (errtext)
588     {
589       snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
590       line[DIM(line)-1] = 0;
591       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
592       if (rc)
593         return unlock_pinentry (rc);
594     }
595
596   memset (&parm, 0, sizeof parm);
597   parm.size = ASSUAN_LINELENGTH/2 - 5;
598   parm.buffer = gcry_malloc_secure (parm.size+10);
599   if (!parm.buffer)
600     return unlock_pinentry (out_of_core ());
601
602   assuan_begin_confidential (entry_ctx);
603   rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm,
604                         NULL, NULL, NULL, NULL);
605   /* Most pinentries out in the wild return the old Assuan error code
606      for canceled which gets translated to an assuan Cancel error and
607      not to the code for a user cancel.  Fix this here. */
608   if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
609     rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
610   if (rc)
611     xfree (parm.buffer);
612   else
613     *retpass = parm.buffer;
614   return unlock_pinentry (rc);
615 }
616
617
618 \f
619 /* Pop up the PIN-entry, display the text and the prompt and ask the
620    user to confirm this.  We return 0 for success, ie. the user
621    confirmed it, GPG_ERR_NOT_CONFIRMED for what the text says or an
622    other error. */
623 int 
624 agent_get_confirmation (ctrl_t ctrl,
625                         const char *desc, const char *ok, const char *cancel)
626 {
627   int rc;
628   char line[ASSUAN_LINELENGTH];
629
630   rc = start_pinentry (ctrl);
631   if (rc)
632     return rc;
633
634   if (desc)
635     snprintf (line, DIM(line)-1, "SETDESC %s", desc);
636   else
637     snprintf (line, DIM(line)-1, "RESET");
638   line[DIM(line)-1] = 0;
639   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
640   /* Most pinentries out in the wild return the old Assuan error code
641      for canceled which gets translated to an assuan Cancel error and
642      not to the code for a user cancel.  Fix this here. */
643   if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
644     rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
645
646   if (rc)
647     return unlock_pinentry (rc);
648
649   if (ok)
650     {
651       snprintf (line, DIM(line)-1, "SETOK %s", ok);
652       line[DIM(line)-1] = 0;
653       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
654       if (rc)
655         return unlock_pinentry (rc);
656     }
657   if (cancel)
658     {
659       snprintf (line, DIM(line)-1, "SETCANCEL %s", cancel);
660       line[DIM(line)-1] = 0;
661       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
662       if (rc)
663         return unlock_pinentry (rc);
664     }
665
666   rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
667   if (rc && gpg_err_source (rc) && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
668     rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
669
670   return unlock_pinentry (rc);
671 }
672
673
674 /* The thread running the popup message. */
675 static void *
676 popup_message_thread (void *arg)
677 {
678   /* We use the --one-button hack instead of the MESSAGE command to
679      allow the use of old Pinentries.  Those old Pinentries will then
680      show an additional Cancel button but that is mostly a visual
681      annoyance. */
682   assuan_transact (entry_ctx, "CONFIRM --one-button", 
683                    NULL, NULL, NULL, NULL, NULL, NULL);
684   popup_finished = 1;
685   return NULL;
686 }
687
688
689 /* Pop up a message window similar to the confirm one but keep it open
690    until agent_popup_message_stop has been called.  It is crucial for
691    the caller to make sure that the stop function gets called as soon
692    as the message is not anymore required because the message is
693    system modal and all other attempts to use the pinentry will fail
694    (after a timeout). */
695 int 
696 agent_popup_message_start (ctrl_t ctrl, const char *desc, const char *ok_btn)
697 {
698   int rc;
699   char line[ASSUAN_LINELENGTH];
700   pth_attr_t tattr;
701
702   rc = start_pinentry (ctrl);
703   if (rc)
704     return rc;
705
706   if (desc)
707     snprintf (line, DIM(line)-1, "SETDESC %s", desc);
708   else
709     snprintf (line, DIM(line)-1, "RESET");
710   line[DIM(line)-1] = 0;
711   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
712   if (rc)
713     return unlock_pinentry (rc);
714
715   if (ok_btn)
716     {
717       snprintf (line, DIM(line)-1, "SETOK %s", ok_btn);
718       line[DIM(line)-1] = 0;
719       rc = assuan_transact (entry_ctx, line, NULL,NULL,NULL,NULL,NULL,NULL);
720       if (rc)
721         return unlock_pinentry (rc);
722     }
723
724   tattr = pth_attr_new();
725   pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1);
726   pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
727   pth_attr_set (tattr, PTH_ATTR_NAME, "popup-message");
728
729   popup_finished = 0;
730   popup_tid = pth_spawn (tattr, popup_message_thread, NULL);
731   if (!popup_tid)
732     {
733       rc = gpg_error_from_syserror ();
734       log_error ("error spawning popup message handler: %s\n",
735                  strerror (errno) );
736       pth_attr_destroy (tattr);
737       return unlock_pinentry (rc);
738     }
739   pth_attr_destroy (tattr);
740
741   return 0;
742 }
743
744 /* Close a popup window. */
745 void
746 agent_popup_message_stop (ctrl_t ctrl)
747 {
748   int rc;
749   pid_t pid;
750
751   if (!popup_tid || !entry_ctx)
752     {
753       log_debug ("agent_popup_message_stop called with no active popup\n");
754       return; 
755     }
756
757   pid = assuan_get_pid (entry_ctx);
758   if (pid == (pid_t)(-1))
759     ; /* No pid available can't send a kill. */
760   else if (popup_finished)
761     ; /* Already finished and ready for joining. */
762 #ifdef HAVE_W32_SYSTEM
763 #  warning need to implement a kill mechanism for pinentry  
764 #else
765   else if (pid && ((rc=waitpid (pid, NULL, WNOHANG))==-1 || (rc == pid)) )
766     { /* The daemon already died.  No need to send a kill.  However
767          because we already waited for the process, we need to tell
768          assuan that it should not wait again (done by
769          unlock_pinentry). */
770       if (rc == pid)
771         assuan_set_flag (entry_ctx, ASSUAN_NO_WAITPID, 1);
772     }
773   else if (pid > 0)
774     kill (pid, SIGKILL);  /* Need to use SIGKILL due to bad
775                              interaction of SIGINT with Pth. */
776 #endif
777
778   /* Now wait for the thread to terminate. */
779   rc = pth_join (popup_tid, NULL);
780   if (!rc)
781     log_debug ("agent_popup_message_stop: pth_join failed: %s\n",
782                strerror (errno));
783   popup_tid = NULL;
784   entry_owner = NULL;
785
786   /* Now we can close the connection. */
787   unlock_pinentry (0);
788 }
789
790