Fix typos found using codespell.
[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 <errno.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <assert.h>
35
36 #include "util.h"
37 #include "i18n.h"
38 #include "sysutils.h"
39 #include "../common/openpgpdefs.h"
40 #include "../common/init.h"
41
42 #include "gpgtar.h"
43
44
45 /* Constants to identify the commands and options. */
46 enum cmd_and_opt_values
47   {
48     aNull = 0,
49     aEncrypt    = 'e',
50     aDecrypt    = 'd',
51     aSign       = 's',
52     aList       = 't',
53
54     oSymmetric  = 'c',
55     oRecipient  = 'r',
56     oUser       = 'u',
57     oOutput     = 'o',
58     oQuiet      = 'q',
59     oVerbose    = 'v',
60     oFilesFrom  = 'T',
61     oNoVerbose  = 500,
62
63     aSignEncrypt,
64     oSkipCrypto,
65     oOpenPGP,
66     oCMS,
67     oSetFilename,
68     oNull
69   };
70
71
72 /* The list of commands and options. */
73 static ARGPARSE_OPTS opts[] = {
74   ARGPARSE_group (300, N_("@Commands:\n ")),
75
76   ARGPARSE_c (aEncrypt,   "encrypt", N_("create an archive")),
77   ARGPARSE_c (aDecrypt,   "decrypt", N_("extract an archive")),
78   ARGPARSE_c (aSign,      "sign",    N_("create a signed archive")),
79   ARGPARSE_c (aList,      "list-archive", N_("list an archive")),
80
81   ARGPARSE_group (301, N_("@\nOptions:\n ")),
82
83   ARGPARSE_s_n (oSymmetric, "symmetric", N_("use symmetric encryption")),
84   ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")),
85   ARGPARSE_s_s (oUser, "local-user",
86                 N_("|USER-ID|use USER-ID to sign or decrypt")),
87   ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")),
88   ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
89   ARGPARSE_s_n (oQuiet, "quiet",  N_("be somewhat more quiet")),
90   ARGPARSE_s_n (oSkipCrypto, "skip-crypto", N_("skip the crypto processing")),
91   ARGPARSE_s_s (oSetFilename, "set-filename", "@"),
92   ARGPARSE_s_s (oFilesFrom, "files-from",
93                 N_("|FILE|get names to create from FILE")),
94   ARGPARSE_s_n (oNull, "null", N_("-T reads null-terminated names")),
95   ARGPARSE_s_n (oOpenPGP, "openpgp", "@"),
96   ARGPARSE_s_n (oCMS, "cms", "@"),
97
98   ARGPARSE_end ()
99 };
100
101
102 \f
103 static void tar_and_encrypt (char **inpattern);
104 static void decrypt_and_untar (const char *fname);
105 static void decrypt_and_list (const char *fname);
106
107
108
109 \f
110 /* Print usage information and and provide strings for help. */
111 static const char *
112 my_strusage( int level )
113 {
114   const char *p;
115
116   switch (level)
117     {
118     case 11: p = "@GPGTAR@ (@GNUPG@)";
119       break;
120     case 13: p = VERSION; break;
121     case 17: p = PRINTABLE_OS_NAME; break;
122     case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
123
124     case 1:
125     case 40:
126       p = _("Usage: gpgtar [options] [files] [directories] (-h for help)");
127       break;
128     case 41:
129       p = _("Syntax: gpgtar [options] [files] [directories]\n"
130             "Encrypt or sign files into an archive\n");
131       break;
132
133     default: p = NULL; break;
134     }
135   return p;
136 }
137
138
139 static void
140 set_cmd (enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd)
141 {
142   enum cmd_and_opt_values cmd = *ret_cmd;
143
144   if (!cmd || cmd == new_cmd)
145     cmd = new_cmd;
146   else if (cmd == aSign && new_cmd == aEncrypt)
147     cmd = aSignEncrypt;
148   else if (cmd == aEncrypt && new_cmd == aSign)
149     cmd = aSignEncrypt;
150   else
151     {
152       log_error (_("conflicting commands\n"));
153       exit (2);
154     }
155
156   *ret_cmd = cmd;
157 }
158
159
160 \f
161 /* gpgtar main. */
162 int
163 main (int argc, char **argv)
164 {
165   ARGPARSE_ARGS pargs;
166   const char *fname;
167   int no_more_options = 0;
168   enum cmd_and_opt_values cmd = 0;
169   int skip_crypto = 0;
170   const char *files_from = NULL;
171   int null_names = 0;
172
173   assert (sizeof (struct ustar_raw_header) == 512);
174
175   gnupg_reopen_std (GPGTAR_NAME);
176   set_strusage (my_strusage);
177   log_set_prefix (GPGTAR_NAME, 1);
178
179   /* Make sure that our subsystems are ready.  */
180   i18n_init();
181   init_common_subsystems (&argc, &argv);
182
183   /* Parse the command line. */
184   pargs.argc  = &argc;
185   pargs.argv  = &argv;
186   pargs.flags = ARGPARSE_FLAG_KEEP;
187   while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
188     {
189       switch (pargs.r_opt)
190         {
191         case oOutput:    opt.outfile = pargs.r.ret_str; break;
192         case oSetFilename: opt.filename = pargs.r.ret_str; break;
193         case oQuiet:     opt.quiet = 1; break;
194         case oVerbose:   opt.verbose++; break;
195         case oNoVerbose: opt.verbose = 0; break;
196         case oFilesFrom: files_from = pargs.r.ret_str; break;
197         case oNull: null_names = 1; break;
198
199         case aList:
200         case aDecrypt:
201         case aEncrypt:
202         case aSign:
203           set_cmd (&cmd, pargs.r_opt);
204           break;
205
206         case oSymmetric:
207           set_cmd (&cmd, aEncrypt);
208           opt.symmetric = 1;
209           break;
210
211         case oSkipCrypto:
212           skip_crypto = 1;
213           break;
214
215         case oOpenPGP: /* Dummy option for now.  */ break;
216         case oCMS:     /* Dummy option for now.  */ break;
217
218         default: pargs.err = 2; break;
219         }
220     }
221
222   if ((files_from && !null_names) || (!files_from && null_names))
223     log_error ("--files-from and --null may only be used in conjunction\n");
224   if (files_from && strcmp (files_from, "-"))
225     log_error ("--files-from only supports argument \"-\"\n");
226
227   if (log_get_errorcount (0))
228     exit (2);
229
230   /* Print a warning if an argument looks like an option.  */
231   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
232     {
233       int i;
234
235       for (i=0; i < argc; i++)
236         if (argv[i][0] == '-' && argv[i][1] == '-')
237           log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
238     }
239
240   switch (cmd)
241     {
242     case aList:
243       if (argc > 1)
244         usage (1);
245       fname = argc ? *argv : NULL;
246       if (opt.filename)
247         log_info ("note: ignoring option --set-filename\n");
248       if (files_from)
249         log_info ("note: ignoring option --files-from\n");
250       if (skip_crypto)
251         gpgtar_list (fname);
252       else
253         decrypt_and_list (fname);
254       break;
255
256     case aEncrypt:
257       if ((!argc && !null_names)
258           || (argc && null_names))
259         usage (1);
260       if (opt.filename)
261         log_info ("note: ignoring option --set-filename\n");
262       if (skip_crypto)
263         gpgtar_create (null_names? NULL :argv);
264       else
265         tar_and_encrypt (null_names? NULL : argv);
266       break;
267
268     case aDecrypt:
269       if (argc != 1)
270         usage (1);
271       if (opt.outfile)
272         log_info ("note: ignoring option --output\n");
273       if (files_from)
274         log_info ("note: ignoring option --files-from\n");
275       fname = argc ? *argv : NULL;
276       if (skip_crypto)
277         gpgtar_extract (fname);
278       else
279         decrypt_and_untar (fname);
280       break;
281
282     default:
283       log_error (_("invalid command (there is no implicit command)\n"));
284       break;
285     }
286
287   return log_get_errorcount (0)? 1:0;
288 }
289
290
291 /* Read the next record from STREAM.  RECORD is a buffer provided by
292    the caller and must be at leadt of size RECORDSIZE.  The function
293    return 0 on success and and error code on failure; a diagnostic
294    printed as well.  Note that there is no need for an EOF indicator
295    because a tarball has an explicit EOF record. */
296 gpg_error_t
297 read_record (estream_t stream, void *record)
298 {
299   gpg_error_t err;
300   size_t nread;
301
302   nread = es_fread (record, 1, RECORDSIZE, stream);
303   if (nread != RECORDSIZE)
304     {
305       err = gpg_error_from_syserror ();
306       if (es_ferror (stream))
307         log_error ("error reading '%s': %s\n",
308                    es_fname_get (stream), gpg_strerror (err));
309       else
310         log_error ("error reading '%s': premature EOF "
311                    "(size of last record: %zu)\n",
312                    es_fname_get (stream), nread);
313     }
314   else
315     err = 0;
316
317   return err;
318 }
319
320
321 /* Write the RECORD of size RECORDSIZE to STREAM.  FILENAME is the
322    name of the file used for diagnostics.  */
323 gpg_error_t
324 write_record (estream_t stream, const void *record)
325 {
326   gpg_error_t err;
327   size_t nwritten;
328
329   nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
330   if (nwritten != RECORDSIZE)
331     {
332       err = gpg_error_from_syserror ();
333       log_error ("error writing '%s': %s\n",
334                  es_fname_get (stream), gpg_strerror (err));
335     }
336   else
337     err = 0;
338
339   return err;
340 }
341
342
343 /* Return true if FP is an unarmored OpenPGP message.  Note that this
344    function reads a few bytes from FP but pushes them back.  */
345 #if 0
346 static int
347 openpgp_message_p (estream_t fp)
348 {
349   int ctb;
350
351   ctb = es_getc (fp);
352   if (ctb != EOF)
353     {
354       if (es_ungetc (ctb, fp))
355         log_fatal ("error ungetting first byte: %s\n",
356                    gpg_strerror (gpg_error_from_syserror ()));
357
358       if ((ctb & 0x80))
359         {
360           switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
361             {
362             case PKT_MARKER:
363             case PKT_SYMKEY_ENC:
364             case PKT_ONEPASS_SIG:
365             case PKT_PUBKEY_ENC:
366             case PKT_SIGNATURE:
367             case PKT_COMMENT:
368             case PKT_OLD_COMMENT:
369             case PKT_PLAINTEXT:
370             case PKT_COMPRESSED:
371             case PKT_ENCRYPTED:
372               return 1; /* Yes, this seems to be an OpenPGP message.  */
373             default:
374               break;
375             }
376         }
377     }
378   return 0;
379 }
380 #endif
381
382
383
384 \f
385 static void
386 tar_and_encrypt (char **inpattern)
387 {
388   (void)inpattern;
389   log_error ("tar_and_encrypt has not yet been implemented\n");
390 }
391
392
393 \f
394 static void
395 decrypt_and_untar (const char *fname)
396 {
397   (void)fname;
398   log_error ("decrypt_and_untar has not yet been implemented\n");
399 }
400
401
402 \f
403 static void
404 decrypt_and_list (const char *fname)
405 {
406   (void)fname;
407   log_error ("decrypt_and_list has not yet been implemented\n");
408 }