6771ab9536e789a85fbfd0b6183903ff54c38203
[gnupg.git] / tools / symcryptrun.c
1 /* symcryptrun.c - Tool to call simple symmetric encryption tools.
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
22 /* Sometimes simple encryption tools are already in use for a long
23    time and there is a desire to integrate them into the GnuPG
24    framework.  The protocols and encryption methods might be
25    non-standard or not even properly documented, so that a
26    full-fledged encryption tool with an interface like gpg is not
27    doable.  This simple wrapper program provides a solution: It
28    operates by calling the encryption/decryption module and providing
29    the passphrase for a key (or even the key directly) using the
30    standard pinentry mechanism through gpg-agent.  */
31
32 /* This program is invoked in the following way:
33
34    symcryptrun --class CLASS --program PROGRAM --keyfile KEYFILE \
35      [--decrypt | --encrypt]
36
37    For encryption, the plain text must be provided on STDIN, and the
38    ciphertext will be output to STDOUT.  For decryption vice versa.
39
40    CLASS can currently only be "confucius".
41
42    PROGRAM must be the path to the crypto engine.
43
44    KEYFILE must contain the secret key, which may be protected by a
45    passphrase.  The passphrase is retrieved via the pinentry program.
46
47
48    The GPG Agent _must_ be running before starting symcryptrun.
49
50    The possible exit status codes:
51
52    0    Success
53    1    Some error occured
54    2    No valid passphrase was provided
55    3    The operation was canceled by the user
56
57    Other classes may be added in the future.  */
58
59 \f
60 #include <config.h>
61
62 #include <unistd.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <errno.h>
67 #include <assert.h>
68 #include <sys/stat.h>
69 #include <sys/types.h>
70 #include <sys/wait.h>
71 #include <pty.h>
72 #include <utmp.h>
73 #include <ctype.h>
74 #ifdef HAVE_LOCALE_H
75 #include <locale.h>
76 #endif
77 #ifdef HAVE_LANGINFO_CODESET
78 #include <langinfo.h>
79 #endif
80 #include <gpg-error.h>
81
82 #define JNLIB_NEED_LOG_LOGV
83 #include "i18n.h"
84 #include "../common/util.h"
85
86 /* FIXME: Bah.  For spwq_secure_free.  */
87 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
88 #include "../common/simple-pwquery.h"
89
90 \f
91 /* Used by gcry for logging */
92 static void
93 my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
94 {
95   /* translate the log levels */
96   switch (level)
97     {
98     case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
99     case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
100     case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
101     case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
102     case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
103     case GCRY_LOG_BUG:  level = JNLIB_LOG_BUG; break;
104     case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
105     default:            level = JNLIB_LOG_ERROR; break;      }
106   log_logv (level, fmt, arg_ptr);
107 }
108
109 \f
110 /* Constants to identify the commands and options. */
111 enum cmd_and_opt_values
112   {
113     aNull = 0,
114     oQuiet      = 'q',
115     oVerbose    = 'v',
116
117     oNoVerbose  = 500,
118     oOptions,
119     oNoOptions,
120     oLogFile,
121     oHomedir,
122     oClass,
123     oProgram,
124     oKeyfile,
125     oDecrypt,
126     oEncrypt,
127     oInput
128   };
129
130
131 /* The list of commands and options.  */
132 static ARGPARSE_OPTS opts[] =
133   {
134     { 301, NULL, 0, N_("@\nCommands:\n ") },
135
136     { oDecrypt, "decrypt", 0, N_("decryption modus") },
137     { oEncrypt, "encrypt", 0, N_("encryption modus") },
138     
139     { 302, NULL, 0, N_("@\nOptions:\n ") },
140     
141     { oClass, "class", 2, N_("tool class (confucius)") },
142     { oProgram, "program", 2, N_("program filename") },
143
144     { oKeyfile, "keyfile", 2, N_("secret key file (required)") },
145     { oInput, "inputfile", 2, N_("input file name (default stdin)") },
146     { oVerbose, "verbose",  0, N_("verbose") },
147     { oQuiet, "quiet",      0, N_("quiet") },
148     { oLogFile, "log-file", 2, N_("use a log file for the server") },
149     { oOptions,  "options"  , 2, N_("|FILE|read options from FILE") },
150
151     /* Hidden options.  */
152     { oNoVerbose, "no-verbose",  0, "@" },
153     { oHomedir, "homedir", 2, "@" },   
154     { oNoOptions, "no-options", 0, "@" },/* shortcut for --options /dev/null */
155
156     {0}
157   };
158
159
160 /* We keep all global options in the structure OPT.  */
161 struct
162 {
163   int verbose;          /* Verbosity level.  */
164   int quiet;            /* Be extra quiet.  */
165   const char *homedir;  /* Configuration directory name */
166
167   char *class;
168   char *program;
169   char *keyfile;
170   char *input;
171 } opt;
172
173 \f
174 /* Print usage information and and provide strings for help.  */
175 static const char *
176 my_strusage (int level)
177 {
178   const char *p;
179
180   switch (level)
181     {
182     case 11: p = "symcryptrun (GnuPG)";
183       break;
184     case 13: p = VERSION; break;
185     case 17: p = PRINTABLE_OS_NAME; break;
186     case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
187       break;
188     case 1:
189     case 40: p = _("Usage: symcryptrun [options] (-h for help)");
190       break;
191     case 41:
192       p = _("Syntax: symcryptrun --class CLASS --program PROGRAM "
193             "--keyfile KEYFILE [options...] COMMAND [inputfile]\n"
194             "Call a simple symmetric encryption tool\n");
195       break;
196     case 31: p = "\nHome: "; break;
197     case 32: p = opt.homedir; break;
198     case 33: p = "\n"; break;
199
200     default: p = NULL; break;
201     }
202   return p;
203 }
204
205
206 /* Initialize the gettext system.  */
207 static void
208 i18n_init(void)
209 {
210 #ifdef USE_SIMPLE_GETTEXT
211   set_gettext_file (PACKAGE_GT);
212 #else
213 # ifdef ENABLE_NLS
214   setlocale (LC_ALL, "");
215   bindtextdomain (PACKAGE_GT, LOCALEDIR);
216   textdomain (PACKAGE_GT);
217 # endif
218 #endif
219 }
220
221 \f
222 /* This is in the GNU C library in unistd.h.  */
223
224 #ifndef TEMP_FAILURE_RETRY
225 /* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno'
226    set to EINTR.  */
227
228 # define TEMP_FAILURE_RETRY(expression) \
229   (__extension__                                                              \
230     ({ long int __result;                                                     \
231        do __result = (long int) (expression);                                 \
232        while (__result == -1L && errno == EINTR);                             \
233        __result; }))
234 #endif
235
236
237 /* Unlink a file, and shred it if SHRED is true.  */
238 int
239 remove_file (char *name, int shred)
240 {
241   if (!shred)
242     return unlink (name);
243   else
244     {
245       int status;
246       pid_t pid;
247
248       pid = fork ();
249       if (pid == 0)
250         {
251           /* Child.  */
252           
253           /* -f forces file to be writable, and -u unlinks it afterwards.  */
254           char *args[] = { SHRED, "-uf", name, NULL };
255           
256           execv (SHRED, args);
257           _exit (127);
258         }
259       else if (pid < 0)
260         {
261           /* Fork failed.  */
262           status = -1;
263         }
264       else
265         {
266           /* Parent.  */
267           
268           if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
269             status = -1;
270         }
271       
272       if (!WIFEXITED (status))
273         {
274           log_error (_("%s on %s aborted with status %i\n"),
275                      SHRED, name, status);
276           unlink (name);
277           return 1;
278         }
279       else if (WEXITSTATUS (status))
280         {
281           log_error (_("%s on %s failed with status %i\n"), SHRED, name,
282                      WEXITSTATUS (status));
283           unlink (name);
284           return 1;
285         }
286
287       return 0;
288     }
289 }
290
291 \f
292 /* Class Confucius.
293
294    "Don't worry that other people don't know you;
295    worry that you don't know other people."            Analects--1.16.  */
296
297 /* Create temporary directory with mode 0700.  Returns a dynamically
298    allocated string with the filename of the directory.  */
299 static char *
300 confucius_mktmpdir (void)
301 {
302   char *name;
303
304   name = strdup ("/tmp/gpg-XXXXXX");
305   if (!name || !mkdtemp (name))
306     {
307       log_error (_("can't create temporary directory `%s': %s\n"),
308                  name?name:"", strerror (errno));
309       return NULL;
310     }
311
312   return name;
313 }
314
315
316 /* Buffer size for I/O operations.  */
317 #define CONFUCIUS_BUFSIZE 4096
318
319 /* Buffer size for output lines.  */
320 #define CONFUCIUS_LINESIZE 4096
321
322
323 /* Copy the file IN to OUT, either of which may be "-".  If PLAIN is
324    true, and the copying fails, and OUT is not STDOUT, then shred the
325    file instead unlinking it.  */
326 static int
327 confucius_copy_file (char *infile, char *outfile, int plain)
328 {
329   FILE *in;
330   int in_is_stdin = 0;
331   FILE *out;
332   int out_is_stdout = 0;
333   char data[CONFUCIUS_BUFSIZE];
334   ssize_t data_len;
335
336   if (infile[0] == '-' && infile[1] == '\0')
337     {
338       /* FIXME: Is stdin in binary mode?  */
339       in = stdin;
340       in_is_stdin = 1;
341     }
342   else
343     {
344       in = fopen (infile, "rb");
345       if (!in)
346         {
347           log_error (_("could not open %s for writing: %s\n"),
348                      infile, strerror (errno));
349           return 1;
350         }
351     }
352
353   if (outfile[0] == '-' && outfile[1] == '\0')
354     {
355       /* FIXME: Is stdout in binary mode?  */
356       out = stdout;
357       out_is_stdout = 1;
358     }
359   else
360     {
361       out = fopen (outfile, "wb");
362       if (!out)
363         {
364           log_error (_("could not open %s for writing: %s\n"),
365                      infile, strerror (errno));
366           return 1;
367         }
368     }
369
370   /* Now copy the data.  */
371   while ((data_len = fread (data, 1, sizeof (data), in)) > 0)
372     {
373       if (fwrite (data, 1, data_len, out) != data_len)
374         {
375           log_error (_("error writing to %s: %s\n"), outfile,
376                      strerror (errno));
377           goto copy_err;
378         }
379     }
380   if (data_len < 0 || ferror (in))
381     {
382       log_error (_("error reading from %s: %s\n"), infile, strerror (errno));
383       goto copy_err;
384     }
385
386   /* Close IN if appropriate.  */
387   if (!in_is_stdin && fclose (in) && ferror (in))
388     {
389       log_error (_("error closing %s: %s\n"), infile, strerror (errno));
390       goto copy_err;
391     }
392
393   /* Close OUT if appropriate.  */
394   if (!out_is_stdout && fclose (out) && ferror (out))
395     {
396       log_error (_("error closing %s: %s\n"), infile, strerror (errno));
397       goto copy_err;
398     }
399
400   return 0;
401
402  copy_err:
403   if (!out_is_stdout)
404     remove_file (outfile, plain);
405
406   return 1;
407 }
408
409
410 /* Get a passphrase in secure storage (if possible).  If AGAIN is
411    true, then this is a repeated attempt.  If CANCELED is not a null
412    pointer, it will be set to true or false, depending on if the user
413    canceled the operation or not.  On error (including cancelation), a
414    null pointer is returned.  The passphrase must be deallocated with
415    confucius_drop_pass.  */
416 char *
417 confucius_get_pass (int again, int *canceled)
418 {
419   int err;
420   char *pw;
421 #ifdef HAVE_LANGINFO_CODESET
422   char *orig_codeset = NULL;
423 #endif
424
425   if (canceled)
426     *canceled = 0;
427   
428 #ifdef ENABLE_NLS
429   /* The Assuan agent protocol requires us to transmit utf-8 strings */
430   orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
431 #ifdef HAVE_LANGINFO_CODESET
432   if (!orig_codeset)
433     orig_codeset = nl_langinfo (CODESET);
434 #endif
435   if (orig_codeset && !strcmp (orig_codeset, "UTF-8"))
436     orig_codeset = NULL;
437   if (orig_codeset)
438     {
439       /* We only switch when we are able to restore the codeset later. */
440       orig_codeset = xstrdup (orig_codeset);
441       if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
442         orig_codeset = NULL; 
443     }
444 #endif
445
446   pw = simple_pwquery (NULL,
447                        again ? _("does not match - try again"):NULL,
448                        _("Passphrase:"), NULL, &err);
449
450 #ifdef ENABLE_NLS
451   if (orig_codeset)
452     {
453       bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
454       xfree (orig_codeset);
455     }
456 #endif
457
458   if (!pw)
459     {
460       if (err)
461         log_error (_("error while asking for the passphrase: %s\n"),
462                    gpg_strerror (err));
463       else
464         {
465           log_info (_("cancelled\n"));
466           if (canceled)
467             *canceled = 1;
468         }      
469     }
470
471   return pw;
472 }
473
474
475 /* Drop a passphrase retrieved with confucius_get_pass.  */
476 void
477 confucius_drop_pass (char *pass)
478 {
479   if (pass)
480     spwq_secure_free (pass);
481 }
482
483
484 /* Run a confucius crypto engine.  If MODE is oEncrypt, encryption is
485    requested.  If it is oDecrypt, decryption is requested.  INFILE and
486    OUTFILE are the temporary files used in the process.  */
487 int
488 confucius_process (int mode, char *infile, char *outfile)
489 {
490   char *const args[] = { opt.program,
491                          mode == oEncrypt ? "-m1" : "-m2",
492                          "-q", infile,
493                          "-z", outfile,
494                          "-s", opt.keyfile,
495                          mode == oEncrypt ? "-af" : "-f",
496                          NULL };
497   int cstderr[2];
498   int master;
499   int slave;
500   int res;
501   pid_t pid;
502   pid_t wpid;
503   int tries = 0;
504
505   signal (SIGPIPE, SIG_IGN);
506
507   if (!opt.program)
508     {
509       log_error (_("no --program option provided\n"));
510       return 1;
511     }
512
513   if (mode != oDecrypt && mode != oEncrypt)
514     {
515       log_error (_("only --decrypt and --encrypt are supported\n"));
516       return 1;
517     }
518
519   if (!opt.keyfile)
520     {
521       log_error (_("no --keyfile option provided\n"));
522       return 1;
523     }
524
525   if (pipe (cstderr) < 0)
526     {
527       log_error (_("could not create pipe: %s\n"), strerror (errno));
528       return 1;
529     }
530
531   if (openpty (&master, &slave, NULL, NULL, NULL) == -1)
532     {
533       log_error (_("could not create pty: %s\n"), strerror (errno));
534       close (cstderr[0]);
535       close (cstderr[1]);
536       return -1;
537     }
538
539   /* We don't want to deal with the worst case scenarios.  */
540   assert (master > 2);
541   assert (slave > 2);
542   assert (cstderr[0] > 2);
543   assert (cstderr[1] > 2);
544
545   pid = fork ();
546   if (pid < 0)
547     {
548       log_error (_("could not fork: %s\n"), strerror (errno));
549       close (master);
550       close (slave);
551       close (cstderr[0]);
552       close (cstderr[1]);
553       return 1;
554     }
555   else if (pid == 0) 
556     {
557       /* Child.  */
558
559       /* Close the parent ends.  */
560       close (master);
561       close (cstderr[0]);
562
563       /* Change controlling terminal.  */
564       if (login_tty (slave))
565         {
566           /* It's too early to output a debug message.  */
567           _exit (1);
568         }
569
570       dup2 (cstderr[1], 2);
571       close (cstderr[1]);
572
573       /* Now kick off the engine program.  */
574       execv (opt.program, args);
575       log_error (_("execv failed: %s\n"), strerror (errno));
576       _exit (1);
577     }
578   else
579     {
580       /* Parent.  */
581       char buffer[CONFUCIUS_LINESIZE];
582       int buffer_len = 0;
583       fd_set fds;
584       int slave_closed = 0;
585       int stderr_closed = 0;
586
587       close (slave);
588       close (cstderr[1]);
589
590       /* Listen on the output FDs.  */
591       do
592         {
593           FD_ZERO (&fds);
594
595           if (!slave_closed)
596             FD_SET (master, &fds);
597           if (!stderr_closed)
598             FD_SET (cstderr[0], &fds);
599
600           res = select (FD_SETSIZE, &fds, NULL, NULL, NULL);
601           if (res < 0)
602             {
603               log_error (_("select failed: %s\n"), strerror (errno));
604
605               kill (pid, SIGTERM);
606               close (master);
607               close (cstderr[0]);
608               return 1;
609             }
610
611           if (FD_ISSET (cstderr[0], &fds))
612             {
613               /* We got some output on stderr.  This is just passed
614                  through via the logging facility.  */
615
616               res = read (cstderr[0], &buffer[buffer_len],
617                           sizeof (buffer) - buffer_len - 1);
618               if (res < 0)
619                 {
620                   log_error (_("read failed: %s\n"), strerror (errno));
621
622                   kill (pid, SIGTERM);
623                   close (master);
624                   close (cstderr[0]);
625                   return 1;
626                 }
627               else  
628                 {
629                   char *newline;
630
631                   buffer_len += res;
632                   for (;;)
633                     {
634                       buffer[buffer_len] = '\0';
635                       newline = strchr (buffer, '\n');
636                       if (newline)
637                         {
638                           *newline = '\0';
639                           log_error ("%s\n", buffer);
640                           buffer_len -= newline + 1 - buffer;
641                           memmove (buffer, newline + 1, buffer_len);
642                         }
643                       else if (buffer_len == sizeof (buffer) - 1)
644                         {
645                           /* Overflow.  */
646                           log_error ("%s\n", buffer);
647                           buffer_len = 0;
648                         }
649                       else
650                         break;
651                     }
652
653                   if (res == 0)
654                     stderr_closed = 1;
655                 }
656             }
657           else if (FD_ISSET (master, &fds))
658             {
659               char data[512];
660
661               res = read (master, data, sizeof (data));
662               if (res < 0)
663                 {
664                   if (errno == EIO)
665                     {
666                       /* Slave-side close leads to readable fd and
667                          EIO.  */
668                       slave_closed = 1;
669                     }
670                   else
671                     {
672                       log_error (_("pty read failed: %s\n"), strerror (errno));
673
674                       kill (pid, SIGTERM);
675                       close (master);
676                       close (cstderr[0]);
677                       return 1;
678                     }
679                 }
680               else if (res == 0)
681                 /* This never seems to be what happens on slave-side
682                    close.  */
683                 slave_closed = 1;
684               else
685                 {
686                   /* Check for password prompt.  */
687                   if (data[res - 1] == ':')
688                     {
689                       char *pass;
690                       int canceled;
691
692                       pass = confucius_get_pass (tries ? 1 : 0, &canceled);
693                       if (!pass)
694                         {
695                           kill (pid, SIGTERM);
696                           close (master);
697                           close (cstderr[0]);
698                           return canceled ? 3 : 1;
699                         }
700                       write (master, pass, strlen (pass));
701                       write (master, "\n", 1);
702                       confucius_drop_pass (pass);
703
704                       tries++;
705                     }
706                 }
707             }
708         }
709       while (!stderr_closed || !slave_closed);
710
711       close (master);
712       close (cstderr[0]);
713
714       wpid = waitpid (pid, &res, 0);
715       if (wpid < 0)
716         {
717           log_error (_("waitpid failed: %s\n"), strerror (errno));
718
719           kill (pid, SIGTERM);
720           return 1;
721         }
722       else
723         {
724           /* Shouldn't happen, as we don't use WNOHANG.  */
725           assert (wpid != 0);
726
727           if (!WIFEXITED (res))
728             {
729               log_error (_("child aborted with status %i\n"), res);
730               return 1;
731             }
732
733           if (WEXITSTATUS (res))
734             {
735               /* We probably exceeded our number of attempts at guessing
736                  the password.  */
737               if (tries >= 3)
738                 return 2;
739               else
740                 return 1;
741             }
742
743           return 0;
744         }
745     }
746
747   /* Not reached.  */
748 }
749
750
751 /* Class confucius main program.  If MODE is oEncrypt, encryption is
752    requested.  If it is oDecrypt, decryption is requested.  The other
753    parameters are taken from the global option data.  */
754 int
755 confucius_main (int mode)
756 {
757   int res;
758   char *tmpdir;
759   char *infile;
760   int infile_from_stdin = 0;
761   char *outfile;
762
763   tmpdir = confucius_mktmpdir ();
764   if (!tmpdir)
765     return 1;
766
767   if (opt.input && !(opt.input[0] == '-' && opt.input[1] == '\0'))
768     infile = xstrdup (opt.input);
769   else
770     {
771       infile_from_stdin = 1;
772
773       /* TMPDIR + "/" + "in" + "\0".  */
774       infile = malloc (strlen (tmpdir) + 1 + 2 + 1);
775       if (!infile)
776         {
777           log_error (_("cannot allocate infile string: %s\n"),
778                      strerror (errno));
779           rmdir (tmpdir);
780           return 1;
781         }
782       strcpy (infile, tmpdir);
783       strcat (infile, "/in");
784     }
785
786   /* TMPDIR + "/" + "out" + "\0".  */
787   outfile = malloc (strlen (tmpdir) + 1 + 3 + 1);
788   if (!outfile)
789     {
790       log_error (_("cannot allocate outfile string: %s\n"), strerror (errno));
791       free (infile);
792       rmdir (tmpdir);
793       return 1;
794     }
795   strcpy (outfile, tmpdir);
796   strcat (outfile, "/out");
797
798   if (infile_from_stdin)
799     {
800       /* Create INFILE and fill it with content.  */
801       res = confucius_copy_file ("-", infile, mode == oEncrypt);
802       if (res)
803         {
804           free (outfile);
805           free (infile);
806           rmdir (tmpdir);
807           return res;
808         }
809     }
810
811   /* Run the engine and thus create the output file, handling
812      passphrase retrieval.  */
813   res = confucius_process (mode, infile, outfile);
814   if (res)
815     {
816       remove_file (outfile, mode == oDecrypt);
817       if (infile_from_stdin)
818         remove_file (infile, mode == oEncrypt);
819       free (outfile);
820       free (infile);
821       rmdir (tmpdir);
822       return res;
823     }
824
825   /* Dump the output file to stdout.  */
826   res = confucius_copy_file (outfile, "-", mode == oDecrypt);
827   if (res)
828     {
829       remove_file (outfile, mode == oDecrypt);
830       if (infile_from_stdin)
831         remove_file (infile, mode == oEncrypt);
832       free (outfile);
833       free (infile);
834       rmdir (tmpdir);
835       return res;
836     }
837   
838   remove_file (outfile, mode == oDecrypt);
839   if (infile_from_stdin)
840     remove_file (infile, mode == oEncrypt);
841   free (outfile);
842   free (infile);
843   rmdir (tmpdir);
844   return 0;
845 }
846
847 \f
848 /* symcryptrun's entry point.  */
849 int
850 main (int argc, char **argv)
851 {
852   ARGPARSE_ARGS pargs;
853   int orig_argc;
854   char **orig_argv;
855   FILE *configfp = NULL;
856   char *configname = NULL;
857   unsigned configlineno; 
858   int mode = 0;
859   int res;
860   char *logfile = NULL;
861   int default_config = 1;
862
863   set_strusage (my_strusage);
864   log_set_prefix ("symcryptrun", 1);
865
866   /* Try to auto set the character set.  */
867   set_native_charset (NULL); 
868
869   i18n_init();
870
871   opt.homedir = default_homedir ();
872
873   /* Check whether we have a config file given on the commandline */
874   orig_argc = argc;
875   orig_argv = argv;
876   pargs.argc = &argc;
877   pargs.argv = &argv;
878   pargs.flags= 1|(1<<6);  /* do not remove the args, ignore version */
879   while (arg_parse( &pargs, opts))
880     {
881       if (pargs.r_opt == oOptions)
882         { /* Yes there is one, so we do not try the default one, but
883              read the option file when it is encountered at the
884              commandline */
885           default_config = 0;
886         }
887       else if (pargs.r_opt == oNoOptions)
888         default_config = 0; /* --no-options */
889       else if (pargs.r_opt == oHomedir)
890         opt.homedir = pargs.r.ret_str;
891     }
892
893   if (default_config)
894     configname = make_filename (opt.homedir, "symcryptrun.conf", NULL );
895   
896   argc = orig_argc;
897   argv = orig_argv;
898   pargs.argc = &argc;
899   pargs.argv = &argv;
900   pargs.flags= 1;  /* do not remove the args */
901  next_pass:
902   if (configname)
903     {
904       configlineno = 0;
905       configfp = fopen (configname, "r");
906       if (!configfp)
907         {
908           if (!default_config)
909             {
910               log_error (_("option file `%s': %s\n"),
911                          configname, strerror(errno) );
912               exit(1);
913             }
914           xfree (configname); 
915           configname = NULL;
916         }
917       default_config = 0;
918     }
919
920   /* Parse the command line. */
921   while (optfile_parse (configfp, configname, &configlineno, &pargs, opts))
922     {
923       switch (pargs.r_opt)
924         {
925         case oDecrypt:   mode = oDecrypt; break;
926         case oEncrypt:   mode = oEncrypt; break;
927
928         case oQuiet:     opt.quiet = 1; break;
929         case oVerbose:   opt.verbose++; break;
930         case oNoVerbose: opt.verbose = 0; break;
931           
932         case oClass:    opt.class = pargs.r.ret_str; break;
933         case oProgram:  opt.program = pargs.r.ret_str; break;
934         case oKeyfile:  opt.keyfile = pargs.r.ret_str; break;
935         case oInput:    opt.input = pargs.r.ret_str; break;
936
937         case oLogFile:  logfile = pargs.r.ret_str; break;
938
939         case oOptions:
940           /* Config files may not be nested (silently ignore them) */
941           if (!configfp)
942             {
943                 xfree(configname);
944                 configname = xstrdup(pargs.r.ret_str);
945                 goto next_pass;
946             }
947           break;
948         case oNoOptions: break; /* no-options */
949         case oHomedir: /* Ignore this option here. */; break;
950
951         default : pargs.err = configfp? 1:2; break;
952         }
953     }
954   if (configfp)
955     {
956       fclose( configfp );
957       configfp = NULL;
958       configname = NULL;
959       goto next_pass;
960     }
961   xfree (configname);
962   configname = NULL;
963
964   /* With --inputfile an argument is not allowed, without only one
965      optional argument is allowed. */
966   if (argc > 1)
967     log_error (_("too many arguments\n"));
968   else if (opt.input && argc)
969     log_error (_("no argument allowed when using option \"%s\"\n"),
970                "--inputfile");
971
972   if (argc)
973     {
974       opt.input = *argv;
975       argv++; argc--;
976     }
977
978   if (!mode)
979     log_error (_("either %s or %s must be given\n"),
980                "--decrypt", "--encrypt");
981
982   if (log_get_errorcount (0))
983     exit (1);
984
985   if (logfile)
986     log_set_file (logfile);
987
988   gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
989   if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
990     {
991       log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
992                  NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
993     }
994   gcry_set_log_handler (my_gcry_logger, NULL);
995   gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
996
997   if (!opt.class)
998     {
999       log_error (_("no class provided\n"));
1000       res = 1;
1001     }
1002   else if (!strcmp (opt.class, "confucius"))
1003     res = confucius_main (mode);
1004   else
1005     {
1006       log_error (_("class %s is not supported\n"), opt.class);
1007       res = 1;
1008     }
1009
1010   return res;
1011 }