gpg: Properly account for ring trust packets.
[gnupg.git] / g10 / plaintext.c
index 17f8ea5..c5d1ddb 100644 (file)
@@ -15,7 +15,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #include <sys/types.h>
 #ifdef HAVE_DOSISH_SYSTEM
 # include <fcntl.h> /* for setmode() */
 #endif
 
 #include "gpg.h"
-#include "util.h"
+#include "../common/util.h"
 #include "options.h"
 #include "packet.h"
-#include "ttyio.h"
+#include "../common/ttyio.h"
 #include "filter.h"
 #include "main.h"
-#include "status.h"
-#include "i18n.h"
+#include "../common/status.h"
+#include "../common/i18n.h"
 
 
-/* Handle a plaintext packet.  If MFX is not NULL, update the MDs
- * Note: We should have used the filter stuff here, but we have to add
- * some easy mimic to set a read limit, so we calculate only the bytes
- * from the plaintext.  */
-int
-handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
-                 int nooutput, int clearsig)
+/* Get the output filename.  On success, the actual filename that is
+   used is set in *FNAMEP and a filepointer is returned in *FP.
+
+   EMBEDDED_NAME AND EMBEDDED_NAMELEN are normally stored in a
+   plaintext packet.  EMBEDDED_NAMELEN should not include any NUL
+   terminator (EMBEDDED_NAME does not need to be NUL terminated).
+
+   DATA is the iobuf containing the input data.  We just use it to get
+   the input file's filename.
+
+   On success, the caller is responsible for calling xfree on *FNAMEP
+   and calling es_close on *FPP.  */
+gpg_error_t
+get_output_file (const byte *embedded_name, int embedded_namelen,
+                 iobuf_t data, char **fnamep, estream_t *fpp)
 {
+  gpg_error_t err = 0;
   char *fname = NULL;
   estream_t fp = NULL;
-  static off_t count = 0;
-  int err = 0;
-  int c;
-  int convert = (pt->mode == 't' || pt->mode == 'u');
-#ifdef __riscos__
-  int filetype = 0xfff;
-#endif
-
-  /* Let people know what the plaintext info is. This allows the
-     receiving program to try and do something different based on the
-     format code (say, recode UTF-8 to local). */
-  if (!nooutput && is_status_enabled ())
-    {
-      char status[50];
-
-      /* Better make sure that stdout has been flushed in case the
-         output will be written to it.  This is to make sure that no
-         not-yet-flushed stuff will be written after the plaintext
-         status message.  */
-      es_fflush (es_stdout);
+  int nooutput = 0;
 
-      snprintf (status, sizeof status, 
-                "%X %lu ", (byte) pt->mode, (ulong) pt->timestamp);
-      write_status_text_and_buffer (STATUS_PLAINTEXT,
-                                   status, pt->name, pt->namelen, 0);
-      
-      if (!pt->is_partial)
-       {
-         snprintf (status, sizeof status, "%lu", (ulong) pt->len);
-         write_status_text (STATUS_PLAINTEXT_LENGTH, status);
-       }
-    }
-  
   /* Create the filename as C string.  */
-  if (nooutput)
-    ;
-  else if (opt.outfp)
+  if (opt.outfp)
     {
       fname = xtrystrdup ("[FP]");
       if (!fname)
@@ -104,16 +79,17 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
           goto leave;
         }
     }
-  else if (pt->namelen == 8 && !memcmp (pt->name, "_CONSOLE", 8))
+  else if (embedded_namelen == 8 && !memcmp (embedded_name, "_CONSOLE", 8))
     {
       log_info (_("data not saved; use option \"--output\" to save it\n"));
       nooutput = 1;
     }
   else if (!opt.flags.use_embedded_filename)
     {
-      fname = make_outfile_name (iobuf_get_real_fname (pt->buf));
+      if (data)
+        fname = make_outfile_name (iobuf_get_real_fname (data));
       if (!fname)
-       fname = ask_outfile_name (pt->name, pt->namelen);
+       fname = ask_outfile_name (embedded_name, embedded_namelen);
       if (!fname)
        {
          err = gpg_error (GPG_ERR_GENERAL);    /* Can't create file. */
@@ -121,7 +97,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
        }
     }
   else
-    fname = utf8_to_native (pt->name, pt->namelen, 0);
+    fname = utf8_to_native (embedded_name, embedded_namelen, 0);
 
   if (nooutput)
     ;
@@ -132,9 +108,25 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
     }
   else if (iobuf_is_pipe_filename (fname) || !*fname)
     {
-      /* No filename or "-" given; write to stdout. */
-      fp = es_stdout;
-      es_set_binary (fp);
+      /* Special file name, no filename, or "-" given; write to the
+       * file descriptor or to stdout. */
+      int fd;
+      char xname[64];
+
+      fd = check_special_filename (fname, 1, 0);
+      if (fd == -1)
+        {
+          /* Not a special filename, thus we want stdout.  */
+          fp = es_stdout;
+          es_set_binary (fp);
+        }
+      else if (!(fp = es_fdopen_nc (fd, "wb")))
+        {
+          err = gpg_error_from_syserror ();
+          snprintf (xname, sizeof xname, "[fd %d]", fd);
+          log_error (_("can't open '%s': %s\n"), xname, gpg_strerror (err));
+          goto leave;
+        }
     }
   else
     {
@@ -144,20 +136,20 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
          if (!tmp || !*tmp)
            {
              xfree (tmp);
-              /* FIXME: Below used to be G10ERR_CREATE_FILE */
-             err = gpg_error (GPG_ERR_GENERAL);        
+              /* FIXME: Below used to be GPG_ERR_CREATE_FILE */
+             err = gpg_error (GPG_ERR_GENERAL);
              goto leave;
            }
          xfree (fname);
          fname = tmp;
        }
     }
-  
+
 #ifndef __riscos__
   if (opt.outfp && is_secured_file (es_fileno (opt.outfp)))
     {
       err = gpg_error (GPG_ERR_EPERM);
-      log_error (_("error creating `%s': %s\n"), fname, gpg_strerror (err));
+      log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err));
       goto leave;
     }
   else if (fp || nooutput)
@@ -166,13 +158,13 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
     {
       gpg_err_set_errno (EPERM);
       err = gpg_error_from_syserror ();
-      log_error (_("error creating `%s': %s\n"), fname, gpg_strerror (err));
+      log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err));
       goto leave;
     }
   else if (!(fp = es_fopen (fname, "wb")))
     {
       err = gpg_error_from_syserror ();
-      log_error (_("error creating `%s': %s\n"), fname, gpg_strerror (err));
+      log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err));
       goto leave;
     }
 #else /* __riscos__ */
@@ -188,15 +180,15 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
     ;
   else
     {
-      /* Note: riscos stuff is not expected to wrok anymore.  If we
+      /* Note: riscos stuff is not expected to work anymore.  If we
          want to port it again to riscos we should do most of the suff
          in estream.  FIXME: Consider to remove all riscos special
          cases.  */
       fp = fopen (fname, "wb");
       if (!fp)
        {
-         log_error (_("error creating `%s': %s\n"), fname, gpg_strerror (err));
-         err = G10ERR_CREATE_FILE;
+         log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err));
+         err = GPG_ERR_CREATE_FILE;
          if (errno == 106)
            log_info ("Do output file and input file have the same name?\n");
          goto leave;
@@ -205,7 +197,8 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
       /* If there's a ,xxx extension in the embedded filename,
          use that, else check whether the user input (in fname)
          has a ,xxx appended, then use that in preference */
-      if ((c = riscos_get_filetype_from_string (pt->name, pt->namelen)) != -1)
+      if ((c = riscos_get_filetype_from_string (embedded_name,
+                                                embedded_namelen)) != -1)
        filetype = c;
       if ((c = riscos_get_filetype_from_string (fname, strlen (fname))) != -1)
        filetype = c;
@@ -213,6 +206,75 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
     }
 #endif /* __riscos__ */
 
+ leave:
+  if (err)
+    {
+      if (fp && fp != es_stdout && fp != opt.outfp)
+        es_fclose (fp);
+      xfree (fname);
+      return err;
+    }
+
+  *fnamep = fname;
+  *fpp = fp;
+  return 0;
+}
+
+/* Handle a plaintext packet.  If MFX is not NULL, update the MDs
+ * Note: We should have used the filter stuff here, but we have to add
+ * some easy mimic to set a read limit, so we calculate only the bytes
+ * from the plaintext.  */
+int
+handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
+                 int nooutput, int clearsig)
+{
+  char *fname = NULL;
+  estream_t fp = NULL;
+  static off_t count = 0;
+  int err = 0;
+  int c;
+  int convert;
+#ifdef __riscos__
+  int filetype = 0xfff;
+#endif
+
+  if (pt->mode == 't' || pt->mode == 'u' || pt->mode == 'm')
+    convert = pt->mode;
+  else
+    convert = 0;
+
+  /* Let people know what the plaintext info is. This allows the
+     receiving program to try and do something different based on the
+     format code (say, recode UTF-8 to local). */
+  if (!nooutput && is_status_enabled ())
+    {
+      char status[50];
+
+      /* Better make sure that stdout has been flushed in case the
+         output will be written to it.  This is to make sure that no
+         not-yet-flushed stuff will be written after the plaintext
+         status message.  */
+      es_fflush (es_stdout);
+
+      snprintf (status, sizeof status,
+                "%X %lu ", (byte) pt->mode, (ulong) pt->timestamp);
+      write_status_text_and_buffer (STATUS_PLAINTEXT,
+                                   status, pt->name, pt->namelen, 0);
+
+      if (!pt->is_partial)
+       {
+         snprintf (status, sizeof status, "%lu", (ulong) pt->len);
+         write_status_text (STATUS_PLAINTEXT_LENGTH, status);
+       }
+    }
+
+  if (! nooutput)
+    {
+      err = get_output_file (pt->name, pt->namelen, pt->buf, &fname, &fp);
+      if (err)
+        goto leave;
+    }
+
   if (!pt->is_partial)
     {
       /* We have an actual length (which might be zero). */
@@ -238,14 +300,16 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
              if (mfx->md)
                gcry_md_putc (mfx->md, c);
 #ifndef HAVE_DOSISH_SYSTEM
-             if (c == '\r')    /* convert to native line ending */
-               continue;       /* fixme: this hack might be too simple */
+              /* Convert to native line ending. */
+              /* fixme: this hack might be too simple */
+             if (c == '\r' && convert != 'm')
+               continue;
 #endif
              if (fp)
                {
                  if (opt.max_output && (++count) > opt.max_output)
                    {
-                     log_error ("error writing to `%s': %s\n",
+                     log_error ("error writing to '%s': %s\n",
                                 fname, "exceeded --max-output limit\n");
                      err = gpg_error (GPG_ERR_TOO_LARGE);
                      goto leave;
@@ -256,7 +320,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
                        err = gpg_error_from_syserror ();
                      else
                        err = gpg_error (GPG_ERR_EOF);
-                     log_error ("error writing to `%s': %s\n",
+                     log_error ("error writing to '%s': %s\n",
                                 fname, gpg_strerror (err));
                      goto leave;
                    }
@@ -284,7 +348,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
                {
                  if (opt.max_output && (count += len) > opt.max_output)
                    {
-                     log_error ("error writing to `%s': %s\n",
+                     log_error ("error writing to '%s': %s\n",
                                 fname, "exceeded --max-output limit\n");
                      err = gpg_error (GPG_ERR_TOO_LARGE);
                      xfree (buffer);
@@ -293,7 +357,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
                  else if (es_fwrite (buffer, 1, len, fp) != len)
                    {
                      err = gpg_error_from_syserror ();
-                     log_error ("error writing to `%s': %s\n",
+                     log_error ("error writing to '%s': %s\n",
                                 fname, gpg_strerror (err));
                      xfree (buffer);
                      goto leave;
@@ -313,14 +377,14 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
              if (mfx->md)
                gcry_md_putc (mfx->md, c);
 #ifndef HAVE_DOSISH_SYSTEM
-             if (convert && c == '\r')
+             if (c == '\r' && convert != 'm')
                continue;       /* fixme: this hack might be too simple */
 #endif
              if (fp)
                {
                  if (opt.max_output && (++count) > opt.max_output)
                    {
-                     log_error ("Error writing to `%s': %s\n",
+                     log_error ("Error writing to '%s': %s\n",
                                 fname, "exceeded --max-output limit\n");
                      err = gpg_error (GPG_ERR_TOO_LARGE);
                      goto leave;
@@ -331,7 +395,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
                        err = gpg_error_from_syserror ();
                      else
                        err = gpg_error (GPG_ERR_EOF);
-                     log_error ("error writing to `%s': %s\n",
+                     log_error ("error writing to '%s': %s\n",
                                 fname, gpg_strerror (err));
                      goto leave;
                    }
@@ -369,7 +433,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
                {
                  if (opt.max_output && (count += len) > opt.max_output)
                    {
-                     log_error ("error writing to `%s': %s\n",
+                     log_error ("error writing to '%s': %s\n",
                                 fname, "exceeded --max-output limit\n");
                      err = gpg_error (GPG_ERR_TOO_LARGE);
                      xfree (buffer);
@@ -378,7 +442,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
                  else if (es_fwrite (buffer, 1, len, fp) != len)
                    {
                      err = gpg_error_from_syserror ();
-                     log_error ("error writing to `%s': %s\n",
+                     log_error ("error writing to '%s': %s\n",
                                 fname, gpg_strerror (err));
                      xfree (buffer);
                      goto leave;
@@ -390,7 +454,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
       pt->buf = NULL;
     }
   else /* Clear text signature - don't hash the last CR,LF.   */
-    { 
+    {
       int state = 0;
 
       while ((c = iobuf_get (pt->buf)) != -1)
@@ -399,7 +463,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
            {
              if (opt.max_output && (++count) > opt.max_output)
                {
-                 log_error ("error writing to `%s': %s\n",
+                 log_error ("error writing to '%s': %s\n",
                             fname, "exceeded --max-output limit\n");
                  err = gpg_error (GPG_ERR_TOO_LARGE);
                  goto leave;
@@ -407,7 +471,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
              else if (es_putc (c, fp) == EOF)
                {
                  err = gpg_error_from_syserror ();
-                 log_error ("error writing to `%s': %s\n",
+                 log_error ("error writing to '%s': %s\n",
                             fname, gpg_strerror (err));
                  goto leave;
                }
@@ -452,7 +516,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
   if (fp && fp != es_stdout && fp != opt.outfp && es_fclose (fp))
     {
       err = gpg_error_from_syserror ();
-      log_error ("error closing `%s': %s\n", fname, gpg_strerror (err));
+      log_error ("error closing '%s': %s\n", fname, gpg_strerror (err));
       fp = NULL;
       goto leave;
     }
@@ -469,7 +533,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx,
          values is a good idea in any case.  */
       if (!err)
         err = gpg_error_from_syserror ();
-      log_error ("error flushing `%s': %s\n", "[stdout]",
+      log_error ("error flushing '%s': %s\n", "[stdout]",
                  gpg_strerror (err));
     }
 
@@ -581,7 +645,7 @@ ask_for_detached_datafile (gcry_md_hd_t md, gcry_md_hd_t md2,
          else if (!fp)
            {
              rc = gpg_error_from_syserror ();
-             log_error (_("can't open `%s': %s\n"), answer,
+             log_error (_("can't open '%s': %s\n"), answer,
                         strerror (errno));
              goto leave;
            }
@@ -594,7 +658,7 @@ ask_for_detached_datafile (gcry_md_hd_t md, gcry_md_hd_t md2,
       if (opt.verbose)
        log_info (_("reading stdin ...\n"));
       fp = iobuf_open (NULL);
-      assert (fp);
+      log_assert (fp);
     }
   do_hash (md, md2, fp, textmode);
   iobuf_close (fp);
@@ -607,10 +671,8 @@ leave:
 
 
 
-/****************
- * Hash the given files and append the hash to hash context md.
- * If FILES is NULL, hash stdin.
- */
+/* Hash the given files and append the hash to hash contexts MD and
+ * MD2.  If FILES is NULL, stdin is hashed.  */
 int
 hash_datafiles (gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files,
                const char *sigfilename, int textmode)
@@ -623,15 +685,22 @@ hash_datafiles (gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files,
 
   if (!files)
     {
-      /* check whether we can open the signed material */
-      fp = open_sigfile (sigfilename, pfx);
-      if (fp)
-       {
-         do_hash (md, md2, fp, textmode);
-         iobuf_close (fp);
-         release_progress_context (pfx);
-         return 0;
-       }
+      /* Check whether we can open the signed material.  We avoid
+         trying to open a file if run in batch mode.  This assumed
+         data file for a sig file feature is just a convenience thing
+         for the command line and the user needs to read possible
+         warning messages. */
+      if (!opt.batch)
+        {
+          fp = open_sigfile (sigfilename, pfx);
+          if (fp)
+            {
+              do_hash (md, md2, fp, textmode);
+              iobuf_close (fp);
+              release_progress_context (pfx);
+              return 0;
+            }
+        }
       log_error (_("no signed data\n"));
       release_progress_context (pfx);
       return gpg_error (GPG_ERR_NO_DATA);
@@ -650,7 +719,7 @@ hash_datafiles (gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files,
       if (!fp)
        {
          int rc = gpg_error_from_syserror ();
-         log_error (_("can't open signed data `%s'\n"),
+         log_error (_("can't open signed data '%s'\n"),
                     print_fname_stdin (sl->d));
          release_progress_context (pfx);
          return rc;