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