2007-05-19 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / tools / gpg-connect-agent.c
blob98a13de11e93ae9284a8659b64a7ecddab3bfca2
1 /* gpg-connect-agent.c - Tool to connect to the agent.
2 * Copyright (C) 2005, 2007 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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.
22 #include <config.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <assuan.h>
31 #include "i18n.h"
32 #include "../common/util.h"
33 #include "../common/asshelp.h"
37 /* Constants to identify the commands and options. */
38 enum cmd_and_opt_values
40 aNull = 0,
41 oQuiet = 'q',
42 oVerbose = 'v',
43 oRawSocket = 'S',
44 oExec = 'E',
46 oNoVerbose = 500,
47 oHomedir,
48 oHex,
49 oDecode,
50 oNoExtConnect
55 /* The list of commands and options. */
56 static ARGPARSE_OPTS opts[] =
58 { 301, NULL, 0, N_("@\nOptions:\n ") },
60 { oVerbose, "verbose", 0, N_("verbose") },
61 { oQuiet, "quiet", 0, N_("quiet") },
62 { oHex, "hex", 0, N_("print data out hex encoded") },
63 { oDecode,"decode", 0, N_("decode received data lines") },
64 { oRawSocket, "raw-socket", 2, N_("|NAME|connect to Assuan socket NAME")},
65 { oExec, "exec", 0, N_("run the Assuan server given on the command line")},
66 { oNoExtConnect, "no-ext-connect",
67 0, N_("do not use extended connect mode")},
69 /* hidden options */
70 { oNoVerbose, "no-verbose", 0, "@"},
71 { oHomedir, "homedir", 2, "@" },
72 {0}
76 /* We keep all global options in the structure OPT. */
77 struct
79 int verbose; /* Verbosity level. */
80 int quiet; /* Be extra quiet. */
81 const char *homedir; /* Configuration directory name */
82 int hex; /* Print data lines in hex format. */
83 int decode; /* Decode received data lines. */
84 const char *raw_socket; /* Name of socket to connect in raw mode. */
85 int exec; /* Run the pgm given on the command line. */
86 unsigned int connect_flags; /* Flags used for connecting. */
87 } opt;
91 /* Definitions for /definq commands and a global linked list with all
92 the definitions. */
93 struct definq_s
95 struct definq_s *next;
96 char *name; /* Name of inquiry or NULL for any name. */
97 int is_prog; /* True if this is a program to run. */
98 char file[1]; /* Name of file or program. */
100 typedef struct definq_s *definq_t;
102 static definq_t definq_list;
103 static definq_t *definq_list_tail = &definq_list;
107 /*-- local prototypes --*/
108 static int read_and_print_response (assuan_context_t ctx);
109 static assuan_context_t start_agent (void);
114 /* Print usage information and and provide strings for help. */
115 static const char *
116 my_strusage( int level )
118 const char *p;
120 switch (level)
122 case 11: p = "gpg-connect-agent (GnuPG)";
123 break;
124 case 13: p = VERSION; break;
125 case 17: p = PRINTABLE_OS_NAME; break;
126 case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
127 break;
128 case 1:
129 case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)");
130 break;
131 case 41:
132 p = _("Syntax: gpg-connect-agent [options]\n"
133 "Connect to a running agent and send commands\n");
134 break;
135 case 31: p = "\nHome: "; break;
136 case 32: p = opt.homedir; break;
137 case 33: p = "\n"; break;
139 default: p = NULL; break;
141 return p;
145 /* Initialize the gettext system. */
146 static void
147 i18n_init(void)
149 #ifdef USE_SIMPLE_GETTEXT
150 set_gettext_file (PACKAGE_GT);
151 #else
152 # ifdef ENABLE_NLS
153 setlocale (LC_ALL, "" );
154 bindtextdomain (PACKAGE_GT, LOCALEDIR);
155 textdomain (PACKAGE_GT);
156 # endif
157 #endif
160 /* Store an inquire response pattern. Note, that this function may
161 change the content of LINE. We assume that leading white spaces
162 are already removed. */
163 static void
164 add_definq (char *line, int is_prog)
166 definq_t d;
167 char *name, *p;
169 /* Get name. */
170 name = line;
171 for (p=name; *p && !spacep (p); p++)
173 if (*p)
174 *p++ = 0;
175 while (spacep (p))
176 p++;
178 d = xmalloc (sizeof *d + strlen (p) );
179 strcpy (d->file, p);
180 d->is_prog = is_prog;
181 if ( !strcmp (name, "*"))
182 d->name = NULL;
183 else
184 d->name = xstrdup (name);
186 d->next = NULL;
187 *definq_list_tail = d;
188 definq_list_tail = &d->next;
192 /* Show all inquiry defintions. */
193 static void
194 show_definq (void)
196 definq_t d;
198 for (d=definq_list; d; d = d->next)
199 if (d->name)
200 printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file);
201 for (d=definq_list; d; d = d->next)
202 if (!d->name)
203 printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file);
207 /* Clear all inquiry definitions. */
208 static void
209 clear_definq (void)
211 while (definq_list)
213 definq_t tmp = definq_list->next;
214 xfree (definq_list->name);
215 xfree (definq_list);
216 definq_list = tmp;
218 definq_list_tail = &definq_list;
222 static void
223 do_sendfd (assuan_context_t ctx, char *line)
225 FILE *fp;
226 char *name, *mode, *p;
227 int rc, fd;
229 /* Get file name. */
230 name = line;
231 for (p=name; *p && !spacep (p); p++)
233 if (*p)
234 *p++ = 0;
235 while (spacep (p))
236 p++;
238 /* Get mode. */
239 mode = p;
240 if (!*mode)
241 mode = "r";
242 else
244 for (p=mode; *p && !spacep (p); p++)
246 if (*p)
247 *p++ = 0;
250 /* Open and send. */
251 fp = fopen (name, mode);
252 if (!fp)
254 log_error ("can't open `%s' in \"%s\" mode: %s\n",
255 name, mode, strerror (errno));
256 return;
258 fd = fileno (fp);
260 if (opt.verbose)
261 log_error ("file `%s' opened in \"%s\" mode, fd=%d\n",
262 name, mode, fd);
264 rc = assuan_sendfd (ctx, fd);
265 if (rc)
266 log_error ("sednig descriptor %d failed: %s\n", fd, gpg_strerror (rc));
267 fclose (fp);
271 static void
272 do_recvfd (assuan_context_t ctx, char *line)
274 log_info ("This command has not yet been implemented\n");
279 /* gpg-connect-agent's entry point. */
281 main (int argc, char **argv)
283 ARGPARSE_ARGS pargs;
284 int no_more_options = 0;
285 assuan_context_t ctx;
286 char *line, *p;
287 size_t linesize;
288 int rc;
290 set_strusage (my_strusage);
291 log_set_prefix ("gpg-connect-agent", 1);
292 assuan_set_assuan_err_source (0);
294 i18n_init();
296 opt.homedir = default_homedir ();
297 opt.connect_flags = 1; /* Use extended connect mode. */
299 /* Parse the command line. */
300 pargs.argc = &argc;
301 pargs.argv = &argv;
302 pargs.flags = 1; /* Do not remove the args. */
303 while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
305 switch (pargs.r_opt)
307 case oQuiet: opt.quiet = 1; break;
308 case oVerbose: opt.verbose++; break;
309 case oNoVerbose: opt.verbose = 0; break;
310 case oHomedir: opt.homedir = pargs.r.ret_str; break;
311 case oHex: opt.hex = 1; break;
312 case oDecode: opt.decode = 1; break;
313 case oRawSocket: opt.raw_socket = pargs.r.ret_str; break;
314 case oExec: opt.exec = 1; break;
315 case oNoExtConnect: opt.connect_flags &= ~(1); break;
317 default: pargs.err = 2; break;
321 if (log_get_errorcount (0))
322 exit (2);
324 if (opt.exec)
326 if (!argc)
328 log_error (_("option \"%s\" requires a program "
329 "and optional arguments\n"), "--exec" );
330 exit (1);
333 else if (argc)
334 usage (1);
336 if (opt.exec && opt.raw_socket)
337 log_info (_("option \"%s\" ignored due to \"%s\"\n"),
338 "--raw-socket", "--exec");
340 if (opt.exec)
342 int no_close[3];
344 no_close[0] = fileno (stderr);
345 no_close[1] = log_get_fd ();
346 no_close[2] = -1;
347 rc = assuan_pipe_connect_ext (&ctx, *argv, (const char **)argv,
348 no_close, NULL, NULL,
349 opt.connect_flags);
350 if (rc)
352 log_error ("assuan_pipe_connect_ext failed: %s\n",
353 gpg_strerror (rc));
354 exit (1);
357 if (opt.verbose)
358 log_info ("server `%s' started\n", *argv);
361 else if (opt.raw_socket)
363 rc = assuan_socket_connect_ext (&ctx, opt.raw_socket, 0,
364 opt.connect_flags);
365 if (rc)
367 log_error ("can't connect to socket `%s': %s\n",
368 opt.raw_socket, gpg_strerror (rc));
369 exit (1);
372 if (opt.verbose)
373 log_info ("connection to socket `%s' established\n", opt.raw_socket);
375 else
376 ctx = start_agent ();
377 line = NULL;
378 linesize = 0;
379 for (;;)
381 int n;
382 size_t maxlength;
384 maxlength = 2048;
385 n = read_line (stdin, &line, &linesize, &maxlength);
386 if (n < 0)
388 log_error (_("error reading input: %s\n"), strerror (errno));
389 exit (1);
391 if (!n)
392 break; /* EOF */
393 if (!maxlength)
395 log_error (_("line too long - skipped\n"));
396 continue;
398 if (memchr (line, 0, n))
399 log_info (_("line shortened due to embedded Nul character\n"));
400 if (line[n-1] == '\n')
401 line[n-1] = 0;
402 if (*line == '/')
404 /* Handle control commands. */
405 char *cmd = line+1;
407 for (p=cmd; *p && !spacep (p); p++)
409 if (*p)
410 *p++ = 0;
411 while (spacep (p))
412 p++;
413 if (!strcmp (cmd, "definqfile"))
415 add_definq (p, 0);
417 else if (!strcmp (cmd, "definqprog"))
419 add_definq (p, 1);
421 else if (!strcmp (cmd, "showdef"))
423 show_definq ();
425 else if (!strcmp (cmd, "cleardef"))
427 clear_definq ();
429 else if (!strcmp (cmd, "echo"))
431 puts (p);
433 else if (!strcmp (cmd, "sendfd"))
435 do_sendfd (ctx, p);
436 continue;
438 else if (!strcmp (cmd, "recvfd"))
440 do_recvfd (ctx, p);
441 continue;
443 else if (!strcmp (cmd, "hex"))
444 opt.hex = 1;
445 else if (!strcmp (cmd, "nohex"))
446 opt.hex = 0;
447 else if (!strcmp (cmd, "decode"))
448 opt.decode = 1;
449 else if (!strcmp (cmd, "nodecode"))
450 opt.decode = 0;
451 else if (!strcmp (cmd, "help"))
453 puts (
454 "Available commands:\n"
455 "/echo ARGS Echo ARGS.\n"
456 "/definqfile NAME FILE\n"
457 " Use content of FILE for inquiries with NAME.\n"
458 " NAME may be \"*\" to match any inquiry.\n"
459 "/definqprog NAME PGM\n"
460 " Run PGM for inquiries matching NAME and pass the\n"
461 " entire line to it as arguments.\n"
462 "/showdef Print all definitions.\n"
463 "/cleardef Delete all definitions.\n"
464 "/sendfd FILE MODE Open FILE and pass descriptor to server.\n"
465 "/recvfd Receive FD from server and print. \n"
466 "/[no]hex Enable hex dumping of received data lines.\n"
467 "/[no]decode Enable decoding of received data lines.\n"
468 "/help Print this help.");
470 else
471 log_error (_("unknown command `%s'\n"), cmd );
473 continue;
476 rc = assuan_write_line (ctx, line);
477 if (rc)
479 log_info (_("sending line failed: %s\n"), gpg_strerror (rc) );
480 continue;
482 if (*line == '#' || !*line)
483 continue; /* Don't expect a response for a comment line. */
485 rc = read_and_print_response (ctx);
486 if (rc)
487 log_info (_("receiving line failed: %s\n"), gpg_strerror (rc) );
490 if (opt.verbose)
491 log_info ("closing connection to agent\n");
493 return 0;
497 /* Handle an Inquire from the server. Return False if it could not be
498 handled; in this case the caller shll complete the operation. LINE
499 is the complete line as received from the server. This function
500 may change the content of LINE. */
501 static int
502 handle_inquire (assuan_context_t ctx, char *line)
504 const char *name;
505 definq_t d;
506 FILE *fp;
507 char buffer[1024];
508 int rc, n;
510 /* Skip the command and trailing spaces. */
511 for (; *line && !spacep (line); line++)
513 while (spacep (line))
514 line++;
515 /* Get the name. */
516 name = line;
517 for (; *line && !spacep (line); line++)
519 if (*line)
520 *line++ = 0;
522 /* Now match it against our list. he second loop is todetect the
523 match all entry. **/
524 for (d=definq_list; d; d = d->next)
525 if (d->name && !strcmp (d->name, name))
526 break;
527 if (!d)
528 for (d=definq_list; d; d = d->next)
529 if (!d->name)
530 break;
531 if (!d)
533 if (opt.verbose)
534 log_info ("no handler for inquiry `%s' found\n", name);
535 return 0;
538 if (d->is_prog)
540 fp = popen (d->file, "r");
541 if (!fp)
542 log_error ("error executing `%s': %s\n", d->file, strerror (errno));
543 else if (opt.verbose)
544 log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
546 else
548 fp = fopen (d->file, "rb");
549 if (!fp)
550 log_error ("error opening `%s': %s\n", d->file, strerror (errno));
551 else if (opt.verbose)
552 log_error ("handling inquiry `%s' by returning content of `%s'\n",
553 name, d->file);
555 if (!fp)
556 return 0;
558 while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
560 rc = assuan_send_data (ctx, buffer, n);
561 if (rc)
563 log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
564 break;
567 if (ferror (fp))
568 log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
570 rc = assuan_send_data (ctx, NULL, 0);
571 if (rc)
572 log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
574 if (d->is_prog)
576 if (pclose (fp))
577 log_error ("error running `%s': %s\n", d->file, strerror (errno));
579 else
580 fclose (fp);
581 return 1;
585 /* Read all response lines from server and print them. Returns 0 on
586 success or an assuan error code. */
587 static int
588 read_and_print_response (assuan_context_t ctx)
590 char *line;
591 size_t linelen;
592 assuan_error_t rc;
593 int i, j;
594 int need_lf = 0;
596 for (;;)
600 rc = assuan_read_line (ctx, &line, &linelen);
601 if (rc)
602 return rc;
604 if (opt.verbose > 1 && *line == '#')
606 fwrite (line, linelen, 1, stdout);
607 putchar ('\n');
610 while (*line == '#' || !linelen);
612 if (linelen >= 1
613 && line[0] == 'D' && line[1] == ' ')
615 if (opt.hex)
617 for (i=2; i < linelen; )
619 int save_i = i;
621 printf ("D[%04X] ", i-2);
622 for (j=0; j < 16 ; j++, i++)
624 if (j == 8)
625 putchar (' ');
626 if (i < linelen)
627 printf (" %02X", ((unsigned char*)line)[i]);
628 else
629 fputs (" ", stdout);
631 fputs (" ", stdout);
632 i= save_i;
633 for (j=0; j < 16; j++, i++)
635 unsigned int c = ((unsigned char*)line)[i];
636 if ( i >= linelen )
637 putchar (' ');
638 else if (isascii (c) && isprint (c) && !iscntrl (c))
639 putchar (c);
640 else
641 putchar ('.');
643 putchar ('\n');
646 else if (opt.decode)
648 const unsigned char *s;
649 int need_d = 1;
650 int c = 0;
652 for (j=2, s=(unsigned char*)line+2; j < linelen; j++, s++ )
654 if (need_d)
656 fputs ("D ", stdout);
657 need_d = 0;
659 if (*s == '%' && j+2 < linelen)
661 s++; j++;
662 c = xtoi_2 ( s );
663 s++; j++;
665 else
666 c = *s;
667 if (c == '\n')
668 need_d = 1;
669 putchar (c);
671 need_lf = (c != '\n');
673 else
675 fwrite (line, linelen, 1, stdout);
676 putchar ('\n');
679 else
681 if (need_lf)
683 putchar ('\n');
684 need_lf = 0;
687 if (linelen >= 1
688 && line[0] == 'S'
689 && (line[1] == '\0' || line[1] == ' '))
691 fwrite (line, linelen, 1, stdout);
692 putchar ('\n');
694 else if (linelen >= 2
695 && line[0] == 'O' && line[1] == 'K'
696 && (line[2] == '\0' || line[2] == ' '))
698 fwrite (line, linelen, 1, stdout);
699 putchar ('\n');
700 return 0;
702 else if (linelen >= 3
703 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
704 && (line[3] == '\0' || line[3] == ' '))
706 fwrite (line, linelen, 1, stdout);
707 putchar ('\n');
708 return 0;
710 else if (linelen >= 7
711 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
712 && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
713 && line[6] == 'E'
714 && (line[7] == '\0' || line[7] == ' '))
716 fwrite (line, linelen, 1, stdout);
717 putchar ('\n');
718 if (!handle_inquire (ctx, line))
719 assuan_write_line (ctx, "CANCEL");
721 else if (linelen >= 3
722 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
723 && (line[3] == '\0' || line[3] == ' '))
725 fwrite (line, linelen, 1, stdout);
726 putchar ('\n');
727 /* Received from server, thus more responses are expected. */
729 else
730 return gpg_error (GPG_ERR_ASS_INV_RESPONSE);
738 /* Connect to the agent and send the standard options. */
739 static assuan_context_t
740 start_agent (void)
742 int rc = 0;
743 char *infostr, *p;
744 assuan_context_t ctx;
746 infostr = getenv ("GPG_AGENT_INFO");
747 if (!infostr || !*infostr)
749 char *sockname;
751 /* Check whether we can connect at the standard socket. */
752 sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
753 rc = assuan_socket_connect (&ctx, sockname, 0);
754 xfree (sockname);
756 else
758 int prot;
759 int pid;
761 infostr = xstrdup (infostr);
762 if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
764 log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
765 xfree (infostr);
766 exit (1);
768 *p++ = 0;
769 pid = atoi (p);
770 while (*p && *p != PATHSEP_C)
771 p++;
772 prot = *p? atoi (p+1) : 0;
773 if (prot != 1)
775 log_error (_("gpg-agent protocol version %d is not supported\n"),
776 prot);
777 xfree (infostr);
778 exit (1);
781 rc = assuan_socket_connect (&ctx, infostr, pid);
782 xfree (infostr);
785 if (rc)
787 log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
788 exit (1);
791 if (opt.verbose)
792 log_info ("connection to agent established\n");
794 rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
795 if (rc)
797 log_error (_("error sending %s command: %s\n"), "RESET",
798 gpg_strerror (rc));
799 exit (1);
802 rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
803 NULL, NULL, NULL, NULL, NULL);
804 if (rc)
806 log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
807 exit (1);
810 return ctx;