tools: Fix error handling.
[gnupg.git] / tools / gpgtar-create.c
index 2b2c441..6780eff 100644 (file)
@@ -36,7 +36,9 @@
 #include <assert.h>
 
 #include "i18n.h"
+#include "../common/exectool.h"
 #include "../common/sysutils.h"
+#include "../common/ccparray.h"
 #include "gpgtar.h"
 
 #ifndef HAVE_LSTAT
@@ -71,18 +73,18 @@ fillup_entry_w32 (tar_header_t hdr)
   for (p=hdr->name; *p; p++)
     if (*p == '/')
       *p = '\\';
-  wfname = utf8_to_wchar (hdr->name);
+  wfname = native_to_wchar (hdr->name);
   for (p=hdr->name; *p; p++)
     if (*p == '\\')
       *p = '/';
   if (!wfname)
     {
-      log_error ("error utf8-ing `%s': %s\n", hdr->name, w32_strerror (-1));
+      log_error ("error converting '%s': %s\n", hdr->name, w32_strerror (-1));
       return gpg_error_from_syserror ();
     }
   if (!GetFileAttributesExW (wfname, GetFileExInfoStandard, &fad))
     {
-      log_error ("error stat-ing `%s': %s\n", hdr->name, w32_strerror (-1));
+      log_error ("error stat-ing '%s': %s\n", hdr->name, w32_strerror (-1));
       xfree (wfname);
       return gpg_error_from_syserror ();
     }
@@ -108,9 +110,9 @@ fillup_entry_w32 (tar_header_t hdr)
   if ((attr & FILE_ATTRIBUTE_READONLY))
     hdr->mode &= ~0200;  /* Clear the user write bit.  */
   if ((attr & FILE_ATTRIBUTE_HIDDEN))
-    hdr->mode &= ~0707;  /* Clear all user and other bits.  */ 
+    hdr->mode &= ~0707;  /* Clear all user and other bits.  */
   if ((attr & FILE_ATTRIBUTE_SYSTEM))
-    hdr->mode |= 0004;   /* Make it readable by other.  */ 
+    hdr->mode |= 0004;   /* Make it readable by other.  */
 
   /* Only set the size for a regular file.  */
   if (hdr->typeflag == TF_REGULAR)
@@ -130,7 +132,7 @@ fillup_entry_w32 (tar_header_t hdr)
 #endif /*HAVE_W32_SYSTEM*/
 
 
-/* Given a fresh header obje`<ct HDR with only the name field set, try
+/* Given a fresh header object HDR with only the name field set, try
    to gather all available info.  This is the POSIX version.  */
 #ifndef HAVE_W32_SYSTEM
 static gpg_error_t
@@ -142,10 +144,10 @@ fillup_entry_posix (tar_header_t hdr)
   if (lstat (hdr->name, &sbuf))
     {
       err = gpg_error_from_syserror ();
-      log_error ("error stat-ing `%s': %s\n", hdr->name, gpg_strerror (err));
+      log_error ("error stat-ing '%s': %s\n", hdr->name, gpg_strerror (err));
       return err;
     }
-  
+
   if (S_ISREG (sbuf.st_mode))
     hdr->typeflag = TF_REGULAR;
   else if (S_ISDIR (sbuf.st_mode))
@@ -158,7 +160,7 @@ fillup_entry_posix (tar_header_t hdr)
     hdr->typeflag = TF_FIFO;
   else if (S_ISLNK (sbuf.st_mode))
     hdr->typeflag = TF_SYMLINK;
-  else 
+  else
     hdr->typeflag = TF_NOTSUP;
 
   /* FIXME: Save DEV and INO? */
@@ -205,7 +207,7 @@ fillup_entry_posix (tar_header_t hdr)
     hdr->size = sbuf.st_size;
 
   hdr->mtime = sbuf.st_mtime;
-  
+
   return 0;
 }
 #endif /*!HAVE_W32_SYSTEM*/
@@ -298,12 +300,12 @@ scan_directory (const char *dname, scanctrl_t scanctrl)
     for (p=fname; *p; p++)
       if (*p == '/')
         *p = '\\';
-    wfname = utf8_to_wchar (fname);
+    wfname = native_to_wchar (fname);
     xfree (fname);
     if (!wfname)
       {
         err = gpg_error_from_syserror ();
-        log_error (_("error reading directory `%s': %s\n"),
+        log_error (_("error reading directory '%s': %s\n"),
                    dname, gpg_strerror (err));
         goto leave;
       }
@@ -311,7 +313,7 @@ scan_directory (const char *dname, scanctrl_t scanctrl)
     if (hd == INVALID_HANDLE_VALUE)
       {
         err = gpg_error_from_syserror ();
-        log_error (_("error reading directory `%s': %s\n"),
+        log_error (_("error reading directory '%s': %s\n"),
                    dname, w32_strerror (-1));
         xfree (wfname);
         goto leave;
@@ -319,13 +321,13 @@ scan_directory (const char *dname, scanctrl_t scanctrl)
     xfree (wfname);
   }
 
-  do 
+  do
     {
-      char *fname = wchar_to_utf8 (fi.cFileName);
+      char *fname = wchar_to_native (fi.cFileName);
       if (!fname)
         {
           err = gpg_error_from_syserror ();
-          log_error ("error utf8-ing filename: %s\n", w32_strerror (-1));
+          log_error ("error converting filename: %s\n", w32_strerror (-1));
           break;
         }
       for (p=fname; *p; p++)
@@ -346,11 +348,11 @@ scan_directory (const char *dname, scanctrl_t scanctrl)
     err = 0;
   else
     {
-      err = gpg_error_from_syserror (); 
-      log_error (_("error reading directory `%s': %s\n"),
+      err = gpg_error_from_syserror ();
+      log_error (_("error reading directory '%s': %s\n"),
                  dname, w32_strerror (-1));
     }
-  
+
  leave:
   if (hd != INVALID_HANDLE_VALUE)
     FindClose (hd);
@@ -366,16 +368,16 @@ scan_directory (const char *dname, scanctrl_t scanctrl)
   if (!dir)
     {
       err = gpg_error_from_syserror ();
-      log_error (_("error reading directory `%s': %s\n"),
+      log_error (_("error reading directory '%s': %s\n"),
                  dname, gpg_strerror (err));
       return err;
     }
-  
+
   while ((de = readdir (dir)))
     {
       if (!strcmp (de->d_name, "." ) || !strcmp (de->d_name, ".."))
         continue; /* Skip self and parent dir entry.  */
-      
+
       err = add_entry (dname, de->d_name, scanctrl);
       if (err)
         goto leave;
@@ -410,10 +412,10 @@ scan_recursive (const char *dname, scanctrl_t scanctrl)
     if (hdr->typeflag == TF_DIRECTORY)
       {
         if (opt.verbose > 1)
-          log_info ("scanning directory `%s'\n", hdr->name);
+          log_info ("scanning directory '%s'\n", hdr->name);
         scan_recursive (hdr->name, scanctrl);
       }
-  
+
   scanctrl->nestlevel--;
   return err;
 }
@@ -434,7 +436,7 @@ pattern_valid_p (const char *pattern)
        || (*pattern >= 'A' && *pattern <= 'Z'))
       && pattern[1] == ':')
     return 0; /* Drive letter are not allowed either.  */
-#endif /*HAVE_DRIVE_LETTERS*/ 
+#endif /*HAVE_DRIVE_LETTERS*/
 
   return 1; /* Okay.  */
 }
@@ -505,14 +507,14 @@ store_uname (char *buffer, size_t length, unsigned long uid)
   if (!initialized || uid != lastuid)
     {
 #ifdef HAVE_W32_SYSTEM
-      mem2str (lastuname, uid? "user":"root", sizeof lastuname); 
+      mem2str (lastuname, uid? "user":"root", sizeof lastuname);
 #else
       struct passwd *pw = getpwuid (uid);
 
       lastuid = uid;
       initialized = 1;
       if (pw)
-        mem2str (lastuname, pw->pw_name, sizeof lastuname); 
+        mem2str (lastuname, pw->pw_name, sizeof lastuname);
       else
         {
           log_info ("failed to get name for uid %lu\n", uid);
@@ -534,14 +536,14 @@ store_gname (char *buffer, size_t length, unsigned long gid)
   if (!initialized || gid != lastgid)
     {
 #ifdef HAVE_W32_SYSTEM
-      mem2str (lastgname, gid? "users":"root", sizeof lastgname); 
+      mem2str (lastgname, gid? "users":"root", sizeof lastgname);
 #else
       struct group *gr = getgrgid (gid);
 
       lastgid = gid;
       initialized = 1;
       if (gr)
-        mem2str (lastgname, gr->gr_name, sizeof lastgname); 
+        mem2str (lastgname, gr->gr_name, sizeof lastgname);
       else
         {
           log_info ("failed to get name for gid %lu\n", gid);
@@ -568,7 +570,7 @@ build_header (void *record, tar_header_t hdr)
   namelen = strlen (hdr->name);
   if (namelen < sizeof raw->name)
     memcpy (raw->name, hdr->name, namelen);
-  else 
+  else
     {
       n = (namelen < sizeof raw->prefix)? namelen : sizeof raw->prefix;
       for (n--; n ; n--)
@@ -584,12 +586,12 @@ build_header (void *record, tar_header_t hdr)
       else
         {
           err = gpg_error (GPG_ERR_TOO_LARGE);
-          log_error ("error storing file `%s': %s\n", 
+          log_error ("error storing file '%s': %s\n",
                      hdr->name, gpg_strerror (err));
           return err;
         }
     }
-  
+
   store_xoctal (raw->mode,  sizeof raw->mode,  hdr->mode);
   store_xoctal (raw->uid,   sizeof raw->uid,   hdr->uid);
   store_xoctal (raw->gid,   sizeof raw->gid,   hdr->gid);
@@ -624,7 +626,7 @@ build_header (void *record, tar_header_t hdr)
       if (nread < 0)
         {
           err = gpg_error_from_syserror ();
-          log_error ("error reading symlink `%s': %s\n", 
+          log_error ("error reading symlink '%s': %s\n",
                      hdr->name, gpg_strerror (err));
           return err;
         }
@@ -659,7 +661,7 @@ write_file (estream_t stream, tar_header_t hdr)
     {
       if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
         {
-          log_info ("skipping unsupported file `%s'\n", hdr->name);
+          log_info ("skipping unsupported file '%s'\n", hdr->name);
           err = 0;
         }
       return err;
@@ -671,7 +673,7 @@ write_file (estream_t stream, tar_header_t hdr)
       if (!infp)
         {
           err = gpg_error_from_syserror ();
-          log_error ("can't open `%s': %s - skipped\n",
+          log_error ("can't open '%s': %s - skipped\n",
                      hdr->name, gpg_strerror (err));
           return err;
         }
@@ -696,7 +698,7 @@ write_file (estream_t stream, tar_header_t hdr)
           if (nread != nbytes)
             {
               err = gpg_error_from_syserror ();
-              log_error ("error reading file `%s': %s%s\n",
+              log_error ("error reading file '%s': %s%s\n",
                          hdr->name, gpg_strerror (err),
                          any? " (file shrunk?)":"");
               goto leave;
@@ -708,15 +710,15 @@ write_file (estream_t stream, tar_header_t hdr)
         }
       nread = es_fread (record, 1, 1, infp);
       if (nread)
-        log_info ("note: file `%s' has grown\n", hdr->name);
+        log_info ("note: file '%s' has grown\n", hdr->name);
     }
 
  leave:
   if (err)
     es_fclose (infp);
   else if ((err = es_fclose (infp)))
-    log_error ("error closing file `%s': %s\n", hdr->name, gpg_strerror (err));
-      
+    log_error ("error closing file '%s': %s\n", hdr->name, gpg_strerror (err));
+
   return err;
 }
 
@@ -736,27 +738,89 @@ write_eof_mark (estream_t stream)
 
 
 \f
-void
-gpgtar_create (char **inpattern)
+/* Create a new tarball using the names in the array INPATTERN.  If
+   INPATTERN is NULL take the pattern as null terminated strings from
+   stdin.  */
+gpg_error_t
+gpgtar_create (char **inpattern, int encrypt, int sign)
 {
   gpg_error_t err = 0;
-  const char *pattern;
   struct scanctrl_s scanctrl_buffer;
   scanctrl_t scanctrl = &scanctrl_buffer;
   tar_header_t hdr, *start_tail;
   estream_t outstream = NULL;
+  estream_t cipher_stream = NULL;
+  int eof_seen = 0;
+
+  if (!inpattern)
+    es_set_binary (es_stdin);
 
   memset (scanctrl, 0, sizeof *scanctrl);
   scanctrl->flist_tail = &scanctrl->flist;
 
-  for (; (pattern = *inpattern); inpattern++)
+  while (!eof_seen)
     {
       char *pat, *p;
+      int skip_this = 0;
 
-      if (!*pattern)
-        continue;
+      if (inpattern)
+        {
+          const char *pattern = *inpattern;
+
+          if (!pattern)
+            break; /* End of array.  */
+          inpattern++;
+
+          if (!*pattern)
+            continue;
+
+          pat = xtrystrdup (pattern);
+        }
+      else /* Read null delimited pattern from stdin.  */
+        {
+          int c;
+          char namebuf[4096];
+          size_t n = 0;
+
+          for (;;)
+            {
+              if ((c = es_getc (es_stdin)) == EOF)
+                {
+                  if (es_ferror (es_stdin))
+                    {
+                      err = gpg_error_from_syserror ();
+                      log_error ("error reading '%s': %s\n",
+                                 "[stdin]", strerror (errno));
+                      goto leave;
+                    }
+                  /* Note: The Nul is a delimiter and not a terminator.  */
+                  c = 0;
+                  eof_seen = 1;
+                }
+              if (n >= sizeof namebuf - 1)
+                {
+                  if (!skip_this)
+                    {
+                      skip_this = 1;
+                      log_error ("error reading '%s': %s\n",
+                                 "[stdin]", "filename too long");
+                    }
+                }
+              else
+                namebuf[n++] = c;
+              if (!c)
+                {
+                  namebuf[n] = 0;
+                  break;
+                }
+            }
+
+          if (skip_this || n < 2)
+            continue;
+
+          pat = xtrystrdup (namebuf);
+        }
 
-      pat = xtrystrdup (pattern);
       if (!pat)
         {
           err = gpg_error_from_syserror ();
@@ -768,11 +832,11 @@ gpgtar_create (char **inpattern)
           *p = '/';
 
       if (opt.verbose > 1)
-        log_info ("scanning `%s'\n", pat);
+        log_info ("scanning '%s'\n", pat);
 
       start_tail = scanctrl->flist_tail;
-      if (!pattern_valid_p (pat))
-        log_error ("skipping invalid name `%s'\n", pat);
+      if (skip_this || !pattern_valid_p (pat))
+        log_error ("skipping invalid name '%s'\n", pat);
       else if (!add_entry (pat, NULL, scanctrl)
                && *start_tail && ((*start_tail)->typeflag & TF_DIRECTORY))
         scan_recursive (pat, scanctrl);
@@ -782,12 +846,13 @@ gpgtar_create (char **inpattern)
 
   if (opt.outfile)
     {
-      outstream = es_fopen (opt.outfile, "wb");
+      if (!strcmp (opt.outfile, "-"))
+        outstream = es_stdout;
+      else
+        outstream = es_fopen (opt.outfile, "wb");
       if (!outstream)
         {
           err = gpg_error_from_syserror ();
-          log_error (_("can't create `%s': %s\n"),
-                     opt.outfile, gpg_strerror (err));
           goto leave;
         }
     }
@@ -796,6 +861,20 @@ gpgtar_create (char **inpattern)
       outstream = es_stdout;
     }
 
+  if (outstream == es_stdout)
+    es_set_binary (es_stdout);
+
+  if (encrypt || sign)
+    {
+      cipher_stream = outstream;
+      outstream = es_fopenmem (0, "rwb");
+      if (! outstream)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+    }
+
   for (hdr = scanctrl->flist; hdr; hdr = hdr->next)
     {
       err = write_file (outstream, hdr);
@@ -803,22 +882,85 @@ gpgtar_create (char **inpattern)
         goto leave;
     }
   err = write_eof_mark (outstream);
+  if (err)
+    goto leave;
+
+  if (encrypt || sign)
+    {
+      strlist_t arg;
+      ccparray_t ccp;
+      const char **argv;
+
+      err = es_fseek (outstream, 0, SEEK_SET);
+      if (err)
+        goto leave;
+
+      /* '--encrypt' may be combined with '--symmetric', but 'encrypt'
+         is set either way.  Clear it if no recipients are specified.
+         XXX: Fix command handling.  */
+      if (opt.symmetric && opt.recipients == NULL)
+        encrypt = 0;
+
+      ccparray_init (&ccp, 0);
+      if (encrypt)
+        ccparray_put (&ccp, "--encrypt");
+      if (sign)
+        ccparray_put (&ccp, "--sign");
+      if (opt.user)
+        {
+          ccparray_put (&ccp, "--local-user");
+          ccparray_put (&ccp, opt.user);
+        }
+      if (opt.symmetric)
+        ccparray_put (&ccp, "--symmetric");
+      for (arg = opt.recipients; arg; arg = arg->next)
+        {
+          ccparray_put (&ccp, "--recipient");
+          ccparray_put (&ccp, arg->d);
+        }
+      for (arg = opt.gpg_arguments; arg; arg = arg->next)
+        ccparray_put (&ccp, arg->d);
+
+      ccparray_put (&ccp, NULL);
+      argv = ccparray_get (&ccp, NULL);
+      if (!argv)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+
+      err = gnupg_exec_tool_stream (opt.gpg_program, argv,
+                                    outstream, NULL, cipher_stream, NULL, NULL);
+      xfree (argv);
+      if (err)
+        goto leave;
+    }
 
  leave:
   if (!err)
     {
+      gpg_error_t first_err;
       if (outstream != es_stdout)
-        err = es_fclose (outstream);
+        first_err = es_fclose (outstream);
       else
-        err = es_fflush (outstream);
+        first_err = es_fflush (outstream);
       outstream = NULL;
+      if (cipher_stream != es_stdout)
+        err = es_fclose (cipher_stream);
+      else
+        err = es_fflush (cipher_stream);
+      cipher_stream = NULL;
+      if (! err)
+        err = first_err;
     }
   if (err)
     {
-      log_error ("creating tarball `%s' failed: %s\n",
-                 es_fname_get (outstream), gpg_strerror (err));
+      log_error ("creating tarball '%s' failed: %s\n",
+                 opt.outfile ? opt.outfile : "-", gpg_strerror (err));
       if (outstream && outstream != es_stdout)
         es_fclose (outstream);
+      if (cipher_stream && cipher_stream != es_stdout)
+        es_fclose (cipher_stream);
       if (opt.outfile)
         gnupg_remove (opt.outfile);
     }
@@ -828,4 +970,5 @@ gpgtar_create (char **inpattern)
       scanctrl->flist = hdr->next;
       xfree (hdr);
     }
+  return err;
 }