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