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