1 /* gpgtar.c - A simple TAR implementation mainly useful for Windows.
2 * Copyright (C) 2010 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
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. */
40 #include "../common/openpgpdefs.h"
41 #include "../common/init.h"
42 #include "../common/strlist.h"
47 /* Constants to identify the commands and options. */
48 enum cmd_and_opt_values
74 /* Compatibility with gpg-zip. */
83 /* The list of commands and options. */
84 static ARGPARSE_OPTS opts[] = {
85 ARGPARSE_group (300, N_("@Commands:\n ")),
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")),
92 ARGPARSE_group (301, N_("@\nOptions:\n ")),
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", "@"),
108 ARGPARSE_group (302, N_("@\nTar options:\n ")),
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")),
116 ARGPARSE_s_s (oGpgArgs, "gpg-args", "@"),
117 ARGPARSE_s_s (oTarArgs, "tar-args", "@"),
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")),
136 /* Print usage information and and provide strings for help. */
138 my_strusage( int level )
144 case 11: p = "@GPGTAR@ (@GNUPG@)";
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;
152 p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
155 p = _("Syntax: gpgtar [options] [files] [directories]\n"
156 "Encrypt or sign files into an archive\n");
159 default: p = NULL; break;
166 set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
168 enum cmd_and_opt_values cmd = *ret_cmd;
170 if (!cmd || cmd == new_cmd)
172 else if (cmd == aSign && new_cmd == aEncrypt)
174 else if (cmd == aEncrypt && new_cmd == aSign)
178 log_error (_("conflicting commands\n"));
185 /* Shell-like argument splitting.
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. */
192 shell_parse_stringlist (const char *str, strlist_t *r_list)
194 strlist_t list = NULL;
200 do { if (p - arg + 2 < sizeof arg) *p++ = (c); else return 1; } while (0)
201 #define addargument() \
206 append_to_strlist (&list, arg); \
212 #define singlequote '\''
213 #define doublequote '"'
222 else if (*s == singlequote || *s == doublequote)
229 if (*s == singlequote)
236 assert (s > str || !"cannot be quoted at first char");
237 if (*s == doublequote && *(s - 1) != '\\')
244 assert (! "reached");
248 /* Append the last argument. */
261 /* Like shell_parse_stringlist, but returns an argv vector
262 instead of a strlist. */
264 shell_parse_argv (const char *s, int *r_argc, char ***r_argv)
269 if (shell_parse_stringlist (s, &list))
272 *r_argc = strlist_length (list);
273 *r_argv = xtrycalloc (*r_argc, sizeof **r_argv);
277 for (i = 0; list; i++)
278 (*r_argv)[i] = list->d, list = list->next;
283 enum cmd_and_opt_values cmd = 0;
285 const char *files_from = NULL;
289 /* Command line parsing. */
291 parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
293 int no_more_options = 0;
295 while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
297 switch (pargs->r_opt)
299 case oOutput: opt.outfile = pargs->r.ret_str; break;
300 case oDirectory: opt.directory = pargs->r.ret_str; break;
301 case oSetFilename: opt.filename = pargs->r.ret_str; break;
302 case oQuiet: opt.quiet = 1; break;
303 case oVerbose: opt.verbose++; break;
304 case oNoVerbose: opt.verbose = 0; break;
305 case oFilesFrom: files_from = pargs->r.ret_str; break;
306 case oNull: null_names = 1; break;
312 set_cmd (&cmd, pargs->r_opt);
316 add_to_strlist (&opt.recipients, pargs->r.ret_str);
320 opt.user = pargs->r.ret_str;
324 set_cmd (&cmd, aEncrypt);
329 opt.gpg_program = pargs->r.ret_str;
336 case oOpenPGP: /* Dummy option for now. */ break;
337 case oCMS: /* Dummy option for now. */ break;
342 if (shell_parse_stringlist (pargs->r.ret_str, &list))
343 log_error ("failed to parse gpg arguments '%s'\n",
347 if (opt.gpg_arguments)
348 strlist_last (opt.gpg_arguments)->next = list;
350 opt.gpg_arguments = list;
360 if (shell_parse_argv (pargs->r.ret_str, &tar_argc, &tar_argv))
361 log_error ("failed to parse tar arguments '%s'\n",
365 ARGPARSE_ARGS tar_args;
366 tar_args.argc = &tar_argc;
367 tar_args.argv = &tar_argv;
368 tar_args.flags = ARGPARSE_FLAG_ARG0;
369 parse_arguments (&tar_args, tar_opts);
371 log_error ("unsupported tar arguments '%s'\n",
373 pargs->err = tar_args.err;
382 default: pargs->err = 2; break;
390 main (int argc, char **argv)
396 assert (sizeof (struct ustar_raw_header) == 512);
398 gnupg_reopen_std (GPGTAR_NAME);
399 set_strusage (my_strusage);
400 log_set_prefix (GPGTAR_NAME, 1);
402 /* Make sure that our subsystems are ready. */
404 init_common_subsystems (&argc, &argv);
406 /* Parse the command line. */
409 pargs.flags = ARGPARSE_FLAG_KEEP;
410 parse_arguments (&pargs, opts);
412 if ((files_from && !null_names) || (!files_from && null_names))
413 log_error ("--files-from and --null may only be used in conjunction\n");
414 if (files_from && strcmp (files_from, "-"))
415 log_error ("--files-from only supports argument \"-\"\n");
417 if (log_get_errorcount (0))
420 /* Print a warning if an argument looks like an option. */
421 if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
425 for (i=0; i < argc; i++)
426 if (argv[i][0] == '-' && argv[i][1] == '-')
427 log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
430 if (! opt.gpg_program)
431 opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
434 opt.debug_level = 1024;
441 fname = argc ? *argv : NULL;
443 log_info ("note: ignoring option --set-filename\n");
445 log_info ("note: ignoring option --files-from\n");
446 err = gpgtar_list (fname, !skip_crypto);
447 if (err && log_get_errorcount (0) == 0)
448 log_error ("listing archive failed: %s\n", gpg_strerror (err));
454 if ((!argc && !null_names)
455 || (argc && null_names))
458 log_info ("note: ignoring option --set-filename\n");
459 err = gpgtar_create (null_names? NULL :argv,
461 && (cmd == aEncrypt || cmd == aSignEncrypt),
462 cmd == aSign || cmd == aSignEncrypt);
463 if (err && log_get_errorcount (0) == 0)
464 log_error ("creating archive failed: %s\n", gpg_strerror (err));
471 log_info ("note: ignoring option --output\n");
473 log_info ("note: ignoring option --files-from\n");
474 fname = argc ? *argv : NULL;
475 err = gpgtar_extract (fname, !skip_crypto);
476 if (err && log_get_errorcount (0) == 0)
477 log_error ("extracting archive failed: %s\n", gpg_strerror (err));
481 log_error (_("invalid command (there is no implicit command)\n"));
485 return log_get_errorcount (0)? 1:0;
489 /* Read the next record from STREAM. RECORD is a buffer provided by
490 the caller and must be at leadt of size RECORDSIZE. The function
491 return 0 on success and and error code on failure; a diagnostic
492 printed as well. Note that there is no need for an EOF indicator
493 because a tarball has an explicit EOF record. */
495 read_record (estream_t stream, void *record)
500 nread = es_fread (record, 1, RECORDSIZE, stream);
501 if (nread != RECORDSIZE)
503 err = gpg_error_from_syserror ();
504 if (es_ferror (stream))
505 log_error ("error reading '%s': %s\n",
506 es_fname_get (stream), gpg_strerror (err));
508 log_error ("error reading '%s': premature EOF "
509 "(size of last record: %zu)\n",
510 es_fname_get (stream), nread);
519 /* Write the RECORD of size RECORDSIZE to STREAM. FILENAME is the
520 name of the file used for diagnostics. */
522 write_record (estream_t stream, const void *record)
527 nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
528 if (nwritten != RECORDSIZE)
530 err = gpg_error_from_syserror ();
531 log_error ("error writing '%s': %s\n",
532 es_fname_get (stream), gpg_strerror (err));
541 /* Return true if FP is an unarmored OpenPGP message. Note that this
542 function reads a few bytes from FP but pushes them back. */
545 openpgp_message_p (estream_t fp)
552 if (es_ungetc (ctb, fp))
553 log_fatal ("error ungetting first byte: %s\n",
554 gpg_strerror (gpg_error_from_syserror ()));
558 switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
562 case PKT_ONEPASS_SIG:
566 case PKT_OLD_COMMENT:
570 return 1; /* Yes, this seems to be an OpenPGP message. */