copy.c 24 KB
Newer Older
Thomas Roessler's avatar
Thomas Roessler committed
1
/*
2
 * Copyright (C) 1996-2000,2002 Michael R. Elkins <[email protected]>
Thomas Roessler's avatar
Thomas Roessler committed
3 4 5 6 7 8 9 10 11 12 13 14 15
 * 
 *     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
16
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
Thomas Roessler's avatar
Thomas Roessler committed
17 18
 */ 

19 20 21 22
#if HAVE_CONFIG_H
# include "config.h"
#endif

Thomas Roessler's avatar
Thomas Roessler committed
23 24 25 26 27
#include "mutt.h"
#include "mailbox.h"
#include "mx.h"
#include "copy.h"
#include "rfc2047.h"
28
#include "mime.h"
29
#include "mutt_crypt.h"
30
#include "mutt_idna.h"
31
#include "mutt_curses.h"
32

Thomas Roessler's avatar
Thomas Roessler committed
33 34 35 36 37
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h> /* needed for SEEK_SET under SunOS 4.1.4 */

38
static int address_header_decode (char **str);
39
static int copy_delete_attach (BODY *b, FILE *fpin, FILE *fpout, char *date);
Thomas Roessler's avatar
Thomas Roessler committed
40 41 42

/* Ok, the only reason for not merging this with mutt_copy_header()
 * below is to avoid creating a HEADER structure in message_handler().
43
 * Also, this one will wrap headers much more aggressively than the other one.
Thomas Roessler's avatar
Thomas Roessler committed
44 45
 */
int
46
mutt_copy_hdr (FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags,
Thomas Roessler's avatar
Thomas Roessler committed
47 48 49
	       const char *prefix)
{
  int from = 0;
50
  int this_is_from;
Thomas Roessler's avatar
Thomas Roessler committed
51
  int ignore = 0;
Sami Farin's avatar
Sami Farin committed
52
  char buf[LONG_STRING]; /* should be long enough to get most fields in one pass */
Thomas Roessler's avatar
Thomas Roessler committed
53 54 55 56 57
  char *nl;
  LIST *t;
  char **headers;
  int hdr_count;
  int x;
58
  char *this_one = NULL;
Fabian Groffen's avatar
Fabian Groffen committed
59
  size_t this_one_len = 0;
Thomas Roessler's avatar
Thomas Roessler committed
60 61
  int error;

62 63
  if (ftello (in) != off_start)
    fseeko (in, off_start, 0);
Thomas Roessler's avatar
Thomas Roessler committed
64 65 66 67

  buf[0] = '\n';
  buf[1] = 0;

Thomas Roessler's avatar
Thomas Roessler committed
68
  if ((flags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | CH_WEED_DELIVERED)) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
69 70 71 72
  {
    /* Without these flags to complicate things
     * we can do a more efficient line to line copying
     */
73
    while (ftello (in) < off_end)
Thomas Roessler's avatar
Thomas Roessler committed
74 75 76 77 78 79 80 81 82 83
    {
      nl = strchr (buf, '\n');

      if ((fgets (buf, sizeof (buf), in)) == NULL)
	break;

      /* Is it the begining of a header? */
      if (nl && buf[0] != ' ' && buf[0] != '\t')
      {
	ignore = 1;
84
	if (!from && mutt_strncmp ("From ", buf, 5) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
85 86 87 88 89
	{
	  if ((flags & CH_FROM) == 0)
	    continue;
	  from = 1;
	}
90 91 92 93
	else if (flags & (CH_NOQFROM) &&
			ascii_strncasecmp (">From ", buf, 6) == 0)
		continue;

Thomas Roessler's avatar
Thomas Roessler committed
94 95 96 97
	else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
	  break; /* end of header */

	if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
98 99
	    (ascii_strncasecmp ("Status:", buf, 7) == 0 ||
	     ascii_strncasecmp ("X-Status:", buf, 9) == 0))
Thomas Roessler's avatar
Thomas Roessler committed
100
	  continue;
101
	if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
102 103
	    (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 ||
	     ascii_strncasecmp ("Lines:", buf, 6) == 0))
Thomas Roessler's avatar
Thomas Roessler committed
104
	  continue;
Cedric Duval's avatar
Cedric Duval committed
105 106 107 108 109 110
	if ((flags & CH_UPDATE_REFS) &&
	    ascii_strncasecmp ("References:", buf, 11) == 0)
	  continue;
	if ((flags & CH_UPDATE_IRT) &&
	    ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0)
	  continue;
Thomas Roessler's avatar
Thomas Roessler committed
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
	ignore = 0;
      }

      if (!ignore && fputs (buf, out) == EOF)
	return (-1);
    }
    return 0;
  }

  hdr_count = 1;
  x = 0;
  error = FALSE;

  /* We are going to read and collect the headers in an array
   * so we are able to do re-ordering.
   * First count the number of entries in the array
   */
  if (flags & CH_REORDER)
  {
    for (t = HeaderOrderList; t; t = t->next)
    {
132
      dprint(3, (debugfile, "Reorder list: %s\n", t->data));
Thomas Roessler's avatar
Thomas Roessler committed
133 134 135 136 137 138 139 140 141
      hdr_count++;
    }
  }

  dprint (1, (debugfile, "WEED is %s\n", (flags & CH_WEED) ? "Set" : "Not"));

  headers = safe_calloc (hdr_count, sizeof (char *));

  /* Read all the headers into the array */
142
  while (ftello (in) < off_end)
Thomas Roessler's avatar
Thomas Roessler committed
143 144 145 146 147 148 149 150 151 152
  {
    nl = strchr (buf, '\n');

    /* Read a line */
    if ((fgets (buf, sizeof (buf), in)) == NULL)
      break;

    /* Is it the begining of a header? */
    if (nl && buf[0] != ' ' && buf[0] != '\t')
    {
153 154 155 156 157 158 159
      /* Do we have anything pending? */
      if (this_one)
      {
	if (flags & CH_DECODE) 
	{
	  if (!address_header_decode (&this_one))
	    rfc2047_decode (&this_one);
Sami Farin's avatar
Sami Farin committed
160
	  this_one_len = mutt_strlen (this_one);
161
	}
Sami Farin's avatar
Sami Farin committed
162

163 164 165 166
	if (!headers[x])
	  headers[x] = this_one;
	else 
	{
Sami Farin's avatar
Sami Farin committed
167 168 169 170
	  int hlen = mutt_strlen (headers[x]);

	  safe_realloc (&headers[x], hlen + this_one_len + sizeof (char));
	  strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */
171 172
	  FREE (&this_one);
	}
173

174 175
	this_one = NULL;
      }
176

Thomas Roessler's avatar
Thomas Roessler committed
177
      ignore = 1;
178
      this_is_from = 0;
179
      if (!from && mutt_strncmp ("From ", buf, 5) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
180 181 182
      {
	if ((flags & CH_FROM) == 0)
	  continue;
183
	this_is_from = from = 1;
Thomas Roessler's avatar
Thomas Roessler committed
184 185 186 187
      }
      else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
	break; /* end of header */

188 189 190
      /* note: CH_FROM takes precedence over header weeding. */
      if (!((flags & CH_FROM) && (flags & CH_FORCE_FROM) && this_is_from) &&
	  (flags & CH_WEED) &&
Thomas Roessler's avatar
Thomas Roessler committed
191 192 193
	  mutt_matches_ignore (buf, Ignore) &&
	  !mutt_matches_ignore (buf, UnIgnore))
	continue;
Thomas Roessler's avatar
Thomas Roessler committed
194
      if ((flags & CH_WEED_DELIVERED) &&
195
	  ascii_strncasecmp ("Delivered-To:", buf, 13) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
196
	continue;
Thomas Roessler's avatar
Thomas Roessler committed
197
      if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
198 199
	  (ascii_strncasecmp ("Status:", buf, 7) == 0 ||
	   ascii_strncasecmp ("X-Status:", buf, 9) == 0))
Thomas Roessler's avatar
Thomas Roessler committed
200
	continue;
201
      if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
202 203
	  (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 ||
	   ascii_strncasecmp ("Lines:", buf, 6) == 0))
Thomas Roessler's avatar
Thomas Roessler committed
204 205
	continue;
      if ((flags & CH_MIME) &&
206 207 208 209
	  ((ascii_strncasecmp ("content-", buf, 8) == 0 &&
	    (ascii_strncasecmp ("transfer-encoding:", buf + 8, 18) == 0 ||
	     ascii_strncasecmp ("type:", buf + 8, 5) == 0)) ||
	   ascii_strncasecmp ("mime-version:", buf, 13) == 0))
Thomas Roessler's avatar
Thomas Roessler committed
210
	continue;
Cedric Duval's avatar
Cedric Duval committed
211 212 213 214 215 216
      if ((flags & CH_UPDATE_REFS) &&
	  ascii_strncasecmp ("References:", buf, 11) == 0)
	continue;
      if ((flags & CH_UPDATE_IRT) &&
	  ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0)
	continue;
Thomas Roessler's avatar
Thomas Roessler committed
217 218 219 220 221 222

      /* Find x -- the array entry where this header is to be saved */
      if (flags & CH_REORDER)
      {
	for (t = HeaderOrderList, x = 0 ; (t) ; t = t->next, x++)
	{
223
	  if (!ascii_strncasecmp (buf, t->data, mutt_strlen (t->data)))
Thomas Roessler's avatar
Thomas Roessler committed
224
	  {
Thomas Roessler's avatar
Thomas Roessler committed
225
	    dprint(2, (debugfile, "Reorder: %s matches %s\n", t->data, buf));
Thomas Roessler's avatar
Thomas Roessler committed
226 227 228 229
	    break;
	  }
	}
      }
230

Thomas Roessler's avatar
Thomas Roessler committed
231 232 233 234 235
      ignore = 0;
    } /* If beginning of header */

    if (!ignore)
    {
Thomas Roessler's avatar
Thomas Roessler committed
236
      dprint (2, (debugfile, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count));
Sami Farin's avatar
Sami Farin committed
237
      if (!this_one) {
238
	this_one = safe_strdup (buf);
Sami Farin's avatar
Sami Farin committed
239 240 241 242 243 244 245
	this_one_len = mutt_strlen (this_one);
      } else {
	int blen = mutt_strlen (buf);

	safe_realloc (&this_one, this_one_len + blen + sizeof (char));
	strcat (this_one + this_one_len, buf); /* __STRCAT_CHECKED__ */
	this_one_len += blen;
Thomas Roessler's avatar
Thomas Roessler committed
246 247
      }
    }
248
  } /* while (ftello (in) < off_end) */
Thomas Roessler's avatar
Thomas Roessler committed
249

250 251 252 253 254 255 256 257 258 259 260 261 262
  /* Do we have anything pending?  -- XXX, same code as in above in the loop. */
  if (this_one)
  {
    if (flags & CH_DECODE) 
    {
      if (!address_header_decode (&this_one))
	rfc2047_decode (&this_one);
    }
    
    if (!headers[x])
      headers[x] = this_one;
    else 
    {
Sami Farin's avatar
Sami Farin committed
263 264 265 266
      int hlen = mutt_strlen (headers[x]);

      safe_realloc (&headers[x], hlen + this_one_len + sizeof (char));
      strcat (headers[x] + hlen, this_one); /* __STRCAT_CHECKED__ */
267 268
      FREE (&this_one);
    }
269

270 271 272
    this_one = NULL;
  }

Thomas Roessler's avatar
Thomas Roessler committed
273 274 275 276 277
  /* Now output the headers in order */
  for (x = 0; x < hdr_count; x++)
  {
    if (headers[x])
    {
278
#if 0
Thomas Roessler's avatar
Thomas Roessler committed
279
      if (flags & CH_DECODE)
280
	rfc2047_decode (&headers[x]);
281
#endif
Thomas Roessler's avatar
Thomas Roessler committed
282 283 284 285

      /* We couldn't do the prefixing when reading because RFC 2047
       * decoding may have concatenated lines.
       */
286 287
      
      if (flags & (CH_DECODE|CH_PREFIX))
Thomas Roessler's avatar
Thomas Roessler committed
288
      {
289
	if (mutt_write_one_header (out, 0, headers[x], 
290
				   flags & CH_PREFIX ? prefix : 0, mutt_term_width (Wrap)) == -1)
291 292
	{
	  error = TRUE;
Thomas Roessler's avatar
Thomas Roessler committed
293
	  break;
294
	}
Thomas Roessler's avatar
Thomas Roessler committed
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
      }
      else
      {      
	if (fputs (headers[x], out) == EOF)
	{
	  error = TRUE;
	  break;
	}
      }
    }
  }

  /* Free in a separate loop to be sure that all headers are freed
   * in case of error. */
  for (x = 0; x < hdr_count; x++)
310 311
    FREE (&headers[x]);
  FREE (&headers);
Thomas Roessler's avatar
Thomas Roessler committed
312 313 314 315 316 317 318 319 320

  if (error)
    return (-1);
  return (0);
}

/* flags
 	CH_DECODE	RFC2047 header decoding
 	CH_FROM		retain the "From " message separator
321
        CH_FORCE_FROM	give CH_FROM precedence over CH_WEED
Thomas Roessler's avatar
Thomas Roessler committed
322
 	CH_MIME		ignore MIME fields
323
	CH_NOLEN	don't write Content-Length: and Lines:
Thomas Roessler's avatar
Thomas Roessler committed
324 325 326 327
 	CH_NONEWLINE	don't output a newline after the header
 	CH_NOSTATUS	ignore the Status: and X-Status:
 	CH_PREFIX	quote header with $indent_str
 	CH_REORDER	output header in order specified by `hdr_order'
328
  	CH_TXTPLAIN	generate text/plain MIME headers [hack alert.]
Thomas Roessler's avatar
Thomas Roessler committed
329 330 331 332
 	CH_UPDATE	write new Status: and X-Status:
 	CH_UPDATE_LEN	write new Content-Length: and Lines:
 	CH_XMIT		ignore Lines: and Content-Length:
 	CH_WEED		do header weeding
333
	CH_NOQFROM      ignore ">From " line
Cedric Duval's avatar
Cedric Duval committed
334 335
	CH_UPDATE_IRT	update the In-Reply-To: header
	CH_UPDATE_REFS	update the References: header
Thomas Roessler's avatar
Thomas Roessler committed
336 337 338 339 340 341 342 343

   prefix
   	string to use if CH_PREFIX is set
 */

int
mutt_copy_header (FILE *in, HEADER *h, FILE *out, int flags, const char *prefix)
{
344
  char buffer[SHORT_STRING];
Cedric Duval's avatar
Cedric Duval committed
345

346 347 348
  if (h->env)
    flags |= (h->env->irt_changed ? CH_UPDATE_IRT : 0)
      | (h->env->refs_changed ? CH_UPDATE_REFS : 0);
349
  
Thomas Roessler's avatar
Thomas Roessler committed
350 351 352 353 354
  if (mutt_copy_hdr (in, out, h->offset, h->content->offset, flags, prefix) == -1)
    return (-1);

  if (flags & CH_TXTPLAIN)
  {
355
    char chsbuf[SHORT_STRING];
356
    fputs ("MIME-Version: 1.0\n", out);
Thomas Roessler's avatar
Thomas Roessler committed
357
    fputs ("Content-Transfer-Encoding: 8bit\n", out);
358
    fputs ("Content-Type: text/plain; charset=", out);
359 360
    mutt_canonical_charset (chsbuf, sizeof (chsbuf), Charset ? Charset : "us-ascii");
    rfc822_cat(buffer, sizeof(buffer), chsbuf, MimeSpecials);
361 362
    fputs(buffer, out);
    fputc('\n', out);
363 364 365 366
    
    if (ferror (out) != 0 || feof (out) != 0)
      return -1;
    
Thomas Roessler's avatar
Thomas Roessler committed
367
  }
368

Thomas Roessler's avatar
Thomas Roessler committed
369 370 371 372
  if (flags & CH_UPDATE)
  {
    if ((flags & CH_NOSTATUS) == 0)
    {
373
      if (h->env->irt_changed && h->env->in_reply_to)
Cedric Duval's avatar
Cedric Duval committed
374
      {
375
	LIST *listp = h->env->in_reply_to;
Cedric Duval's avatar
Cedric Duval committed
376 377 378 379 380 381 382 383 384 385 386 387

	if (fputs ("In-Reply-To: ", out) == EOF)
	  return (-1);

	for (; listp; listp = listp->next)
	  if ((fputs (listp->data, out) == EOF) || (fputc (' ', out) == EOF))
	    return (-1);

	if (fputc ('\n', out) == EOF)
	  return (-1);
      }

388
      if (h->env->refs_changed && h->env->references)
Cedric Duval's avatar
Cedric Duval committed
389
      {
390
	LIST *listp = h->env->references, *refs = NULL, *t;
Cedric Duval's avatar
Cedric Duval committed
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409

	if (fputs ("References: ", out) == EOF)
	  return (-1);

	/* Mutt stores references in reverse order, thus we create
	 * a reordered refs list that we can put in the headers */
	for (; listp; listp = listp->next, refs = t)
	{
	  t = (LIST *)safe_malloc (sizeof (LIST));
	  t->data = listp->data;
	  t->next = refs;
	}

	for (; refs; refs = refs->next)
	  if ((fputs (refs->data, out) == EOF) || (fputc (' ', out) == EOF))
	    return (-1);

	/* clearing refs from memory */
	for (t = refs; refs; refs = t->next, t = refs)
410
	  FREE (&refs);
Cedric Duval's avatar
Cedric Duval committed
411 412 413 414 415

	if (fputc ('\n', out) == EOF)
	  return (-1);
      }

Thomas Roessler's avatar
Thomas Roessler committed
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
      if (h->old || h->read)
      {
	if (fputs ("Status: ", out) == EOF)
	  return (-1);

	if (h->read)
	{
	  if (fputs ("RO", out) == EOF)
	    return (-1);
	}
	else if (h->old)
	{
	  if (fputc ('O', out) == EOF)
	    return (-1);
	}

	if (fputc ('\n', out) == EOF)
	  return (-1);
      }

      if (h->flagged || h->replied)
      {
	if (fputs ("X-Status: ", out) == EOF)
	  return (-1);

	if (h->replied)
	{
	  if (fputc ('A', out) == EOF)
	    return (-1);
	}

	if (h->flagged)
	{
	  if (fputc ('F', out) == EOF)
	    return (-1);
	}
	
	if (fputc ('\n', out) == EOF)
	  return (-1);
      }
    }
  }

459 460
  if (flags & CH_UPDATE_LEN &&
      (flags & CH_NOLEN) == 0)
Thomas Roessler's avatar
Thomas Roessler committed
461
  {
462
    fprintf (out, "Content-Length: " OFF_T_FMT "\n", h->content->length);
Thomas Roessler's avatar
Thomas Roessler committed
463 464 465 466 467 468
    if (h->lines != 0 || h->content->length == 0)
      fprintf (out, "Lines: %d\n", h->lines);
  }

  if ((flags & CH_NONEWLINE) == 0)
  {
469 470
    if (flags & CH_PREFIX)
      fputs(prefix, out);
Thomas Roessler's avatar
Thomas Roessler committed
471 472 473 474
    if (fputc ('\n', out) == EOF) /* add header terminator */
      return (-1);
  }

475 476 477
  if (ferror (out) || feof (out))
    return -1;
  
Thomas Roessler's avatar
Thomas Roessler committed
478 479 480
  return (0);
}

481
/* Count the number of lines and bytes to be deleted in this body*/
482
static int count_delete_lines (FILE *fp, BODY *b, LOFF_T *length, size_t datelen)
483 484 485 486 487 488 489
{
  int dellines = 0;
  long l;
  int ch;

  if (b->deleted)
  {
490
    fseeko (fp, b->offset, SEEK_SET);
491 492 493 494 495 496 497 498 499
    for (l = b->length ; l ; l --)
    {
      ch = getc (fp);
      if (ch == EOF)
	break;
      if (ch == '\n')
	dellines ++;
    }
    dellines -= 3;
500 501 502 503
    *length -= b->length - (84 + datelen);
    /* Count the number of digits exceeding the first one to write the size */
    for (l = 10 ; b->length >= l ; l *= 10)
      (*length) ++;
504 505 506 507
  }
  else
  {
    for (b = b->parts ; b ; b = b->next)
508
      dellines += count_delete_lines (fp, b, length, datelen);
509 510 511 512
  }
  return dellines;
}

Thomas Roessler's avatar
Thomas Roessler committed
513
/* make a copy of a message
514 515 516 517 518 519 520 521 522 523
 * 
 * fpout	where to write output
 * fpin		where to get input
 * hdr		header of message being copied
 * body		structure of message being copied
 * flags
 * 	M_CM_NOHEADER	don't copy header
 * 	M_CM_PREFIX	quote header and body
 *	M_CM_DECODE	decode message body to text/plain
 *	M_CM_DISPLAY	displaying output to the user
524
 *      M_CM_PRINTING   printing the message
525 526 527 528
 *	M_CM_UPDATE	update structures in memory after syncing
 *	M_CM_DECODE_PGP	used for decoding PGP messages
 *	M_CM_CHARCONV	perform character set conversion 
 * chflags	flags to mutt_copy_header()
Thomas Roessler's avatar
Thomas Roessler committed
529 530 531 532 533 534 535 536
 */

int
_mutt_copy_message (FILE *fpout, FILE *fpin, HEADER *hdr, BODY *body,
		    int flags, int chflags)
{
  char prefix[SHORT_STRING];
  STATE s;
537
  LOFF_T new_offset = -1;
538
  int rc = 0;
Thomas Roessler's avatar
Thomas Roessler committed
539 540

  if (flags & M_CM_PREFIX)
541 542 543 544 545 546
  {
    if (option (OPTTEXTFLOWED))
      strfcpy (prefix, ">", sizeof (prefix));
    else
      _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context, hdr, 0);
  }
Thomas Roessler's avatar
Thomas Roessler committed
547 548 549 550 551 552

  if ((flags & M_CM_NOHEADER) == 0)
  {
    if (flags & M_CM_PREFIX)
      chflags |= CH_PREFIX;

553 554 555
    else if (hdr->attach_del && (chflags & CH_UPDATE_LEN))
    {
      int new_lines;
556
      LOFF_T new_length = body->length;
557
      char date[SHORT_STRING];
558

559 560 561 562
      mutt_make_date (date, sizeof (date));
      date[5] = date[mutt_strlen (date) - 1] = '\"';

      /* Count the number of lines and bytes to be deleted */
563
      fseeko (fpin, body->offset, SEEK_SET);
564 565
      new_lines = hdr->lines -
	count_delete_lines (fpin, body, &new_length, mutt_strlen (date));
566 567

      /* Copy the headers */
568 569
      if (mutt_copy_header (fpin, hdr, fpout,
			    chflags | CH_NOLEN | CH_NONEWLINE, NULL))
570
	return -1;
571
      fprintf (fpout, "Content-Length: " OFF_T_FMT "\n", new_length);
572 573 574
      if (new_lines <= 0)
	new_lines = 0;
      else
575 576 577
	fprintf (fpout, "Lines: %d\n", new_lines);

      putc ('\n', fpout);
578
      if (ferror (fpout) || feof (fpout))
579
	return -1;
580
      new_offset = ftello (fpout);
581

582
      /* Copy the body */
583
      fseeko (fpin, body->offset, SEEK_SET);
584
      if (copy_delete_attach (body, fpin, fpout, date))
585
	return -1;
586

587 588
#ifdef DEBUG
      {
589
	LOFF_T fail = ((ftello (fpout) - new_offset) - new_length);
590 591 592 593 594

	if (fail)
	{
	  mutt_error ("The length calculation was wrong by %ld bytes", fail);
	  new_length += fail;
595
	  mutt_sleep (1);
596 597 598 599
	}
      }
#endif

600 601 602 603 604 605
      /* Update original message if we are sync'ing a mailfolder */ 
      if (flags & M_CM_UPDATE)
      {
	hdr->attach_del = 0;
	hdr->lines = new_lines;
	body->offset = new_offset;
606 607 608 609 610 611 612 613 614 615

	/* update the total size of the mailbox to reflect this deletion */
	Context->size -= body->length - new_length;
	/*
	 * if the message is visible, update the visible size of the mailbox
	 * as well.
	 */
	if (Context->v2r[hdr->msgno] != -1)
	  Context->vsize -= body->length - new_length;

616
	body->length = new_length;
617 618 619 620 621
	mutt_free_body (&body->parts);
      }

      return 0;
    }
Thomas Roessler's avatar
Thomas Roessler committed
622 623 624 625

    if (mutt_copy_header (fpin, hdr, fpout, chflags,
			  (chflags & CH_PREFIX) ? prefix : NULL) == -1)
      return -1;
626

627
    new_offset = ftello (fpout);
Thomas Roessler's avatar
Thomas Roessler committed
628 629 630 631 632 633 634 635 636 637 638 639
  }

  if (flags & M_CM_DECODE)
  {
    /* now make a text/plain version of the message */
    memset (&s, 0, sizeof (STATE));
    s.fpin = fpin;
    s.fpout = fpout;
    if (flags & M_CM_PREFIX)
      s.prefix = prefix;
    if (flags & M_CM_DISPLAY)
      s.flags |= M_DISPLAY;
640 641
    if (flags & M_CM_PRINTING)
      s.flags |= M_PRINTING;
642 643
    if (flags & M_CM_WEED)
      s.flags |= M_WEED;
644 645
    if (flags & M_CM_CHARCONV)
      s.flags |= M_CHARCONV;
TAKAHASHI Tamotsu's avatar
TAKAHASHI Tamotsu committed
646 647
    if (flags & M_CM_REPLYING)
      s.flags |= M_REPLYING;
648
    
649
    if (WithCrypto && flags & M_CM_VERIFY)
Thomas Roessler's avatar
Thomas Roessler committed
650 651
      s.flags |= M_VERIFY;

652
    rc = mutt_body_handler (body, &s);
653
  }
654 655
  else if (WithCrypto
           && (flags & M_CM_DECODE_CRYPT) && (hdr->security & ENCRYPT))
656 657 658
  {
    BODY *cur;
    FILE *fp;
Thomas Roessler's avatar
Thomas Roessler committed
659

660 661
    if ((WithCrypto & APPLICATION_PGP)
        && (flags & M_CM_DECODE_PGP) && (hdr->security & APPLICATION_PGP) &&
662 663
	hdr->content->type == TYPEMULTIPART)
    {
664
      if (crypt_pgp_decrypt_mime (fpin, &fp, hdr->content, &cur))
665
	return (-1);
666
      fputs ("MIME-Version: 1.0\n", fpout);
667
    }
668 669 670

    if ((WithCrypto & APPLICATION_SMIME)
        && (flags & M_CM_DECODE_SMIME) && (hdr->security & APPLICATION_SMIME)
671 672
	     && hdr->content->type == TYPEAPPLICATION)
    {
673
      if (crypt_smime_decrypt_mime (fpin, &fp, hdr->content, &cur))
674 675
	return (-1);
    }
676

677 678
    mutt_write_mime_header (cur, fpout);
    fputc ('\n', fpout);
Thomas Roessler's avatar
Thomas Roessler committed
679

680
    fseeko (fp, cur->offset, 0);
681 682 683 684 685 686 687 688
    if (mutt_copy_bytes (fp, fpout, cur->length) == -1)
    {
      fclose (fp);
      mutt_free_body (&cur);
      return (-1);
    }
    mutt_free_body (&cur);
    fclose (fp);
Thomas Roessler's avatar
Thomas Roessler committed
689 690 691
  }
  else
  {
692
    fseeko (fpin, body->offset, 0);
Thomas Roessler's avatar
Thomas Roessler committed
693 694
    if (flags & M_CM_PREFIX)
    {
Thomas Roessler's avatar
Thomas Roessler committed
695
      int c;
Thomas Roessler's avatar
Thomas Roessler committed
696
      size_t bytes = body->length;
Thomas Roessler's avatar
Thomas Roessler committed
697 698 699 700
      
      fputs(prefix, fpout);
      
      while((c = fgetc(fpin)) != EOF && bytes--)
Thomas Roessler's avatar
Thomas Roessler committed
701
      {
Thomas Roessler's avatar
Thomas Roessler committed
702 703 704 705 706 707
	fputc(c, fpout);
	if(c == '\n')
	{
	  fputs(prefix, fpout);
	}
      } 
Thomas Roessler's avatar
Thomas Roessler committed
708
    }
709 710 711 712 713 714 715 716 717
    else if (mutt_copy_bytes (fpin, fpout, body->length) == -1)
      return -1;
  }

  if ((flags & M_CM_UPDATE) && (flags & M_CM_NOHEADER) == 0 
      && new_offset != -1)
  {
    body->offset = new_offset;
    mutt_free_body (&body->parts);
Thomas Roessler's avatar
Thomas Roessler committed
718
  }
719

720
  return rc;
Thomas Roessler's avatar
Thomas Roessler committed
721 722
}

723 724
/* should be made to return -1 on fatal errors, and 1 on non-fatal errors
 * like partial decode, where it is worth displaying as much as possible */
Thomas Roessler's avatar
Thomas Roessler committed
725 726 727 728 729 730 731 732 733
int
mutt_copy_message (FILE *fpout, CONTEXT *src, HEADER *hdr, int flags,
		   int chflags)
{
  MESSAGE *msg;
  int r;
  
  if ((msg = mx_open_message (src, hdr->msgno)) == NULL)
    return -1;
734 735 736 737 738 739
  if ((r = _mutt_copy_message (fpout, msg->fp, hdr, hdr->content, flags, chflags)) == 0 
      && (ferror (fpout) || feof (fpout)))
  {
    dprint (1, (debugfile, "_mutt_copy_message failed to detect EOF!\n"));
    r = -1;
  }
Thomas Roessler's avatar
Thomas Roessler committed
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
  mx_close_message (&msg);
  return r;
}

/* appends a copy of the given message to a mailbox
 *
 * dest		destination mailbox
 * fpin		where to get input
 * src		source mailbox
 * hdr		message being copied
 * body		structure of message being copied
 * flags	mutt_copy_message() flags
 * chflags	mutt_copy_header() flags
 */

int
_mutt_append_message (CONTEXT *dest, FILE *fpin, CONTEXT *src, HEADER *hdr,
		      BODY *body, int flags, int chflags)
{
759
  char buf[STRING];
Thomas Roessler's avatar
Thomas Roessler committed
760 761 762
  MESSAGE *msg;
  int r;

763
  fseeko (fpin, hdr->offset, 0);
764 765 766 767
  if (fgets (buf, sizeof (buf), fpin) == NULL)
    return -1;
  
  if ((msg = mx_open_new_message (dest, hdr, is_from (buf, NULL, 0, NULL) ? 0 : M_ADD_FROM)) == NULL)
Thomas Roessler's avatar
Thomas Roessler committed
768
    return -1;
769
  if (dest->magic == M_MBOX || dest->magic == M_MMDF)
770
    chflags |= CH_FROM | CH_FORCE_FROM;
Thomas Roessler's avatar
Thomas Roessler committed
771 772
  chflags |= (dest->magic == M_MAILDIR ? CH_NOSTATUS : CH_UPDATE);
  r = _mutt_copy_message (msg->fp, fpin, hdr, body, flags, chflags);
773 774 775 776
  if (mx_commit_message (msg, dest) != 0)
    r = -1;

  mx_close_message (&msg);
Thomas Roessler's avatar
Thomas Roessler committed
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
  return r;
}

int
mutt_append_message (CONTEXT *dest, CONTEXT *src, HEADER *hdr, int cmflags,
		     int chflags)
{
  MESSAGE *msg;
  int r;

  if ((msg = mx_open_message (src, hdr->msgno)) == NULL)
    return -1;
  r = _mutt_append_message (dest, msg->fp, src, hdr, hdr->content, cmflags, chflags);
  mx_close_message (&msg);
  return r;
}

/*
795 796 797
 * This function copies a message body, while deleting _in_the_copy_
 * any attachments which are marked for deletion.
 * Nothing is changed in the original message -- this is left to the caller.
798
 *
799
 * The function will return 0 on success and -1 on failure.
800
 */
801
static int copy_delete_attach (BODY *b, FILE *fpin, FILE *fpout, char *date)
Thomas Roessler's avatar
Thomas Roessler committed
802
{
803
  BODY *part;
804

805
  for (part = b->parts ; part ; part = part->next)
Thomas Roessler's avatar
Thomas Roessler committed
806
  {
807
    if (part->deleted || part->parts)
808
    {
809
      /* Copy till start of this part */
810
      if (mutt_copy_bytes (fpin, fpout, part->hdr_offset - ftello (fpin)))
811
	return -1;
Thomas Roessler's avatar
Thomas Roessler committed
812

813 814 815 816
      if (part->deleted)
      {
	fprintf (fpout,
		 "Content-Type: message/external-body; access-type=x-mutt-deleted;\n"
817
		 "\texpiration=%s; length=" OFF_T_FMT "\n"
818 819 820
		 "\n", date + 5, part->length);
	if (ferror (fpout))
	  return -1;
Thomas Roessler's avatar
Thomas Roessler committed
821

822
	/* Copy the original mime headers */
823
	if (mutt_copy_bytes (fpin, fpout, part->offset - ftello (fpin)))
824
	  return -1;
825

826
	/* Skip the deleted body */
827
	fseeko (fpin, part->offset + part->length, SEEK_SET);
Thomas Roessler's avatar
Thomas Roessler committed
828
      }
829
      else
830
      {
831
	if (copy_delete_attach (part, fpin, fpout, date))
832
	  return -1;
Thomas Roessler's avatar
Thomas Roessler committed
833 834 835
      }
    }
  }
836

837
  /* Copy the last parts */
838
  if (mutt_copy_bytes (fpin, fpout, b->offset + b->length - ftello (fpin)))
839
    return -1;
Thomas Roessler's avatar
Thomas Roessler committed
840

841
  return 0;
Thomas Roessler's avatar
Thomas Roessler committed
842
}
843

844 845 846 847 848 849 850 851 852 853 854 855 856 857
/* 
 * This function is the equivalent of mutt_write_address_list(),
 * but writes to a buffer instead of writing to a stream.
 * mutt_write_address_list could be re-used if we wouldn't store
 * all the decoded headers in a huge array, first. 
 *
 * XXX - fix that. 
 */

static void format_address_header (char **h, ADDRESS *a)
{
  char buf[HUGE_STRING];
  char cbuf[STRING];
  char c2buf[STRING];
Fabian Groffen's avatar
Fabian Groffen committed
858
  char *p = NULL;
Sami Farin's avatar
Sami Farin committed
859 860
  int l, linelen, buflen, count, cbuflen, c2buflen, plen;

861
  linelen = mutt_strlen (*h);
Sami Farin's avatar
Sami Farin committed
862
  plen = linelen;
863
  buflen  = linelen + 3;
Sami Farin's avatar
Sami Farin committed
864

865
  safe_realloc (h, buflen);
866 867 868 869 870
  for (count = 0; a; a = a->next, count++)
  {
    ADDRESS *tmp = a->next;
    a->next = NULL;
    *buf = *cbuf = *c2buf = '\0';
Sami Farin's avatar
Sami Farin committed
871
    l = rfc822_write_address (buf, sizeof (buf), a, 0);
872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893
    a->next = tmp;
    
    if (count && linelen + l > 74) 
    {
      strcpy (cbuf, "\n\t");  	/* __STRCPY_CHECKED__ */
      linelen = l + 8;
    }
    else
    {
      if (a->mailbox)
      {
	strcpy (cbuf, " ");	/* __STRCPY_CHECKED__ */
	linelen++;
      }
      linelen += l;
    }
    if (!a->group && a->next && a->next->mailbox)
    {
      linelen++;
      buflen++;
      strcpy (c2buf, ",");	/* __STRCPY_CHECKED__ */
    }
Sami Farin's avatar
Sami Farin committed
894 895 896 897

    cbuflen = mutt_strlen (cbuf);
    c2buflen = mutt_strlen (c2buf);
    buflen += l + cbuflen + c2buflen;
898
    safe_realloc (h, buflen);
Sami Farin's avatar
Sami Farin committed
899 900 901 902 903 904 905
    p = *h;
    strcat (p + plen, cbuf);		/* __STRCAT_CHECKED__ */
    plen += cbuflen;
    strcat (p + plen, buf);		/* __STRCAT_CHECKED__ */
    plen += l;
    strcat (p + plen, c2buf);		/* __STRCAT_CHECKED__ */
    plen += c2buflen;
906 907 908
  }
  
  /* Space for this was allocated in the beginning of this function. */
Sami Farin's avatar
Sami Farin committed
909
  strcat (p + plen, "\n");		/* __STRCAT_CHECKED__ */
910 911
}

912 913 914
static int address_header_decode (char **h)
{
  char *s = *h;
915 916
  int l;

917
  ADDRESS *a = NULL;
918

919
  switch (tolower ((unsigned char) *s))
920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
  {
    case 'r': 
    {
      if (ascii_strncasecmp (s, "return-path:", 12) == 0)
      {
	l = 12;
	break;
      }
      else if (ascii_strncasecmp (s, "reply-to:", 9) == 0)
      {
	l = 9;
	break;
      }
      return 0;
    }
    case 'f': 
    {
      if (ascii_strncasecmp (s, "from:", 5)) 
	return 0; 
      l = 5;
      break;
    }
    case 'c':
    {
944
      if (ascii_strncasecmp (s, "cc:", 3))
945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
	return 0;
      l = 3;
      break;
      
    }
    case 'b':
    {
      if (ascii_strncasecmp (s, "bcc:", 4))
	return 0;
      l = 4;
      break;
    }
    case 's':
    {
      if (ascii_strncasecmp (s, "sender:", 7))
	return 0;
      l = 7;
      break;
    }
Thomas Roessler's avatar
Thomas Roessler committed
964 965 966 967 968 969 970
    case 't':
    {
      if (ascii_strncasecmp (s, "to:", 3))
	return 0;
      l = 3;
      break;
    }
971 972 973 974 975 976 977 978 979 980
    case 'm':
    {
      if (ascii_strncasecmp (s, "mail-followup-to:", 17))
	return 0;
      l = 17;
      break;
    }
    default: return 0;    
  }

Thomas Roessler's avatar
Thomas Roessler committed
981
  if ((a = rfc822_parse_adrlist (a, s + l)) == NULL)
982 983 984 985 986
    return 0;
  
  mutt_addrlist_to_local (a);
  rfc2047_decode_adrlist (a);
  
987
  *h = safe_calloc (1, l + 2);
988
  
989
  strfcpy (*h, s, l + 1);
990
  
991 992 993
  format_address_header (h, a);

  rfc822_free_address (&a);
994 995 996 997
  
  FREE (&s);
  return 1;
}