First steps towards supporting W32.
[gnupg.git] / tools / gpg-connect-agent.c
1 /* gpg-connect-agent.c - Tool to connect to the agent.
2  *      Copyright (C) 2005, 2007 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     oExec       = 'E',
45
46     oNoVerbose  = 500,
47     oHomedir,
48     oHex,
49     oDecode,
50     oNoExtConnect
51
52   };
53
54
55 /* The list of commands and options. */
56 static ARGPARSE_OPTS opts[] =
57   {
58     { 301, NULL, 0, N_("@\nOptions:\n ") },
59     
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")},
68
69     /* hidden options */
70     { oNoVerbose, "no-verbose",  0, "@"},
71     { oHomedir, "homedir", 2, "@" },   
72     {0}
73   };
74
75
76 /* We keep all global options in the structure OPT.  */
77 struct
78 {
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;
88
89
90
91 /* Definitions for /definq commands and a global linked list with all
92    the definitions. */
93 struct definq_s
94 {
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. */
99 };
100 typedef struct definq_s *definq_t;
101
102 static definq_t definq_list;
103 static definq_t *definq_list_tail = &definq_list;
104
105
106
107 /*-- local prototypes --*/
108 static int read_and_print_response (assuan_context_t ctx);
109 static assuan_context_t start_agent (void);
110
111
112
113 \f
114 /* Print usage information and and provide strings for help. */
115 static const char *
116 my_strusage( int level )
117 {
118   const char *p;
119
120   switch (level)
121     {
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;
138
139     default: p = NULL; break;
140     }
141   return p;
142 }
143
144
145 /* Store an inquire response pattern.  Note, that this function may
146    change the content of LINE.  We assume that leading white spaces
147    are already removed. */
148 static void
149 add_definq (char *line, int is_prog)
150 {
151   definq_t d;
152   char *name, *p;
153
154   /* Get name. */
155   name = line;
156   for (p=name; *p && !spacep (p); p++)
157     ;
158   if (*p)
159     *p++ = 0;
160   while (spacep (p))
161     p++;
162
163   d = xmalloc (sizeof *d + strlen (p) );
164   strcpy (d->file, p);
165   d->is_prog = is_prog;
166   if ( !strcmp (name, "*"))
167     d->name = NULL;
168   else
169     d->name = xstrdup (name);
170
171   d->next = NULL;
172   *definq_list_tail = d;
173   definq_list_tail = &d->next;
174 }
175
176
177 /* Show all inquiry defintions. */
178 static void
179 show_definq (void)
180 {
181   definq_t d;
182
183   for (d=definq_list; d; d = d->next)
184     if (d->name)
185       printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file);
186   for (d=definq_list; d; d = d->next)
187     if (!d->name)
188       printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file);
189 }
190
191
192 /* Clear all inquiry definitions. */
193 static void
194 clear_definq (void)
195 {
196   while (definq_list)
197     { 
198       definq_t tmp = definq_list->next;
199       xfree (definq_list->name);
200       xfree (definq_list);
201       definq_list = tmp;
202     }
203   definq_list_tail = &definq_list;
204 }      
205
206
207 static void
208 do_sendfd (assuan_context_t ctx, char *line)
209 {
210   FILE *fp;
211   char *name, *mode, *p;
212   int rc, fd;
213
214   /* Get file name. */
215   name = line;
216   for (p=name; *p && !spacep (p); p++)
217     ;
218   if (*p)
219     *p++ = 0;
220   while (spacep (p))
221     p++;
222
223   /* Get mode.  */
224   mode = p;
225   if (!*mode)
226     mode = "r";
227   else
228     {
229       for (p=mode; *p && !spacep (p); p++)
230         ;
231       if (*p)
232         *p++ = 0;
233     }
234
235   /* Open and send. */
236   fp = fopen (name, mode);
237   if (!fp)
238     {
239       log_error ("can't open `%s' in \"%s\" mode: %s\n",
240                  name, mode, strerror (errno));
241       return;
242     }
243   fd = fileno (fp);
244
245   if (opt.verbose)
246     log_error ("file `%s' opened in \"%s\" mode, fd=%d\n",
247                name, mode, fd);
248
249   rc = assuan_sendfd (ctx, fd);
250   if (rc)
251     log_error ("sednig  descriptor %d failed: %s\n", fd, gpg_strerror (rc));
252   fclose (fp);
253 }
254
255
256 static void
257 do_recvfd (assuan_context_t ctx, char *line)
258 {
259   log_info ("This command has not yet been implemented\n");
260 }
261
262
263
264 /* gpg-connect-agent's entry point. */
265 int
266 main (int argc, char **argv)
267 {
268   ARGPARSE_ARGS pargs;
269   int no_more_options = 0;
270   assuan_context_t ctx;
271   char *line, *p;
272   size_t linesize;
273   int rc;
274
275   set_strusage (my_strusage);
276   log_set_prefix ("gpg-connect-agent", 1);
277   assuan_set_assuan_err_source (0);
278
279   i18n_init();
280
281   opt.homedir = default_homedir ();
282   opt.connect_flags = 1; /* Use extended connect mode.  */
283
284   /* Parse the command line. */
285   pargs.argc  = &argc;
286   pargs.argv  = &argv;
287   pargs.flags =  1;  /* Do not remove the args.  */
288   while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
289     {
290       switch (pargs.r_opt)
291         {
292         case oQuiet:     opt.quiet = 1; break;
293         case oVerbose:   opt.verbose++; break;
294         case oNoVerbose: opt.verbose = 0; break;
295         case oHomedir:   opt.homedir = pargs.r.ret_str; break;
296         case oHex:       opt.hex = 1; break;
297         case oDecode:    opt.decode = 1; break;
298         case oRawSocket: opt.raw_socket = pargs.r.ret_str; break;
299         case oExec:      opt.exec = 1; break;
300         case oNoExtConnect: opt.connect_flags &= ~(1); break;
301
302         default: pargs.err = 2; break;
303         }
304     }
305
306   if (log_get_errorcount (0))
307     exit (2);
308
309   if (opt.exec)
310     {
311       if (!argc)
312         {
313           log_error (_("option \"%s\" requires a program "
314                        "and optional arguments\n"), "--exec" );
315           exit (1);
316         }
317     }
318   else if (argc)
319     usage (1);
320
321   if (opt.exec && opt.raw_socket)
322     log_info (_("option \"%s\" ignored due to \"%s\"\n"),
323               "--raw-socket", "--exec");
324
325   if (opt.exec)
326     {
327       int no_close[3];
328
329       no_close[0] = fileno (stderr);
330       no_close[1] = log_get_fd ();
331       no_close[2] = -1;
332       rc = assuan_pipe_connect_ext (&ctx, *argv, (const char **)argv,
333                                     no_close, NULL, NULL,
334                                     opt.connect_flags);
335       if (rc)
336         {
337           log_error ("assuan_pipe_connect_ext failed: %s\n",
338                      gpg_strerror (rc));
339           exit (1);
340         }
341
342       if (opt.verbose)
343         log_info ("server `%s' started\n", *argv);
344
345     }
346   else if (opt.raw_socket)
347     {
348       rc = assuan_socket_connect_ext (&ctx, opt.raw_socket, 0,
349                                       opt.connect_flags);
350       if (rc)
351         {
352           log_error ("can't connect to socket `%s': %s\n",
353                      opt.raw_socket, gpg_strerror (rc));
354           exit (1);
355         }
356
357       if (opt.verbose)
358         log_info ("connection to socket `%s' established\n", opt.raw_socket);
359     }
360   else
361     ctx = start_agent ();
362   line = NULL;
363   linesize = 0;
364   for (;;)
365     {
366       int n;
367       size_t maxlength;
368
369       maxlength = 2048;
370       n = read_line (stdin, &line, &linesize, &maxlength);
371       if (n < 0)
372         {
373           log_error (_("error reading input: %s\n"), strerror (errno));
374           exit (1);
375         }
376       if (!n)
377         break; /* EOF */
378       if (!maxlength)
379         {
380           log_error (_("line too long - skipped\n"));
381           continue;
382         }
383       if (memchr (line, 0, n))
384         log_info (_("line shortened due to embedded Nul character\n"));
385       if (line[n-1] == '\n')
386         line[n-1] = 0;
387       if (*line == '/')
388         {
389           /* Handle control commands. */
390           char *cmd = line+1;
391
392           for (p=cmd; *p && !spacep (p); p++)
393             ;
394           if (*p)
395             *p++ = 0;
396           while (spacep (p))
397             p++;
398           if (!strcmp (cmd, "definqfile"))
399             {
400               add_definq (p, 0);
401             }
402           else if (!strcmp (cmd, "definqprog"))
403             {
404               add_definq (p, 1);
405             }
406           else if (!strcmp (cmd, "showdef"))
407             {
408               show_definq ();
409             }
410           else if (!strcmp (cmd, "cleardef"))
411             {
412               clear_definq ();
413             }
414           else if (!strcmp (cmd, "echo"))
415             {
416               puts (p);
417             }
418           else if (!strcmp (cmd, "sendfd"))
419             {
420               do_sendfd (ctx, p);
421               continue;
422             }
423           else if (!strcmp (cmd, "recvfd"))
424             {
425               do_recvfd (ctx, p);
426               continue;
427             }
428           else if (!strcmp (cmd, "hex"))
429             opt.hex = 1;
430           else if (!strcmp (cmd, "nohex"))
431             opt.hex = 0;
432           else if (!strcmp (cmd, "decode"))
433             opt.decode = 1;
434           else if (!strcmp (cmd, "nodecode"))
435             opt.decode = 0;
436           else if (!strcmp (cmd, "help"))
437             {
438               puts (
439 "Available commands:\n"
440 "/echo ARGS             Echo ARGS.\n"
441 "/definqfile NAME FILE\n"
442 "    Use content of FILE for inquiries with NAME.\n"
443 "    NAME may be \"*\" to match any inquiry.\n"
444 "/definqprog NAME PGM\n"
445 "    Run PGM for inquiries matching NAME and pass the\n"
446 "    entire line to it as arguments.\n"
447 "/showdef               Print all definitions.\n"
448 "/cleardef              Delete all definitions.\n"
449 "/sendfd FILE MODE      Open FILE and pass descriptor to server.\n"
450 "/recvfd                Receive FD from server and print. \n"
451 "/[no]hex               Enable hex dumping of received data lines.\n"
452 "/[no]decode            Enable decoding of received data lines.\n"
453 "/help                  Print this help.");
454             }
455           else
456             log_error (_("unknown command `%s'\n"), cmd );
457       
458           continue;
459         }
460       
461       rc = assuan_write_line (ctx, line);
462       if (rc)
463         {
464           log_info (_("sending line failed: %s\n"), gpg_strerror (rc) );
465           continue;
466         }
467       if (*line == '#' || !*line)
468         continue; /* Don't expect a response for a comment line. */
469
470       rc = read_and_print_response (ctx);
471       if (rc)
472         log_info (_("receiving line failed: %s\n"), gpg_strerror (rc) );
473     }
474
475   if (opt.verbose)
476     log_info ("closing connection to agent\n");
477   
478   return 0; 
479 }
480
481
482 /* Handle an Inquire from the server.  Return False if it could not be
483    handled; in this case the caller shll complete the operation.  LINE
484    is the complete line as received from the server.  This function
485    may change the content of LINE. */
486 static int
487 handle_inquire (assuan_context_t ctx, char *line)
488 {
489   const char *name;
490   definq_t d;
491   FILE *fp;
492   char buffer[1024];
493   int rc, n;
494
495   /* Skip the command and trailing spaces. */
496   for (; *line && !spacep (line); line++)
497     ;
498   while (spacep (line))
499     line++;
500   /* Get the name. */
501   name = line;
502   for (; *line && !spacep (line); line++)
503     ;
504   if (*line)
505     *line++ = 0;
506
507   /* Now match it against our list. he second loop is todetect the
508      match all entry. **/
509   for (d=definq_list; d; d = d->next)
510     if (d->name && !strcmp (d->name, name))
511         break;
512   if (!d)
513     for (d=definq_list; d; d = d->next)
514       if (!d->name)
515         break;
516   if (!d)
517     {
518       if (opt.verbose)
519         log_info ("no handler for inquiry `%s' found\n", name);
520       return 0;
521     }
522
523   if (d->is_prog)
524     {
525       fp = popen (d->file, "r");
526       if (!fp)
527         log_error ("error executing `%s': %s\n", d->file, strerror (errno));
528       else if (opt.verbose)
529         log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
530     }
531   else
532     {
533       fp = fopen (d->file, "rb");
534       if (!fp)
535         log_error ("error opening `%s': %s\n", d->file, strerror (errno));
536       else if (opt.verbose)
537         log_error ("handling inquiry `%s' by returning content of `%s'\n",
538                    name, d->file);
539     }
540   if (!fp)
541     return 0;
542
543   while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
544     {
545       rc = assuan_send_data (ctx, buffer, n);
546       if (rc)
547         {
548           log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
549           break;
550         }
551     }
552   if (ferror (fp))
553     log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
554
555   rc = assuan_send_data (ctx, NULL, 0);
556   if (rc)
557     log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
558
559   if (d->is_prog)
560     {
561       if (pclose (fp))
562         log_error ("error running `%s': %s\n", d->file, strerror (errno));
563     }
564   else
565     fclose (fp);
566   return 1;
567 }
568
569
570 /* Read all response lines from server and print them.  Returns 0 on
571    success or an assuan error code. */
572 static int
573 read_and_print_response (assuan_context_t ctx)
574 {
575   char *line;
576   size_t linelen;
577   assuan_error_t rc;
578   int i, j;
579   int need_lf = 0;
580
581   for (;;)
582     {
583       do 
584         {
585           rc = assuan_read_line (ctx, &line, &linelen);
586           if (rc)
587             return rc;
588
589           if (opt.verbose > 1 && *line == '#')
590             {
591               fwrite (line, linelen, 1, stdout);
592               putchar ('\n');
593             }
594         }    
595       while (*line == '#' || !linelen);
596
597       if (linelen >= 1
598           && line[0] == 'D' && line[1] == ' ')
599         {
600           if (opt.hex)
601             {
602               for (i=2; i < linelen; )
603                 {
604                   int save_i = i;
605
606                   printf ("D[%04X] ", i-2);
607                   for (j=0; j < 16 ; j++, i++)
608                     {
609                       if (j == 8)
610                         putchar (' ');
611                       if (i < linelen)
612                         printf (" %02X", ((unsigned char*)line)[i]);
613                       else
614                         fputs ("   ", stdout);
615                     }
616                   fputs ("   ", stdout);
617                   i= save_i;
618                   for (j=0; j < 16; j++, i++)
619                     {
620                       unsigned int c = ((unsigned char*)line)[i];
621                       if ( i >= linelen )
622                         putchar (' ');
623                       else if (isascii (c) && isprint (c) && !iscntrl (c))
624                         putchar (c);
625                       else
626                         putchar ('.');
627                     }
628                   putchar ('\n');
629                 }
630             }
631           else if (opt.decode)
632             {
633               const unsigned char *s;
634               int need_d = 1;
635               int c = 0;
636
637               for (j=2, s=(unsigned char*)line+2; j < linelen; j++, s++ )
638                 {
639                   if (need_d)
640                     {
641                       fputs ("D ", stdout);
642                       need_d = 0;
643                     }
644                   if (*s == '%' && j+2 < linelen)
645                     { 
646                       s++; j++;
647                       c = xtoi_2 ( s );
648                       s++; j++;
649                     }
650                   else
651                     c = *s;
652                   if (c == '\n')
653                     need_d = 1;
654                   putchar (c);
655                 }
656               need_lf = (c != '\n');
657             }
658           else
659             {
660               fwrite (line, linelen, 1, stdout);
661               putchar ('\n');
662             }
663         }
664       else 
665         {
666           if (need_lf)
667             {
668               putchar ('\n');
669               need_lf = 0;
670             }
671
672           if (linelen >= 1
673               && line[0] == 'S' 
674               && (line[1] == '\0' || line[1] == ' '))
675             {
676               fwrite (line, linelen, 1, stdout);
677               putchar ('\n');
678             }  
679           else if (linelen >= 2
680                    && line[0] == 'O' && line[1] == 'K'
681                    && (line[2] == '\0' || line[2] == ' '))
682             {
683               fwrite (line, linelen, 1, stdout);
684               putchar ('\n');
685               return 0;
686             }
687           else if (linelen >= 3
688                    && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
689                    && (line[3] == '\0' || line[3] == ' '))
690             {
691               fwrite (line, linelen, 1, stdout);
692               putchar ('\n');
693               return 0;
694             }  
695           else if (linelen >= 7
696                    && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
697                    && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
698                    && line[6] == 'E' 
699                    && (line[7] == '\0' || line[7] == ' '))
700             {
701               fwrite (line, linelen, 1, stdout);
702               putchar ('\n');
703               if (!handle_inquire (ctx, line))
704                 assuan_write_line (ctx, "CANCEL");
705             }
706           else if (linelen >= 3
707                    && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
708                    && (line[3] == '\0' || line[3] == ' '))
709             {
710               fwrite (line, linelen, 1, stdout);
711               putchar ('\n');
712               /* Received from server, thus more responses are expected.  */
713             }
714           else
715             return gpg_error (GPG_ERR_ASS_INV_RESPONSE);
716         }
717     }
718 }
719
720
721
722
723 /* Connect to the agent and send the standard options.  */
724 static assuan_context_t
725 start_agent (void)
726 {
727   int rc = 0;
728   char *infostr, *p;
729   assuan_context_t ctx;
730
731   infostr = getenv ("GPG_AGENT_INFO");
732   if (!infostr || !*infostr)
733     {
734       char *sockname;
735
736       /* Check whether we can connect at the standard socket.  */
737       sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
738       rc = assuan_socket_connect (&ctx, sockname, 0);
739       xfree (sockname);
740     }
741   else
742     {
743       int prot;
744       int pid;
745
746       infostr = xstrdup (infostr);
747       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
748         {
749           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
750           xfree (infostr);
751           exit (1);
752         }
753       *p++ = 0;
754       pid = atoi (p);
755       while (*p && *p != PATHSEP_C)
756         p++;
757       prot = *p? atoi (p+1) : 0;
758       if (prot != 1)
759         {
760           log_error (_("gpg-agent protocol version %d is not supported\n"),
761                      prot);
762           xfree (infostr);
763           exit (1);
764         }
765
766       rc = assuan_socket_connect (&ctx, infostr, pid);
767       xfree (infostr);
768     }
769
770   if (rc)
771     {
772       log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
773       exit (1);
774     }
775
776   if (opt.verbose)
777     log_info ("connection to agent established\n");
778
779   rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
780   if (rc)
781     {
782       log_error (_("error sending %s command: %s\n"), "RESET", 
783                  gpg_strerror (rc));
784       exit (1);
785     }
786
787   rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
788                                   NULL, NULL, NULL, NULL, NULL);
789   if (rc)
790     {
791       log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
792       exit (1);
793     }
794
795   return ctx;
796 }