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