/* gpg-connect-agent.c - Tool to connect to the agent. * Copyright (C) 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include "i18n.h" #include "../common/util.h" #include "../common/asshelp.h" /* Constants to identify the commands and options. */ enum cmd_and_opt_values { aNull = 0, oQuiet = 'q', oVerbose = 'v', oNoVerbose = 500, oHomedir, oHex }; /* The list of commands and options. */ static ARGPARSE_OPTS opts[] = { { 301, NULL, 0, N_("@\nOptions:\n ") }, { oVerbose, "verbose", 0, N_("verbose") }, { oQuiet, "quiet", 0, N_("quiet") }, { oHex, "hex", 0, N_("print data out hex encoded") }, /* hidden options */ { oNoVerbose, "no-verbose", 0, "@"}, { oHomedir, "homedir", 2, "@" }, {0} }; /* We keep all global options in the structure OPT. */ struct { int verbose; /* Verbosity level. */ int quiet; /* Be extra quiet. */ const char *homedir; /* Configuration directory name */ int hex; /* Print data lines in hex format. */ } opt; /*-- local prototypes --*/ static int read_and_print_response (assuan_context_t ctx); static assuan_context_t start_agent (void); /* Print usage information and and provide strings for help. */ static const char * my_strusage( int level ) { const char *p; switch (level) { case 11: p = "gpg-connect-agent (GnuPG)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n"); break; case 1: case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)"); break; case 41: p = _("Syntax: gpg-connect-agent [options]\n" "Connect to a running agent and send commands\n"); break; case 31: p = "\nHome: "; break; case 32: p = opt.homedir; break; case 33: p = "\n"; break; default: p = NULL; break; } return p; } /* Initialize the gettext system. */ static void i18n_init(void) { #ifdef USE_SIMPLE_GETTEXT set_gettext_file (PACKAGE_GT); #else # ifdef ENABLE_NLS setlocale (LC_ALL, "" ); bindtextdomain (PACKAGE_GT, LOCALEDIR); textdomain (PACKAGE_GT); # endif #endif } /* gpg-connect-agent's entry point. */ int main (int argc, char **argv) { ARGPARSE_ARGS pargs; const char *fname; int no_more_options = 0; assuan_context_t ctx; char *line; size_t linesize; int rc; set_strusage (my_strusage); log_set_prefix ("gpg-connect-agent", 1); i18n_init(); opt.homedir = default_homedir (); /* Parse the command line. */ pargs.argc = &argc; pargs.argv = &argv; pargs.flags = 1; /* Do not remove the args. */ while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts)) { switch (pargs.r_opt) { case oQuiet: opt.quiet = 1; break; case oVerbose: opt.verbose++; break; case oNoVerbose: opt.verbose = 0; break; case oHomedir: opt.homedir = pargs.r.ret_str; break; case oHex: opt.hex = 1; break; default: pargs.err = 2; break; } } if (log_get_errorcount (0)) exit (2); fname = argc ? *argv : NULL; ctx = start_agent (); line = NULL; linesize = 0; for (;;) { int n; size_t maxlength; maxlength = 2048; n = read_line (stdin, &line, &linesize, &maxlength); if (n < 0) { log_error (_("error reading input: %s\n"), strerror (errno)); exit (1); } if (!n) break; /* EOF */ if (!maxlength) { log_error (_("line too long - skipped\n")); continue; } if (memchr (line, 0, n)) log_info (_("line shortened due to embedded Nul character\n")); if (line[n-1] == '\n') line[n-1] = 0; rc = assuan_write_line (ctx, line); if (rc) { log_info (_("sending line failed: %s\n"), assuan_strerror (rc) ); continue; } if (*line == '#' || !*line) continue; /* Don't expect a response for a coment line. */ rc = read_and_print_response (ctx); if (rc) log_info (_("receiving line failed: %s\n"), assuan_strerror (rc) ); } if (opt.verbose) log_info ("closing connection to agent\n"); return 0; } /* Read all response lines from server and print them. Returns 0 on success or an assuan error code. */ static int read_and_print_response (assuan_context_t ctx) { char *line; int linelen; assuan_error_t rc; int i, j; for (;;) { do { rc = assuan_read_line (ctx, &line, &linelen); if (rc) return rc; } while (*line == '#' || !linelen); if (linelen >= 1 && line[0] == 'D' && line[1] == ' ') { if (opt.hex) { for (i=2; i < linelen; ) { int save_i = i; printf ("D[%04X] ", i-2); for (j=0; j < 16 ; j++, i++) { if (j == 8) putchar (' '); if (i < linelen) printf (" %02X", ((unsigned char*)line)[i]); else fputs (" ", stdout); } fputs (" ", stdout); i= save_i; for (j=0; j < 16; j++, i++) { unsigned int c = ((unsigned char*)line)[i]; if ( i >= linelen ) putchar (' '); else if (isascii (c) && isprint (c) && !iscntrl (c)) putchar (c); else putchar ('.'); } putchar ('\n'); } } else { fwrite (line, linelen, 1, stdout); putchar ('\n'); } } else if (linelen >= 1 && line[0] == 'S' && (line[1] == '\0' || line[1] == ' ')) { fwrite (line, linelen, 1, stdout); putchar ('\n'); } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { fwrite (line, linelen, 1, stdout); putchar ('\n'); return 0; } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { fwrite (line, linelen, 1, stdout); putchar ('\n'); return 0; } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { fwrite (line, linelen, 1, stdout); putchar ('\n'); return 0; } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (line[3] == '\0' || line[3] == ' ')) { fwrite (line, linelen, 1, stdout); putchar ('\n'); /* Received from server, thus more responses are expected. */ } else return ASSUAN_Invalid_Response; } } /* Connect to the agent and send the standard options. */ static assuan_context_t start_agent (void) { int rc = 0; char *infostr, *p; assuan_context_t ctx; infostr = getenv ("GPG_AGENT_INFO"); if (!infostr || !*infostr) { char *sockname; /* Check whether we can connect at the standard socket. */ sockname = make_filename (opt.homedir, "S.gpg-agent", NULL); rc = assuan_socket_connect (&ctx, sockname, 0); xfree (sockname); } else { int prot; int pid; infostr = xstrdup (infostr); if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr) { log_error (_("malformed GPG_AGENT_INFO environment variable\n")); xfree (infostr); exit (1); } *p++ = 0; pid = atoi (p); while (*p && *p != PATHSEP_C) p++; prot = *p? atoi (p+1) : 0; if (prot != 1) { log_error (_("gpg-agent protocol version %d is not supported\n"), prot); xfree (infostr); exit (1); } rc = assuan_socket_connect (&ctx, infostr, pid); xfree (infostr); } if (rc) { log_error ("can't connect to the agent: %s\n", assuan_strerror (rc)); exit (1); } if (opt.verbose) log_info ("connection to agent established\n"); rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL); if (rc) { log_error (_("error sending %s command: %s\n"), "RESET", assuan_strerror (rc)); exit (1); } rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT, NULL, NULL, NULL, NULL, NULL); if (rc) { log_error (_("error sending standard options: %s\n"), gpg_strerror (rc)); exit (1); } return ctx; }