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