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. */
36 # include <sys/stat.h>
42 #include "../common/openpgpdefs.h"
47 /* Constants to identify the commands and options. */
48 enum cmd_and_opt_values
74 /* The list of commands and options. */
75 static ARGPARSE_OPTS opts[] = {
76 ARGPARSE_group (300, N_("@Commands:\n ")),
78 ARGPARSE_c (aEncrypt, "encrypt", N_("create an archive")),
79 ARGPARSE_c (aDecrypt, "decrypt", N_("extract an archive")),
80 ARGPARSE_c (aSign, "sign", N_("create a signed archive")),
81 ARGPARSE_c (aList, "list-archive", N_("list an archive")),
83 ARGPARSE_group (301, N_("@\nOptions:\n ")),
85 ARGPARSE_s_n (oSymmetric, "symmetric", N_("use symmetric encryption")),
86 ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
87 ARGPARSE_s_s (oUser, "local-user",
88 N_("|USER-ID|use USER-ID to sign or decrypt")),
89 ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
90 ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
91 ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
92 ARGPARSE_s_n (oSkipCrypto, "skip-crypto", N_("skip the crypto processing")),
93 ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
94 ARGPARSE_s_s (oFilesFrom, "files-from",
95 N_("|FILE|get names to create from FILE")),
96 ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
97 ARGPARSE_s_n (oOpenPGP, "openpgp", "@"),
98 ARGPARSE_s_n (oCMS, "cms", "@"),
105 static void tar_and_encrypt (char **inpattern);
106 static void decrypt_and_untar (const char *fname);
107 static void decrypt_and_list (const char *fname);
112 /* Print usage information and and provide strings for help. */
114 my_strusage( int level )
120 case 11: p = "gpgtar (GnuPG)";
122 case 13: p = VERSION; break;
123 case 17: p = PRINTABLE_OS_NAME; break;
124 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
128 p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
131 p = _("Syntax: gpgtar [options] [files] [directories]\n"
132 "Encrypt or sign files into an archive\n");
135 default: p = NULL; break;
142 set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
144 enum cmd_and_opt_values cmd = *ret_cmd;
146 if (!cmd || cmd == new_cmd)
148 else if (cmd == aSign && new_cmd == aEncrypt)
150 else if (cmd == aEncrypt && new_cmd == aSign)
154 log_error (_("conflicting commands\n"));
165 main (int argc, char **argv)
169 int no_more_options = 0;
170 enum cmd_and_opt_values cmd = 0;
172 const char *files_from = NULL;
175 assert (sizeof (struct ustar_raw_header) == 512);
177 gnupg_reopen_std ("gpgtar");
178 set_strusage (my_strusage);
179 log_set_prefix ("gpgtar", 1);
181 /* Make sure that our subsystems are ready. */
183 init_common_subsystems ();
185 /* Parse the command line. */
188 pargs.flags = ARGPARSE_FLAG_KEEP;
189 while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
193 case oOutput: opt.outfile = pargs.r.ret_str; break;
194 case oSetFilename: opt.filename = pargs.r.ret_str; break;
195 case oQuiet: opt.quiet = 1; break;
196 case oVerbose: opt.verbose++; break;
197 case oNoVerbose: opt.verbose = 0; break;
198 case oFilesFrom: files_from = pargs.r.ret_str; break;
199 case oNull: null_names = 1; break;
205 set_cmd (&cmd, pargs.r_opt);
209 set_cmd (&cmd, aEncrypt);
217 case oOpenPGP: /* Dummy option for now. */ break;
218 case oCMS: /* Dummy option for now. */ break;
220 default: pargs.err = 2; break;
224 if ((files_from && !null_names) || (!files_from && null_names))
225 log_error ("--files-from and --null may only be used in conjunction\n");
226 if (files_from && strcmp (files_from, "-"))
227 log_error ("--files-from only supports argument \"-\"\n");
229 if (log_get_errorcount (0))
237 fname = argc ? *argv : NULL;
239 log_info ("note: ignoring option --set-filename\n");
241 log_info ("note: ignoring option --files-from\n");
245 decrypt_and_list (fname);
249 if ((!argc && !null_names)
250 || (argc && null_names))
253 log_info ("note: ignoring option --set-filename\n");
255 gpgtar_create (null_names? NULL :argv);
257 tar_and_encrypt (null_names? NULL : argv);
264 log_info ("note: ignoring option --output\n");
266 log_info ("note: ignoring option --files-from\n");
267 fname = argc ? *argv : NULL;
269 gpgtar_extract (fname);
271 decrypt_and_untar (fname);
275 log_error (_("invalid command (there is no implicit command)\n"));
279 return log_get_errorcount (0)? 1:0;
283 /* Read the next record from STREAM. RECORD is a buffer provided by
284 the caller and must be at leadt of size RECORDSIZE. The function
285 return 0 on success and and error code on failure; a diagnostic
286 printed as well. Note that there is no need for an EOF indicator
287 because a tarball has an explicit EOF record. */
289 read_record (estream_t stream, void *record)
294 nread = es_fread (record, 1, RECORDSIZE, stream);
295 if (nread != RECORDSIZE)
297 err = gpg_error_from_syserror ();
298 if (es_ferror (stream))
299 log_error ("error reading `%s': %s\n",
300 es_fname_get (stream), gpg_strerror (err));
302 log_error ("error reading `%s': premature EOF "
303 "(size of last record: %zu)\n",
304 es_fname_get (stream), nread);
313 /* Write the RECORD of size RECORDSIZE to STREAM. FILENAME is the
314 name of the file used for diagnostics. */
316 write_record (estream_t stream, const void *record)
321 nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
322 if (nwritten != RECORDSIZE)
324 err = gpg_error_from_syserror ();
325 log_error ("error writing `%s': %s\n",
326 es_fname_get (stream), gpg_strerror (err));
335 /* Return true if FP is an unarmored OpenPGP message. Note that this
336 fucntion reads a few bytes from FP but pushes them back. */
339 openpgp_message_p (estream_t fp)
346 if (es_ungetc (ctb, fp))
347 log_fatal ("error ungetting first byte: %s\n",
348 gpg_strerror (gpg_error_from_syserror ()));
352 switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
356 case PKT_ONEPASS_SIG:
360 case PKT_OLD_COMMENT:
364 return 1; /* Yes, this seems to be an OpenPGP message. */
378 tar_and_encrypt (char **inpattern)
381 log_error ("tar_and_encrypt has not yet been implemented\n");
387 decrypt_and_untar (const char *fname)
390 log_error ("decrypt_and_untar has not yet been implemented\n");
396 decrypt_and_list (const char *fname)
399 log_error ("decrypt_and_list has not yet been implemented\n");
405 /* A wrapper around mkdir which takes a string for the mode argument.
406 This makes it easier to handle the mode argument which is not
407 defined on all systems. The format of the modestring is
411 '-' is a don't care or not set. 'r', 'w', 'x' are read allowed,
412 write allowed, execution allowed with the first group for the user,
413 the second for the group and the third for all others. If the
414 string is shorter than above the missing mode characters are meant
417 gnupg_mkdir (const char *name, const char *modestr)
419 #ifdef HAVE_W32CE_SYSTEM
423 wname = utf8_to_wchar (name);
426 if (!CreateDirectoryW (wname, NULL))
429 return -1; /* ERRNO is automagically provided by gpg-error.h. */
433 #elif MKDIR_TAKES_ONE_ARG
435 /* Note: In the case of W32 we better use CreateDirectory and try to
436 set appropriate permissions. However using mkdir is easier
437 because this sets ERRNO. */
442 if (modestr && *modestr)
445 if (*modestr && *modestr++ == 'r')
447 if (*modestr && *modestr++ == 'w')
449 if (*modestr && *modestr++ == 'x')
451 if (*modestr && *modestr++ == 'r')
453 if (*modestr && *modestr++ == 'w')
455 if (*modestr && *modestr++ == 'x')
457 if (*modestr && *modestr++ == 'r')
459 if (*modestr && *modestr++ == 'w')
461 if (*modestr && *modestr++ == 'x')
464 return mkdir (name, mode);
468 #ifdef HAVE_W32_SYSTEM
469 /* Return a malloced string encoded in UTF-8 from the wide char input
470 string STRING. Caller must free this value. Returns NULL and sets
471 ERRNO on failure. Calling this function with STRING set to NULL is
474 wchar_to_utf8 (const wchar_t *string)
479 n = WideCharToMultiByte (CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
486 result = xtrymalloc (n+1);
490 n = WideCharToMultiByte (CP_UTF8, 0, string, -1, result, n, NULL, NULL);
501 /* Return a malloced wide char string from an UTF-8 encoded input
502 string STRING. Caller must free this value. Returns NULL and sets
503 ERRNO on failure. Calling this function with STRING set to NULL is
506 utf8_to_wchar (const char *string)
512 n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0);
519 nbytes = (size_t)(n+1) * sizeof(*result);
520 if (nbytes / sizeof(*result) != (n+1))
525 result = xtrymalloc (nbytes);
529 n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n);
538 #endif /*HAVE_W32_SYSTEM*/