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