Imported Upstream version 2.1.11
[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 <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           opt.user = pargs->r.ret_str;
322           break;
323
324         case oSymmetric:
325           set_cmd (&cmd, aEncrypt);
326           opt.symmetric = 1;
327           break;
328
329         case oGpgProgram:
330           opt.gpg_program = pargs->r.ret_str;
331           break;
332
333         case oSkipCrypto:
334           skip_crypto = 1;
335           break;
336
337         case oOpenPGP: /* Dummy option for now.  */ break;
338         case oCMS:     /* Dummy option for now.  */ break;
339
340         case oGpgArgs:;
341           {
342             strlist_t list;
343             if (shell_parse_stringlist (pargs->r.ret_str, &list))
344               log_error ("failed to parse gpg arguments '%s'\n",
345                          pargs->r.ret_str);
346             else
347               {
348                 if (opt.gpg_arguments)
349                   strlist_last (opt.gpg_arguments)->next = list;
350                 else
351                   opt.gpg_arguments = list;
352               }
353           }
354           break;
355
356         case oTarArgs:;
357           {
358             int tar_argc;
359             char **tar_argv;
360
361             if (shell_parse_argv (pargs->r.ret_str, &tar_argc, &tar_argv))
362               log_error ("failed to parse tar arguments '%s'\n",
363                          pargs->r.ret_str);
364             else
365               {
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);
371                 if (tar_args.err)
372                   log_error ("unsupported tar arguments '%s'\n",
373                              pargs->r.ret_str);
374                 pargs->err = tar_args.err;
375               }
376           }
377           break;
378
379         case oDryRun:
380           opt.dry_run = 1;
381           break;
382
383         default: pargs->err = 2; break;
384         }
385     }
386 }
387
388 \f
389 /* gpgtar main. */
390 int
391 main (int argc, char **argv)
392 {
393   gpg_error_t err;
394   const char *fname;
395   ARGPARSE_ARGS pargs;
396
397   assert (sizeof (struct ustar_raw_header) == 512);
398
399   gnupg_reopen_std (GPGTAR_NAME);
400   set_strusage (my_strusage);
401   log_set_prefix (GPGTAR_NAME, 1);
402
403   /* Make sure that our subsystems are ready.  */
404   i18n_init();
405   init_common_subsystems (&argc, &argv);
406
407   /* Parse the command line. */
408   pargs.argc  = &argc;
409   pargs.argv  = &argv;
410   pargs.flags = ARGPARSE_FLAG_KEEP;
411   parse_arguments (&pargs, opts);
412
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");
417
418   if (log_get_errorcount (0))
419     exit (2);
420
421   /* Print a warning if an argument looks like an option.  */
422   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
423     {
424       int i;
425
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]);
429     }
430
431   if (! opt.gpg_program)
432     opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
433
434   if (opt.verbose > 1)
435     opt.debug_level = 1024;
436
437   switch (cmd)
438     {
439     case aList:
440       if (argc > 1)
441         usage (1);
442       fname = argc ? *argv : NULL;
443       if (opt.filename)
444         log_info ("note: ignoring option --set-filename\n");
445       if (files_from)
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));
450       break;
451
452     case aEncrypt:
453     case aSign:
454     case aSignEncrypt:
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,
461                            !skip_crypto
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));
466       break;
467
468     case aDecrypt:
469       if (argc != 1)
470         usage (1);
471       if (opt.outfile)
472         log_info ("note: ignoring option --output\n");
473       if (files_from)
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));
479       break;
480
481     default:
482       log_error (_("invalid command (there is no implicit command)\n"));
483       break;
484     }
485
486   return log_get_errorcount (0)? 1:0;
487 }
488
489
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. */
495 gpg_error_t
496 read_record (estream_t stream, void *record)
497 {
498   gpg_error_t err;
499   size_t nread;
500
501   nread = es_fread (record, 1, RECORDSIZE, stream);
502   if (nread != RECORDSIZE)
503     {
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));
508       else
509         log_error ("error reading '%s': premature EOF "
510                    "(size of last record: %zu)\n",
511                    es_fname_get (stream), nread);
512     }
513   else
514     err = 0;
515
516   return err;
517 }
518
519
520 /* Write the RECORD of size RECORDSIZE to STREAM.  FILENAME is the
521    name of the file used for diagnostics.  */
522 gpg_error_t
523 write_record (estream_t stream, const void *record)
524 {
525   gpg_error_t err;
526   size_t nwritten;
527
528   nwritten = es_fwrite (record, 1, RECORDSIZE, stream);
529   if (nwritten != RECORDSIZE)
530     {
531       err = gpg_error_from_syserror ();
532       log_error ("error writing '%s': %s\n",
533                  es_fname_get (stream), gpg_strerror (err));
534     }
535   else
536     err = 0;
537
538   return err;
539 }
540
541
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.  */
544 #if 0
545 static int
546 openpgp_message_p (estream_t fp)
547 {
548   int ctb;
549
550   ctb = es_getc (fp);
551   if (ctb != EOF)
552     {
553       if (es_ungetc (ctb, fp))
554         log_fatal ("error ungetting first byte: %s\n",
555                    gpg_strerror (gpg_error_from_syserror ()));
556
557       if ((ctb & 0x80))
558         {
559           switch ((ctb & 0x40) ? (ctb & 0x3f) : ((ctb>>2)&0xf))
560             {
561             case PKT_MARKER:
562             case PKT_SYMKEY_ENC:
563             case PKT_ONEPASS_SIG:
564             case PKT_PUBKEY_ENC:
565             case PKT_SIGNATURE:
566             case PKT_COMMENT:
567             case PKT_OLD_COMMENT:
568             case PKT_PLAINTEXT:
569             case PKT_COMPRESSED:
570             case PKT_ENCRYPTED:
571               return 1; /* Yes, this seems to be an OpenPGP message.  */
572             default:
573               break;
574             }
575         }
576     }
577   return 0;
578 }
579 #endif