399a5d3697d4b9471cd064a6d7d702e7b84fbfde
[gnupg.git] / tools / gpg-connect-agent.c
1 /* gpg-connect-agent.c - Tool to connect to the agent.
2  *      Copyright (C) 2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assuan.h>
28
29 #include "i18n.h"
30 #include "../common/util.h"
31 #include "../common/asshelp.h"
32
33
34
35 /* Constants to identify the commands and options. */
36 enum cmd_and_opt_values
37   {
38     aNull = 0,
39     oQuiet      = 'q',
40     oVerbose    = 'v',
41
42     oNoVerbose  = 500,
43     oHomedir
44
45   };
46
47
48 /* The list of commands and options. */
49 static ARGPARSE_OPTS opts[] =
50   {
51     { 301, NULL, 0, N_("@\nOptions:\n ") },
52     
53     { oVerbose, "verbose",  0, N_("verbose") },
54     { oQuiet, "quiet",      0, N_("quiet") },
55
56     /* hidden options */
57     { oNoVerbose, "no-verbose",  0, "@"},
58     { oHomedir, "homedir", 2, "@" },   
59     {0}
60   };
61
62
63 /* We keep all global options in the structure OPT.  */
64 struct
65 {
66   int verbose;          /* Verbosity level.  */
67   int quiet;            /* Be extra quiet.  */
68   const char *homedir;  /* Configuration directory name */
69
70 } opt;
71
72
73 /*-- local prototypes --*/
74 static int read_and_print_response (assuan_context_t ctx);
75 static assuan_context_t start_agent (void);
76
77
78
79 \f
80 /* Print usage information and and provide strings for help. */
81 static const char *
82 my_strusage( int level )
83 {
84   const char *p;
85
86   switch (level)
87     {
88     case 11: p = "gpg-connect-agent (GnuPG)";
89       break;
90     case 13: p = VERSION; break;
91     case 17: p = PRINTABLE_OS_NAME; break;
92     case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
93       break;
94     case 1:
95     case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)");
96       break;
97     case 41:
98       p = _("Syntax: gpg-connect-agent [options]\n"
99             "Connect to a running agent and send commands\n");
100       break;
101     case 31: p = "\nHome: "; break;
102     case 32: p = opt.homedir; break;
103     case 33: p = "\n"; break;
104
105     default: p = NULL; break;
106     }
107   return p;
108 }
109
110
111 /* Initialize the gettext system. */
112 static void
113 i18n_init(void)
114 {
115 #ifdef USE_SIMPLE_GETTEXT
116   set_gettext_file (PACKAGE_GT);
117 #else
118 # ifdef ENABLE_NLS
119   setlocale (LC_ALL, "" );
120   bindtextdomain (PACKAGE_GT, LOCALEDIR);
121   textdomain (PACKAGE_GT);
122 # endif
123 #endif
124 }
125
126
127 /* gpg-connect-agent's entry point. */
128 int
129 main (int argc, char **argv)
130 {
131   ARGPARSE_ARGS pargs;
132   const char *fname;
133   int no_more_options = 0;
134   assuan_context_t ctx;
135   char *line;
136   size_t linesize;
137   int rc;
138
139   set_strusage (my_strusage);
140   log_set_prefix ("gpg-connect-agent", 1);
141
142   i18n_init();
143
144   opt.homedir = default_homedir ();
145
146   /* Parse the command line. */
147   pargs.argc  = &argc;
148   pargs.argv  = &argv;
149   pargs.flags =  1;  /* Do not remove the args.  */
150   while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
151     {
152       switch (pargs.r_opt)
153         {
154         case oQuiet:     opt.quiet = 1; break;
155         case oVerbose:   opt.verbose++; break;
156         case oNoVerbose: opt.verbose = 0; break;
157         case oHomedir:   opt.homedir = pargs.r.ret_str; break;
158
159         default: pargs.err = 2; break;
160         }
161     }
162
163   if (log_get_errorcount (0))
164     exit (2);
165   
166   fname = argc ? *argv : NULL;
167
168   ctx = start_agent ();
169   line = NULL;
170   linesize = 0;
171   for (;;)
172     {
173       int n;
174       size_t maxlength;
175
176       maxlength = 2048;
177       n = read_line (stdin, &line, &linesize, &maxlength);
178       if (n < 0)
179         {
180           log_error (_("error reading input: %s\n"), strerror (errno));
181           exit (1);
182         }
183       if (!n)
184         break; /* EOF */
185       if (!maxlength)
186         {
187           log_error (_("line too long - skipped\n"));
188           continue;
189         }
190       if (memchr (line, 0, n))
191         log_info (_("line shortened due to embedded Nul character\n"));
192       if (line[n-1] == '\n')
193         line[n-1] = 0;
194       rc = assuan_write_line (ctx, line);
195       if (rc)
196         {
197           log_info (_("sending line failed: %s\n"), assuan_strerror (rc) );
198           continue;
199         }
200       if (*line == '#' || !*line)
201         continue; /* Don't expect a response for a coment line. */
202
203       rc = read_and_print_response (ctx);
204       if (rc)
205         log_info (_("receiving line failed: %s\n"), assuan_strerror (rc) );
206     }
207
208   if (opt.verbose)
209     log_info ("closing connection to agent\n");
210   
211   return 0; 
212 }
213
214
215 /* Read all response lines from server and print them.  Returns 0 on
216    success or an assuan error code. */
217 static int
218 read_and_print_response (assuan_context_t ctx)
219 {
220   char *line;
221   int linelen;
222   assuan_error_t rc;
223
224   for (;;)
225     {
226       do 
227         {
228           rc = assuan_read_line (ctx, &line, &linelen);
229           if (rc)
230             return rc;
231         }    
232       while (*line == '#' || !linelen);
233
234       if (linelen >= 1
235           && line[0] == 'D' && line[1] == ' ')
236         {
237           fwrite (line, linelen, 1, stdout);
238           putchar ('\n');
239         }
240       else if (linelen >= 1
241                && line[0] == 'S' 
242                && (line[1] == '\0' || line[1] == ' '))
243         {
244           fwrite (line, linelen, 1, stdout);
245           putchar ('\n');
246         }  
247       else if (linelen >= 2
248                && line[0] == 'O' && line[1] == 'K'
249                && (line[2] == '\0' || line[2] == ' '))
250         {
251           fwrite (line, linelen, 1, stdout);
252           putchar ('\n');
253           return 0;
254         }
255       else if (linelen >= 3
256                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
257                && (line[3] == '\0' || line[3] == ' '))
258         {
259           fwrite (line, linelen, 1, stdout);
260           putchar ('\n');
261           return 0;
262         }  
263       else if (linelen >= 7
264                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
265                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
266                && line[6] == 'E' 
267                && (line[7] == '\0' || line[7] == ' '))
268         {
269           fwrite (line, linelen, 1, stdout);
270           putchar ('\n');
271           return 0;
272         }
273       else if (linelen >= 3
274                && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
275                && (line[3] == '\0' || line[3] == ' '))
276         {
277           fwrite (line, linelen, 1, stdout);
278           putchar ('\n');
279           /* Received from server, thus more responses are expected.  */
280         }
281       else
282         return ASSUAN_Invalid_Response;
283     }
284 }
285
286
287
288
289 /* Connect to teh agebnt and send the standard options.  */
290 static assuan_context_t
291 start_agent (void)
292 {
293   int rc = 0;
294   char *infostr, *p;
295   assuan_context_t ctx;
296
297   infostr = getenv ("GPG_AGENT_INFO");
298   if (!infostr || !*infostr)
299     {
300       char *sockname;
301
302       /* Check whether we can connect at the standard socket.  */
303       sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
304       rc = assuan_socket_connect (&ctx, sockname, 0);
305       xfree (sockname);
306     }
307   else
308     {
309       int prot;
310       int pid;
311
312       infostr = xstrdup (infostr);
313       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
314         {
315           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
316           xfree (infostr);
317           exit (1);
318         }
319       *p++ = 0;
320       pid = atoi (p);
321       while (*p && *p != ':')
322         p++;
323       prot = *p? atoi (p+1) : 0;
324       if (prot != 1)
325         {
326           log_error (_("gpg-agent protocol version %d is not supported\n"),
327                      prot);
328           xfree (infostr);
329           exit (1);
330         }
331
332       rc = assuan_socket_connect (&ctx, infostr, pid);
333       xfree (infostr);
334     }
335
336   if (rc)
337     {
338       log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
339       exit (1);
340     }
341
342   if (opt.verbose)
343     log_info ("connection to agent established\n");
344
345   rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
346   if (rc)
347     {
348       log_error (_("error sending %s command: %s\n"), "RESET", 
349                  assuan_strerror (rc));
350       exit (1);
351     }
352
353   rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
354                                   NULL, NULL, NULL, NULL, NULL);
355   if (rc)
356     {
357       log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
358       exit (1);
359     }
360
361   return ctx;
362 }