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