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. */
41 #include "../common/openpgpdefs.h"
42 #include "../common/init.h"
43 #include "../common/strlist.h"
48 /* Constants to identify the commands and options. */
49 enum cmd_and_opt_values
75 /* Compatibility with gpg-zip. */
84 /* The list of commands and options. */
85 static ARGPARSE_OPTS opts[] = {
86 ARGPARSE_group (300, N_("@Commands:\n ")),
88 ARGPARSE_c (aEncrypt, "encrypt", N_("create an archive")),
89 ARGPARSE_c (aDecrypt, "decrypt", N_("extract an archive")),
90 ARGPARSE_c (aSign, "sign", N_("create a signed archive")),
91 ARGPARSE_c (aList, "list-archive", N_("list an archive")),
93 ARGPARSE_group (301, N_("@\nOptions:\n ")),
95 ARGPARSE_s_n (oSymmetric, "symmetric", N_("use symmetric encryption")),
96 ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
97 ARGPARSE_s_s (oUser, "local-user",
98 N_("|USER-ID|use USER-ID to sign or decrypt")),
99 ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
100 ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
101 ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
102 ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
103 ARGPARSE_s_n (oSkipCrypto, "skip-crypto", N_("skip the crypto processing")),
104 ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")),
105 ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
106 ARGPARSE_s_n (oOpenPGP, "openpgp", "@"),
107 ARGPARSE_s_n (oCMS, "cms", "@"),
109 ARGPARSE_group (302, N_("@\nTar options:\n ")),
111 ARGPARSE_s_s (oDirectory, "directory",
112 N_("|DIRECTORY|extract files into DIRECTORY")),
113 ARGPARSE_s_s (oFilesFrom, "files-from",
114 N_("|FILE|get names to create from FILE")),
115 ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
117 ARGPARSE_s_s (oGpgArgs, "gpg-args", "@"),
118 ARGPARSE_s_s (oTarArgs, "tar-args", "@"),
124 /* The list of commands and options for tar that we understand. */
125 static ARGPARSE_OPTS tar_opts[] = {
126 ARGPARSE_s_s (oDirectory, "directory",
127 N_("|DIRECTORY|extract files into DIRECTORY")),
128 ARGPARSE_s_s (oFilesFrom, "files-from",
129 N_("|FILE|get names to create from FILE")),
130 ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
137 /* Print usage information and and provide strings for help. */
139 my_strusage( int level )
145 case 11: p = "@GPGTAR@ (@GNUPG@)";
147 case 13: p = VERSION; break;
148 case 17: p = PRINTABLE_OS_NAME; break;
149 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
153 p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
156 p = _("Syntax: gpgtar [options] [files] [directories]\n"
157 "Encrypt or sign files into an archive\n");
160 default: p = NULL; break;
167 set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
169 enum cmd_and_opt_values cmd = *ret_cmd;
171 if (!cmd || cmd == new_cmd)
173 else if (cmd == aSign && new_cmd == aEncrypt)
175 else if (cmd == aEncrypt && new_cmd == aSign)
179 log_error (_("conflicting commands\n"));
186 /* Shell-like argument splitting.
188 For compatibility with gpg-zip we accept arguments for GnuPG and
189 tar given as a string argument to '--gpg-args' and '--tar-args'.
190 gpg-zip was implemented as a Bourne Shell script, and therefore, we
191 need to split the string the same way the shell would. */
193 shell_parse_stringlist (const char *str, strlist_t *r_list)
195 strlist_t list = NULL;
201 do { if (p - arg + 2 < sizeof arg) *p++ = (c); else return 1; } while (0)
202 #define addargument() \
207 append_to_strlist (&list, arg); \
213 #define singlequote '\''
214 #define doublequote '"'
223 else if (*s == singlequote || *s == doublequote)
230 if (*s == singlequote)
237 assert (s > str || !"cannot be quoted at first char");
238 if (*s == doublequote && *(s - 1) != '\\')
245 assert (! "reached");
249 /* Append the last argument. */
262 /* Like shell_parse_stringlist, but returns an argv vector
263 instead of a strlist. */
265 shell_parse_argv (const char *s, int *r_argc, char ***r_argv)
270 if (shell_parse_stringlist (s, &list))
273 *r_argc = strlist_length (list);
274 *r_argv = xtrycalloc (*r_argc, sizeof **r_argv);
278 for (i = 0; list; i++)
279 (*r_argv)[i] = list->d, list = list->next;
284 enum cmd_and_opt_values cmd = 0;
286 const char *files_from = NULL;
290 /* Command line parsing. */
292 parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
294 int no_more_options = 0;
296 while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
298 switch (pargs->r_opt)
300 case oOutput: opt.outfile = pargs->r.ret_str; break;
301 case oDirectory: opt.directory = pargs->r.ret_str; break;
302 case oSetFilename: opt.filename = pargs->r.ret_str; break;
303 case oQuiet: opt.quiet = 1; break;
304 case oVerbose: opt.verbose++; break;
305 case oNoVerbose: opt.verbose = 0; break;
306 case oFilesFrom: files_from = pargs->r.ret_str; break;
307 case oNull: null_names = 1; break;
313 set_cmd (&cmd, pargs->r_opt);
317 add_to_strlist (&opt.recipients, pargs->r.ret_str);
321 opt.user = pargs->r.ret_str;
325 set_cmd (&cmd, aEncrypt);
330 opt.gpg_program = pargs->r.ret_str;
337 case oOpenPGP: /* Dummy option for now. */ break;
338 case oCMS: /* Dummy option for now. */ break;
343 if (shell_parse_stringlist (pargs->r.ret_str, &list))
344 log_error ("failed to parse gpg arguments '%s'\n",
348 if (opt.gpg_arguments)
349 strlist_last (opt.gpg_arguments)->next = list;
351 opt.gpg_arguments = list;
361 if (shell_parse_argv (pargs->r.ret_str, &tar_argc, &tar_argv))
362 log_error ("failed to parse tar arguments '%s'\n",
366 ARGPARSE_ARGS tar_args;
367 tar_args.argc = &tar_argc;
368 tar_args.argv = &tar_argv;
369 tar_args.flags = ARGPARSE_FLAG_ARG0;
370 parse_arguments (&tar_args, tar_opts);
372 log_error ("unsupported tar arguments '%s'\n",
374 pargs->err = tar_args.err;
383 default: pargs->err = 2; break;
391 main (int argc, char **argv)
397 assert (sizeof (struct ustar_raw_header) == 512);
399 gnupg_reopen_std (GPGTAR_NAME);
400 set_strusage (my_strusage);
401 log_set_prefix (GPGTAR_NAME, 1);
403 /* Make sure that our subsystems are ready. */
405 init_common_subsystems (&argc, &argv);
407 /* Parse the command line. */
410 pargs.flags = ARGPARSE_FLAG_KEEP;
411 parse_arguments (&pargs, opts);
413 if ((files_from && !null_names) || (!files_from && null_names))
414 log_error ("--files-from and --null may only be used in conjunction\n");
415 if (files_from && strcmp (files_from, "-"))
416 log_error ("--files-from only supports argument \"-\"\n");
418 if (log_get_errorcount (0))
421 /* Print a warning if an argument looks like an option. */
422 if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
426 for (i=0; i < argc; i++)
427 if (argv[i][0] == '-' && argv[i][1] == '-')
428 log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
431 if (! opt.gpg_program)
432 opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
435 opt.debug_level = 1024;
442 fname = argc ? *argv : NULL;
444 log_info ("note: ignoring option --set-filename\n");
446 log_info ("note: ignoring option --files-from\n");
447 err = gpgtar_list (fname, !skip_crypto);
448 if (err && log_get_errorcount (0) == 0)
449 log_error ("listing archive failed: %s\n", gpg_strerror (err));
455 if ((!argc && !null_names)
456 || (argc && null_names))
459 log_info ("note: ignoring option --set-filename\n");
460 err = gpgtar_create (null_names? NULL :argv,
462 && (cmd == aEncrypt || cmd == aSignEncrypt),
463 cmd == aSign || cmd == aSignEncrypt);
464 if (err && log_get_errorcount (0) == 0)
465 log_error ("creating archive failed: %s\n", gpg_strerror (err));
472 log_info ("note: ignoring option --output\n");
474 log_info ("note: ignoring option --files-from\n");
475 fname = argc ? *argv : NULL;
476 err = gpgtar_extract (fname, !skip_crypto);
477 if (err && log_get_errorcount (0) == 0)
478 log_error ("extracting archive failed: %s\n", gpg_strerror (err));
482 log_error (_("invalid command (there is no implicit command)\n"));
486 return log_get_errorcount (0)? 1:0;
490 /* Read the next record from STREAM. RECORD is a buffer provided by
491 the caller and must be at leadt of size RECORDSIZE. The function
492 return 0 on success and and error code on failure; a diagnostic
493 printed as well. Note that there is no need for an EOF indicator
494 because a tarball has an explicit EOF record. */
496 read_record (estream_t stream, void *record)
501 nread = es_fread (record, 1, RECORDSIZE, stream);
502 if (nread != RECORDSIZE)
504 err = gpg_error_from_syserror ();
505 if (es_ferror (stream))
506 log_error ("error reading '%s': %s\n",
507 es_fname_get (stream), gpg_strerror (err));
509 log_error ("error reading '%s': premature EOF "
510 "(size of last record: %zu)\n",
511 es_fname_get (stream), nread);
520 /* Write the RECORD of size RECORDSIZE to STREAM. FILENAME is the
521 name of the file used for diagnostics. */
523 write_record (estream_t stream, const void *record)
528 nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
529 if (nwritten != RECORDSIZE)
531 err = gpg_error_from_syserror ();
532 log_error ("error writing '%s': %s\n",
533 es_fname_get (stream), gpg_strerror (err));
542 /* Return true if FP is an unarmored OpenPGP message. Note that this
543 function reads a few bytes from FP but pushes them back. */
546 openpgp_message_p (estream_t fp)
553 if (es_ungetc (ctb, fp))
554 log_fatal ("error ungetting first byte: %s\n",
555 gpg_strerror (gpg_error_from_syserror ()));
559 switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
563 case PKT_ONEPASS_SIG:
567 case PKT_OLD_COMMENT:
571 return 1; /* Yes, this seems to be an OpenPGP message. */