gpg-connect-agent: Make it easier to connect to the dirmngr.
[gnupg.git] / g10 / server.c
1 /* server.c - server mode for gpg
2  * Copyright (C) 2006, 2008  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 <stdarg.h>
26 #include <ctype.h>
27 #include <unistd.h>
28
29
30 #include "gpg.h"
31 #include <assuan.h>
32 #include "util.h"
33 #include "i18n.h"
34 #include "options.h"
35 #include "../common/sysutils.h"
36 #include "status.h"
37
38
39 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
40
41
42 /* Data used to associate an Assuan context with local server data.  */
43 struct server_local_s
44 {
45   /* Our current Assuan context. */
46   assuan_context_t assuan_ctx;
47   /* File descriptor as set by the MESSAGE command. */
48   gnupg_fd_t message_fd;
49
50   /* List of prepared recipients.  */
51   pk_list_t recplist;
52
53   /* Set if pinentry notifications should be passed back to the
54      client. */
55   int allow_pinentry_notify;
56 };
57
58
59 \f
60 /* Helper to close the message fd if it is open. */
61 static void
62 close_message_fd (ctrl_t ctrl)
63 {
64   if (ctrl->server_local->message_fd != GNUPG_INVALID_FD)
65     {
66       assuan_sock_close (ctrl->server_local->message_fd);
67       ctrl->server_local->message_fd = GNUPG_INVALID_FD;
68     }
69 }
70
71
72 /* Skip over options.  Blanks after the options are also removed.  */
73 static char *
74 skip_options (const char *line)
75 {
76   while (spacep (line))
77     line++;
78   while ( *line == '-' && line[1] == '-' )
79     {
80       while (*line && !spacep (line))
81         line++;
82       while (spacep (line))
83         line++;
84     }
85   return (char*)line;
86 }
87
88
89 /* Check whether the option NAME appears in LINE.  */
90 static int
91 has_option (const char *line, const char *name)
92 {
93   const char *s;
94   int n = strlen (name);
95
96   s = strstr (line, name);
97   if (s && s >= skip_options (line))
98     return 0;
99   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
100 }
101
102
103
104
105 \f
106 /* Called by libassuan for Assuan options.  See the Assuan manual for
107    details. */
108 static gpg_error_t
109 option_handler (assuan_context_t ctx, const char *key, const char *value)
110 {
111   ctrl_t ctrl = assuan_get_pointer (ctx);
112
113   (void)value;
114
115   /* Fixme: Implement the tty and locale args. */
116   if (!strcmp (key, "display"))
117     {
118     }
119   else if (!strcmp (key, "ttyname"))
120     {
121     }
122   else if (!strcmp (key, "ttytype"))
123     {
124     }
125   else if (!strcmp (key, "lc-ctype"))
126     {
127     }
128   else if (!strcmp (key, "lc-messages"))
129     {
130     }
131   else if (!strcmp (key, "xauthority"))
132     {
133     }
134   else if (!strcmp (key, "pinentry_user_data"))
135     {
136     }
137   else if (!strcmp (key, "list-mode"))
138     {
139       /* This is for now a dummy option. */
140     }
141   else if (!strcmp (key, "allow-pinentry-notify"))
142     {
143       ctrl->server_local->allow_pinentry_notify = 1;
144     }
145   else
146     return gpg_error (GPG_ERR_UNKNOWN_OPTION);
147
148   return 0;
149 }
150
151
152 /* Called by libassuan for RESET commands. */
153 static gpg_error_t
154 reset_notify (assuan_context_t ctx, char *line)
155 {
156   ctrl_t ctrl = assuan_get_pointer (ctx);
157
158   (void)line;
159
160   release_pk_list (ctrl->server_local->recplist);
161   ctrl->server_local->recplist = NULL;
162
163   close_message_fd (ctrl);
164   assuan_close_input_fd (ctx);
165   assuan_close_output_fd (ctx);
166   return 0;
167 }
168
169
170 /* Called by libassuan for INPUT commands. */
171 static gpg_error_t
172 input_notify (assuan_context_t ctx, char *line)
173 {
174 /*   ctrl_t ctrl = assuan_get_pointer (ctx); */
175
176   (void)ctx;
177
178   if (strstr (line, "--armor"))
179     ; /* FIXME */
180   else if (strstr (line, "--base64"))
181     ; /* FIXME */
182   else if (strstr (line, "--binary"))
183     ;
184   else
185     {
186       /* FIXME (autodetect encoding) */
187     }
188   return 0;
189 }
190
191
192 /* Called by libassuan for OUTPUT commands. */
193 static gpg_error_t
194 output_notify (assuan_context_t ctx, char *line)
195 {
196 /*   ctrl_t ctrl = assuan_get_pointer (ctx); */
197
198   (void)ctx;
199
200   if (strstr (line, "--armor"))
201     ; /* FIXME */
202   else if (strstr (line, "--base64"))
203     {
204       /* FIXME */
205     }
206   return 0;
207 }
208
209
210
211 \f
212 /*  RECIPIENT [--hidden] <userID>
213
214    Set the recipient for the encryption.  <userID> should be the
215    internal representation of the key; the server may accept any other
216    way of specification.  If this is a valid and trusted recipient the
217    server does respond with OK, otherwise the return is an ERR with
218    the reason why the recipient can't be used, the encryption will
219    then not be done for this recipient.  If the policy is not to
220    encrypt at all if not all recipients are valid, the client has to
221    take care of this.  All RECIPIENT commands are cumulative until a
222    RESET or an successful ENCRYPT command.  */
223 static gpg_error_t
224 cmd_recipient (assuan_context_t ctx, char *line)
225 {
226   ctrl_t ctrl = assuan_get_pointer (ctx);
227   gpg_error_t err;
228   int hidden;
229
230   hidden = has_option (line,"--hidden");
231   line = skip_options (line);
232
233   /* FIXME: Expand groups
234   if (opt.grouplist)
235     remusr = expand_group (rcpts);
236   else
237     remusr = rcpts;
238   */
239
240   err = find_and_check_key (ctrl, line, PUBKEY_USAGE_ENC, hidden,
241                             &ctrl->server_local->recplist);
242
243   if (err)
244     log_error ("command '%s' failed: %s\n", "RECIPIENT", gpg_strerror (err));
245   return err;
246 }
247
248
249 \f
250 /*  SIGNER <userID>
251
252    Set the signer's keys for the signature creation.  <userID> should
253    be the internal representation of the key; the server may accept
254    any other way of specification.  If this is a valid and usable
255    signing key the server does respond with OK, otherwise it returns
256    an ERR with the reason why the key can't be used, the signing will
257    then not be done for this key.  If the policy is not to sign at all
258    if not all signer keys are valid, the client has to take care of
259    this.  All SIGNER commands are cumulative until a RESET but they
260    are *not* reset by an SIGN command becuase it can be expected that
261    set of signers are used for more than one sign operation.
262
263    Note that this command returns an INV_RECP status which is a bit
264    strange, but they are very similar.  */
265 static gpg_error_t
266 cmd_signer (assuan_context_t ctx, char *line)
267 {
268   (void)ctx;
269   (void)line;
270   return gpg_error (GPG_ERR_NOT_SUPPORTED);
271 }
272
273
274 \f
275 /*  ENCRYPT
276
277    Do the actual encryption process.  Takes the plaintext from the
278    INPUT command, writes the ciphertext to the file descriptor set
279    with the OUTPUT command, take the recipients from all the
280    recipients set so far with RECIPIENTS.
281
282    If this command fails the clients should try to delete all output
283    currently done or otherwise mark it as invalid.  GPG does ensure
284    that there won't be any security problem with leftover data on the
285    output in this case.
286
287    In most cases this command won't fail because most necessary checks
288    have been done while setting the recipients.  However some checks
289    can only be done right here and thus error may occur anyway (for
290    example, no recipients at all).
291
292    The input, output and message pipes are closed after this
293    command.  */
294 static gpg_error_t
295 cmd_encrypt (assuan_context_t ctx, char *line)
296 {
297   ctrl_t ctrl = assuan_get_pointer (ctx);
298   gpg_error_t err;
299   int inp_fd, out_fd;
300
301   (void)line; /* LINE is not used.  */
302
303   if ( !ctrl->server_local->recplist )
304     {
305       write_status_text (STATUS_NO_RECP, "0");
306       err = gpg_error (GPG_ERR_NO_USER_ID);
307       goto leave;
308     }
309
310   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
311   if (inp_fd == -1)
312     {
313       err = set_error (GPG_ERR_ASS_NO_INPUT, NULL);
314       goto leave;
315     }
316   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
317   if (out_fd == -1)
318     {
319       err = set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
320       goto leave;
321     }
322
323   /* Fixme: Check that we are using real files and not pipes if in
324      PGP-2 mode.  Do all the other checks we do in gpg.c for aEncr.
325      Maybe we should drop the PGP2 compatibility. */
326
327
328   /* FIXME: GPGSM does this here: Add all encrypt-to marked recipients
329      from the default list. */
330
331   /* fixme: err = ctrl->audit? 0 : start_audit_session (ctrl);*/
332
333   err = encrypt_crypt (ctrl, inp_fd, NULL, NULL, 0,
334                        ctrl->server_local->recplist,
335                        out_fd);
336
337  leave:
338   /* Release the recipient list on success.  */
339   if (!err)
340     {
341       release_pk_list (ctrl->server_local->recplist);
342       ctrl->server_local->recplist = NULL;
343     }
344
345   /* Close and reset the fds. */
346   close_message_fd (ctrl);
347   assuan_close_input_fd (ctx);
348   assuan_close_output_fd (ctx);
349
350   if (err)
351     log_error ("command '%s' failed: %s\n", "ENCRYPT", gpg_strerror (err));
352   return err;
353 }
354
355
356 \f
357 /*  DECRYPT
358
359     This performs the decrypt operation.  */
360 static gpg_error_t
361 cmd_decrypt (assuan_context_t ctx, char *line)
362 {
363   ctrl_t ctrl = assuan_get_pointer (ctx);
364   gpg_error_t err;
365   int inp_fd, out_fd;
366
367   (void)line; /* LINE is not used.  */
368
369   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
370   if (inp_fd == -1)
371     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
372   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
373   if (out_fd == -1)
374     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
375
376   glo_ctrl.lasterr = 0;
377   err = decrypt_message_fd (ctrl, inp_fd, out_fd);
378   if (!err)
379     err = glo_ctrl.lasterr;
380
381   /* Close and reset the fds. */
382   close_message_fd (ctrl);
383   assuan_close_input_fd (ctx);
384   assuan_close_output_fd (ctx);
385
386   if (err)
387     log_error ("command '%s' failed: %s\n", "DECRYPT", gpg_strerror (err));
388   return err;
389 }
390
391
392 \f
393 /*  VERIFY
394
395    This does a verify operation on the message send to the input-FD.
396    The result is written out using status lines.  If an output FD was
397    given, the signed text will be written to that.
398
399    If the signature is a detached one, the server will inquire about
400    the signed material and the client must provide it.
401  */
402 static gpg_error_t
403 cmd_verify (assuan_context_t ctx, char *line)
404 {
405   int rc;
406 #ifdef HAVE_W32_SYSTEM
407   (void)ctx;
408   (void)line;
409   rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
410 #else
411   ctrl_t ctrl = assuan_get_pointer (ctx);
412   gnupg_fd_t fd = assuan_get_input_fd (ctx);
413   gnupg_fd_t out_fd = assuan_get_output_fd (ctx);
414   estream_t out_fp = NULL;
415
416   /* FIXME: Revamp this code it is nearly to 3 years old and was only
417      intended as a quick test.  */
418
419   (void)line;
420
421   if (fd == GNUPG_INVALID_FD)
422     return gpg_error (GPG_ERR_ASS_NO_INPUT);
423
424   if (out_fd != GNUPG_INVALID_FD)
425     {
426       es_syshd_t syshd;
427
428 #ifdef HAVE_W32_SYSTEM
429       syshd.type = ES_SYSHD_HANDLE;
430       syshd.u.handle = out_fd;
431 #else
432       syshd.type = ES_SYSHD_FD;
433       syshd.u.fd = out_fd;
434 #endif
435       out_fp = es_sysopen_nc (&syshd, "w");
436       if (!out_fp)
437         return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
438     }
439
440   log_debug ("WARNING: The server mode is WORK "
441              "IN PROGRESS and not ready for use\n");
442
443   rc = gpg_verify (ctrl, fd, ctrl->server_local->message_fd, out_fp);
444
445   es_fclose (out_fp);
446   close_message_fd (ctrl);
447   assuan_close_input_fd (ctx);
448   assuan_close_output_fd (ctx);
449 #endif
450
451   if (rc)
452     log_error ("command '%s' failed: %s\n", "VERIFY", gpg_strerror (rc));
453   return rc;
454 }
455
456
457 \f
458 /*  SIGN [--detached]
459
460    Sign the data set with the INPUT command and write it to the sink
461    set by OUTPUT.  With "--detached" specified, a detached signature
462    is created.  */
463 static gpg_error_t
464 cmd_sign (assuan_context_t ctx, char *line)
465 {
466   (void)ctx;
467   (void)line;
468   return gpg_error (GPG_ERR_NOT_SUPPORTED);
469 }
470
471
472 \f
473 /*  IMPORT
474
475   Import keys as read from the input-fd, return status message for
476   each imported one.  The import checks the validity of the key.  */
477 static gpg_error_t
478 cmd_import (assuan_context_t ctx, char *line)
479 {
480   (void)ctx;
481   (void)line;
482   return gpg_error (GPG_ERR_NOT_SUPPORTED);
483 }
484
485
486 \f
487 /*  EXPORT [--data [--armor|--base64]] [--] pattern
488
489    Similar to the --export command line command, this command exports
490    public keys matching PATTERN.  The output is send to the output fd
491    unless the --data option has been used in which case the output
492    gets send inline using regular data lines.  The options "--armor"
493    and "--base" ospecify an output format if "--data" has been used.
494    Recall that in general the output format is set with the OUTPUT
495    command.
496  */
497 static gpg_error_t
498 cmd_export (assuan_context_t ctx, char *line)
499 {
500   (void)ctx;
501   (void)line;
502   return gpg_error (GPG_ERR_NOT_SUPPORTED);
503 }
504
505
506 \f
507 /*  DELKEYS
508
509     Fixme
510 */
511 static gpg_error_t
512 cmd_delkeys (assuan_context_t ctx, char *line)
513 {
514   (void)ctx;
515   (void)line;
516   return gpg_error (GPG_ERR_NOT_SUPPORTED);
517 }
518
519
520 \f
521 /*  MESSAGE FD[=<n>]
522
523    Set the file descriptor to read a message which is used with
524    detached signatures.  */
525 static gpg_error_t
526 cmd_message (assuan_context_t ctx, char *line)
527 {
528   int rc;
529   gnupg_fd_t fd;
530   ctrl_t ctrl = assuan_get_pointer (ctx);
531
532   rc = assuan_command_parse_fd (ctx, line, &fd);
533   if (rc)
534     return rc;
535   if (fd == GNUPG_INVALID_FD)
536     return gpg_error (GPG_ERR_ASS_NO_INPUT);
537   ctrl->server_local->message_fd = fd;
538   return 0;
539 }
540
541
542 \f
543 /* LISTKEYS [<patterns>]
544    LISTSECRETKEYS [<patterns>]
545
546    fixme
547 */
548 static gpg_error_t
549 do_listkeys (assuan_context_t ctx, char *line, int mode)
550 {
551   (void)ctx;
552   (void)line;
553   (void)mode;
554
555   return gpg_error (GPG_ERR_NOT_SUPPORTED);
556 }
557
558
559 static gpg_error_t
560 cmd_listkeys (assuan_context_t ctx, char *line)
561 {
562   return do_listkeys (ctx, line, 3);
563 }
564
565
566 static gpg_error_t
567 cmd_listsecretkeys (assuan_context_t ctx, char *line)
568 {
569   return do_listkeys (ctx, line, 2);
570 }
571
572
573 \f
574 /* GENKEY
575
576    Read the parameters in native format from the input fd and create a
577    new OpenPGP key.
578  */
579 static gpg_error_t
580 cmd_genkey (assuan_context_t ctx, char *line)
581 {
582   (void)ctx;
583   (void)line;
584   return gpg_error (GPG_ERR_NOT_SUPPORTED);
585 }
586
587
588 /* GETINFO <what>
589
590    Multipurpose function to return a variety of information.
591    Supported values for WHAT are:
592
593      version     - Return the version of the program.
594      pid         - Return the process id of the server.
595
596  */
597 static gpg_error_t
598 cmd_getinfo (assuan_context_t ctx, char *line)
599 {
600   int rc;
601
602   if (!strcmp (line, "version"))
603     {
604       const char *s = VERSION;
605       rc = assuan_send_data (ctx, s, strlen (s));
606     }
607   else if (!strcmp (line, "pid"))
608     {
609       char numbuf[50];
610
611       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
612       rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
613     }
614   else
615     rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
616   return rc;
617 }
618
619 static const char hlp_passwd[] =
620   "PASSWD <userID>\n"
621   "\n"
622   "Change the passphrase of the secret key for USERID.";
623 static gpg_error_t
624 cmd_passwd (assuan_context_t ctx, char *line)
625 {
626   /* ctrl_t ctrl = assuan_get_pointer (ctx); */
627   gpg_error_t err;
628
629   (void)ctx;
630   line = skip_options (line);
631
632   err = gpg_error (GPG_ERR_NOT_SUPPORTED);
633
634   return err;
635 }
636
637
638
639 \f
640 /* Helper to register our commands with libassuan. */
641 static int
642 register_commands (assuan_context_t ctx)
643 {
644   static struct
645   {
646     const char *name;
647     assuan_handler_t handler;
648     const char * const help;
649   } table[] = {
650     { "RECIPIENT",     cmd_recipient },
651     { "SIGNER",        cmd_signer    },
652     { "ENCRYPT",       cmd_encrypt   },
653     { "DECRYPT",       cmd_decrypt   },
654     { "VERIFY",        cmd_verify    },
655     { "SIGN",          cmd_sign      },
656     { "IMPORT",        cmd_import    },
657     { "EXPORT",        cmd_export    },
658     { "INPUT",         NULL          },
659     { "OUTPUT",        NULL          },
660     { "MESSAGE",       cmd_message   },
661     { "LISTKEYS",      cmd_listkeys  },
662     { "LISTSECRETKEYS",cmd_listsecretkeys },
663     { "GENKEY",        cmd_genkey    },
664     { "DELKEYS",       cmd_delkeys   },
665     { "GETINFO",       cmd_getinfo   },
666     { "PASSWD",        cmd_passwd,  hlp_passwd},
667     { NULL }
668   };
669   int i, rc;
670
671   for (i=0; table[i].name; i++)
672     {
673       rc = assuan_register_command (ctx, table[i].name,
674                                     table[i].handler, table[i].help);
675       if (rc)
676         return rc;
677     }
678   return 0;
679 }
680
681
682
683 \f
684 /* Startup the server.  CTRL must have been allocated by the caller
685    and set to the default values. */
686 int
687 gpg_server (ctrl_t ctrl)
688 {
689   int rc;
690 #ifndef HAVE_W32_SYSTEM
691   int filedes[2];
692 #endif
693   assuan_context_t ctx = NULL;
694   static const char hello[] = ("GNU Privacy Guard's OpenPGP server "
695                                VERSION " ready");
696
697   /* We use a pipe based server so that we can work from scripts.
698      assuan_init_pipe_server will automagically detect when we are
699      called with a socketpair and ignore FILEDES in this case.  */
700 #ifndef HAVE_W32_SYSTEM
701   filedes[0] = assuan_fdopen (0);
702   filedes[1] = assuan_fdopen (1);
703 #endif
704   rc = assuan_new (&ctx);
705   if (rc)
706     {
707       log_error ("failed to allocate the assuan context: %s\n",
708                  gpg_strerror (rc));
709       goto leave;
710     }
711
712 #ifdef HAVE_W32_SYSTEM
713   rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
714 #else
715   rc = assuan_init_pipe_server (ctx, filedes);
716 #endif
717   if (rc)
718     {
719       log_error ("failed to initialize the server: %s\n", gpg_strerror (rc));
720       goto leave;
721     }
722
723   rc = register_commands (ctx);
724   if (rc)
725     {
726       log_error ("failed to the register commands with Assuan: %s\n",
727                  gpg_strerror(rc));
728       goto leave;
729     }
730
731   assuan_set_pointer (ctx, ctrl);
732   if (opt.verbose || opt.debug)
733     {
734       char *tmp = NULL;
735       const char *s1 = getenv (GPG_AGENT_INFO_NAME);
736
737       tmp = xtryasprintf ("Home: %s\n"
738                           "Config: %s\n"
739                           "AgentInfo: %s\n"
740                           "%s",
741                           opt.homedir,
742                           "fixme: need config filename",
743                           s1?s1:"[not set]",
744                           hello);
745       if (tmp)
746         {
747           assuan_set_hello_line (ctx, tmp);
748           xfree (tmp);
749         }
750     }
751   else
752     assuan_set_hello_line (ctx, hello);
753   assuan_register_reset_notify (ctx, reset_notify);
754   assuan_register_input_notify (ctx, input_notify);
755   assuan_register_output_notify (ctx, output_notify);
756   assuan_register_option_handler (ctx, option_handler);
757
758   ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
759   if (!ctrl->server_local)
760     {
761       rc = gpg_error_from_syserror ();
762       goto leave;
763     }
764   ctrl->server_local->assuan_ctx = ctx;
765   ctrl->server_local->message_fd = GNUPG_INVALID_FD;
766
767   for (;;)
768     {
769       rc = assuan_accept (ctx);
770       if (rc == -1)
771         {
772           rc = 0;
773           break;
774         }
775       else if (rc)
776         {
777           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
778           break;
779         }
780
781       rc = assuan_process (ctx);
782       if (rc)
783         {
784           log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
785           continue;
786         }
787     }
788
789  leave:
790   if (ctrl->server_local)
791     {
792       release_pk_list (ctrl->server_local->recplist);
793
794       xfree (ctrl->server_local);
795       ctrl->server_local = NULL;
796     }
797   assuan_release (ctx);
798   return rc;
799 }
800
801
802 /* Helper to notify the client about Pinentry events.  Because that
803    might disturb some older clients, this is only done when enabled
804    via an option.  If it is not enabled we tell Windows to allow
805    setting the foreground window right here.  Returns an gpg error
806    code. */
807 gpg_error_t
808 gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
809 {
810   if (!ctrl || !ctrl->server_local
811       || !ctrl->server_local->allow_pinentry_notify)
812     {
813       gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
814       /* Client might be interested in that event - send as status line.  */
815       if (!strncmp (line, "PINENTRY_LAUNCHED", 17)
816           && (line[17]==' '||!line[17]))
817         {
818           for (line += 17; *line && spacep (line); line++)
819             ;
820           write_status_text (STATUS_PINENTRY_LAUNCHED, line);
821         }
822       return 0;
823     }
824   return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
825 }