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