]> git.llucax.com Git - software/mutt-debian.git/blobdiff - sendlib.c
Imported Upstream version 1.5.20
[software/mutt-debian.git] / sendlib.c
index ab4ace2f07be06e72b8e33e7f2859e5c2993e6e3..48b94e8a670979a9f7366e5070aba5eeee15fe3d 100644 (file)
--- a/sendlib.c
+++ b/sendlib.c
@@ -1,20 +1,20 @@
 /*
  * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
- * 
+ *
  *     This program is free software; you can redistribute it and/or modify
  *     it under the terms of the GNU General Public License as published by
  *     the Free Software Foundation; either version 2 of the License, or
  *     (at your option) any later version.
- * 
+ *
  *     This program is distributed in the hope that it will be useful,
  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *     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, write to the Free Software
  *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */ 
+ */
 
 #define _SENDLIB_C 1
 
@@ -34,6 +34,7 @@
 #include "charset.h"
 #include "mutt_crypt.h"
 #include "mutt_idna.h"
+#include "buffy.h"
 
 #include <string.h>
 #include <stdlib.h>
@@ -95,7 +96,7 @@ static void encode_quoted (FGETCONV * fc, FILE *fout, int istext)
       if (line[linelen-3] == '=')
       {
         line[linelen-3] = 0;
-        fputs (line, fout);      
+        fputs (line, fout);
         fputs ("=\n", fout);
         line[linelen] = 0;
         line[0] = '=';
@@ -226,15 +227,15 @@ static void b64_flush(FILE *fout)
     fputc('\n', fout);
     b64_linelen = 0;
   }
-  
+
   for(i = b64_num; i < 3; i++)
     b64_buffer[i] = '\0';
-  
-  fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout); 
+
+  fputc(B64Chars[(b64_buffer[0] >> 2) & 0x3f], fout);
   b64_linelen++;
   fputc(B64Chars[((b64_buffer[0] & 0x3) << 4) | ((b64_buffer[1] >> 4) & 0xf) ], fout);
   b64_linelen++;
-  
+
   if(b64_num > 1)
   {
     fputc(B64Chars[((b64_buffer[1] & 0xf) << 2) | ((b64_buffer[2] >> 6) & 0x3) ], fout);
@@ -245,32 +246,32 @@ static void b64_flush(FILE *fout)
       b64_linelen++;
     }
   }
-  
+
   while(b64_linelen % 4)
   {
     fputc('=', fout);
     b64_linelen++;
   }
-  
+
   b64_num = 0;
 }
-    
-  
+
+
 static void b64_putc(char c, FILE *fout)
 {
   if(b64_num == 3)
     b64_flush(fout);
-  
+
   b64_buffer[b64_num++] = c;
 }
-  
-  
+
+
 static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
 {
   int ch, ch1 = EOF;
-  
+
   b64_num = b64_linelen = 0;
-  
+
   while ((ch = fgetconv (fc)) != EOF)
   {
     if (istext && ch == '\n' && ch1 != '\r')
@@ -285,11 +286,11 @@ static void encode_base64 (FGETCONV * fc, FILE *fout, int istext)
 static void encode_8bit (FGETCONV *fc, FILE *fout, int istext)
 {
   int ch;
-  
+
   while ((ch = fgetconv (fc)) != EOF)
     fputc (ch, fout);
 }
-  
+
 
 int mutt_write_mime_header (BODY *a, FILE *f)
 {
@@ -300,7 +301,7 @@ int mutt_write_mime_header (BODY *a, FILE *f)
   int len;
   int tmplen;
   int encode;
-  
+
   fprintf (f, "Content-Type: %s/%s", TYPE (a), a->subtype);
 
   if (a->parameter)
@@ -310,10 +311,10 @@ int mutt_write_mime_header (BODY *a, FILE *f)
     for(p = a->parameter; p; p = p->next)
     {
       char *tmp;
-      
+
       if(!p->value)
        continue;
-      
+
       fputc (';', f);
 
       buffer[0] = 0;
@@ -321,7 +322,7 @@ int mutt_write_mime_header (BODY *a, FILE *f)
       encode = rfc2231_encode_string (&tmp);
       rfc822_cat (buffer, sizeof (buffer), tmp, MimeSpecials);
 
-      /* Dirty hack to make messages readable by Outlook Express 
+      /* Dirty hack to make messages readable by Outlook Express
        * for the Mac: force quotes around the boundary parameter
        * even when they aren't needed.
        */
@@ -360,7 +361,7 @@ int mutt_write_mime_header (BODY *a, FILE *f)
   {
     if(!(fn = a->d_filename))
       fn = a->filename;
-    
+
     if (fn)
     {
       char *tmp;
@@ -370,7 +371,7 @@ int mutt_write_mime_header (BODY *a, FILE *f)
        t++;
       else
        t = fn;
-      
+
       buffer[0] = 0;
       tmp = safe_strdup (t);
       encode = rfc2231_encode_string (&tmp);
@@ -400,7 +401,7 @@ int mutt_write_mime_body (BODY *a, FILE *f)
   FILE *fpin;
   BODY *t;
   FGETCONV *fc;
-  
+
   if (a->type == TYPEMULTIPART)
   {
     /* First, find the boundary to use */
@@ -442,7 +443,7 @@ int mutt_write_mime_body (BODY *a, FILE *f)
   }
 
   if (a->type == TYPETEXT && (!a->noconv))
-    fc = fgetconv_open (fpin, a->charset, 
+    fc = fgetconv_open (fpin, a->charset,
                        mutt_get_body_charset (send_charset, sizeof (send_charset), a),
                        0);
   else
@@ -458,7 +459,7 @@ int mutt_write_mime_body (BODY *a, FILE *f)
     mutt_copy_stream (fpin, f);
 
   fgetconv_close (&fc);
-  fclose (fpin);
+  safe_fclose (&fpin);
 
   return (ferror (f) ? -1 : 0);
 }
@@ -473,10 +474,10 @@ void mutt_generate_boundary (PARAMETER **parm)
   int i;
 
   rs[BOUNDARYLEN] = 0;
-  for (i=0;i<BOUNDARYLEN;i++) 
+  for (i=0;i<BOUNDARYLEN;i++)
     *p++ = B64Chars[LRAND() % sizeof (B64Chars)];
   *p = 0;
-  
+
   mutt_set_parameter ("boundary", rs, parm);
 }
 
@@ -595,7 +596,7 @@ static void update_content_info (CONTENT *info, CONTENT_STATE *s, char *d, size_
   s->dot = dot;
   s->linelen = linelen;
   s->was_cr = was_cr;
-  
+
 }
 
 /* Define as 1 if iconv sometimes returns -1(EILSEQ) instead of transcribing. */
@@ -766,7 +767,7 @@ static size_t convert_file_from_to (FILE *file,
                                    const char *fromcodes, const char *tocodes,
                                    char **fromcode, char **tocode, CONTENT *info)
 {
-  char *fcode;
+  char *fcode = NULL;
   char **tcode;
   const char *c, *c1;
   size_t ret;
@@ -799,7 +800,7 @@ static size_t convert_file_from_to (FILE *file,
       if ((c1 = strchr (c, ':')) == c)
        continue;
       fcode = mutt_substrdup (c, c1);
-      
+
       ret = convert_file_to (file, fcode, ncodes, (const char **)tcode,
                             &cn, info);
       if (ret != (size_t)(-1))
@@ -828,12 +829,12 @@ static size_t convert_file_from_to (FILE *file,
   for (i = 0; i < ncodes; i++)
     FREE (&tcode[i]);
 
-  FREE (tcode);                /* __FREE_CHECKED__ */
-  
+  FREE (&tcode);
+
   return ret;
 }
 
-/* 
+/*
  * Analyze the contents of a file to determine which MIME encoding to use.
  * Also set the body charset, sometimes, or not.
  */
@@ -849,7 +850,7 @@ CONTENT *mutt_get_content_info (const char *fname, BODY *b)
   size_t r;
 
   struct stat sb;
-  
+
   if(b && !fname) fname = b->filename;
 
   if (stat (fname, &sb) == -1)
@@ -857,13 +858,13 @@ CONTENT *mutt_get_content_info (const char *fname, BODY *b)
     mutt_error (_("Can't stat %s: %s"), fname, strerror (errno));
     return NULL;
   }
-  
+
   if (!S_ISREG(sb.st_mode))
   {
     mutt_error (_("%s isn't a regular file."), fname);
     return NULL;
   }
-  
+
   if ((fp = fopen (fname, "r")) == NULL)
   {
     dprint (1, (debugfile, "mutt_get_content_info: %s: %s (errno %d).\n",
@@ -873,7 +874,7 @@ CONTENT *mutt_get_content_info (const char *fname, BODY *b)
 
   info = safe_calloc (1, sizeof (CONTENT));
   memset (&state, 0, sizeof (state));
-  
+
   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
   {
     char *chs = mutt_get_parameter ("charset", b->parameter);
@@ -901,7 +902,7 @@ CONTENT *mutt_get_content_info (const char *fname, BODY *b)
   update_content_info (info, &state, 0, 0);
 
   safe_fclose (&fp);
-  
+
   if (b != NULL && b->type == TYPETEXT && (!b->noconv && !b->force_charset))
     mutt_set_parameter ("charset", (!info->hibin ? "us-ascii" :
                                    Charset  && !mutt_is_us_ascii (Charset) ? Charset : "unknown-8bit"),
@@ -993,7 +994,7 @@ int mutt_lookup_mime_type (BODY *att, const char *path)
 
            for (q = p; *q && !ISSPACE (*q); q++)
              ;
-           
+
            mutt_substrcpy (subtype, p, q, sizeof (subtype));
 
            if ((type = mutt_check_mime_type (ct)) == TYPEOTHER)
@@ -1004,10 +1005,10 @@ int mutt_lookup_mime_type (BODY *att, const char *path)
          p = NULL;
        }
       }
-      fclose (f);
+      safe_fclose (&f);
     }
   }
-  
+
  bye:
 
   if (type != TYPEOTHER || *xtype != '\0')
@@ -1016,7 +1017,7 @@ int mutt_lookup_mime_type (BODY *att, const char *path)
     mutt_str_replace (&att->subtype, subtype);
     mutt_str_replace (&att->xtype, xtype);
   }
-  
+
   return (type);
 }
 
@@ -1034,14 +1035,14 @@ void mutt_message_to_7bit (BODY *a, FILE *fp)
   {
     mutt_error (_("Could not open %s"), a->filename ? a->filename : "(null)");
     return;
-  } 
+  }
   else
   {
     a->offset = 0;
     if (stat (a->filename, &sb) == -1)
     {
       mutt_perror ("stat");
-      fclose (fpin);
+      safe_fclose (&fpin);
     }
     a->length = sb.st_size;
   }
@@ -1057,32 +1058,32 @@ void mutt_message_to_7bit (BODY *a, FILE *fp)
   a->parts = mutt_parse_messageRFC822 (fpin, a);
 
   transform_to_7bit (a->parts, fpin);
-  
-  mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length, 
+
+  mutt_copy_hdr (fpin, fpout, a->offset, a->offset + a->length,
                 CH_MIME | CH_NONEWLINE | CH_XMIT, NULL);
 
   fputs ("MIME-Version: 1.0\n", fpout);
   mutt_write_mime_header (a->parts, fpout);
   fputc ('\n', fpout);
   mutt_write_mime_body (a->parts, fpout);
-  
+
  cleanup:
   FREE (&line);
 
-  if (fpin && !fp)
-    fclose (fpin);
+  if (fpin && fpin != fp)
+    safe_fclose (&fpin);
   if (fpout)
-    fclose (fpout);
+    safe_fclose (&fpout);
   else
     return;
-  
+
   a->encoding = ENC7BIT;
   a->d_filename = a->filename;
   if (a->filename && a->unlink)
     unlink (a->filename);
   a->filename = safe_strdup (temp);
   a->unlink = 1;
-  if(stat (a->filename, &sb) == -1) 
+  if(stat (a->filename, &sb) == -1)
   {
     mutt_perror ("stat");
     return;
@@ -1107,29 +1108,29 @@ static void transform_to_7bit (BODY *a, FILE *fpin)
        a->encoding = ENC7BIT;
 
       transform_to_7bit (a->parts, fpin);
-    } 
+    }
     else if (mutt_is_message_type(a->type, a->subtype))
     {
       mutt_message_to_7bit (a, fpin);
     }
-    else 
+    else
     {
       a->noconv = 1;
       a->force_charset = 1;
-      
+
       mutt_mktemp (buff);
-      if ((s.fpout = safe_fopen (buff, "w")) == NULL) 
+      if ((s.fpout = safe_fopen (buff, "w")) == NULL)
       {
        mutt_perror ("fopen");
        return;
       }
       s.fpin = fpin;
       mutt_decode_attachment (a, &s);
-      fclose (s.fpout);
+      safe_fclose (&s.fpout);
       a->d_filename = a->filename;
       a->filename = safe_strdup (buff);
       a->unlink = 1;
-      if (stat (a->filename, &sb) == -1) 
+      if (stat (a->filename, &sb) == -1)
       {
        mutt_perror ("stat");
        return;
@@ -1165,7 +1166,7 @@ static void mutt_set_encoding (BODY *b, CONTENT *info)
     if (info->lobin || info->hibin)
     {
       if (option (OPTALLOW8BIT) && !info->lobin)
-       b->encoding = ENC8BIT; 
+       b->encoding = ENC8BIT;
       else
        mutt_message_to_7bit (b, NULL);
     }
@@ -1181,7 +1182,7 @@ static void mutt_set_encoding (BODY *b, CONTENT *info)
 #endif
   {
     /* Determine which encoding is smaller  */
-    if (1.33 * (float)(info->lobin+info->hibin+info->ascii) < 
+    if (1.33 * (float)(info->lobin+info->hibin+info->ascii) <
         3.0 * (float)(info->lobin + info->hibin) + (float)info->ascii)
       b->encoding = ENCBASE64;
     else
@@ -1207,7 +1208,7 @@ char *mutt_get_body_charset (char *d, size_t dlen, BODY *b)
   if (b && b->type != TYPETEXT)
     return NULL;
 
-  if (b) 
+  if (b)
     p = mutt_get_parameter ("charset", b->parameter);
 
   if (p)
@@ -1315,7 +1316,7 @@ BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
   }
 
   mutt_copy_message (fp, ctx, hdr, cmflags, chflags);
-  
+
   fflush(fp);
   rewind(fp);
 
@@ -1328,8 +1329,8 @@ BODY *mutt_make_message_attach (CONTEXT *ctx, HEADER *hdr, int attach_msg)
   mutt_update_encoding (body);
   body->parts = body->hdr->content;
 
-  fclose(fp);
-  
+  safe_fclose (&fp);
+
   return (body);
 }
 
@@ -1346,8 +1347,8 @@ BODY *mutt_make_file_attach (const char *path)
    */
 
 #if 0
-  
-  if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER 
+
+  if ((n = mutt_lookup_mime_type (buf, sizeof (buf), xbuf, sizeof (xbuf), path)) != TYPEOTHER
       || *xbuf != '\0')
   {
     att->type = n;
@@ -1356,11 +1357,11 @@ BODY *mutt_make_file_attach (const char *path)
   }
 
 #else
-  
+
   mutt_lookup_mime_type (att, path);
 
 #endif
-  
+
   if ((info = mutt_get_content_info (path, att)) == NULL)
   {
     mutt_free_body (&att);
@@ -1372,7 +1373,7 @@ BODY *mutt_make_file_attach (const char *path)
     if (info->lobin == 0 || (info->lobin + info->hibin + info->ascii)/ info->lobin >= 10)
     {
       /*
-       * Statistically speaking, there should be more than 10% "lobin" 
+       * Statistically speaking, there should be more than 10% "lobin"
        * chars if this is really a binary file...
        */
       att->type = TYPETEXT;
@@ -1383,8 +1384,9 @@ BODY *mutt_make_file_attach (const char *path)
       att->type = TYPEAPPLICATION;
       att->subtype = safe_strdup ("octet-stream");
     }
-  } 
+  }
 
+  FREE(&info);
   mutt_update_encoding (att);
   return (att);
 }
@@ -1437,7 +1439,7 @@ BODY *mutt_make_multipart (BODY *b)
       mutt_delete_parameter ("boundary", &new->parameter);
   }
   while (!mutt_get_parameter ("boundary", new->parameter));
-  new->use_disp = 0;  
+  new->use_disp = 0;
   new->disposition = DISPINLINE;
   new->parts = b;
 
@@ -1520,17 +1522,15 @@ void mutt_write_address_list (ADDRESS *adr, FILE *fp, int linelen, int display)
 /* arbitrary number of elements to grow the array by */
 #define REF_INC 16
 
-#define TrimRef 10
-
 /* need to write the list in reverse because they are stored in reverse order
  * when parsed to speed up threading
  */
-static void write_references (LIST *r, FILE *f)
+void mutt_write_references (LIST *r, FILE *f, int trim)
 {
   LIST **ref = NULL;
   int refcnt = 0, refmax = 0;
 
-  for ( ; (TrimRef == 0 || refcnt < TrimRef) && r ; r = r->next)
+  for ( ; (trim == 0 || refcnt < trim) && r ; r = r->next)
   {
     if (refcnt == refmax)
       safe_realloc (&ref, (refmax += REF_INC) * sizeof (LIST *));
@@ -1541,185 +1541,277 @@ static void write_references (LIST *r, FILE *f)
   {
     fputc (' ', f);
     fputs (ref[refcnt]->data, f);
+    if (refcnt >= 1)
+      fputc ('\n', f);
   }
 
   FREE (&ref);
 }
 
+static const char *find_word (const char *src)
+{
+  const char *p = src;
 
-static void foldingstrfcpy (char *d, const char *s, int n)
+  while (p && *p && strchr (" \t\n", *p))
+    p++;
+  while (p && *p && !strchr (" \t\n", *p))
+    p++;
+  return p;
+}
+
+/* like wcwidth(), but gets const char* not wchar_t* */
+static int my_width (const char *str, int col, int flags)
 {
-  while (--n >= 0 && *s)
+  wchar_t wc;
+  int l, w = 0, nl = 0;
+  const char *p = str;
+
+  while (p && *p)
   {
-    *d = *s++;
-    if (*d == '\t')
-      *d = ' ';
-    if (!(d[0] == '\n' && (*s == '\t' || *s == ' ')))
-       d++;
+    if (mbtowc (&wc, p, MB_CUR_MAX) >= 0)
+    {
+      l = wcwidth (wc);
+      if (l < 0)
+       l = 1;
+      /* correctly calc tab stop, even for sending as the
+       * line should look pretty on the receiving end */
+      if (wc == L'\t' || (nl && wc == L' '))
+      {
+       nl = 0;
+       l = 8 - (col % 8);
+      }
+      /* track newlines for display-case: if we have a space
+       * after a newline, assume 8 spaces as for display we
+       * always tab-fold */
+      else if ((flags & CH_DISPLAY) && wc == '\n')
+       nl = 1;
+    }
+    else
+      l = 1;
+    w += l;
+    p++;
   }
-  *d = '\0';
+  return w;
 }
 
-int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen)
+static int print_val (FILE *fp, const char *pfx, const char *value, int flags)
 {
-  int col = 0;
-  int i, k, n;
-  const char *cp;
-  char buf [HUGE_STRING];
-  wchar_t w = (wchar_t) -1;
-  wchar_t last = (wchar_t) '\n';
-  int l = 0;
-  int first = 1;
-  int wrapped = 0;
-  int in_encoded_word = 0;
-  
-  if (wraplen <= 0)
-    wraplen = 76;
-  
-  if (tag)
+  while (value && *value)
   {
-    if (fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
+    if (fputc (*value, fp) == EOF)
       return -1;
-    
-    col = mutt_strlen (tag) + 2 + mutt_strlen (pfx);
-  }
-  else
-    col = 0;
-  
-  *buf = '\0';
-  cp = value;
-  
-  while (cp && *cp) 
-  {
-    if (!col) 
+    if (*value == '\n')
     {
-      if (fputs (NONULL (pfx), fp) == EOF)
+      if (*(value + 1) && pfx && *pfx && fputs (pfx, fp) == EOF)
        return -1;
-      col = mutt_strlen (pfx);
-
-      /* Space padding, but only if necessary */      
-      if (!first && *cp != '\t' && *cp != ' ')
+      /* for display, turn folding spaces into folding tabs */
+      if ((flags & CH_DISPLAY) && (*(value + 1) == ' ' || *(value + 1) == '\t'))
       {
+       value++;
+       while (*value && (*value == ' ' || *value == '\t'))
+         value++;
        if (fputc ('\t', fp) == EOF)
          return -1;
-       col += 8 - (col % 8);
+       continue;
       }
     }
+    value++;
+  }
+  return 0;
+}
 
-    if (first)
+static int fold_one_header (FILE *fp, const char *tag, const char *value,
+                             const char *pfx, int wraplen, int flags)
+{
+  const char *p = value, *next, *sp;
+  char buf[HUGE_STRING] = "";
+  int first = 1, enc, col = 0, w, l = 0, fold;
+
+  dprint(4,(debugfile,"mwoh: pfx=[%s], tag=[%s], flags=%d value=[%s]\n",
+           pfx, tag, flags, value));
+
+  if (fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
+    return -1;
+  col = mutt_strlen (tag) + 2 + mutt_strlen (pfx);
+
+  while (p && *p)
+  {
+    fold = 0;
+
+    /* find the next word and place it in `buf'. it may start with
+     * whitespace we can fold before */
+    next = find_word (p);
+    l = MIN(sizeof (buf), next - p);
+    memcpy (buf, p, l);
+    buf[l] = 0;
+
+    /* determine width: character cells for display, bytes for sending
+     * (we get pure ascii only) */
+    w = my_width (buf, col, flags);
+    enc = mutt_strncmp (buf, "=?", 2) == 0;
+
+    dprint(5,(debugfile,"mwoh: word=[%s], col=%d, w=%d, next=[0x0%x]\n",
+             buf, col, w, *next));
+
+    /* insert a folding \n before the current word's lwsp except for
+     * header name, first word on a line (word longer than wrap width)
+     * and encoded words */
+    if (!first && !enc && col && col + w >= wraplen)
     {
-      last = '\n';
-      wrapped = 0;
-      first = 0;
+      col = mutt_strlen (pfx);
+      fold = 1;
+      if (fprintf (fp, "\n%s", NONULL(pfx)) <= 0)
+       return -1;
     }
 
-    /*
-     * i is our running pointer, and always points to the *beginning* of an mb character.
-     * k is the pointer to the beginning of the last white-space character we have seen.
-     * n is the pointer to the beginning of the first character after white-space.
-     * 
-     * yuck
-     */
-    
-    for (i = 0, k = 0, l = 0, n = 0; i + MB_CUR_MAX < sizeof (buf)
-           && cp[i] != '\0' && (col < wraplen || in_encoded_word);
-        i += l, last = w)
+    /* print the actual word; for display, ignore leading ws for word
+     * and fold with tab for readability */
+    if ((flags & CH_DISPLAY) && fold)
     {
-
-      /* Brief look at the last character we had... */
-      if (iswspace (last))
+      char *p = buf;
+      while (*p && (*p == ' ' || *p == '\t'))
       {
-       /* ... and if the next thing is an encoded word ... */
-       if (strncmp (&cp[i], "=?", 2) == 0)
-         in_encoded_word = 1;
-       else
-         in_encoded_word = 0;
+       p++;
+       col--;
       }
-      
-      /* If there is a line break in the header, honor it. */
-      if (cp[i] == '\n')
-      {
-       in_encoded_word = 0;
+      if (fputc ('\t', fp) == EOF)
+       return -1;
+      if (print_val (fp, pfx, p, flags) < 0)
+       return -1;
+      col += 8;
+    }
+    else if (print_val (fp, pfx, buf, flags) < 0)
+      return -1;
+    col += w;
 
-       if (cp[i+1] != ' ' && cp[i+1] != '\t')
-         first = 1;
-       
-       if (first || !wrapped)
-       {
-         k = i;
-         n = k + 1;
-         l = 1;
-         w = (wchar_t) '\n';
-         break;
-       }
-      }
+    /* if the current word ends in \n, ignore all its trailing spaces
+     * and reset column; this prevents us from putting only spaces (or
+     * even none) on a line if the trailing spaces are located at our
+     * current line width
+     * XXX this covers ASCII space only, for display we probably
+     * XXX want something like iswspace() here */
+    sp = next;
+    while (*sp && (*sp == ' ' || *sp == '\t'))
+      sp++;
+    if (*sp == '\n')
+    {
+      next = sp;
+      col = 0;
+    }
 
-      /* Eat the current character; cannot be '\0' */
+    p = next;
+    first = 0;
+  }
 
-      if ((l = mbtowc (&w, &cp[i], MB_CUR_MAX)) <= 0)
-      {
-       dprint (1, (debugfile, "mutt_write_one_header: encoutered bad multi-byte character at %d.\n", i));
-       l = 1; /* if bad, move on by one character */
-       w = (wchar_t) -1;
-      }
-      else 
-      {
-       if (wcwidth (w) >= 0)
-         col += wcwidth (w);
+  /* if we have printed something but didn't \n-terminate it, do it
+   * except the last word we printed ended in \n already */
+  if (col && buf[l - 1] != '\n')
+    if (putc ('\n', fp) == EOF)
+      return -1;
 
-       if (iswspace (w) && 
-           (!k || col <= wraplen))
-       {
-         if (!k || i != n)
-           k = i;
-         n = i + l;
-       }
-      }
+  return 0;
+}
 
-      /* 
-       * As long as we haven't seen whitespace, we advance at least by one character.
-       */
-      if (!k)
-       n = i + l;
-    }
+static int write_one_header (FILE *fp, int pfxw, int max, int wraplen,
+                            const char *pfx, const char *start, const char *end,
+                            int flags)
+{
+  char *tagbuf, *valbuf, *t;
 
-    /* If no whitespace was found, copy as much as we can */
-    if (!k)
-      k = n;
-    
-    /* If we're done, we're done. */
-    if (!cp[i])
-      k = n = i;
+  /* only pass through folding machinery if necessary for sending */
+  if (!(flags & CH_DISPLAY) && pfxw + max <= wraplen)
+  {
+    valbuf = mutt_substrdup (start, end);
+    dprint(4,(debugfile,"mwoh: buf[%s%s] short enough, "
+             "max width = %d <= %d\n",
+             NONULL(pfx), valbuf, max, wraplen));
+    if (pfx && *pfx)
+      if (fputs (pfx, fp) == EOF)
+       return -1;
+    if (print_val (fp, pfx, valbuf, flags) < 0)
+    {
+      FREE(&valbuf);
+      return -1;
+    }
+    FREE(&valbuf);
+  }
+  else
+  {
+    t = strchr (start, ':');
+    tagbuf = mutt_substrdup (start, t);
+    valbuf = mutt_substrdup (t + 2, end);
+    dprint(4,(debugfile,"mwoh: buf[%s%s] too long, "
+             "max width = %d > %dn",
+             NONULL(pfx), valbuf, max, wraplen));
+    if (fold_one_header (fp, tagbuf, valbuf, pfx, wraplen, flags) < 0)
+      return -1;
+    FREE (&tagbuf);
+    FREE (&valbuf);
+  }
+  return 0;
+}
 
-    if (k < i) /* we had to go back to an earlier wrapping point */
-      wrapped = 1;
-    
-    buf[0] = *cp;
-    foldingstrfcpy (buf + 1, cp + 1, k - 1);
+/* split several headers into individual ones and call write_one_header
+ * for each one */
+int mutt_write_one_header (FILE *fp, const char *tag, const char *value,
+                          const char *pfx, int wraplen, int flags)
+{
+  char *p = (char *)value, *last, *line;
+  int max = 0, w;
+  int pfxw = mutt_strwidth (pfx);
 
-    if (fprintf (fp, "%s\n", buf) < 0)
-      return -1;
-    col = 0;
-    
-    cp = &cp[n];
+  /* when not displaying, use sane wrap value */
+  if (!(flags & CH_DISPLAY))
+    wraplen = 76;
+  else if (wraplen <= 0 || wraplen > COLS)
+    wraplen = COLS;
 
-    while (*cp)
+  if (tag)
+  {
+    /* if header is short enough, simply print it */
+    if (!(flags & CH_DISPLAY) && mutt_strwidth (tag) + 2 + pfxw +
+       mutt_strwidth (value) <= wraplen)
     {
-      last = w;
-      if ((l = mbtowc (&w, cp, MB_CUR_MAX)) > 0 && iswspace (w))
-       cp += l;
-      else
-       break;
+      dprint(4,(debugfile,"mwoh: buf[%s%s: %s] is short enough\n",
+               NONULL(pfx), tag, value));
+      if (fprintf (fp, "%s%s: %s\n", NONULL(pfx), tag, value) <= 0)
+       return -1;
+      return 0;
     }
+    else
+      return fold_one_header (fp, tag, value, pfx, wraplen, flags);
   }
 
-  if (col)
+  p = last = line = (char *)value;
+  while (p && *p)
   {
-    if (fputc ('\n', fp) == EOF)
-      return -1;
-    col = 0;
+    p = strchr (p, '\n');
+
+    /* find maximum line width in current header */
+    if (p)
+      *p = 0;
+    if ((w = my_width (line, 0, flags)) > max)
+      max = w;
+    if (p)
+      *p = '\n';
+
+    if (!p)
+      break;
+
+    line = ++p;
+    if (*p != ' ' && *p != '\t')
+    {
+      if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
+       return -1;
+      last = p;
+      max = 0;
+    }
   }
 
+  if (last && *last)
+    if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
+      return -1;
+
   return 0;
 }
 
@@ -1727,13 +1819,13 @@ int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const c
 /* Note: all RFC2047 encoding should be done outside of this routine, except
  * for the "real name."  This will allow this routine to be used more than
  * once, if necessary.
- * 
+ *
  * Likewise, all IDN processing should happen outside of this routine.
  *
  * mode == 1  => "lite" mode (used for edit_hdrs)
  * mode == 0  => normal mode.  write full header + MIME headers
  * mode == -1 => write just the envelope info (used for postponing messages)
- * 
+ *
  * privacy != 0 => will omit any headers which may identify the user.
  *               Output generated is suitable for being sent through
  *              anonymous remailer chains.
@@ -1742,14 +1834,14 @@ int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const c
 
 
 
-int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach, 
+int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
                              int mode, int privacy)
 {
   char buffer[LONG_STRING];
   char *p, *q;
   LIST *tmp = env->userhdrs;
   int has_agent = 0; /* user defined user-agent header field exists */
-  
+
   if (mode == 0 && !privacy)
     fputs (mutt_make_date (buffer, sizeof(buffer)), fp);
 
@@ -1798,13 +1890,13 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
     fputs ("Bcc: \n", fp);
 
   if (env->subject)
-    mutt_write_one_header (fp, "Subject", env->subject, NULL, 0);
+    mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
   else if (mode == 1)
     fputs ("Subject: \n", fp);
 
   /* save message id if the user has set it */
   if (env->message_id && !privacy)
-    mutt_write_one_header (fp, "Message-ID", env->message_id, NULL, 0);
+    fprintf (fp, "Message-ID: %s\n", env->message_id);
 
   if (env->reply_to)
   {
@@ -1825,7 +1917,7 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
     if (env->references)
     {
       fputs ("References:", fp);
-      write_references (env->references, fp);
+      mutt_write_references (env->references, fp, 10);
       fputc('\n', fp);
     }
 
@@ -1837,19 +1929,19 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
   if (env->in_reply_to)
   {
     fputs ("In-Reply-To:", fp);
-    write_references (env->in_reply_to, fp);
+    mutt_write_references (env->in_reply_to, fp, 0);
     fputc ('\n', fp);
   }
-  
+
   /* Add any user defined headers */
   for (; tmp; tmp = tmp->next)
   {
     if ((p = strchr (tmp->data, ':')))
     {
       q = p;
-      
+
       *p = '\0';
-      
+
       p++; SKIPWS (p);
       if (!*p)
       {
@@ -1867,8 +1959,8 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
          continue;
        }
       }
-      
-      mutt_write_one_header (fp, tmp->data, p, NULL, 0);
+
+      mutt_write_one_header (fp, tmp->data, p, NULL, 0, 0);
       *q = ':';
     }
   }
@@ -1887,7 +1979,7 @@ static void encode_headers (LIST *h)
   char *tmp;
   char *p;
   int i;
-  
+
   for (; h; h = h->next)
   {
     if (!(p = strchr (h->data, ':')))
@@ -1899,24 +1991,24 @@ static void encode_headers (LIST *h)
 
     if (!tmp)
       continue;
-    
+
     rfc2047_encode_string (&tmp);
     safe_realloc (&h->data, mutt_strlen (h->data) + 2 + mutt_strlen (tmp) + 1);
 
     sprintf (h->data + i, ": %s", NONULL (tmp));  /* __SPRINTF_CHECKED__ */
-    
+
     FREE (&tmp);
   }
 }
 
 const char *mutt_fqdn(short may_hide_host)
 {
-  char *p = NULL, *q;
-  
+  char *p = NULL;
+
   if(Fqdn && Fqdn[0] != '@')
   {
     p = Fqdn;
-    
+
     if(may_hide_host && option(OPTHIDDENHOST))
     {
       if((p = strchr(Fqdn, '.')))
@@ -1925,8 +2017,8 @@ const char *mutt_fqdn(short may_hide_host)
       /* sanity check: don't hide the host if
        * the fqdn is something like detebe.org.
        */
-      
-      if(!p || !(q = strchr(p, '.')))
+
+      if(!p || !strchr(p, '.'))
        p = Fqdn;
     }
   }
@@ -1999,7 +2091,7 @@ send_msg (const char *path, char **args, const char *msg, char **tempfile)
      * so we put ourselves into another session right away
      */
     setsid ();
-  
+
     /* next we close all open files */
     close (0);
 #if defined(OPEN_MAX)
@@ -2027,7 +2119,7 @@ send_msg (const char *path, char **args, const char *msg, char **tempfile)
       }
       unlink (msg);
 
-      if (SendmailWait >= 0 && tempfile)
+      if (SendmailWait >= 0 && tempfile && *tempfile)
       {
        /* *tempfile will be opened as stdout */
        if (open (*tempfile, O_WRONLY | O_APPEND | O_CREAT | O_EXCL, 0600) < 0)
@@ -2079,7 +2171,7 @@ send_msg (const char *path, char **args, const char *msg, char **tempfile)
     if (waitpid (pid, &st, 0) > 0)
     {
       st = WIFEXITED (st) ? WEXITSTATUS (st) : S_ERR;
-      if (SendmailWait && st == (0xff & EX_OK) && tempfile)
+      if (SendmailWait && st == (0xff & EX_OK) && tempfile && *tempfile)
       {
        unlink (*tempfile); /* no longer needed */
        FREE (tempfile);                /* __FREE_CHECKED__ */
@@ -2089,7 +2181,7 @@ send_msg (const char *path, char **args, const char *msg, char **tempfile)
     {
       st = (SendmailWait > 0 && errno == EINTR && SigAlrm) ?
              S_BKG : S_ERR;
-      if (SendmailWait > 0 && tempfile)
+      if (SendmailWait > 0 && tempfile && *tempfile)
       {
        unlink (*tempfile);
        FREE (tempfile);                /* __FREE_CHECKED__ */
@@ -2100,7 +2192,7 @@ send_msg (const char *path, char **args, const char *msg, char **tempfile)
     alarm (0);
     sigaction (SIGALRM, &oldalrm, NULL);
 
-    if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile)
+    if (kill (ppid, 0) == -1 && errno == ESRCH && tempfile && *tempfile)
     {
       /* the parent is already dead */
       unlink (*tempfile);
@@ -2215,7 +2307,7 @@ mutt_invoke_sendmail (ADDRESS *from,      /* the sender */
 
   if (argslen == argsmax)
     safe_realloc (&args, sizeof (char *) * (++argsmax));
-  
+
   args[argslen++] = NULL;
 
   if ((i = send_msg (path, args, msg, option(OPTNOCURSES) ? NULL : &childout)) != (EX_OK & 0xff))
@@ -2229,13 +2321,13 @@ mutt_invoke_sendmail (ADDRESS *from,    /* the sender */
       if (childout)
       {
        struct stat st;
-       
+
        if (stat (childout, &st) == 0 && st.st_size > 0)
          mutt_do_pager (_("Output of the delivery process"), childout, 0, NULL);
       }
     }
   }
-  else
+  else if (childout)
     unlink (childout);
 
   FREE (&childout);
@@ -2252,59 +2344,6 @@ mutt_invoke_sendmail (ADDRESS *from,     /* the sender */
   return (i);
 }
 
-/* appends string 'b' to string 'a', and returns the pointer to the new
-   string. */
-char *mutt_append_string (char *a, const char *b)
-{
-  size_t la = mutt_strlen (a);
-  safe_realloc (&a, la + mutt_strlen (b) + 1);
-  strcpy (a + la, b);  /* __STRCPY_CHECKED__ */
-  return (a);
-}
-
-/* returns 1 if char `c' needs to be quoted to protect from shell
-   interpretation when executing commands in a subshell */
-#define INVALID_CHAR(c) (!isalnum ((unsigned char)c) && !strchr ("@.+-_,:", c))
-
-/* returns 1 if string `s' contains characters which could cause problems
-   when used on a command line to execute a command */
-int mutt_needs_quote (const char *s)
-{
-  while (*s)
-  {
-    if (INVALID_CHAR (*s))
-      return 1;
-    s++;
-  }
-  return 0;
-}
-
-/* Quote a string to prevent shell escapes when this string is used on the
-   command line to send mail. */
-char *mutt_quote_string (const char *s)
-{
-  char *r, *pr;
-  size_t rlen;
-
-  rlen = mutt_strlen (s) + 3;
-  pr = r = (char *) safe_malloc (rlen);
-  *pr++ = '"';
-  while (*s)
-  {
-    if (INVALID_CHAR (*s))
-    {
-      size_t o = pr - r;
-      safe_realloc (&r, ++rlen);
-      pr = r + o;
-      *pr++ = '\\';
-    }
-    *pr++ = *s++;
-  }
-  *pr++ = '"';
-  *pr = 0;
-  return (r);
-}
-
 /* For postponing (!final) do the necessary encodings only */
 void mutt_prepare_envelope (ENVELOPE *env, int final)
 {
@@ -2397,20 +2436,23 @@ static int _mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to, const char *r
   if ((f = safe_fopen (tempfile, "w")) != NULL)
   {
     int ch_flags = CH_XMIT | CH_NONEWLINE | CH_NOQFROM;
-    
+    char* msgid_str;
+
     if (!option (OPTBOUNCEDELIVERED))
       ch_flags |= CH_WEED_DELIVERED;
-    
+
     fseeko (fp, h->offset, 0);
     fprintf (f, "Resent-From: %s", resent_from);
     fprintf (f, "\nResent-%s", mutt_make_date (date, sizeof(date)));
-    fprintf (f, "Resent-Message-ID: %s\n", mutt_gen_msgid());
+    msgid_str = mutt_gen_msgid();
+    fprintf (f, "Resent-Message-ID: %s\n", msgid_str);
     fputs ("Resent-To: ", f);
     mutt_write_address_list (to, f, 11, 0);
     mutt_copy_header (fp, h, f, ch_flags, NULL);
     fputc ('\n', f);
     mutt_copy_bytes (fp, f, h->content->length);
-    fclose (f);
+    safe_fclose (&f);
+    FREE (&msgid_str);
 
 #if USE_SMTP
     if (SmtpUrl)
@@ -2435,10 +2477,20 @@ int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
   char resent_from[STRING];
   int ret;
   char *err;
-  
+
   resent_from[0] = '\0';
   from = mutt_default_from ();
 
+  /*
+   * mutt_default_from() does not use $realname if the real name is not set
+   * in $from, so we add it here.  The reason it is not added in
+   * mutt_default_from() is that during normal sending, we execute
+   * send-hooks and set the realname last so that it can be changed based
+   * upon message criteria.
+   */
+  if (! from->personal)
+    from->personal = safe_strdup(Realname);
+
   if (fqdn)
     rfc822_qualify (from, fqdn);
 
@@ -2447,6 +2499,7 @@ int mutt_bounce_message (FILE *fp, HEADER *h, ADDRESS *to)
   {
     mutt_error (_("Bad IDN %s while preparing resent-from."),
                err);
+    rfc822_free_address (&from);
     return -1;
   }
   rfc822_write_address (resent_from, sizeof (resent_from), from, 0);
@@ -2471,33 +2524,33 @@ ADDRESS *mutt_remove_duplicates (ADDRESS *addr)
   {
     for (tmp = top, dup = 0; tmp && tmp != addr; tmp = tmp->next)
     {
-      if (tmp->mailbox && addr->mailbox && 
+      if (tmp->mailbox && addr->mailbox &&
          !ascii_strcasecmp (addr->mailbox, tmp->mailbox))
       {
        dup = 1;
        break;
       }
     }
-    
+
     if (dup)
     {
       dprint (2, (debugfile, "mutt_remove_duplicates: Removing %s\n",
                  addr->mailbox));
-      
+
       *last = addr->next;
 
       addr->next = NULL;
       rfc822_free_address(&addr);
-      
+
       addr = *last;
     }
-    else 
+    else
     {
       last = &addr->next;
       addr = addr->next;
     }
   }
-  
+
   return (top);
 }
 
@@ -2523,11 +2576,12 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
   MESSAGE *msg;
   char tempfile[_POSIX_PATH_MAX];
   FILE *tempfp = NULL;
-  int r;
+  int r, need_buffy_cleanup = 0;
+  struct stat st;
 
   if (post)
     set_noconv_flags (hdr->content, 1);
-  
+
   if (mx_open_mailbox (path, M_APPEND | M_QUIET, &f) == NULL)
   {
     dprint (1, (debugfile, "mutt_write_fcc(): unable to open mailbox %s in append-mode, aborting.\n",
@@ -2536,7 +2590,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
   }
 
   /* We need to add a Content-Length field to avoid problems where a line in
-   * the message body begins with "From "   
+   * the message body begins with "From "
    */
   if (f.magic == M_MMDF || f.magic == M_MBOX)
   {
@@ -2547,6 +2601,9 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
       mx_close_mailbox (&f, NULL);
       return (-1);
     }
+    /* remember new mail status before appending message */
+    need_buffy_cleanup = 1;
+    stat (path, &st);
   }
 
   hdr->read = !post; /* make sure to put it in the `cur' directory (maildir) */
@@ -2557,7 +2614,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
   }
 
   /* post == 1 => postpone message. Set mode = -1 in mutt_write_rfc822_header()
-   * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header() 
+   * post == 0 => Normal mode. Set mode = 0 in mutt_write_rfc822_header()
    * */
   mutt_write_rfc822_header (msg->fp, hdr->env, hdr->content, post ? -post : 0, 0);
 
@@ -2569,9 +2626,9 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
    */
   if (post && msgid)
     fprintf (msg->fp, "X-Mutt-References: %s\n", msgid);
-  
+
   /* (postponment) save the Fcc: using a special X-Mutt- header so that
-   * it can be picked up when the message is recalled 
+   * it can be picked up when the message is recalled
    */
   if (post && fcc)
     fprintf (msg->fp, "X-Mutt-Fcc: %s\n", fcc);
@@ -2584,7 +2641,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
       && post && (hdr->security & APPLICATION_PGP))
   {
     fputs ("X-Mutt-PGP: ", msg->fp);
-    if (hdr->security & ENCRYPT) 
+    if (hdr->security & ENCRYPT)
       fputc ('E', msg->fp);
     if (hdr->security & SIGN)
     {
@@ -2618,10 +2675,10 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
   }
 
 #ifdef MIXMASTER
-  /* (postponement) if the mail is to be sent through a mixmaster 
+  /* (postponement) if the mail is to be sent through a mixmaster
    * chain, save that information
    */
-  
+
   if (post && hdr->chain && hdr->chain)
   {
     LIST *p;
@@ -2629,10 +2686,10 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
     fputs ("X-Mutt-Mix:", msg->fp);
     for (p = hdr->chain; p; p = p->next)
       fprintf (msg->fp, " %s", (char *) p->data);
-    
+
     fputc ('\n', msg->fp);
   }
-#endif    
+#endif
 
   if (tempfp)
   {
@@ -2642,7 +2699,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
     mutt_write_mime_body (hdr->content, tempfp);
 
     /* make sure the last line ends with a newline.  Emacs doesn't ensure
-     * this will happen, and it can cause problems parsing the mailbox   
+     * this will happen, and it can cause problems parsing the mailbox
      * later.
      */
     fseek (tempfp, -1, 2);
@@ -2656,7 +2713,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
     if (ferror (tempfp))
     {
       dprint (1, (debugfile, "mutt_write_fcc(): %s: write failed.\n", tempfile));
-      fclose (tempfp);
+      safe_fclose (&tempfp);
       unlink (tempfile);
       mx_commit_message (msg, &f);     /* XXX - really? */
       mx_close_message (&msg);
@@ -2668,7 +2725,7 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
     rewind (tempfp);
     while (fgets (sasha, sizeof (sasha), tempfp) != NULL)
       lines++;
-    fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftell (tempfp));
+    fprintf (msg->fp, "Content-Length: " OFF_T_FMT "\n", (LOFF_T) ftello (tempfp));
     fprintf (msg->fp, "Lines: %d\n\n", lines);
 
     /* copy the body and clean up */
@@ -2691,8 +2748,11 @@ int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int post,
   mx_close_message (&msg);
   mx_close_mailbox (&f, NULL);
 
+  if (!post && need_buffy_cleanup)
+    mutt_buffy_cleanup (path, &st);
+
   if (post)
     set_noconv_flags (hdr->content, 0);
-  
+
   return r;
 }