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