Changed to GPLv3.
[gnupg.git] / common / asshelp.c
1 /* asshelp.c - Helper functions for Assuan
2  * Copyright (C) 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 <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #ifdef HAVE_LOCALE_H
27 #include <locale.h>
28 #endif
29
30 #include "i18n.h"
31 #include "util.h"
32 #include "exechelp.h"
33 #include "sysutils.h"
34 #include "errors.h"  /* FIXME: This one conatisn only status code - rename it*/
35 #include "asshelp.h"
36
37
38 static gpg_error_t
39 send_one_option (assuan_context_t ctx, gpg_err_source_t errsource,
40                  const char *name, const char *value)
41 {
42   gpg_error_t err;
43   char *optstr;
44
45   if (!value || !*value)
46     err = 0;  /* Avoid sending empty strings.  */
47   else if (asprintf (&optstr, "OPTION %s=%s", name, value ) < 0)
48     err = gpg_error_from_syserror ();
49   else
50     {
51       err = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL);
52       free (optstr);
53     }
54
55   return err;
56 }
57
58
59 /* Send the assuan commands pertaining to the pinenry environment.  The
60    OPT_* arguments are optional and may be used to override the
61    defaults taken from the current locale. */
62 gpg_error_t
63 send_pinentry_environment (assuan_context_t ctx,
64                            gpg_err_source_t errsource,
65                            const char *opt_display,
66                            const char *opt_ttyname,
67                            const char *opt_ttytype,
68                            const char *opt_lc_ctype,
69                            const char *opt_lc_messages)
70 {
71   gpg_error_t err = 0;
72   char *dft_display = NULL;
73   char *dft_ttyname = NULL;
74   char *dft_ttytype = NULL;
75   char *old_lc = NULL; 
76   char *dft_lc = NULL;
77
78   /* Send the DISPLAY variable.  */
79   dft_display = getenv ("DISPLAY");
80   if (opt_display || dft_display)
81     {
82       err = send_one_option (ctx, errsource, "display", 
83                              opt_display ? opt_display : dft_display);
84       if (err)
85         return err;
86     }
87
88   /* Send the name of the TTY.  */
89   if (!opt_ttyname)
90     {
91       dft_ttyname = getenv ("GPG_TTY");
92       if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
93         dft_ttyname = ttyname (0);
94     }
95   if (opt_ttyname || dft_ttyname)
96     {
97       err = send_one_option (ctx, errsource, "ttyname", 
98                              opt_ttyname ? opt_ttyname : dft_ttyname);
99       if (err)
100         return err;
101     }
102
103   /* Send the type of the TTY.  */
104   dft_ttytype = getenv ("TERM");
105   if (opt_ttytype || (dft_ttyname && dft_ttytype))
106     {
107       err = send_one_option (ctx, errsource, "ttytype", 
108                              opt_ttyname ? opt_ttytype : dft_ttytype);
109       if (err)
110         return err;
111     }
112
113   /* Send the value for LC_CTYPE.  */
114 #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
115   old_lc = setlocale (LC_CTYPE, NULL);
116   if (old_lc)
117     {
118       old_lc = strdup (old_lc);
119       if (!old_lc)
120         return gpg_error_from_syserror ();
121     }
122   dft_lc = setlocale (LC_CTYPE, "");
123 #endif
124   if (opt_lc_ctype || (dft_ttyname && dft_lc))
125     {
126       err = send_one_option (ctx, errsource, "lc-ctype", 
127                              opt_lc_ctype ? opt_lc_ctype : dft_lc);
128     }
129 #if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
130   if (old_lc)
131     {
132       setlocale (LC_CTYPE, old_lc);
133       free (old_lc);
134     }
135 #endif
136   if (err)
137     return err;
138
139   /* Send the value for LC_MESSAGES.  */
140 #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
141   old_lc = setlocale (LC_MESSAGES, NULL);
142   if (old_lc)
143     {
144       old_lc = strdup (old_lc);
145       if (!old_lc)
146         return gpg_error_from_syserror ();
147     }
148   dft_lc = setlocale (LC_MESSAGES, "");
149 #endif
150   if (opt_lc_messages || (dft_ttyname && dft_lc))
151     {
152       err = send_one_option (ctx, errsource, "lc-messages", 
153                              opt_lc_messages ? opt_lc_messages : dft_lc);
154     }
155 #if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
156   if (old_lc)
157     {
158       setlocale (LC_MESSAGES, old_lc);
159       free (old_lc);
160     }
161 #endif
162   if (err)
163     return err;
164
165   return 0;
166 }
167
168
169 /* Try to connect to the agent via socket or fork it off and work by
170    pipes.  Handle the server's initial greeting.  Returns a new assuan
171    context at R_CTX or an error code. */
172 gpg_error_t
173 start_new_gpg_agent (assuan_context_t *r_ctx,
174                      gpg_err_source_t errsource,
175                      const char *homedir,
176                      const char *agent_program,
177                      const char *opt_display,
178                      const char *opt_ttyname,
179                      const char *opt_ttytype,
180                      const char *opt_lc_ctype,
181                      const char *opt_lc_messages,
182                      int verbose, int debug,
183                      gpg_error_t (*status_cb)(ctrl_t, int, ...),
184                      ctrl_t status_cb_arg)
185 {
186   /* If we ever failed to connect via a socket we will force the use
187      of the pipe based server for the lifetime of the process.  */
188   static int force_pipe_server = 0;
189
190   gpg_error_t rc = 0;
191   char *infostr, *p;
192   assuan_context_t ctx;
193
194   *r_ctx = NULL;
195
196  restart:
197   infostr = force_pipe_server? NULL : getenv ("GPG_AGENT_INFO");
198   if (!infostr || !*infostr)
199     {
200       char *sockname;
201
202       /* First check whether we can connect at the standard
203          socket.  */
204       sockname = make_filename (homedir, "S.gpg-agent", NULL);
205       rc = assuan_socket_connect (&ctx, sockname, 0);
206
207       if (rc)
208         {
209           /* With no success start a new server.  */
210           if (verbose)
211             log_info (_("no running gpg-agent - starting one\n"));
212           
213           if (status_cb)
214             status_cb (status_cb_arg, STATUS_PROGRESS, 
215                        "starting_agent ? 0 0", NULL);
216           
217           if (fflush (NULL))
218             {
219               gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
220               log_error ("error flushing pending output: %s\n",
221                          strerror (errno));
222               xfree (sockname);
223               return tmperr;
224             }
225           
226           if (!agent_program || !*agent_program)
227             agent_program = gnupg_module_name (GNUPG_MODULE_NAME_AGENT);
228
229 #ifdef HAVE_W32_SYSTEM
230           {
231             /* Under Windows we start the server in daemon mode.  This
232                is because the default is to use the standard socket
233                and thus there is no need for the GPG_AGENT_INFO
234                envvar.  This is possible as we don't have a real unix
235                domain socket but use a plain file and thus there is no
236                need to care about non-local file systems. */
237             const char *argv[3];
238
239             argv[0] = "--daemon";
240             argv[1] = "--use-standard-socket"; 
241             argv[2] = NULL;  
242
243             rc = gnupg_spawn_process_detached (agent_program, argv, NULL);
244             if (rc)
245               log_debug ("failed to start agent `%s': %s\n",
246                          agent_program, gpg_strerror (rc));
247             else
248               {
249                 /* Give the agent some time to prepare itself. */
250                 gnupg_sleep (3);
251                 /* Now try again to connect the agent.  */
252                 rc = assuan_socket_connect (&ctx, sockname, 0);
253               }
254           }
255 #else /*!HAVE_W32_SYSTEM*/
256           {
257             const char *pgmname;
258             const char *argv[3];
259             int no_close_list[3];
260             int i;
261
262             if ( !(pgmname = strrchr (agent_program, '/')))
263               pgmname = agent_program;
264             else
265               pgmname++;
266             
267             argv[0] = pgmname;
268             argv[1] = "--server";
269             argv[2] = NULL;
270             
271             i=0;
272             if (log_get_fd () != -1)
273               no_close_list[i++] = log_get_fd ();
274             no_close_list[i++] = fileno (stderr);
275             no_close_list[i] = -1;
276             
277             /* Connect to the agent and perform initial handshaking. */
278             rc = assuan_pipe_connect (&ctx, agent_program, argv,
279                                       no_close_list);
280           }
281 #endif /*!HAVE_W32_SYSTEM*/
282         }
283       xfree (sockname);
284     }
285   else
286     {
287       int prot;
288       int pid;
289
290       infostr = xstrdup (infostr);
291       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
292         {
293           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
294           xfree (infostr);
295           force_pipe_server = 1;
296           goto restart;
297         }
298       *p++ = 0;
299       pid = atoi (p);
300       while (*p && *p != PATHSEP_C)
301         p++;
302       prot = *p? atoi (p+1) : 0;
303       if (prot != 1)
304         {
305           log_error (_("gpg-agent protocol version %d is not supported\n"),
306                      prot);
307           xfree (infostr);
308           force_pipe_server = 1;
309           goto restart;
310         }
311
312       rc = assuan_socket_connect (&ctx, infostr, pid);
313       xfree (infostr);
314       if (gpg_err_code (rc) == GPG_ERR_ASS_CONNECT_FAILED)
315         {
316           log_info (_("can't connect to the agent - trying fall back\n"));
317           force_pipe_server = 1;
318           goto restart;
319         }
320     }
321
322   if (rc)
323     {
324       log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
325       return gpg_error (GPG_ERR_NO_AGENT);
326     }
327
328   if (debug)
329     log_debug ("connection to agent established\n");
330
331   rc = assuan_transact (ctx, "RESET",
332                         NULL, NULL, NULL, NULL, NULL, NULL);
333   if (!rc)
334     rc = send_pinentry_environment (ctx, errsource,
335                                     opt_display, opt_ttyname, opt_ttytype,
336                                     opt_lc_ctype, opt_lc_messages);
337   if (rc)
338     {
339       assuan_disconnect (ctx);
340       return rc;
341     }
342
343   *r_ctx = ctx;
344   return 0;
345 }
346