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