Moved 1.9 branch to trunk
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <assuan.h>
30
31 #include "i18n.h"
32 #include "../common/util.h"
33 #include "../common/asshelp.h"
34
35
36
37 /* Constants to identify the commands and options. */
38 enum cmd_and_opt_values
39   {
40     aNull = 0,
41     oQuiet      = 'q',
42     oVerbose    = 'v',
43     oRawSocket  = 'S',
44
45     oNoVerbose  = 500,
46     oHomedir,
47     oHex
48
49   };
50
51
52 /* The list of commands and options. */
53 static ARGPARSE_OPTS opts[] =
54   {
55     { 301, NULL, 0, N_("@\nOptions:\n ") },
56     
57     { oVerbose, "verbose",  0, N_("verbose") },
58     { oQuiet, "quiet",      0, N_("quiet") },
59     { oHex,   "hex",        0, N_("print data out hex encoded") },
60     { oRawSocket, "raw-socket", 2, N_("|NAME|connect to Assuan socket NAME")},
61
62     /* hidden options */
63     { oNoVerbose, "no-verbose",  0, "@"},
64     { oHomedir, "homedir", 2, "@" },   
65     {0}
66   };
67
68
69 /* We keep all global options in the structure OPT.  */
70 struct
71 {
72   int verbose;          /* Verbosity level.  */
73   int quiet;            /* Be extra quiet.  */
74   const char *homedir;  /* Configuration directory name */
75   int hex;              /* Print data lines in hex format. */
76   const char *raw_socket; /* Name of socket to connect in raw mode. */
77 } opt;
78
79
80
81 /* Definitions for /definq commands and a global linked list with all
82    the definitions. */
83 struct definq_s
84 {
85   struct definq_s *next;
86   char *name;     /* Name of inquiry or NULL for any name. */
87   int is_prog;     /* True if this is a program to run. */
88   char file[1];   /* Name of file or program. */
89 };
90 typedef struct definq_s *definq_t;
91
92 static definq_t definq_list;
93 static definq_t *definq_list_tail = &definq_list;
94
95
96
97 /*-- local prototypes --*/
98 static int read_and_print_response (assuan_context_t ctx);
99 static assuan_context_t start_agent (void);
100
101
102
103 \f
104 /* Print usage information and and provide strings for help. */
105 static const char *
106 my_strusage( int level )
107 {
108   const char *p;
109
110   switch (level)
111     {
112     case 11: p = "gpg-connect-agent (GnuPG)";
113       break;
114     case 13: p = VERSION; break;
115     case 17: p = PRINTABLE_OS_NAME; break;
116     case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
117       break;
118     case 1:
119     case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)");
120       break;
121     case 41:
122       p = _("Syntax: gpg-connect-agent [options]\n"
123             "Connect to a running agent and send commands\n");
124       break;
125     case 31: p = "\nHome: "; break;
126     case 32: p = opt.homedir; break;
127     case 33: p = "\n"; break;
128
129     default: p = NULL; break;
130     }
131   return p;
132 }
133
134
135 /* Initialize the gettext system. */
136 static void
137 i18n_init(void)
138 {
139 #ifdef USE_SIMPLE_GETTEXT
140   set_gettext_file (PACKAGE_GT);
141 #else
142 # ifdef ENABLE_NLS
143   setlocale (LC_ALL, "" );
144   bindtextdomain (PACKAGE_GT, LOCALEDIR);
145   textdomain (PACKAGE_GT);
146 # endif
147 #endif
148 }
149
150 /* Store an inquire response pattern.  Note, that this function may
151    change the content of LINE.  We assume that leading white spaces
152    are already removed. */
153 static void
154 add_definq (char *line, int is_prog)
155 {
156   definq_t d;
157   char *name, *p;
158
159   /* Get name. */
160   name = line;
161   for (p=name; *p && !spacep (p); p++)
162     ;
163   if (*p)
164     *p++ = 0;
165   while (spacep (p))
166     p++;
167
168   d = xmalloc (sizeof *d + strlen (p) );
169   strcpy (d->file, p);
170   d->is_prog = is_prog;
171   if ( !strcmp (name, "*"))
172     d->name = NULL;
173   else
174     d->name = xstrdup (name);
175
176   d->next = NULL;
177   *definq_list_tail = d;
178   definq_list_tail = &d->next;
179 }
180
181
182 /* Show all inquiry defintions. */
183 static void
184 show_definq (void)
185 {
186   definq_t d;
187
188   for (d=definq_list; d; d = d->next)
189     if (d->name)
190       printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file);
191   for (d=definq_list; d; d = d->next)
192     if (!d->name)
193       printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file);
194 }
195
196
197 /* Clear all inquiry definitions. */
198 static void
199 clear_definq (void)
200 {
201   while (definq_list)
202     { 
203       definq_t tmp = definq_list->next;
204       xfree (definq_list->name);
205       xfree (definq_list);
206       definq_list = tmp;
207     }
208   definq_list_tail = &definq_list;
209 }      
210
211
212
213 /* gpg-connect-agent's entry point. */
214 int
215 main (int argc, char **argv)
216 {
217   ARGPARSE_ARGS pargs;
218   const char *fname;
219   int no_more_options = 0;
220   assuan_context_t ctx;
221   char *line, *p;
222   size_t linesize;
223   int rc;
224
225   set_strusage (my_strusage);
226   log_set_prefix ("gpg-connect-agent", 1);
227
228   i18n_init();
229
230   opt.homedir = default_homedir ();
231
232   /* Parse the command line. */
233   pargs.argc  = &argc;
234   pargs.argv  = &argv;
235   pargs.flags =  1;  /* Do not remove the args.  */
236   while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
237     {
238       switch (pargs.r_opt)
239         {
240         case oQuiet:     opt.quiet = 1; break;
241         case oVerbose:   opt.verbose++; break;
242         case oNoVerbose: opt.verbose = 0; break;
243         case oHomedir:   opt.homedir = pargs.r.ret_str; break;
244         case oHex:       opt.hex = 1; break;
245         case oRawSocket: opt.raw_socket = pargs.r.ret_str; break;
246
247         default: pargs.err = 2; break;
248         }
249     }
250
251   if (log_get_errorcount (0))
252     exit (2);
253   
254   fname = argc ? *argv : NULL;
255
256   if (opt.raw_socket)
257     {
258       rc = assuan_socket_connect (&ctx, opt.raw_socket, 0);
259       if (rc)
260         {
261           log_error ("can't connect to socket `%s': %s\n",
262                      opt.raw_socket, assuan_strerror (rc));
263           exit (1);
264         }
265
266       if (opt.verbose)
267         log_info ("connection to socket `%s' established\n", opt.raw_socket);
268     }
269   else
270     ctx = start_agent ();
271   line = NULL;
272   linesize = 0;
273   for (;;)
274     {
275       int n;
276       size_t maxlength;
277
278       maxlength = 2048;
279       n = read_line (stdin, &line, &linesize, &maxlength);
280       if (n < 0)
281         {
282           log_error (_("error reading input: %s\n"), strerror (errno));
283           exit (1);
284         }
285       if (!n)
286         break; /* EOF */
287       if (!maxlength)
288         {
289           log_error (_("line too long - skipped\n"));
290           continue;
291         }
292       if (memchr (line, 0, n))
293         log_info (_("line shortened due to embedded Nul character\n"));
294       if (line[n-1] == '\n')
295         line[n-1] = 0;
296       if (*line == '/')
297         {
298           /* Handle control commands. */
299           char *cmd = line+1;
300
301           for (p=cmd; *p && !spacep (p); p++)
302             ;
303           if (*p)
304             *p++ = 0;
305           while (spacep (p))
306             p++;
307           if (!strcmp (cmd, "definqfile"))
308             {
309               add_definq (p, 0);
310             }
311           else if (!strcmp (cmd, "definqprog"))
312             {
313               add_definq (p, 1);
314             }
315           else if (!strcmp (cmd, "showdef"))
316             {
317               show_definq ();
318             }
319           else if (!strcmp (cmd, "cleardef"))
320             {
321               clear_definq ();
322             }
323           else if (!strcmp (cmd, "echo"))
324             {
325               puts (p);
326             }
327           else if (!strcmp (cmd, "help"))
328             {
329               puts ("Available commands:\n"
330                     "/echo ARGS             Echo ARGS.\n"
331                     "/definqfile NAME FILE\n"
332                     "    Use content of FILE for inquiries with NAME.\n"
333                     "    NAME may be \"*\" to match any inquiry.\n"
334                     "/definqprog NAME PGM\n"
335                     "    Run PGM for inquiries matching NAME and pass the\n"
336                     "    entire line to it as arguments.\n"
337                     "/showdef               Print all definitions.\n"
338                     "/cleardef              Delete all definitions.\n"
339                     "/help                  Print this help.");
340             }
341           else
342             log_error (_("unknown command `%s'\n"), cmd );
343       
344           continue;
345         }
346       
347       rc = assuan_write_line (ctx, line);
348       if (rc)
349         {
350           log_info (_("sending line failed: %s\n"), assuan_strerror (rc) );
351           continue;
352         }
353       if (*line == '#' || !*line)
354         continue; /* Don't expect a response for a coment line. */
355
356       rc = read_and_print_response (ctx);
357       if (rc)
358         log_info (_("receiving line failed: %s\n"), assuan_strerror (rc) );
359     }
360
361   if (opt.verbose)
362     log_info ("closing connection to agent\n");
363   
364   return 0; 
365 }
366
367
368 /* Handle an Inquire from the server.  Return False if it could not be
369    handled; in this case the caller shll complete the operation.  LINE
370    is the complete line as received from the server.  This function
371    may change the content of LINE. */
372 static int
373 handle_inquire (assuan_context_t ctx, char *line)
374 {
375   const char *name;
376   definq_t d;
377   FILE *fp;
378   char buffer[1024];
379   int rc, n;
380
381   /* Skip the command and trailing spaces. */
382   for (; *line && !spacep (line); line++)
383     ;
384   while (spacep (line))
385     line++;
386   /* Get the name. */
387   name = line;
388   for (; *line && !spacep (line); line++)
389     ;
390   if (*line)
391     *line++ = 0;
392
393   /* Now match it against our list. he second loop is todetect the
394      match all entry. **/
395   for (d=definq_list; d; d = d->next)
396     if (d->name && !strcmp (d->name, name))
397         break;
398   if (!d)
399     for (d=definq_list; d; d = d->next)
400       if (!d->name)
401         break;
402   if (!d)
403     {
404       if (opt.verbose)
405         log_info ("no handler for inquiry `%s' found\n", name);
406       return 0;
407     }
408
409   if (d->is_prog)
410     {
411       fp = popen (d->file, "r");
412       if (!fp)
413         log_error ("error executing `%s': %s\n", d->file, strerror (errno));
414       else if (opt.verbose)
415         log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
416     }
417   else
418     {
419       fp = fopen (d->file, "rb");
420       if (!fp)
421         log_error ("error opening `%s': %s\n", d->file, strerror (errno));
422       else if (opt.verbose)
423         log_error ("handling inquiry `%s' by returning content of `%s'\n",
424                    name, d->file);
425     }
426   if (!fp)
427     return 0;
428
429   while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
430     {
431       rc = assuan_send_data (ctx, buffer, n);
432       if (rc)
433         {
434           log_error ("sending data back failed: %s\n", assuan_strerror (rc) );
435           break;
436         }
437     }
438   if (ferror (fp))
439     log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
440
441   rc = assuan_send_data (ctx, NULL, 0);
442   if (rc)
443     log_error ("sending data back failed: %s\n", assuan_strerror (rc) );
444
445   if (d->is_prog)
446     {
447       if (pclose (fp))
448         log_error ("error running `%s': %s\n", d->file, strerror (errno));
449     }
450   else
451     fclose (fp);
452   return 1;
453 }
454
455
456 /* Read all response lines from server and print them.  Returns 0 on
457    success or an assuan error code. */
458 static int
459 read_and_print_response (assuan_context_t ctx)
460 {
461   char *line;
462   size_t linelen;
463   assuan_error_t rc;
464   int i, j;
465
466   for (;;)
467     {
468       do 
469         {
470           rc = assuan_read_line (ctx, &line, &linelen);
471           if (rc)
472             return rc;
473         }    
474       while (*line == '#' || !linelen);
475
476       if (linelen >= 1
477           && line[0] == 'D' && line[1] == ' ')
478         {
479           if (opt.hex)
480             {
481               for (i=2; i < linelen; )
482                 {
483                   int save_i = i;
484
485                   printf ("D[%04X] ", i-2);
486                   for (j=0; j < 16 ; j++, i++)
487                     {
488                       if (j == 8)
489                         putchar (' ');
490                       if (i < linelen)
491                         printf (" %02X", ((unsigned char*)line)[i]);
492                       else
493                         fputs ("   ", stdout);
494                     }
495                   fputs ("   ", stdout);
496                   i= save_i;
497                   for (j=0; j < 16; j++, i++)
498                     {
499                       unsigned int c = ((unsigned char*)line)[i];
500                       if ( i >= linelen )
501                         putchar (' ');
502                       else if (isascii (c) && isprint (c) && !iscntrl (c))
503                         putchar (c);
504                       else
505                         putchar ('.');
506                     }
507                   putchar ('\n');
508                 }
509             }
510           else
511             {
512               fwrite (line, linelen, 1, stdout);
513               putchar ('\n');
514             }
515         }
516       else if (linelen >= 1
517                && line[0] == 'S' 
518                && (line[1] == '\0' || line[1] == ' '))
519         {
520           fwrite (line, linelen, 1, stdout);
521           putchar ('\n');
522         }  
523       else if (linelen >= 2
524                && line[0] == 'O' && line[1] == 'K'
525                && (line[2] == '\0' || line[2] == ' '))
526         {
527           fwrite (line, linelen, 1, stdout);
528           putchar ('\n');
529           return 0;
530         }
531       else if (linelen >= 3
532                && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
533                && (line[3] == '\0' || line[3] == ' '))
534         {
535           fwrite (line, linelen, 1, stdout);
536           putchar ('\n');
537           return 0;
538         }  
539       else if (linelen >= 7
540                && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
541                && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
542                && line[6] == 'E' 
543                && (line[7] == '\0' || line[7] == ' '))
544         {
545           fwrite (line, linelen, 1, stdout);
546           putchar ('\n');
547           if (!handle_inquire (ctx, line))
548             assuan_write_line (ctx, "CANCEL");
549         }
550       else if (linelen >= 3
551                && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
552                && (line[3] == '\0' || line[3] == ' '))
553         {
554           fwrite (line, linelen, 1, stdout);
555           putchar ('\n');
556           /* Received from server, thus more responses are expected.  */
557         }
558       else
559         return ASSUAN_Invalid_Response;
560     }
561 }
562
563
564
565
566 /* Connect to the agent and send the standard options.  */
567 static assuan_context_t
568 start_agent (void)
569 {
570   int rc = 0;
571   char *infostr, *p;
572   assuan_context_t ctx;
573
574   infostr = getenv ("GPG_AGENT_INFO");
575   if (!infostr || !*infostr)
576     {
577       char *sockname;
578
579       /* Check whether we can connect at the standard socket.  */
580       sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
581       rc = assuan_socket_connect (&ctx, sockname, 0);
582       xfree (sockname);
583     }
584   else
585     {
586       int prot;
587       int pid;
588
589       infostr = xstrdup (infostr);
590       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
591         {
592           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
593           xfree (infostr);
594           exit (1);
595         }
596       *p++ = 0;
597       pid = atoi (p);
598       while (*p && *p != PATHSEP_C)
599         p++;
600       prot = *p? atoi (p+1) : 0;
601       if (prot != 1)
602         {
603           log_error (_("gpg-agent protocol version %d is not supported\n"),
604                      prot);
605           xfree (infostr);
606           exit (1);
607         }
608
609       rc = assuan_socket_connect (&ctx, infostr, pid);
610       xfree (infostr);
611     }
612
613   if (rc)
614     {
615       log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
616       exit (1);
617     }
618
619   if (opt.verbose)
620     log_info ("connection to agent established\n");
621
622   rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
623   if (rc)
624     {
625       log_error (_("error sending %s command: %s\n"), "RESET", 
626                  assuan_strerror (rc));
627       exit (1);
628     }
629
630   rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
631                                   NULL, NULL, NULL, NULL, NULL);
632   if (rc)
633     {
634       log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
635       exit (1);
636     }
637
638   return ctx;
639 }