tools: Add options to gpg-wks-server.
[gnupg.git] / tools / gpgtar.c
1 /* gpgtar.c - A simple TAR implementation mainly useful for Windows.
2  * Copyright (C) 2010 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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* GnuPG comes with a shell script gpg-zip which creates archive files
21    in the same format as PGP Zip, which is actually a USTAR format.
22    That is fine and works nicely on all Unices but for Windows we
23    don't have a compatible shell and the supply of tar programs is
24    limited.  Given that we need just a few tar option and it is an
25    open question how many Unix concepts are to be mapped to Windows,
26    we might as well write our own little tar customized for use with
27    gpg.  So here we go.  */
28
29 #include <config.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <assert.h>
36
37 #include "util.h"
38 #include "i18n.h"
39 #include "sysutils.h"
40 #include "../common/openpgpdefs.h"
41 #include "../common/init.h"
42 #include "../common/strlist.h"
43
44 #include "gpgtar.h"
45
46
47 /* Constants to identify the commands and options. */
48 enum cmd_and_opt_values
49   {
50     aNull = 0,
51     aEncrypt    = 'e',
52     aDecrypt    = 'd',
53     aSign       = 's',
54     aList       = 't',
55
56     oSymmetric  = 'c',
57     oRecipient  = 'r',
58     oUser       = 'u',
59     oOutput     = 'o',
60     oDirectory  = 'C',
61     oQuiet      = 'q',
62     oVerbose    = 'v',
63     oFilesFrom  = 'T',
64     oNoVerbose  = 500,
65
66     aSignEncrypt,
67     oGpgProgram,
68     oSkipCrypto,
69     oOpenPGP,
70     oCMS,
71     oSetFilename,
72     oNull,
73
74     /* Compatibility with gpg-zip.  */
75     oGpgArgs,
76     oTarArgs,
77
78     /* Debugging.  */
79     oDryRun,
80   };
81
82
83 /* The list of commands and options. */
84 static ARGPARSE_OPTS opts[] = {
85   ARGPARSE_group (300, N_("@Commands:\n ")),
86
87   ARGPARSE_c (aEncrypt,   "encrypt", N_("create an archive")),
88   ARGPARSE_c (aDecrypt,   "decrypt", N_("extract an archive")),
89   ARGPARSE_c (aSign,      "sign",    N_("create a signed archive")),
90   ARGPARSE_c (aList,      "list-archive", N_("list an archive")),
91
92   ARGPARSE_group (301, N_("@\nOptions:\n ")),
93
94   ARGPARSE_s_n (oSymmetric, "symmetric", N_("use symmetric encryption")),
95   ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
96   ARGPARSE_s_s (oUser, "local-user",
97                 N_("|USER-ID|use USER-ID to sign or decrypt")),
98   ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
99   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
100   ARGPARSE_s_n (oQuiet, "quiet",  N_("be somewhat more quiet")),
101   ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
102   ARGPARSE_s_n (oSkipCrypto, "skip-crypto", N_("skip the crypto processing")),
103   ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
104   ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
105   ARGPARSE_s_n (oOpenPGP, "openpgp", "@"),
106   ARGPARSE_s_n (oCMS, "cms", "@"),
107
108   ARGPARSE_group (302, N_("@\nTar options:\n ")),
109
110   ARGPARSE_s_s (oDirectory, "directory",
111                 N_("|DIRECTORY|extract files into DIRECTORY")),
112   ARGPARSE_s_s (oFilesFrom, "files-from",
113                 N_("|FILE|get names to create from FILE")),
114   ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
115
116   ARGPARSE_s_s (oGpgArgs, "gpg-args", "@"),
117   ARGPARSE_s_s (oTarArgs, "tar-args", "@"),
118
119   ARGPARSE_end ()
120 };
121
122
123 /* The list of commands and options for tar that we understand. */
124 static ARGPARSE_OPTS tar_opts[] = {
125   ARGPARSE_s_s (oDirectory, "directory",
126                 N_("|DIRECTORY|extract files into DIRECTORY")),
127   ARGPARSE_s_s (oFilesFrom, "files-from",
128                 N_("|FILE|get names to create from FILE")),
129   ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
130
131   ARGPARSE_end ()
132 };
133
134
135 \f
136 /* Print usage information and and provide strings for help. */
137 static const char *
138 my_strusage( int level )
139 {
140   const char *p;
141
142   switch (level)
143     {
144     case 11: p = "@GPGTAR@ (@GNUPG@)";
145       break;
146     case 13: p = VERSION; break;
147     case 17: p = PRINTABLE_OS_NAME; break;
148     case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
149
150     case 1:
151     case 40:
152       p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
153       break;
154     case 41:
155       p = _("Syntax: gpgtar [options] [files] [directories]\n"
156             "Encrypt or sign files into an archive\n");
157       break;
158
159     default: p = NULL; break;
160     }
161   return p;
162 }
163
164
165 static void
166 set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
167 {
168   enum cmd_and_opt_values cmd = *ret_cmd;
169
170   if (!cmd || cmd == new_cmd)
171     cmd = new_cmd;
172   else if (cmd == aSign && new_cmd == aEncrypt)
173     cmd = aSignEncrypt;
174   else if (cmd == aEncrypt && new_cmd == aSign)
175     cmd = aSignEncrypt;
176   else
177     {
178       log_error (_("conflicting commands\n"));
179       exit (2);
180     }
181
182   *ret_cmd = cmd;
183 }
184 \f
185 /* Shell-like argument splitting.
186
187    For compatibility with gpg-zip we accept arguments for GnuPG and
188    tar given as a string argument to '--gpg-args' and '--tar-args'.
189    gpg-zip was implemented as a Bourne Shell script, and therefore, we
190    need to split the string the same way the shell would.  */
191 static int
192 shell_parse_stringlist (const char *str, strlist_t *r_list)
193 {
194   strlist_t list = NULL;
195   const char *s = str;
196   char quoted = 0;
197   char arg[1024];
198   char *p = arg;
199 #define addchar(c) \
200   do { if (p - arg + 2 < sizeof arg) *p++ = (c); else return 1; } while (0)
201 #define addargument()                           \
202   do {                                          \
203     if (p > arg)                                \
204       {                                         \
205         *p = 0;                                 \
206         append_to_strlist (&list, arg);         \
207         p = arg;                                \
208       }                                         \
209   } while (0)
210
211 #define unquoted        0
212 #define singlequote     '\''
213 #define doublequote     '"'
214
215   for (; *s; s++)
216     {
217       switch (quoted)
218         {
219         case unquoted:
220           if (isspace (*s))
221             addargument ();
222           else if (*s == singlequote || *s == doublequote)
223             quoted = *s;
224           else
225             addchar (*s);
226           break;
227
228         case singlequote:
229           if (*s == singlequote)
230             quoted = unquoted;
231           else
232             addchar (*s);
233           break;
234
235         case doublequote:
236           assert (s > str || !"cannot be quoted at first char");
237           if (*s == doublequote && *(s - 1) != '\\')
238             quoted = unquoted;
239           else
240             addchar (*s);
241           break;
242
243         default:
244           assert (! "reached");
245         }
246     }
247
248   /* Append the last argument.  */
249   addargument ();
250
251 #undef doublequote
252 #undef singlequote
253 #undef unquoted
254 #undef addargument
255 #undef addchar
256   *r_list = list;
257   return 0;
258 }
259
260
261 /* Like shell_parse_stringlist, but returns an argv vector
262    instead of a strlist.  */
263 static int
264 shell_parse_argv (const char *s, int *r_argc, char ***r_argv)
265 {
266   int i;
267   strlist_t list;
268
269   if (shell_parse_stringlist (s, &list))
270     return 1;
271
272   *r_argc = strlist_length (list);
273   *r_argv = xtrycalloc (*r_argc, sizeof **r_argv);
274   if (*r_argv == NULL)
275     return 1;
276
277   for (i = 0; list; i++)
278     {
279       gpgrt_annotate_leaked_object (list);
280       (*r_argv)[i] = list->d;
281       list = list->next;
282     }
283   gpgrt_annotate_leaked_object (*r_argv);
284   return 0;
285 }
286 \f
287 /* Global flags.  */
288 enum cmd_and_opt_values cmd = 0;
289 int skip_crypto = 0;
290 const char *files_from = NULL;
291 int null_names = 0;
292
293
294 /* Command line parsing.  */
295 static void
296 parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
297 {
298   int no_more_options = 0;
299
300   while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
301     {
302       switch (pargs->r_opt)
303         {
304         case oOutput:    opt.outfile = pargs->r.ret_str; break;
305         case oDirectory: opt.directory = pargs->r.ret_str; break;
306         case oSetFilename: opt.filename = pargs->r.ret_str; break;
307         case oQuiet:     opt.quiet = 1; break;
308         case oVerbose:   opt.verbose++; break;
309         case oNoVerbose: opt.verbose = 0; break;
310         case oFilesFrom: files_from = pargs->r.ret_str; break;
311         case oNull: null_names = 1; break;
312
313         case aList:
314         case aDecrypt:
315         case aEncrypt:
316         case aSign:
317           set_cmd (&cmd, pargs->r_opt);
318           break;
319
320         case oRecipient:
321           add_to_strlist (&opt.recipients, pargs->r.ret_str);
322           break;
323
324         case oUser:
325           opt.user = pargs->r.ret_str;
326           break;
327
328         case oSymmetric:
329           set_cmd (&cmd, aEncrypt);
330           opt.symmetric = 1;
331           break;
332
333         case oGpgProgram:
334           opt.gpg_program = pargs->r.ret_str;
335           break;
336
337         case oSkipCrypto:
338           skip_crypto = 1;
339           break;
340
341         case oOpenPGP: /* Dummy option for now.  */ break;
342         case oCMS:     /* Dummy option for now.  */ break;
343
344         case oGpgArgs:;
345           {
346             strlist_t list;
347             if (shell_parse_stringlist (pargs->r.ret_str, &list))
348               log_error ("failed to parse gpg arguments '%s'\n",
349                          pargs->r.ret_str);
350             else
351               {
352                 if (opt.gpg_arguments)
353                   strlist_last (opt.gpg_arguments)->next = list;
354                 else
355                   opt.gpg_arguments = list;
356               }
357           }
358           break;
359
360         case oTarArgs:;
361           {
362             int tar_argc;
363             char **tar_argv;
364
365             if (shell_parse_argv (pargs->r.ret_str, &tar_argc, &tar_argv))
366               log_error ("failed to parse tar arguments '%s'\n",
367                          pargs->r.ret_str);
368             else
369               {
370                 ARGPARSE_ARGS tar_args;
371                 tar_args.argc = &tar_argc;
372                 tar_args.argv = &tar_argv;
373                 tar_args.flags = ARGPARSE_FLAG_ARG0;
374                 parse_arguments (&tar_args, tar_opts);
375                 if (tar_args.err)
376                   log_error ("unsupported tar arguments '%s'\n",
377                              pargs->r.ret_str);
378                 pargs->err = tar_args.err;
379               }
380           }
381           break;
382
383         case oDryRun:
384           opt.dry_run = 1;
385           break;
386
387         default: pargs->err = 2; break;
388         }
389     }
390 }
391
392 \f
393 /* gpgtar main. */
394 int
395 main (int argc, char **argv)
396 {
397   gpg_error_t err;
398   const char *fname;
399   ARGPARSE_ARGS pargs;
400
401   assert (sizeof (struct ustar_raw_header) == 512);
402
403   gnupg_reopen_std (GPGTAR_NAME);
404   set_strusage (my_strusage);
405   log_set_prefix (GPGTAR_NAME, 1);
406
407   /* Make sure that our subsystems are ready.  */
408   i18n_init();
409   init_common_subsystems (&argc, &argv);
410
411   /* Parse the command line. */
412   pargs.argc  = &argc;
413   pargs.argv  = &argv;
414   pargs.flags = ARGPARSE_FLAG_KEEP;
415   parse_arguments (&pargs, opts);
416
417   if ((files_from && !null_names) || (!files_from && null_names))
418     log_error ("--files-from and --null may only be used in conjunction\n");
419   if (files_from && strcmp (files_from, "-"))
420     log_error ("--files-from only supports argument \"-\"\n");
421
422   if (log_get_errorcount (0))
423     exit (2);
424
425   /* Print a warning if an argument looks like an option.  */
426   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
427     {
428       int i;
429
430       for (i=0; i < argc; i++)
431         if (argv[i][0] == '-' && argv[i][1] == '-')
432           log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
433     }
434
435   if (! opt.gpg_program)
436     opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
437
438   if (opt.verbose > 1)
439     opt.debug_level = 1024;
440
441   switch (cmd)
442     {
443     case aList:
444       if (argc > 1)
445         usage (1);
446       fname = argc ? *argv : NULL;
447       if (opt.filename)
448         log_info ("note: ignoring option --set-filename\n");
449       if (files_from)
450         log_info ("note: ignoring option --files-from\n");
451       err = gpgtar_list (fname, !skip_crypto);
452       if (err && log_get_errorcount (0) == 0)
453         log_error ("listing archive failed: %s\n", gpg_strerror (err));
454       break;
455
456     case aEncrypt:
457     case aSign:
458     case aSignEncrypt:
459       if ((!argc && !null_names)
460           || (argc && null_names))
461         usage (1);
462       if (opt.filename)
463         log_info ("note: ignoring option --set-filename\n");
464       err = gpgtar_create (null_names? NULL :argv,
465                            !skip_crypto
466                            && (cmd == aEncrypt || cmd == aSignEncrypt),
467                            cmd == aSign || cmd == aSignEncrypt);
468       if (err && log_get_errorcount (0) == 0)
469         log_error ("creating archive failed: %s\n", gpg_strerror (err));
470       break;
471
472     case aDecrypt:
473       if (argc != 1)
474         usage (1);
475       if (opt.outfile)
476         log_info ("note: ignoring option --output\n");
477       if (files_from)
478         log_info ("note: ignoring option --files-from\n");
479       fname = argc ? *argv : NULL;
480       err = gpgtar_extract (fname, !skip_crypto);
481       if (err && log_get_errorcount (0) == 0)
482         log_error ("extracting archive failed: %s\n", gpg_strerror (err));
483       break;
484
485     default:
486       log_error (_("invalid command (there is no implicit command)\n"));
487       break;
488     }
489
490   return log_get_errorcount (0)? 1:0;
491 }
492
493
494 /* Read the next record from STREAM.  RECORD is a buffer provided by
495    the caller and must be at leadt of size RECORDSIZE.  The function
496    return 0 on success and and error code on failure; a diagnostic
497    printed as well.  Note that there is no need for an EOF indicator
498    because a tarball has an explicit EOF record. */
499 gpg_error_t
500 read_record (estream_t stream, void *record)
501 {
502   gpg_error_t err;
503   size_t nread;
504
505   nread = es_fread (record, 1, RECORDSIZE, stream);
506   if (nread != RECORDSIZE)
507     {
508       err = gpg_error_from_syserror ();
509       if (es_ferror (stream))
510         log_error ("error reading '%s': %s\n",
511                    es_fname_get (stream), gpg_strerror (err));
512       else
513         log_error ("error reading '%s': premature EOF "
514                    "(size of last record: %zu)\n",
515                    es_fname_get (stream), nread);
516     }
517   else
518     err = 0;
519
520   return err;
521 }
522
523
524 /* Write the RECORD of size RECORDSIZE to STREAM.  FILENAME is the
525    name of the file used for diagnostics.  */
526 gpg_error_t
527 write_record (estream_t stream, const void *record)
528 {
529   gpg_error_t err;
530   size_t nwritten;
531
532   nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
533   if (nwritten != RECORDSIZE)
534     {
535       err = gpg_error_from_syserror ();
536       log_error ("error writing '%s': %s\n",
537                  es_fname_get (stream), gpg_strerror (err));
538     }
539   else
540     err = 0;
541
542   return err;
543 }
544
545
546 /* Return true if FP is an unarmored OpenPGP message.  Note that this
547    function reads a few bytes from FP but pushes them back.  */
548 #if 0
549 static int
550 openpgp_message_p (estream_t fp)
551 {
552   int ctb;
553
554   ctb = es_getc (fp);
555   if (ctb != EOF)
556     {
557       if (es_ungetc (ctb, fp))
558         log_fatal ("error ungetting first byte: %s\n",
559                    gpg_strerror (gpg_error_from_syserror ()));
560
561       if ((ctb & 0x80))
562         {
563           switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
564             {
565             case PKT_MARKER:
566             case PKT_SYMKEY_ENC:
567             case PKT_ONEPASS_SIG:
568             case PKT_PUBKEY_ENC:
569             case PKT_SIGNATURE:
570             case PKT_COMMENT:
571             case PKT_OLD_COMMENT:
572             case PKT_PLAINTEXT:
573             case PKT_COMPRESSED:
574             case PKT_ENCRYPTED:
575               return 1; /* Yes, this seems to be an OpenPGP message.  */
576             default:
577               break;
578             }
579         }
580     }
581   return 0;
582 }
583 #endif