some code simplifications.
[apps/madmutt.git] / copy.c
1 /*
2  * Copyright notice from original mutt:
3  * Copyright (C) 1996-2000,2002 Michael R. Elkins <me@mutt.org>
4  *
5  * This file is part of mutt-ng, see http://www.muttng.org/.
6  * It's licensed under the GNU General Public License,
7  * please see the file GPL in the top level source directory.
8  */
9
10 #include <lib-lib/lib-lib.h>
11
12 #include <lib-mime/mime.h>
13 #include <lib-mx/mx.h>
14
15 #include "mutt.h"
16 #include "handler.h"
17 #include "copy.h"
18 #include <lib-crypt/crypt.h>
19 #include <lib-ui/curses.h>
20 #include "mutt_idna.h"
21
22 static int address_header_decode (char **str);
23 static int copy_delete_attach (BODY * b, FILE * fpin, FILE * fpout,
24                                char *date);
25
26 /* Ok, the only reason for not merging this with mutt_copy_header()
27  * below is to avoid creating a HEADER structure in message_handler().
28  */
29 int
30 mutt_copy_hdr (FILE* in, FILE* out, off_t off_start, off_t off_end,
31                int flags, const char *prefix) {
32   int from = 0;
33   int this_is_from;
34   int ignore = 0;
35   char buf[STRING];             /* should be long enough to get most fields in one pass */
36   char *eol;
37   string_list_t *t;
38   char **headers;
39   int hdr_count;
40   int x;
41   char *this_one = NULL;
42   int error;
43   int curline = 0;
44
45   if (ftello (in) != off_start)
46     fseeko (in, off_start, 0);
47
48   buf[0] = '\n';
49   buf[1] = 0;
50
51   if ((flags &
52        (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX |
53         CH_WEED_DELIVERED)) == 0) {
54     /* Without these flags to complicate things
55      * we can do a more efficient line to line copying
56      */
57     while (ftello (in) < off_end) {
58       eol = strchr (buf, '\n');
59
60       if ((fgets (buf, sizeof (buf), in)) == NULL)
61         break;
62
63       /* Is it the begining of a header? */
64       if (eol && buf[0] != ' ' && buf[0] != '\t') {
65         ignore = 1;
66         if (!from && m_strncmp("From ", buf, 5) == 0) {
67           if ((flags & CH_FROM) == 0)
68             continue;
69           from = 1;
70         }
71         else if (flags & (CH_NOQFROM) &&
72                  ascii_strncasecmp (">From ", buf, 6) == 0)
73           continue;
74
75         else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
76           break;                /* end of header */
77
78         if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
79             (ascii_strncasecmp ("Status:", buf, 7) == 0 ||
80              ascii_strncasecmp ("X-Status:", buf, 9) == 0))
81           continue;
82         if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
83             (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 ||
84              ascii_strncasecmp ("Lines:", buf, 6) == 0))
85           continue;
86         if ((flags & CH_UPDATE_REFS) &&
87             ascii_strncasecmp ("References:", buf, 11) == 0)
88           continue;
89         if ((flags & CH_UPDATE_IRT) &&
90             ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0)
91           continue;
92         ignore = 0;
93       }
94
95       if (!ignore && fputs (buf, out) == EOF)
96         return (-1);
97     }
98     return 0;
99   }
100
101   hdr_count = 1;
102   x = 0;
103   error = FALSE;
104
105   /* We are going to read and collect the headers in an array
106    * so we are able to do re-ordering.
107    * First count the number of entries in the array
108    */
109   if (flags & CH_REORDER) {
110     for (t = HeaderOrderList; t; t = t->next) {
111       hdr_count++;
112     }
113   }
114
115   headers = p_new(char *, hdr_count);
116
117   /* Read all the headers into the array */
118   while (ftello (in) < off_end) {
119     eol = strchr (buf, '\n');
120
121     /* Read a line */
122     if ((fgets (buf, sizeof (buf), in)) == NULL)
123       break;
124
125     /* Is it the begining of a header? */
126     if (eol && buf[0] != ' ' && buf[0] != '\t') {
127
128       /* set curline to 1 for To:/Cc:/Bcc: and 0 otherwise */
129       curline = (flags & CH_WEED) && (m_strncmp("To:", buf, 3) == 0 ||
130                                       m_strncmp("Cc:", buf, 3) == 0 ||
131                                       m_strncmp("Bcc:", buf, 4) == 0);
132
133       /* Do we have anything pending? */
134       if (this_one) {
135         if (flags & CH_DECODE) {
136           if (!address_header_decode (&this_one))
137             rfc2047_decode (&this_one);
138         }
139
140         if (!headers[x])
141           headers[x] = this_one;
142         else {
143           p_realloc(&headers[x], m_strlen(headers[x]) + m_strlen(this_one) + 1);
144           strcat(headers[x], this_one);        /* __STRCAT_CHECKED__ */
145           p_delete(&this_one);
146         }
147
148         this_one = NULL;
149       }
150
151       ignore = 1;
152       this_is_from = 0;
153       if (!from && m_strncmp("From ", buf, 5) == 0) {
154         if ((flags & CH_FROM) == 0)
155           continue;
156         this_is_from = from = 1;
157       }
158       else if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
159         break;                  /* end of header */
160
161       /* note: CH_FROM takes precedence over header weeding. */
162       if (!((flags & CH_FROM) && (flags & CH_FORCE_FROM) && this_is_from) &&
163           (flags & CH_WEED) &&
164           mutt_matches_ignore (buf, Ignore) &&
165           !mutt_matches_ignore (buf, UnIgnore))
166         continue;
167       if ((flags & CH_WEED_DELIVERED) &&
168           ascii_strncasecmp ("Delivered-To:", buf, 13) == 0)
169         continue;
170       if ((flags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
171           (ascii_strncasecmp ("Status:", buf, 7) == 0 ||
172            ascii_strncasecmp ("X-Status:", buf, 9) == 0))
173         continue;
174       if ((flags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
175           (ascii_strncasecmp ("Content-Length:", buf, 15) == 0 ||
176            ascii_strncasecmp ("Lines:", buf, 6) == 0))
177         continue;
178       if ((flags & CH_MIME) &&
179           ((ascii_strncasecmp ("content-", buf, 8) == 0 &&
180             (ascii_strncasecmp ("transfer-encoding:", buf + 8, 18) == 0 ||
181              ascii_strncasecmp ("type:", buf + 8, 5) == 0)) ||
182            ascii_strncasecmp ("mime-version:", buf, 13) == 0))
183         continue;
184       if ((flags & CH_UPDATE_REFS) &&
185           ascii_strncasecmp ("References:", buf, 11) == 0)
186         continue;
187       if ((flags & CH_UPDATE_IRT) &&
188           ascii_strncasecmp ("In-Reply-To:", buf, 12) == 0)
189         continue;
190
191       /* Find x -- the array entry where this header is to be saved */
192       if (flags & CH_REORDER) {
193         for (t = HeaderOrderList, x = 0; (t); t = t->next, x++) {
194           if (!ascii_strncasecmp (buf, t->data, m_strlen(t->data))) {
195             break;
196           }
197         }
198       }
199
200       ignore = 0;
201     }                           /* If beginning of header */
202
203     if (!ignore) {
204       if (!this_one)
205         this_one = m_strdup(buf);
206       /* we do want to see all lines if this header doesn't feature
207        * abbreviations (curline is 0), $max_display_recips is 0 and
208        * while the number hasn't reached $max_display_recips yet */
209       else if (curline == 0 || MaxDispRecips == 0 || ++curline <= MaxDispRecips) {
210         p_realloc(&this_one, m_strlen(this_one) + m_strlen(buf) + 1);
211         strcat (this_one, buf); /* __STRCAT_CHECKED__ */
212       /* only for the first line which doesn't exeeds
213        * $max_display_recips: abbreviate it */
214       } else if (curline == MaxDispRecips+1) {
215         p_realloc(&this_one, m_strlen(this_one) + 5);
216         strcat (this_one, " ...");
217       }
218     }
219   }                             /* while (ftello (in) < off_end) */
220
221   /* Do we have anything pending?  -- XXX, same code as in above in the loop. */
222   if (this_one) {
223     if (flags & CH_DECODE) {
224       if (!address_header_decode (&this_one))
225         rfc2047_decode (&this_one);
226     }
227
228     if (!headers[x])
229       headers[x] = this_one;
230     else {
231       p_realloc(&headers[x], m_strlen(headers[x]) + m_strlen(this_one) + 1);
232       strcat (headers[x], this_one);    /* __STRCAT_CHECKED__ */
233       p_delete(&this_one);
234     }
235
236     this_one = NULL;
237   }
238
239   /* Now output the headers in order */
240   for (x = 0; x < hdr_count; x++) {
241     if (headers[x]) {
242       /* We couldn't do the prefixing when reading because RFC 2047
243        * decoding may have concatenated lines.
244        */
245       if (flags & CH_PREFIX) {
246         char *ch = headers[x];
247         int print_prefix = 1;
248
249         while (*ch) {
250           if (print_prefix) {
251             if (fputs (prefix, out) == EOF) {
252               error = TRUE;
253               break;
254             }
255             print_prefix = 0;
256           }
257
258           if (*ch == '\n' && ch[1])
259             print_prefix = 1;
260
261           if (putc (*ch++, out) == EOF) {
262             error = TRUE;
263             break;
264           }
265         }
266         if (error)
267           break;
268       }
269       else {
270         if (fputs (headers[x], out) == EOF) {
271           error = TRUE;
272           break;
273         }
274       }
275     }
276   }
277
278   /* Free in a separate loop to be sure that all headers are freed
279    * in case of error. */
280   for (x = 0; x < hdr_count; x++)
281     p_delete(&headers[x]);
282   p_delete(&headers);
283
284   if (error)
285     return (-1);
286   return (0);
287 }
288
289 /* flags
290         CH_DECODE       RFC2047 header decoding
291         CH_FROM         retain the "From " message separator
292         CH_FORCE_FROM   give CH_FROM precedence over CH_WEED
293         CH_MIME         ignore MIME fields
294         CH_NOLEN        don't write Content-Length: and Lines:
295         CH_NONEWLINE    don't output a newline after the header
296         CH_NOSTATUS     ignore the Status: and X-Status:
297         CH_PREFIX       quote header with $indent_str
298         CH_REORDER      output header in order specified by `hdr_order'
299         CH_TXTPLAIN     generate text/plain MIME headers [hack alert.]
300         CH_UPDATE       write new Status: and X-Status:
301         CH_UPDATE_LEN   write new Content-Length: and Lines:
302         CH_XMIT         ignore Lines: and Content-Length:
303         CH_WEED         do header weeding
304         CH_NOQFROM      ignore ">From " line
305         CH_UPDATE_IRT   update the In-Reply-To: header
306         CH_UPDATE_REFS  update the References: header
307
308    prefix
309         string to use if CH_PREFIX is set
310  */
311
312 int
313 mutt_copy_header (FILE * in, HEADER * h, FILE * out, int flags,
314                   const char *prefix)
315 {
316   char buffer[SHORT_STRING];
317
318   if (h->env)
319     flags |= (h->env->irt_changed ? CH_UPDATE_IRT : 0) |
320       (h->env->refs_changed ? CH_UPDATE_REFS : 0);
321
322   if (mutt_copy_hdr (in, out, h->offset, h->content->offset, flags, prefix) ==
323       -1)
324     return (-1);
325
326   if (flags & CH_TXTPLAIN) {
327     char chsbuf[SHORT_STRING];
328
329     fputs ("MIME-Version: 1.0\n", out);
330     fputs ("Content-Transfer-Encoding: 8bit\n", out);
331     fputs ("Content-Type: text/plain; charset=", out);
332     charset_canonicalize(chsbuf, sizeof (chsbuf), Charset);
333     rfc822_strcpy(buffer, sizeof(buffer), chsbuf, MimeSpecials);
334     fputs (buffer, out);
335     fputc ('\n', out);
336
337     if (ferror (out) != 0 || feof (out) != 0)
338       return -1;
339
340   }
341
342   if (flags & CH_UPDATE) {
343     if ((flags & CH_NOSTATUS) == 0) {
344       if (h->env->irt_changed && h->env->in_reply_to) {
345         string_list_t *listp = h->env->in_reply_to;
346
347         if (fputs ("In-Reply-To: ", out) == EOF)
348           return (-1);
349
350         for (; listp; listp = listp->next)
351           if ((fputs (listp->data, out) == EOF) || (fputc (' ', out) == EOF))
352             return (-1);
353
354         if (fputc ('\n', out) == EOF)
355           return (-1);
356       }
357
358       if (h->env->refs_changed && h->env->references) {
359         string_list_t *listp = h->env->references, *refs = NULL, *t;
360
361         if (fputs ("References: ", out) == EOF)
362           return (-1);
363
364         /* Mutt stores references in reverse order, thus we create
365          * a reordered refs list that we can put in the headers */
366         for (; listp; listp = listp->next, refs = t) {
367           t = p_new(string_list_t, 1);
368           t->data = listp->data;
369           t->next = refs;
370         }
371
372         for (; refs; refs = refs->next)
373           if ((fputs (refs->data, out) == EOF) || (fputc (' ', out) == EOF))
374             return (-1);
375
376         /* clearing refs from memory */
377         for (t = refs; refs; refs = t->next, t = refs)
378           p_delete(&refs);
379
380         if (fputc ('\n', out) == EOF)
381           return (-1);
382       }
383
384       if (h->old || h->read) {
385         if (fputs ("Status: ", out) == EOF)
386           return (-1);
387
388         if (h->read) {
389           if (fputs ("RO", out) == EOF)
390             return (-1);
391         }
392         else if (h->old) {
393           if (fputc ('O', out) == EOF)
394             return (-1);
395         }
396
397         if (fputc ('\n', out) == EOF)
398           return (-1);
399       }
400
401       if (h->flagged || h->replied) {
402         if (fputs ("X-Status: ", out) == EOF)
403           return (-1);
404
405         if (h->replied) {
406           if (fputc ('A', out) == EOF)
407             return (-1);
408         }
409
410         if (h->flagged) {
411           if (fputc ('F', out) == EOF)
412             return (-1);
413         }
414
415         if (fputc ('\n', out) == EOF)
416           return (-1);
417       }
418     }
419   }
420
421   if (flags & CH_UPDATE_LEN && (flags & CH_NOLEN) == 0) {
422     fprintf (out, "Content-Length: %lld\n", h->content->length);
423     if (h->lines != 0 || h->content->length == 0)
424       fprintf (out, "Lines: %d\n", h->lines);
425   }
426
427   if ((flags & CH_NONEWLINE) == 0) {
428     if (flags & CH_PREFIX)
429       fputs (prefix, out);
430     if (fputc ('\n', out) == EOF)       /* add header terminator */
431       return (-1);
432   }
433
434   if (ferror (out) || feof (out))
435     return -1;
436
437   return (0);
438 }
439
440 /* Count the number of lines and bytes to be deleted in this body*/
441 static int count_delete_lines (FILE * fp, BODY * b, off_t *length,
442                                size_t datelen)
443 {
444   int dellines = 0;
445   long l;
446   int ch;
447
448   if (b->deleted) {
449     fseeko (fp, b->offset, SEEK_SET);
450     for (l = b->length; l; l--) {
451       ch = getc (fp);
452       if (ch == EOF)
453         break;
454       if (ch == '\n')
455         dellines++;
456     }
457     dellines -= 3;
458     *length -= b->length - (84 + datelen);
459     /* Count the number of digits exceeding the first one to write the size */
460     for (l = 10; b->length >= l; l *= 10)
461       (*length)++;
462   }
463   else {
464     for (b = b->parts; b; b = b->next)
465       dellines += count_delete_lines (fp, b, length, datelen);
466   }
467   return dellines;
468 }
469
470 /* make a copy of a message
471  * 
472  * fpout        where to write output
473  * fpin         where to get input
474  * hdr          header of message being copied
475  * body         structure of message being copied
476  * flags
477  *      M_CM_NOHEADER   don't copy header
478  *      M_CM_PREFIX     quote header and body
479  *      M_CM_DECODE     decode message body to text/plain
480  *      M_CM_DISPLAY    displaying output to the user
481  *      M_CM_PRINTING   printing the message
482  *      M_CM_UPDATE     update structures in memory after syncing
483  *      M_CM_DECODE_PGP used for decoding PGP messages
484  *      M_CM_CHARCONV   perform character set conversion 
485  * chflags      flags to mutt_copy_header()
486  */
487
488 int
489 _mutt_copy_message (FILE * fpout, FILE * fpin, HEADER * hdr, BODY * body,
490                     int flags, int chflags)
491 {
492   char prefix[SHORT_STRING];
493   STATE s;
494   off_t new_offset = -1;
495   int rc = 0;
496
497   if (flags & M_CM_PREFIX) {
498     if (option (OPTTEXTFLOWED))
499       m_strcpy(prefix, sizeof(prefix), ">");
500     else
501       _mutt_make_string (prefix, sizeof (prefix), NONULL (Prefix), Context,
502                          hdr, 0);
503   }
504
505   if ((flags & M_CM_NOHEADER) == 0) {
506     if (flags & M_CM_PREFIX)
507       chflags |= CH_PREFIX;
508
509     else if (hdr->attach_del && (chflags & CH_UPDATE_LEN)) {
510       int new_lines;
511       off_t new_length = body->length;
512       char date[SHORT_STRING];
513
514       mutt_make_date (date, sizeof (date));
515       date[5] = date[m_strlen(date) - 1] = '\"';
516
517       /* Count the number of lines and bytes to be deleted */
518       fseeko (fpin, body->offset, SEEK_SET);
519       new_lines = hdr->lines -
520         count_delete_lines (fpin, body, &new_length, m_strlen(date));
521
522       /* Copy the headers */
523       if (mutt_copy_header (fpin, hdr, fpout,
524                             chflags | CH_NOLEN | CH_NONEWLINE, NULL))
525         return -1;
526       fprintf (fpout, "Content-Length: %lld\n", new_length);
527       if (new_lines <= 0)
528         new_lines = 0;
529       else
530         fprintf (fpout, "Lines: %d\n\n", new_lines);
531       if (ferror (fpout) || feof (fpout))
532         return -1;
533       new_offset = ftello (fpout);
534
535       /* Copy the body */
536       fseeko (fpin, body->offset, SEEK_SET);
537       if (copy_delete_attach (body, fpin, fpout, date))
538         return -1;
539
540       /* Update original message if we are sync'ing a mailfolder */
541       if (flags & M_CM_UPDATE) {
542         hdr->attach_del = 0;
543         hdr->lines = new_lines;
544         body->offset = new_offset;
545
546         /* update the total size of the mailbox to reflect this deletion */
547         Context->size -= body->length - new_length;
548         /*
549          * if the message is visible, update the visible size of the mailbox
550          * as well.
551          */
552         if (Context->v2r[hdr->msgno] != -1)
553           Context->vsize -= body->length - new_length;
554
555         body->length = new_length;
556         body_list_wipe(&body->parts);
557       }
558
559       return 0;
560     }
561
562     if (mutt_copy_header (fpin, hdr, fpout, chflags,
563                           (chflags & CH_PREFIX) ? prefix : NULL) == -1)
564       return -1;
565
566     new_offset = ftello (fpout);
567   }
568
569   if (flags & M_CM_DECODE) {
570     /* now make a text/plain version of the message */
571     p_clear(&s, 1);
572     s.fpin = fpin;
573     s.fpout = fpout;
574     if (flags & M_CM_PREFIX)
575       s.prefix = prefix;
576     if (flags & M_CM_DISPLAY)
577       s.flags |= M_DISPLAY;
578     if (flags & M_CM_PRINTING)
579       s.flags |= M_PRINTING;
580     if (flags & M_CM_WEED)
581       s.flags |= M_WEED;
582     if (flags & M_CM_CHARCONV)
583       s.flags |= M_CHARCONV;
584     if (flags & M_CM_REPLYING)
585       s.flags |= M_REPLYING;
586
587     if (flags & M_CM_VERIFY)
588       s.flags |= M_VERIFY;
589
590     rc = mutt_body_handler (body, &s);
591   }
592   else if ((flags & M_CM_DECODE_CRYPT) && (hdr->security & ENCRYPT)) {
593     BODY *cur;
594     FILE *fp;
595
596     if ((flags & M_CM_DECODE_PGP) && (hdr->security & APPLICATION_PGP) &&
597         hdr->content->type == TYPEMULTIPART) {
598       if (crypt_pgp_decrypt_mime (fpin, &fp, hdr->content, &cur))
599         return (-1);
600       fputs ("MIME-Version: 1.0\n", fpout);
601     }
602
603     if ((flags & M_CM_DECODE_SMIME) && (hdr->security & APPLICATION_SMIME)
604         && hdr->content->type == TYPEAPPLICATION) {
605       if (crypt_smime_decrypt_mime (fpin, &fp, hdr->content, &cur))
606         return (-1);
607     }
608
609     mutt_write_mime_header (cur, fpout);
610     fputc ('\n', fpout);
611
612     fseeko (fp, cur->offset, 0);
613     if (mutt_copy_bytes (fp, fpout, cur->length) == -1) {
614       m_fclose(&fp);
615       body_list_wipe(&cur);
616       return (-1);
617     }
618     body_list_wipe(&cur);
619     m_fclose(&fp);
620   }
621   else {
622     fseeko (fpin, body->offset, 0);
623     if (flags & M_CM_PREFIX) {
624       int c;
625       size_t bytes = body->length;
626
627       fputs (prefix, fpout);
628
629       while ((c = fgetc (fpin)) != EOF && bytes--) {
630         fputc (c, fpout);
631         if (c == '\n') {
632           fputs (prefix, fpout);
633         }
634       }
635     }
636     else if (mutt_copy_bytes (fpin, fpout, body->length) == -1)
637       return -1;
638   }
639
640   if ((flags & M_CM_UPDATE) && (flags & M_CM_NOHEADER) == 0
641       && new_offset != -1) {
642     body->offset = new_offset;
643     body_list_wipe(&body->parts);
644   }
645
646   return rc;
647 }
648
649 int
650 mutt_copy_message (FILE * fpout, CONTEXT * src, HEADER * hdr, int flags,
651                    int chflags)
652 {
653   MESSAGE *msg;
654   int r;
655
656   if ((msg = mx_open_message (src, hdr->msgno)) == NULL)
657     return -1;
658   if ((r =
659        _mutt_copy_message (fpout, msg->fp, hdr, hdr->content, flags,
660                            chflags)) == 0 && (ferror (fpout)
661                                               || feof (fpout))) {
662     r = -1;
663   }
664   mx_close_message (&msg);
665   return r;
666 }
667
668 /* appends a copy of the given message to a mailbox
669  *
670  * dest         destination mailbox
671  * fpin         where to get input
672  * src          source mailbox
673  * hdr          message being copied
674  * body         structure of message being copied
675  * flags        mutt_copy_message() flags
676  * chflags      mutt_copy_header() flags
677  */
678
679 int
680 _mutt_append_message (CONTEXT * dest, FILE * fpin, CONTEXT * src __attribute__ ((unused)),
681                       HEADER * hdr, BODY * body, int flags, int chflags) {
682   char buf[STRING];
683   MESSAGE *msg;
684   int r;
685
686   fseeko(fpin, hdr->offset, 0);
687   if (fgets (buf, sizeof (buf), fpin) == NULL)
688     return (-1);
689   if ((msg = mx_open_new_message (dest, hdr, is_from (buf, NULL, 0, NULL) ? 0 : M_ADD_FROM)) == NULL)
690     return (-1);
691   if (dest->magic == M_MBOX || dest->magic == M_MMDF)
692     chflags |= CH_FROM | CH_FORCE_FROM;
693   chflags |= (dest->magic == M_MAILDIR ? CH_NOSTATUS : CH_UPDATE);
694   r = _mutt_copy_message (msg->fp, fpin, hdr, body, flags, chflags);
695   if (mx_commit_message (msg, dest) != 0)
696     r = -1;
697
698   mx_close_message (&msg);
699   return r;
700 }
701
702 int
703 mutt_append_message (CONTEXT * dest, CONTEXT * src, HEADER * hdr, int cmflags,
704                      int chflags)
705 {
706   MESSAGE *msg;
707   int r;
708
709   if ((msg = mx_open_message (src, hdr->msgno)) == NULL)
710     return -1;
711   r =
712     _mutt_append_message (dest, msg->fp, src, hdr, hdr->content, cmflags,
713                           chflags);
714   mx_close_message (&msg);
715   return r;
716 }
717
718 /*
719  * This function copies a message body, while deleting _in_the_copy_
720  * any attachments which are marked for deletion.
721  * Nothing is changed in the original message -- this is left to the caller.
722  *
723  * The function will return 0 on success and -1 on failure.
724  */
725 static int copy_delete_attach (BODY * b, FILE * fpin, FILE * fpout,
726                                char *date)
727 {
728   BODY *part;
729
730   for (part = b->parts; part; part = part->next) {
731     if (part->deleted || part->parts) {
732       /* Copy till start of this part */
733       if (mutt_copy_bytes (fpin, fpout, part->hdr_offset - ftello (fpin)))
734         return -1;
735
736       if (part->deleted) {
737         fprintf (fpout,
738                  "Content-Type: message/external-body; access-type=x-mutt-deleted;\n"
739                  "\texpiration=%s; length=%lld\n"
740                  "\n", date + 5, part->length);
741         if (ferror (fpout))
742           return -1;
743
744         /* Copy the original mime headers */
745         if (mutt_copy_bytes (fpin, fpout, part->offset - ftello (fpin)))
746           return -1;
747
748         /* Skip the deleted body */
749         fseeko (fpin, part->offset + part->length, SEEK_SET);
750       }
751       else {
752         if (copy_delete_attach (part, fpin, fpout, date))
753           return -1;
754       }
755     }
756   }
757
758   /* Copy the last parts */
759   if (mutt_copy_bytes (fpin, fpout, b->offset + b->length - ftello (fpin)))
760     return -1;
761
762   return 0;
763 }
764
765 /* 
766  * This function is the equivalent of mutt_write_address_list(),
767  * but writes to a buffer instead of writing to a stream.
768  * mutt_write_address_list could be re-used if we wouldn't store
769  * all the decoded headers in a huge array, first. 
770  *
771  * XXX - fix that. 
772  */
773
774 static void format_address_header (char **h, address_t * a)
775 {
776   char buf[HUGE_STRING];
777   char cbuf[STRING];
778   char c2buf[STRING];
779
780   int l, linelen, buflen, count;
781
782   linelen = m_strlen(*h);
783   buflen = linelen + 3;
784
785
786   p_realloc(h, buflen);
787   for (count = 0; a; a = a->next, count++) {
788     address_t *tmp = a->next;
789
790     a->next = NULL;
791     *buf = *cbuf = *c2buf = '\0';
792     rfc822_write_address (buf, sizeof (buf), a, 0);
793     a->next = tmp;
794
795     l = m_strlen(buf);
796     if (count && linelen + l > 74) {
797       strcpy (cbuf, "\n\t");    /* __STRCPY_CHECKED__ */
798       linelen = l + 8;
799     }
800     else {
801       if (a->mailbox) {
802         strcpy (cbuf, " ");     /* __STRCPY_CHECKED__ */
803         linelen++;
804       }
805       linelen += l;
806     }
807     if (!a->group && a->next && a->next->mailbox) {
808       linelen++;
809       buflen++;
810       strcpy (c2buf, ",");      /* __STRCPY_CHECKED__ */
811     }
812
813     buflen += l + m_strlen(cbuf) + m_strlen(c2buf);
814     p_realloc(h, buflen);
815     strcat (*h, cbuf);          /* __STRCAT_CHECKED__ */
816     strcat (*h, buf);           /* __STRCAT_CHECKED__ */
817     strcat (*h, c2buf);         /* __STRCAT_CHECKED__ */
818   }
819
820   /* Space for this was allocated in the beginning of this function. */
821   strcat (*h, "\n");            /* __STRCAT_CHECKED__ */
822 }
823
824 static int address_header_decode (char **h)
825 {
826   char *s = *h;
827   int l;
828
829   address_t *a = NULL;
830
831   switch (tolower ((unsigned char) *s)) {
832   case 'r':
833     {
834       if (ascii_strncasecmp (s, "return-path:", 12) == 0) {
835         l = 12;
836         break;
837       }
838       else if (ascii_strncasecmp (s, "reply-to:", 9) == 0) {
839         l = 9;
840         break;
841       }
842       return 0;
843     }
844   case 'f':
845     {
846       if (ascii_strncasecmp (s, "from:", 5))
847         return 0;
848       l = 5;
849       break;
850     }
851   case 'c':
852     {
853       if (ascii_strncasecmp (s, "cc:", 3))
854         return 0;
855       l = 3;
856       break;
857
858     }
859   case 'b':
860     {
861       if (ascii_strncasecmp (s, "bcc:", 4))
862         return 0;
863       l = 4;
864       break;
865     }
866   case 's':
867     {
868       if (ascii_strncasecmp (s, "sender:", 7))
869         return 0;
870       l = 7;
871       break;
872     }
873   case 't':
874     {
875       if (ascii_strncasecmp (s, "to:", 3))
876         return 0;
877       l = 3;
878       break;
879     }
880   case 'm':
881     {
882       if (ascii_strncasecmp (s, "mail-followup-to:", 17))
883         return 0;
884       l = 17;
885       break;
886     }
887   default:
888     return 0;
889   }
890
891   if ((a = rfc822_parse_adrlist (a, s + l)) == NULL)
892     return 0;
893
894   mutt_addrlist_to_local (a);
895   rfc2047_decode_adrlist (a);
896
897   *h = p_dupstr(s, l + 1);
898
899   format_address_header (h, a);
900
901   address_list_wipe(&a);
902
903   p_delete(&s);
904   return 1;
905 }