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