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