A whole bunch of changes to allow building for Windows.
[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
278   /* Make sure that our subsystems are ready.  */
279   init_common_subsystems ();
280
281   assuan_set_assuan_err_source (0);
282
283   i18n_init();
284
285   opt.homedir = default_homedir ();
286   opt.connect_flags = 1; /* Use extended connect mode.  */
287
288   /* Parse the command line. */
289   pargs.argc  = &argc;
290   pargs.argv  = &argv;
291   pargs.flags =  1;  /* Do not remove the args.  */
292   while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
293     {
294       switch (pargs.r_opt)
295         {
296         case oQuiet:     opt.quiet = 1; break;
297         case oVerbose:   opt.verbose++; break;
298         case oNoVerbose: opt.verbose = 0; break;
299         case oHomedir:   opt.homedir = pargs.r.ret_str; break;
300         case oHex:       opt.hex = 1; break;
301         case oDecode:    opt.decode = 1; break;
302         case oRawSocket: opt.raw_socket = pargs.r.ret_str; break;
303         case oExec:      opt.exec = 1; break;
304         case oNoExtConnect: opt.connect_flags &= ~(1); break;
305
306         default: pargs.err = 2; break;
307         }
308     }
309
310   if (log_get_errorcount (0))
311     exit (2);
312
313   if (opt.exec)
314     {
315       if (!argc)
316         {
317           log_error (_("option \"%s\" requires a program "
318                        "and optional arguments\n"), "--exec" );
319           exit (1);
320         }
321     }
322   else if (argc)
323     usage (1);
324
325   if (opt.exec && opt.raw_socket)
326     log_info (_("option \"%s\" ignored due to \"%s\"\n"),
327               "--raw-socket", "--exec");
328
329   if (opt.exec)
330     {
331       int no_close[3];
332
333       no_close[0] = fileno (stderr);
334       no_close[1] = log_get_fd ();
335       no_close[2] = -1;
336       rc = assuan_pipe_connect_ext (&ctx, *argv, (const char **)argv,
337                                     no_close, NULL, NULL,
338                                     opt.connect_flags);
339       if (rc)
340         {
341           log_error ("assuan_pipe_connect_ext failed: %s\n",
342                      gpg_strerror (rc));
343           exit (1);
344         }
345
346       if (opt.verbose)
347         log_info ("server `%s' started\n", *argv);
348
349     }
350   else if (opt.raw_socket)
351     {
352       rc = assuan_socket_connect_ext (&ctx, opt.raw_socket, 0,
353                                       opt.connect_flags);
354       if (rc)
355         {
356           log_error ("can't connect to socket `%s': %s\n",
357                      opt.raw_socket, gpg_strerror (rc));
358           exit (1);
359         }
360
361       if (opt.verbose)
362         log_info ("connection to socket `%s' established\n", opt.raw_socket);
363     }
364   else
365     ctx = start_agent ();
366   line = NULL;
367   linesize = 0;
368   for (;;)
369     {
370       int n;
371       size_t maxlength;
372
373       maxlength = 2048;
374       n = read_line (stdin, &line, &linesize, &maxlength);
375       if (n < 0)
376         {
377           log_error (_("error reading input: %s\n"), strerror (errno));
378           exit (1);
379         }
380       if (!n)
381         break; /* EOF */
382       if (!maxlength)
383         {
384           log_error (_("line too long - skipped\n"));
385           continue;
386         }
387       if (memchr (line, 0, n))
388         log_info (_("line shortened due to embedded Nul character\n"));
389       if (line[n-1] == '\n')
390         line[n-1] = 0;
391       if (*line == '/')
392         {
393           /* Handle control commands. */
394           char *cmd = line+1;
395
396           for (p=cmd; *p && !spacep (p); p++)
397             ;
398           if (*p)
399             *p++ = 0;
400           while (spacep (p))
401             p++;
402           if (!strcmp (cmd, "definqfile"))
403             {
404               add_definq (p, 0);
405             }
406           else if (!strcmp (cmd, "definqprog"))
407             {
408               add_definq (p, 1);
409             }
410           else if (!strcmp (cmd, "showdef"))
411             {
412               show_definq ();
413             }
414           else if (!strcmp (cmd, "cleardef"))
415             {
416               clear_definq ();
417             }
418           else if (!strcmp (cmd, "echo"))
419             {
420               puts (p);
421             }
422           else if (!strcmp (cmd, "sendfd"))
423             {
424               do_sendfd (ctx, p);
425               continue;
426             }
427           else if (!strcmp (cmd, "recvfd"))
428             {
429               do_recvfd (ctx, p);
430               continue;
431             }
432           else if (!strcmp (cmd, "hex"))
433             opt.hex = 1;
434           else if (!strcmp (cmd, "nohex"))
435             opt.hex = 0;
436           else if (!strcmp (cmd, "decode"))
437             opt.decode = 1;
438           else if (!strcmp (cmd, "nodecode"))
439             opt.decode = 0;
440           else if (!strcmp (cmd, "help"))
441             {
442               puts (
443 "Available commands:\n"
444 "/echo ARGS             Echo ARGS.\n"
445 "/definqfile NAME FILE\n"
446 "    Use content of FILE for inquiries with NAME.\n"
447 "    NAME may be \"*\" to match any inquiry.\n"
448 "/definqprog NAME PGM\n"
449 "    Run PGM for inquiries matching NAME and pass the\n"
450 "    entire line to it as arguments.\n"
451 "/showdef               Print all definitions.\n"
452 "/cleardef              Delete all definitions.\n"
453 "/sendfd FILE MODE      Open FILE and pass descriptor to server.\n"
454 "/recvfd                Receive FD from server and print. \n"
455 "/[no]hex               Enable hex dumping of received data lines.\n"
456 "/[no]decode            Enable decoding of received data lines.\n"
457 "/help                  Print this help.");
458             }
459           else
460             log_error (_("unknown command `%s'\n"), cmd );
461       
462           continue;
463         }
464       
465       rc = assuan_write_line (ctx, line);
466       if (rc)
467         {
468           log_info (_("sending line failed: %s\n"), gpg_strerror (rc) );
469           continue;
470         }
471       if (*line == '#' || !*line)
472         continue; /* Don't expect a response for a comment line. */
473
474       rc = read_and_print_response (ctx);
475       if (rc)
476         log_info (_("receiving line failed: %s\n"), gpg_strerror (rc) );
477     }
478
479   if (opt.verbose)
480     log_info ("closing connection to agent\n");
481   
482   return 0; 
483 }
484
485
486 /* Handle an Inquire from the server.  Return False if it could not be
487    handled; in this case the caller shll complete the operation.  LINE
488    is the complete line as received from the server.  This function
489    may change the content of LINE. */
490 static int
491 handle_inquire (assuan_context_t ctx, char *line)
492 {
493   const char *name;
494   definq_t d;
495   FILE *fp;
496   char buffer[1024];
497   int rc, n;
498
499   /* Skip the command and trailing spaces. */
500   for (; *line && !spacep (line); line++)
501     ;
502   while (spacep (line))
503     line++;
504   /* Get the name. */
505   name = line;
506   for (; *line && !spacep (line); line++)
507     ;
508   if (*line)
509     *line++ = 0;
510
511   /* Now match it against our list. he second loop is todetect the
512      match all entry. **/
513   for (d=definq_list; d; d = d->next)
514     if (d->name && !strcmp (d->name, name))
515         break;
516   if (!d)
517     for (d=definq_list; d; d = d->next)
518       if (!d->name)
519         break;
520   if (!d)
521     {
522       if (opt.verbose)
523         log_info ("no handler for inquiry `%s' found\n", name);
524       return 0;
525     }
526
527   if (d->is_prog)
528     {
529       fp = popen (d->file, "r");
530       if (!fp)
531         log_error ("error executing `%s': %s\n", d->file, strerror (errno));
532       else if (opt.verbose)
533         log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
534     }
535   else
536     {
537       fp = fopen (d->file, "rb");
538       if (!fp)
539         log_error ("error opening `%s': %s\n", d->file, strerror (errno));
540       else if (opt.verbose)
541         log_error ("handling inquiry `%s' by returning content of `%s'\n",
542                    name, d->file);
543     }
544   if (!fp)
545     return 0;
546
547   while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
548     {
549       rc = assuan_send_data (ctx, buffer, n);
550       if (rc)
551         {
552           log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
553           break;
554         }
555     }
556   if (ferror (fp))
557     log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
558
559   rc = assuan_send_data (ctx, NULL, 0);
560   if (rc)
561     log_error ("sending data back failed: %s\n", gpg_strerror (rc) );
562
563   if (d->is_prog)
564     {
565       if (pclose (fp))
566         log_error ("error running `%s': %s\n", d->file, strerror (errno));
567     }
568   else
569     fclose (fp);
570   return 1;
571 }
572
573
574 /* Read all response lines from server and print them.  Returns 0 on
575    success or an assuan error code. */
576 static int
577 read_and_print_response (assuan_context_t ctx)
578 {
579   char *line;
580   size_t linelen;
581   assuan_error_t rc;
582   int i, j;
583   int need_lf = 0;
584
585   for (;;)
586     {
587       do 
588         {
589           rc = assuan_read_line (ctx, &line, &linelen);
590           if (rc)
591             return rc;
592
593           if (opt.verbose > 1 && *line == '#')
594             {
595               fwrite (line, linelen, 1, stdout);
596               putchar ('\n');
597             }
598         }    
599       while (*line == '#' || !linelen);
600
601       if (linelen >= 1
602           && line[0] == 'D' && line[1] == ' ')
603         {
604           if (opt.hex)
605             {
606               for (i=2; i < linelen; )
607                 {
608                   int save_i = i;
609
610                   printf ("D[%04X] ", i-2);
611                   for (j=0; j < 16 ; j++, i++)
612                     {
613                       if (j == 8)
614                         putchar (' ');
615                       if (i < linelen)
616                         printf (" %02X", ((unsigned char*)line)[i]);
617                       else
618                         fputs ("   ", stdout);
619                     }
620                   fputs ("   ", stdout);
621                   i= save_i;
622                   for (j=0; j < 16; j++, i++)
623                     {
624                       unsigned int c = ((unsigned char*)line)[i];
625                       if ( i >= linelen )
626                         putchar (' ');
627                       else if (isascii (c) && isprint (c) && !iscntrl (c))
628                         putchar (c);
629                       else
630                         putchar ('.');
631                     }
632                   putchar ('\n');
633                 }
634             }
635           else if (opt.decode)
636             {
637               const unsigned char *s;
638               int need_d = 1;
639               int c = 0;
640
641               for (j=2, s=(unsigned char*)line+2; j < linelen; j++, s++ )
642                 {
643                   if (need_d)
644                     {
645                       fputs ("D ", stdout);
646                       need_d = 0;
647                     }
648                   if (*s == '%' && j+2 < linelen)
649                     { 
650                       s++; j++;
651                       c = xtoi_2 ( s );
652                       s++; j++;
653                     }
654                   else
655                     c = *s;
656                   if (c == '\n')
657                     need_d = 1;
658                   putchar (c);
659                 }
660               need_lf = (c != '\n');
661             }
662           else
663             {
664               fwrite (line, linelen, 1, stdout);
665               putchar ('\n');
666             }
667         }
668       else 
669         {
670           if (need_lf)
671             {
672               putchar ('\n');
673               need_lf = 0;
674             }
675
676           if (linelen >= 1
677               && line[0] == 'S' 
678               && (line[1] == '\0' || line[1] == ' '))
679             {
680               fwrite (line, linelen, 1, stdout);
681               putchar ('\n');
682             }  
683           else if (linelen >= 2
684                    && line[0] == 'O' && line[1] == 'K'
685                    && (line[2] == '\0' || line[2] == ' '))
686             {
687               fwrite (line, linelen, 1, stdout);
688               putchar ('\n');
689               return 0;
690             }
691           else if (linelen >= 3
692                    && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
693                    && (line[3] == '\0' || line[3] == ' '))
694             {
695               fwrite (line, linelen, 1, stdout);
696               putchar ('\n');
697               return 0;
698             }  
699           else if (linelen >= 7
700                    && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
701                    && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
702                    && line[6] == 'E' 
703                    && (line[7] == '\0' || line[7] == ' '))
704             {
705               fwrite (line, linelen, 1, stdout);
706               putchar ('\n');
707               if (!handle_inquire (ctx, line))
708                 assuan_write_line (ctx, "CANCEL");
709             }
710           else if (linelen >= 3
711                    && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
712                    && (line[3] == '\0' || line[3] == ' '))
713             {
714               fwrite (line, linelen, 1, stdout);
715               putchar ('\n');
716               /* Received from server, thus more responses are expected.  */
717             }
718           else
719             return gpg_error (GPG_ERR_ASS_INV_RESPONSE);
720         }
721     }
722 }
723
724
725
726
727 /* Connect to the agent and send the standard options.  */
728 static assuan_context_t
729 start_agent (void)
730 {
731   int rc = 0;
732   char *infostr, *p;
733   assuan_context_t ctx;
734
735   infostr = getenv ("GPG_AGENT_INFO");
736   if (!infostr || !*infostr)
737     {
738       char *sockname;
739
740       /* Check whether we can connect at the standard socket.  */
741       sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
742       rc = assuan_socket_connect (&ctx, sockname, 0);
743       xfree (sockname);
744     }
745   else
746     {
747       int prot;
748       int pid;
749
750       infostr = xstrdup (infostr);
751       if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
752         {
753           log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
754           xfree (infostr);
755           exit (1);
756         }
757       *p++ = 0;
758       pid = atoi (p);
759       while (*p && *p != PATHSEP_C)
760         p++;
761       prot = *p? atoi (p+1) : 0;
762       if (prot != 1)
763         {
764           log_error (_("gpg-agent protocol version %d is not supported\n"),
765                      prot);
766           xfree (infostr);
767           exit (1);
768         }
769
770       rc = assuan_socket_connect (&ctx, infostr, pid);
771       xfree (infostr);
772     }
773
774   if (rc)
775     {
776       log_error ("can't connect to the agent: %s\n", gpg_strerror (rc));
777       exit (1);
778     }
779
780   if (opt.verbose)
781     log_info ("connection to agent established\n");
782
783   rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
784   if (rc)
785     {
786       log_error (_("error sending %s command: %s\n"), "RESET", 
787                  gpg_strerror (rc));
788       exit (1);
789     }
790
791   rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
792                                   NULL, NULL, NULL, NULL, NULL);
793   if (rc)
794     {
795       log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
796       exit (1);
797     }
798
799   return ctx;
800 }