NeoMutt  2022-04-29-70-g0c028c
Teaching an old dog new tricks
DOXYGEN
copy.c File Reference

Duplicate the structure of an entire email. More...

#include "config.h"
#include <ctype.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "email/lib.h"
#include "core/lib.h"
#include "gui/lib.h"
#include "mutt.h"
#include "copy.h"
#include "index/lib.h"
#include "ncrypt/lib.h"
#include "send/lib.h"
#include "format_flags.h"
#include "handler.h"
#include "hdrline.h"
#include "mutt_globals.h"
#include "mx.h"
#include "notmuch/lib.h"
#include "muttlib.h"
#include <libintl.h>
+ Include dependency graph for copy.c:

Go to the source code of this file.

Functions

static int address_header_decode (char **h)
 Parse an email's headers. More...
 
static int copy_delete_attach (struct Body *b, FILE *fp_in, FILE *fp_out, const char *quoted_date)
 Copy a message, deleting marked attachments. More...
 
 ARRAY_HEAD (Headers, char *)
 
static void add_one_header (struct Headers *headers, size_t pos, char *value)
 Add a header to a Headers array. More...
 
int mutt_copy_hdr (FILE *fp_in, FILE *fp_out, LOFF_T off_start, LOFF_T off_end, CopyHeaderFlags chflags, const char *prefix, int wraplen)
 Copy header from one file to another. More...
 
int mutt_copy_header (FILE *fp_in, struct Email *e, FILE *fp_out, CopyHeaderFlags chflags, const char *prefix, int wraplen)
 Copy Email header. More...
 
static int count_delete_lines (FILE *fp, struct Body *b, LOFF_T *length, size_t datelen)
 Count lines to be deleted in this email body. More...
 
int mutt_copy_message_fp (FILE *fp_out, FILE *fp_in, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
 Make a copy of a message from a FILE pointer. More...
 
int mutt_copy_message (FILE *fp_out, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
 Copy a message from a Mailbox. More...
 
static int append_message (struct Mailbox *dest, FILE *fp_in, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
 Appends a copy of the given message to a mailbox. More...
 
int mutt_append_message (struct Mailbox *m_dst, struct Mailbox *m_src, struct Email *e, struct Message *msg, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
 Append a message. More...
 
static void format_address_header (char **h, struct AddressList *al)
 Write address headers to a buffer. More...
 

Detailed Description

Duplicate the structure of an entire email.

Authors
  • Michael R. Elkins
  • Pietro Cerutti

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, see http://www.gnu.org/licenses/.

Definition in file copy.c.

Function Documentation

◆ address_header_decode()

static int address_header_decode ( char **  h)
static

Parse an email's headers.

Parameters
[out]hArray of header strings
Return values
0Success
1Failure

Definition at line 1103 of file copy.c.

1104 {
1105  char *s = *h;
1106  size_t l;
1107  bool rp = false;
1108 
1109  switch (tolower((unsigned char) *s))
1110  {
1111  case 'b':
1112  {
1113  if (!(l = mutt_istr_startswith(s, "bcc:")))
1114  return 0;
1115  break;
1116  }
1117  case 'c':
1118  {
1119  if (!(l = mutt_istr_startswith(s, "cc:")))
1120  return 0;
1121  break;
1122  }
1123  case 'f':
1124  {
1125  if (!(l = mutt_istr_startswith(s, "from:")))
1126  return 0;
1127  break;
1128  }
1129  case 'm':
1130  {
1131  if (!(l = mutt_istr_startswith(s, "mail-followup-to:")))
1132  return 0;
1133  break;
1134  }
1135  case 'r':
1136  {
1137  if ((l = mutt_istr_startswith(s, "return-path:")))
1138  {
1139  rp = true;
1140  break;
1141  }
1142  else if ((l = mutt_istr_startswith(s, "reply-to:")))
1143  {
1144  break;
1145  }
1146  return 0;
1147  }
1148  case 's':
1149  {
1150  if (!(l = mutt_istr_startswith(s, "sender:")))
1151  return 0;
1152  break;
1153  }
1154  case 't':
1155  {
1156  if (!(l = mutt_istr_startswith(s, "to:")))
1157  return 0;
1158  break;
1159  }
1160  default:
1161  return 0;
1162  }
1163 
1164  struct AddressList al = TAILQ_HEAD_INITIALIZER(al);
1165  mutt_addrlist_parse(&al, s + l);
1166  if (TAILQ_EMPTY(&al))
1167  return 0;
1168 
1171  struct Address *a = NULL;
1172  TAILQ_FOREACH(a, &al, entries)
1173  {
1174  if (a->personal)
1175  {
1177  }
1178  }
1179 
1180  /* angle brackets for return path are mandated by RFC5322,
1181  * so leave Return-Path as-is */
1182  if (rp)
1183  *h = mutt_str_dup(s);
1184  else
1185  {
1186  *h = mutt_mem_calloc(1, l + 2);
1187  mutt_str_copy(*h, s, l + 1);
1188  format_address_header(h, &al);
1189  }
1190 
1191  mutt_addrlist_clear(&al);
1192 
1193  FREE(&s);
1194  return 1;
1195 }
void mutt_addrlist_clear(struct AddressList *al)
Unlink and free all Address in an AddressList.
Definition: address.c:1470
int mutt_addrlist_to_local(struct AddressList *al)
Convert an Address list from Punycode.
Definition: address.c:1388
int mutt_addrlist_parse(struct AddressList *al, const char *s)
Parse a list of email addresses.
Definition: address.c:458
static void format_address_header(char **h, struct AddressList *al)
Write address headers to a buffer.
Definition: copy.c:1037
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:40
void mutt_str_dequote_comment(char *str)
Un-escape characters in an email address comment.
Definition: string.c:752
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
size_t mutt_str_copy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:629
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:239
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:725
#define TAILQ_HEAD_INITIALIZER(head)
Definition: queue.h:637
#define TAILQ_EMPTY(head)
Definition: queue.h:721
void rfc2047_decode_addrlist(struct AddressList *al)
Decode any RFC2047 headers in an Address list.
Definition: rfc2047.c:770
An email address.
Definition: address.h:36
char * personal
Real name of address.
Definition: address.h:37
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ copy_delete_attach()

static int copy_delete_attach ( struct Body b,
FILE *  fp_in,
FILE *  fp_out,
const char *  quoted_date 
)
static

Copy a message, deleting marked attachments.

Parameters
bEmail Body
fp_inFILE pointer to read from
fp_outFILE pointer to write to
quoted_dateDate stamp
Return values
0Success
-1Failure

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.

Definition at line 970 of file copy.c.

971 {
972  struct Body *part = NULL;
973 
974  for (part = b->parts; part; part = part->next)
975  {
976  if (part->deleted || part->parts)
977  {
978  /* Copy till start of this part */
979  if (mutt_file_copy_bytes(fp_in, fp_out, part->hdr_offset - ftello(fp_in)))
980  {
981  return -1;
982  }
983 
984  if (part->deleted)
985  {
986  /* If this is modified, count_delete_lines() needs to be changed too */
987  fprintf(fp_out,
988  "Content-Type: message/external-body; access-type=x-mutt-deleted;\n"
989  "\texpiration=%s; length=" OFF_T_FMT "\n"
990  "\n",
991  quoted_date, part->length);
992  if (ferror(fp_out))
993  {
994  return -1;
995  }
996 
997  /* Copy the original mime headers */
998  if (mutt_file_copy_bytes(fp_in, fp_out, part->offset - ftello(fp_in)))
999  {
1000  return -1;
1001  }
1002 
1003  /* Skip the deleted body */
1004  if (!mutt_file_seek(fp_in, part->offset + part->length, SEEK_SET))
1005  {
1006  return -1;
1007  }
1008  }
1009  else
1010  {
1011  if (copy_delete_attach(part, fp_in, fp_out, quoted_date))
1012  {
1013  return -1;
1014  }
1015  }
1016  }
1017  }
1018 
1019  /* Copy the last parts */
1020  if (mutt_file_copy_bytes(fp_in, fp_out, b->offset + b->length - ftello(fp_in)))
1021  return -1;
1022 
1023  return 0;
1024 }
static int copy_delete_attach(struct Body *b, FILE *fp_in, FILE *fp_out, const char *quoted_date)
Copy a message, deleting marked attachments.
Definition: copy.c:970
int mutt_file_copy_bytes(FILE *fp_in, FILE *fp_out, size_t size)
Copy some content from one file to another.
Definition: file.c:230
bool mutt_file_seek(FILE *fp, LOFF_T offset, int whence)
Wrapper for fseeko with error handling.
Definition: file.c:690
The body of an email.
Definition: body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:72
LOFF_T offset
offset where the actual data begins
Definition: body.h:52
bool deleted
Attachment marked for deletion.
Definition: body.h:87
LOFF_T length
length (in bytes) of attachment
Definition: body.h:53
struct Body * next
next attachment in the list
Definition: body.h:71
long hdr_offset
Offset in stream where the headers begin.
Definition: body.h:80
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ ARRAY_HEAD()

ARRAY_HEAD ( Headers  ,
char *   
)

◆ add_one_header()

static void add_one_header ( struct Headers *  headers,
size_t  pos,
char *  value 
)
static

Add a header to a Headers array.

Parameters
headersHeaders array
posPosition to insert new header
valueText to insert

If a header already exists in that position, the new text will be concatenated on the old.

Definition at line 74 of file copy.c.

75 {
76  char **old = ARRAY_GET(headers, pos);
77  if (old && *old)
78  {
79  char *new_value = NULL;
80  mutt_str_asprintf(&new_value, "%s%s", *old, value);
81  FREE(old);
82  FREE(&value);
83  value = new_value;
84  }
85  ARRAY_SET(headers, pos, value);
86 }
#define ARRAY_SET(head, idx, elem)
Set an element in the array.
Definition: array.h:122
#define ARRAY_GET(head, idx)
Return the element at index.
Definition: array.h:108
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1008
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_copy_hdr()

int mutt_copy_hdr ( FILE *  fp_in,
FILE *  fp_out,
LOFF_T  off_start,
LOFF_T  off_end,
CopyHeaderFlags  chflags,
const char *  prefix,
int  wraplen 
)

Copy header from one file to another.

Parameters
fp_inFILE pointer to read from
fp_outFILE pointer to write to
off_startOffset to start from
off_endOffset to finish at
chflagsFlags, see CopyHeaderFlags
prefixPrefix for quoting headers
wraplenWidth to wrap at (when chflags & CH_DISPLAY)
Return values
0Success
-1Failure

Ok, the only reason for not merging this with mutt_copy_header() below is to avoid creating a Email structure in message_handler(). Also, this one will wrap headers much more aggressively than the other one.

Definition at line 104 of file copy.c.

106 {
107  bool from = false;
108  bool this_is_from = false;
109  bool ignore = false;
110  char buf[1024]; /* should be long enough to get most fields in one pass */
111  char *nl = NULL;
112  struct Headers headers = ARRAY_HEAD_INITIALIZER;
113  int hdr_count;
114  int x;
115  char *this_one = NULL;
116  size_t this_one_len = 0;
117 
118  if (off_start < 0)
119  return -1;
120 
121  if (ftello(fp_in) != off_start)
122  if (!mutt_file_seek(fp_in, off_start, SEEK_SET))
123  return -1;
124 
125  buf[0] = '\n';
126  buf[1] = '\0';
127 
128  if ((chflags & (CH_REORDER | CH_WEED | CH_MIME | CH_DECODE | CH_PREFIX | CH_WEED_DELIVERED)) == 0)
129  {
130  /* Without these flags to complicate things
131  * we can do a more efficient line to line copying */
132  while (ftello(fp_in) < off_end)
133  {
134  nl = strchr(buf, '\n');
135 
136  if (!fgets(buf, sizeof(buf), fp_in))
137  break;
138 
139  /* Is it the beginning of a header? */
140  if (nl && (buf[0] != ' ') && (buf[0] != '\t'))
141  {
142  ignore = true;
143  if (!from && mutt_str_startswith(buf, "From "))
144  {
145  if ((chflags & CH_FROM) == 0)
146  continue;
147  from = true;
148  }
149  else if ((chflags & CH_NOQFROM) && mutt_istr_startswith(buf, ">From "))
150  continue;
151  else if ((buf[0] == '\n') || ((buf[0] == '\r') && (buf[1] == '\n')))
152  break; /* end of header */
153 
154  if ((chflags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
155  (mutt_istr_startswith(buf, "Status:") || mutt_istr_startswith(buf, "X-Status:")))
156  {
157  continue;
158  }
159  if ((chflags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
160  (mutt_istr_startswith(buf, "Content-Length:") ||
161  mutt_istr_startswith(buf, "Lines:")))
162  {
163  continue;
164  }
165  if ((chflags & CH_UPDATE_REFS) && mutt_istr_startswith(buf, "References:"))
166  {
167  continue;
168  }
169  if ((chflags & CH_UPDATE_IRT) && mutt_istr_startswith(buf, "In-Reply-To:"))
170  {
171  continue;
172  }
173  if (chflags & CH_UPDATE_LABEL && mutt_istr_startswith(buf, "X-Label:"))
174  continue;
175  if ((chflags & CH_UPDATE_SUBJECT) && mutt_istr_startswith(buf, "Subject:"))
176  {
177  continue;
178  }
179 
180  ignore = false;
181  }
182 
183  if (!ignore && (fputs(buf, fp_out) == EOF))
184  return -1;
185  }
186  return 0;
187  }
188 
189  hdr_count = 1;
190  x = 0;
191 
192  /* We are going to read and collect the headers in an array
193  * so we are able to do re-ordering.
194  * First count the number of entries in the array */
195  if (chflags & CH_REORDER)
196  {
197  struct ListNode *np = NULL;
198  STAILQ_FOREACH(np, &HeaderOrderList, entries)
199  {
200  mutt_debug(LL_DEBUG3, "Reorder list: %s\n", np->data);
201  hdr_count++;
202  }
203  }
204 
205  mutt_debug(LL_DEBUG1, "WEED is %sset\n", (chflags & CH_WEED) ? "" : "not ");
206 
207  ARRAY_RESERVE(&headers, hdr_count);
208 
209  /* Read all the headers into the array */
210  while (ftello(fp_in) < off_end)
211  {
212  nl = strchr(buf, '\n');
213 
214  /* Read a line */
215  if (!fgets(buf, sizeof(buf), fp_in))
216  break;
217 
218  /* Is it the beginning of a header? */
219  if (nl && (buf[0] != ' ') && (buf[0] != '\t'))
220  {
221  /* Do we have anything pending? */
222  if (this_one)
223  {
224  if (chflags & CH_DECODE)
225  {
226  if (address_header_decode(&this_one) == 0)
227  rfc2047_decode(&this_one);
228  this_one_len = mutt_str_len(this_one);
229 
230  /* Convert CRLF line endings to LF */
231  if ((this_one_len > 2) && (this_one[this_one_len - 2] == '\r') &&
232  (this_one[this_one_len - 1] == '\n'))
233  {
234  this_one[this_one_len - 2] = '\n';
235  this_one[this_one_len - 1] = '\0';
236  }
237  }
238 
239  add_one_header(&headers, x, this_one);
240  this_one = NULL;
241  }
242 
243  ignore = true;
244  this_is_from = false;
245  if (!from && mutt_str_startswith(buf, "From "))
246  {
247  if ((chflags & CH_FROM) == 0)
248  continue;
249  this_is_from = true;
250  from = true;
251  }
252  else if ((buf[0] == '\n') || ((buf[0] == '\r') && (buf[1] == '\n')))
253  break; /* end of header */
254 
255  /* note: CH_FROM takes precedence over header weeding. */
256  if (!((chflags & CH_FROM) && (chflags & CH_FORCE_FROM) && this_is_from) &&
257  (chflags & CH_WEED) && mutt_matches_ignore(buf))
258  {
259  continue;
260  }
261  if ((chflags & CH_WEED_DELIVERED) && mutt_istr_startswith(buf, "Delivered-To:"))
262  {
263  continue;
264  }
265  if ((chflags & (CH_UPDATE | CH_XMIT | CH_NOSTATUS)) &&
266  (mutt_istr_startswith(buf, "Status:") || mutt_istr_startswith(buf, "X-Status:")))
267  {
268  continue;
269  }
270  if ((chflags & (CH_UPDATE_LEN | CH_XMIT | CH_NOLEN)) &&
271  (mutt_istr_startswith(buf, "Content-Length:") || mutt_istr_startswith(buf, "Lines:")))
272  {
273  continue;
274  }
275  if ((chflags & CH_MIME))
276  {
277  if (mutt_istr_startswith(buf, "mime-version:"))
278  {
279  continue;
280  }
281  size_t plen = mutt_istr_startswith(buf, "content-");
282  if ((plen != 0) && (mutt_istr_startswith(buf + plen, "transfer-encoding:") ||
283  mutt_istr_startswith(buf + plen, "type:")))
284  {
285  continue;
286  }
287  }
288  if ((chflags & CH_UPDATE_REFS) && mutt_istr_startswith(buf, "References:"))
289  {
290  continue;
291  }
292  if ((chflags & CH_UPDATE_IRT) && mutt_istr_startswith(buf, "In-Reply-To:"))
293  {
294  continue;
295  }
296  if ((chflags & CH_UPDATE_LABEL) && mutt_istr_startswith(buf, "X-Label:"))
297  continue;
298  if ((chflags & CH_UPDATE_SUBJECT) && mutt_istr_startswith(buf, "Subject:"))
299  {
300  continue;
301  }
302 
303  /* Find x -- the array entry where this header is to be saved */
304  if (chflags & CH_REORDER)
305  {
306  struct ListNode *np = NULL;
307  x = 0;
308  int match = -1;
309  size_t match_len = 0, hdr_order_len;
310 
311  STAILQ_FOREACH(np, &HeaderOrderList, entries)
312  {
313  x++;
314  hdr_order_len = mutt_str_len(np->data);
315  if (mutt_istrn_equal(buf, np->data, hdr_order_len))
316  {
317  if ((match == -1) || (hdr_order_len > match_len))
318  {
319  match = x;
320  match_len = hdr_order_len;
321  }
322  mutt_debug(LL_DEBUG2, "Reorder: %s matches %s", np->data, buf);
323  }
324  }
325  if (match != -1)
326  x = match;
327  }
328 
329  ignore = false;
330  } /* If beginning of header */
331 
332  if (!ignore)
333  {
334  mutt_debug(LL_DEBUG2, "Reorder: x = %d; hdr_count = %d\n", x, hdr_count);
335  if (this_one)
336  {
337  size_t blen = mutt_str_len(buf);
338 
339  mutt_mem_realloc(&this_one, this_one_len + blen + sizeof(char));
340  strcat(this_one + this_one_len, buf);
341  this_one_len += blen;
342  }
343  else
344  {
345  this_one = mutt_str_dup(buf);
346  this_one_len = mutt_str_len(this_one);
347  }
348  }
349  } /* while (ftello (fp_in) < off_end) */
350 
351  /* Do we have anything pending? -- XXX, same code as in above in the loop. */
352  if (this_one)
353  {
354  if (chflags & CH_DECODE)
355  {
356  if (address_header_decode(&this_one) == 0)
357  rfc2047_decode(&this_one);
358  this_one_len = mutt_str_len(this_one);
359  }
360 
361  add_one_header(&headers, x, this_one);
362  this_one = NULL;
363  }
364 
365  /* Now output the headers in order */
366  bool error = false;
367  char **hp = NULL;
368  ARRAY_FOREACH(hp, &headers)
369  {
370  if (!error && hp && *hp)
371  {
372  /* We couldn't do the prefixing when reading because RFC2047
373  * decoding may have concatenated lines. */
374  if (chflags & (CH_DECODE | CH_PREFIX))
375  {
376  const char *pre = (chflags & CH_PREFIX) ? prefix : NULL;
377  const short c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
378  wraplen = mutt_window_wrap_cols(wraplen, c_wrap);
379 
380  if (mutt_write_one_header(fp_out, 0, *hp, pre, wraplen, chflags, NeoMutt->sub) == -1)
381  {
382  error = true;
383  }
384  }
385  else
386  {
387  if (fputs(*hp, fp_out) == EOF)
388  {
389  error = true;
390  }
391  }
392  }
393 
394  FREE(hp);
395  }
396  ARRAY_FREE(&headers);
397 
398  if (error)
399  return -1;
400  return 0;
401 }
#define ARRAY_RESERVE(head, num)
Reserve memory for the array.
Definition: array.h:188
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:211
#define ARRAY_FREE(head)
Release all memory.
Definition: array.h:203
#define ARRAY_HEAD_INITIALIZER
Static initializer for arrays.
Definition: array.h:57
static int address_header_decode(char **h)
Parse an email's headers.
Definition: copy.c:1103
static void add_one_header(struct Headers *headers, size_t pos, char *value)
Add a header to a Headers array.
Definition: copy.c:74
#define CH_DECODE
Do RFC2047 header decoding.
Definition: copy.h:54
#define CH_XMIT
Transmitting this message? (Ignore Lines: and Content-Length:)
Definition: copy.h:55
#define CH_PREFIX
Quote header using $indent_string string?
Definition: copy.h:57
#define CH_UPDATE
Update the status and x-status fields?
Definition: copy.h:52
#define CH_NOSTATUS
Suppress the status and x-status fields.
Definition: copy.h:58
#define CH_FROM
Retain the "From " message separator?
Definition: copy.h:56
#define CH_WEED_DELIVERED
Weed eventual Delivered-To headers.
Definition: copy.h:65
#define CH_UPDATE_LABEL
Update X-Label: from email->env->x_label?
Definition: copy.h:71
#define CH_WEED
Weed the headers?
Definition: copy.h:53
#define CH_REORDER
Re-order output of headers (specified by 'hdr_order')
Definition: copy.h:59
#define CH_MIME
Ignore MIME fields.
Definition: copy.h:61
#define CH_UPDATE_REFS
Update References:
Definition: copy.h:69
#define CH_NOQFROM
Ignore ">From " line.
Definition: copy.h:67
#define CH_UPDATE_LEN
Update Lines: and Content-Length:
Definition: copy.h:62
#define CH_UPDATE_IRT
Update In-Reply-To:
Definition: copy.h:68
#define CH_FORCE_FROM
Give CH_FROM precedence over CH_WEED?
Definition: copy.h:66
#define CH_UPDATE_SUBJECT
Update Subject: protected header update.
Definition: copy.h:72
#define CH_NOLEN
Don't write Content-Length: and Lines:
Definition: copy.h:64
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
int mutt_write_one_header(FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen, CopyHeaderFlags chflags, struct ConfigSubset *sub)
Write one header line to a file.
Definition: header.c:420
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
@ LL_DEBUG3
Log at debug level 3.
Definition: logging.h:42
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
void mutt_mem_realloc(void *ptr, size_t size)
Resize a block of memory on the heap.
Definition: memory.c:114
size_t mutt_str_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix.
Definition: string.c:227
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:544
bool mutt_istrn_equal(const char *a, const char *b, size_t num)
Check for equality of two strings ignoring case (to a maximum), safely.
Definition: string.c:501
struct ListHead HeaderOrderList
List of header fields in the order they should be displayed.
Definition: mutt_globals.h:62
int mutt_window_wrap_cols(int width, short wrap)
Calculate the wrap column for a given screen width.
Definition: mutt_window.c:365
bool mutt_matches_ignore(const char *s)
Does the string match the ignore list.
Definition: parse.c:313
static size_t plen
Length of cached packet.
Definition: pgppacket.c:39
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
void rfc2047_decode(char **pd)
Decode any RFC2047-encoded header fields.
Definition: rfc2047.c:649
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_copy_header()

int mutt_copy_header ( FILE *  fp_in,
struct Email e,
FILE *  fp_out,
CopyHeaderFlags  chflags,
const char *  prefix,
int  wraplen 
)

Copy Email header.

Parameters
fp_inFILE pointer to read from
eEmail
fp_outFILE pointer to write to
chflagsSee CopyHeaderFlags
prefixPrefix for quoting headers (if CH_PREFIX is set)
wraplenWidth to wrap at (when chflags & CH_DISPLAY)
Return values
0Success
-1Failure

Definition at line 414 of file copy.c.

416 {
417  char *temp_hdr = NULL;
418 
419  if (e->env)
420  {
421  chflags |= ((e->env->changed & MUTT_ENV_CHANGED_IRT) ? CH_UPDATE_IRT : 0) |
425  }
426 
427  if (mutt_copy_hdr(fp_in, fp_out, e->offset, e->body->offset, chflags, prefix, wraplen) == -1)
428  return -1;
429 
430  if (chflags & CH_TXTPLAIN)
431  {
432  char chsbuf[128];
433  char buf[128];
434  fputs("MIME-Version: 1.0\n", fp_out);
435  fputs("Content-Transfer-Encoding: 8bit\n", fp_out);
436  fputs("Content-Type: text/plain; charset=", fp_out);
437  const char *const c_charset = cs_subset_string(NeoMutt->sub, "charset");
438  mutt_ch_canonical_charset(chsbuf, sizeof(chsbuf), c_charset ? c_charset : "us-ascii");
439  mutt_addr_cat(buf, sizeof(buf), chsbuf, MimeSpecials);
440  fputs(buf, fp_out);
441  fputc('\n', fp_out);
442  }
443 
444  if ((chflags & CH_UPDATE_IRT) && !STAILQ_EMPTY(&e->env->in_reply_to))
445  {
446  fputs("In-Reply-To:", fp_out);
447  struct ListNode *np = NULL;
448  STAILQ_FOREACH(np, &e->env->in_reply_to, entries)
449  {
450  fputc(' ', fp_out);
451  fputs(np->data, fp_out);
452  }
453  fputc('\n', fp_out);
454  }
455 
456  if ((chflags & CH_UPDATE_REFS) && !STAILQ_EMPTY(&e->env->references))
457  {
458  fputs("References:", fp_out);
459  mutt_write_references(&e->env->references, fp_out, 0);
460  fputc('\n', fp_out);
461  }
462 
463  if ((chflags & CH_UPDATE) && ((chflags & CH_NOSTATUS) == 0))
464  {
465  if (e->old || e->read)
466  {
467  fputs("Status: ", fp_out);
468  if (e->read)
469  fputs("RO", fp_out);
470  else if (e->old)
471  fputc('O', fp_out);
472  fputc('\n', fp_out);
473  }
474 
475  if (e->flagged || e->replied)
476  {
477  fputs("X-Status: ", fp_out);
478  if (e->replied)
479  fputc('A', fp_out);
480  if (e->flagged)
481  fputc('F', fp_out);
482  fputc('\n', fp_out);
483  }
484  }
485 
486  if (chflags & CH_UPDATE_LEN && ((chflags & CH_NOLEN) == 0))
487  {
488  fprintf(fp_out, "Content-Length: " OFF_T_FMT "\n", e->body->length);
489  if ((e->lines != 0) || (e->body->length == 0))
490  fprintf(fp_out, "Lines: %d\n", e->lines);
491  }
492 
493  const bool c_weed = cs_subset_bool(NeoMutt->sub, "weed");
494 #ifdef USE_NOTMUCH
495  if (chflags & CH_VIRTUAL)
496  {
497  /* Add some fake headers based on notmuch data */
498  char *folder = nm_email_get_folder(e);
499  if (folder && !(c_weed && mutt_matches_ignore("folder")))
500  {
501  char buf[1024];
502  mutt_str_copy(buf, folder, sizeof(buf));
503  mutt_pretty_mailbox(buf, sizeof(buf));
504 
505  fputs("Folder: ", fp_out);
506  fputs(buf, fp_out);
507  fputc('\n', fp_out);
508  }
509  }
510 #endif
511  char *tags = driver_tags_get(&e->tags);
512  if (tags && !(c_weed && mutt_matches_ignore("tags")))
513  {
514  fputs("Tags: ", fp_out);
515  fputs(tags, fp_out);
516  fputc('\n', fp_out);
517  }
518  FREE(&tags);
519 
520  const struct Slist *const c_send_charset = cs_subset_slist(NeoMutt->sub, "send_charset");
521  const short c_wrap = cs_subset_number(NeoMutt->sub, "wrap");
522  if ((chflags & CH_UPDATE_LABEL) && e->env->x_label)
523  {
524  temp_hdr = e->env->x_label;
525  /* env->x_label isn't currently stored with direct references elsewhere.
526  * Mailbox->label_hash strdups the keys. But to be safe, encode a copy */
527  if (!(chflags & CH_DECODE))
528  {
529  temp_hdr = mutt_str_dup(temp_hdr);
530  rfc2047_encode(&temp_hdr, NULL, sizeof("X-Label:"), c_send_charset);
531  }
532  if (mutt_write_one_header(fp_out, "X-Label", temp_hdr, (chflags & CH_PREFIX) ? prefix : 0,
533  mutt_window_wrap_cols(wraplen, c_wrap), chflags,
534  NeoMutt->sub) == -1)
535  {
536  return -1;
537  }
538  if (!(chflags & CH_DECODE))
539  FREE(&temp_hdr);
540  }
541 
542  if ((chflags & CH_UPDATE_SUBJECT) && e->env->subject)
543  {
544  temp_hdr = e->env->subject;
545  /* env->subject is directly referenced in Mailbox->subj_hash, so we
546  * have to be careful not to encode (and thus free) that memory. */
547  if (!(chflags & CH_DECODE))
548  {
549  temp_hdr = mutt_str_dup(temp_hdr);
550  rfc2047_encode(&temp_hdr, NULL, sizeof("Subject:"), c_send_charset);
551  }
552  if (mutt_write_one_header(fp_out, "Subject", temp_hdr, (chflags & CH_PREFIX) ? prefix : 0,
553  mutt_window_wrap_cols(wraplen, c_wrap), chflags,
554  NeoMutt->sub) == -1)
555  {
556  return -1;
557  }
558  if (!(chflags & CH_DECODE))
559  FREE(&temp_hdr);
560  }
561 
562  if ((chflags & CH_NONEWLINE) == 0)
563  {
564  if (chflags & CH_PREFIX)
565  fputs(prefix, fp_out);
566  fputc('\n', fp_out); /* add header terminator */
567  }
568 
569  if (ferror(fp_out) || feof(fp_out))
570  return -1;
571 
572  return 0;
573 }
void mutt_addr_cat(char *buf, size_t buflen, const char *value, const char *specials)
Copy a string and wrap it in quotes if it contains special characters.
Definition: address.c:681
int mutt_copy_hdr(FILE *fp_in, FILE *fp_out, LOFF_T off_start, LOFF_T off_end, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy header from one file to another.
Definition: copy.c:104
#define CH_NONEWLINE
Don't output terminating newline after the header.
Definition: copy.h:60
#define CH_TXTPLAIN
Generate text/plain MIME headers.
Definition: copy.h:63
#define CH_VIRTUAL
Write virtual header lines too.
Definition: copy.h:73
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:521
#define MUTT_ENV_CHANGED_SUBJECT
Protected header update.
Definition: envelope.h:37
#define MUTT_ENV_CHANGED_XLABEL
X-Label edited.
Definition: envelope.h:36
#define MUTT_ENV_CHANGED_IRT
In-Reply-To changed to link/break threads.
Definition: envelope.h:34
#define MUTT_ENV_CHANGED_REFS
References changed to break thread.
Definition: envelope.h:35
void mutt_write_references(const struct ListHead *r, FILE *fp, size_t trim)
Add the message references to a list.
Definition: header.c:514
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:268
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
const char MimeSpecials[]
Characters that need special treatment in MIME.
Definition: mime.c:67
void mutt_ch_canonical_charset(char *buf, size_t buflen, const char *name)
Canonicalise the charset of a string.
Definition: charset.c:351
char * nm_email_get_folder(struct Email *e)
Get the folder for a Email.
Definition: notmuch.c:1449
#define STAILQ_EMPTY(head)
Definition: queue.h:348
void rfc2047_encode(char **pd, const char *specials, int col, const struct Slist *charsets)
RFC-2047-encode a string.
Definition: rfc2047.c:619
bool read
Email is read.
Definition: email.h:48
struct Envelope * env
Envelope information.
Definition: email.h:66
int lines
How many lines in the body of this message?
Definition: email.h:60
struct Body * body
List of MIME parts.
Definition: email.h:67
bool old
Email is seen, but unread.
Definition: email.h:47
LOFF_T offset
Where in the stream does this message begin?
Definition: email.h:69
bool flagged
Marked important?
Definition: email.h:45
bool replied
Email has been replied to.
Definition: email.h:49
struct TagList tags
For drivers that support server tagging.
Definition: email.h:70
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:92
struct ListHead references
message references (in reverse order)
Definition: envelope.h:85
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:86
char * subject
Email's subject.
Definition: envelope.h:70
char * x_label
X-Label.
Definition: envelope.h:76
String list.
Definition: slist.h:47
char * driver_tags_get(struct TagList *list)
Get tags.
Definition: tags.c:145
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ count_delete_lines()

static int count_delete_lines ( FILE *  fp,
struct Body b,
LOFF_T *  length,
size_t  datelen 
)
static

Count lines to be deleted in this email body.

Parameters
fpFILE pointer to read from
bEmail Body
lengthNumber of bytes to be deleted
datelenLength of the date
Return values
numNumber of lines to be deleted
-1on error

Count the number of lines and bytes to be deleted in this body

Definition at line 586 of file copy.c.

587 {
588  int dellines = 0;
589 
590  if (b->deleted)
591  {
592  if (!mutt_file_seek(fp, b->offset, SEEK_SET))
593  {
594  return -1;
595  }
596  for (long l = b->length; l; l--)
597  {
598  const int ch = getc(fp);
599  if (ch == EOF)
600  break;
601  if (ch == '\n')
602  dellines++;
603  }
604  /* 3 and 89 come from the added header of three lines in
605  * copy_delete_attach(). 89 is the size of the header(including
606  * the newlines, tabs, and a single digit length), not including
607  * the date length. */
608  dellines -= 3;
609  *length -= b->length - (89 + datelen);
610  /* Count the number of digits exceeding the first one to write the size */
611  for (long l = 10; b->length >= l; l *= 10)
612  (*length)++;
613  }
614  else
615  {
616  for (b = b->parts; b; b = b->next)
617  {
618  const int del = count_delete_lines(fp, b, length, datelen);
619  if (del == -1)
620  {
621  return -1;
622  }
623  dellines += del;
624  }
625  }
626  return dellines;
627 }
static int count_delete_lines(FILE *fp, struct Body *b, LOFF_T *length, size_t datelen)
Count lines to be deleted in this email body.
Definition: copy.c:586
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_copy_message_fp()

int mutt_copy_message_fp ( FILE *  fp_out,
FILE *  fp_in,
struct Email e,
CopyMessageFlags  cmflags,
CopyHeaderFlags  chflags,
int  wraplen 
)

Make a copy of a message from a FILE pointer.

Parameters
fp_outWhere to write output
fp_inWhere to get input
eEmail being copied
cmflagsFlags, see CopyMessageFlags
chflagsFlags, see CopyHeaderFlags
wraplenWidth to wrap at (when chflags & CH_DISPLAY)
Return values
0Success
-1Failure

Definition at line 640 of file copy.c.

642 {
643  struct Body *body = e->body;
644  char prefix[128];
645  LOFF_T new_offset = -1;
646  int rc = 0;
647 
648  if (cmflags & MUTT_CM_PREFIX)
649  {
650  const bool c_text_flowed = cs_subset_bool(NeoMutt->sub, "text_flowed");
651  if (c_text_flowed)
652  mutt_str_copy(prefix, ">", sizeof(prefix));
653  else
654  {
655  const char *const c_indent_string = cs_subset_string(NeoMutt->sub, "indent_string");
656  struct Mailbox *m_cur = get_current_mailbox();
657  mutt_make_string(prefix, sizeof(prefix), wraplen, NONULL(c_indent_string),
658  m_cur, -1, e, MUTT_FORMAT_NO_FLAGS, NULL);
659  }
660  }
661 
662  if ((cmflags & MUTT_CM_NOHEADER) == 0)
663  {
664  if (cmflags & MUTT_CM_PREFIX)
665  chflags |= CH_PREFIX;
666  else if (e->attach_del && (chflags & CH_UPDATE_LEN))
667  {
668  int new_lines;
669  int rc_attach_del = -1;
670  LOFF_T new_length = body->length;
671  struct Buffer *quoted_date = NULL;
672 
673  quoted_date = mutt_buffer_pool_get();
674  mutt_buffer_addch(quoted_date, '"');
675  mutt_date_make_date(quoted_date, cs_subset_bool(NeoMutt->sub, "local_date_header"));
676  mutt_buffer_addch(quoted_date, '"');
677 
678  /* Count the number of lines and bytes to be deleted */
679  if (!mutt_file_seek(fp_in, body->offset, SEEK_SET))
680  {
681  goto attach_del_cleanup;
682  }
683  const int del = count_delete_lines(fp_in, body, &new_length,
684  mutt_buffer_len(quoted_date));
685  if (del == -1)
686  {
687  goto attach_del_cleanup;
688  }
689  new_lines = e->lines - del;
690 
691  /* Copy the headers */
692  if (mutt_copy_header(fp_in, e, fp_out, chflags | CH_NOLEN | CH_NONEWLINE, NULL, wraplen))
693  goto attach_del_cleanup;
694  fprintf(fp_out, "Content-Length: " OFF_T_FMT "\n", new_length);
695  if (new_lines <= 0)
696  new_lines = 0;
697  else
698  fprintf(fp_out, "Lines: %d\n", new_lines);
699 
700  putc('\n', fp_out);
701  if (ferror(fp_out) || feof(fp_out))
702  goto attach_del_cleanup;
703  new_offset = ftello(fp_out);
704 
705  /* Copy the body */
706  if (!mutt_file_seek(fp_in, body->offset, SEEK_SET))
707  goto attach_del_cleanup;
708  if (copy_delete_attach(body, fp_in, fp_out, mutt_buffer_string(quoted_date)))
709  goto attach_del_cleanup;
710 
711  mutt_buffer_pool_release(&quoted_date);
712 
713  LOFF_T fail = ((ftello(fp_out) - new_offset) - new_length);
714  if (fail)
715  {
716  mutt_error(ngettext("The length calculation was wrong by %ld byte",
717  "The length calculation was wrong by %ld bytes", fail),
718  fail);
719  new_length += fail;
720  }
721 
722  /* Update original message if we are sync'ing a mailfolder */
723  if (cmflags & MUTT_CM_UPDATE)
724  {
725  e->attach_del = false;
726  e->lines = new_lines;
727  body->offset = new_offset;
728 
729  body->length = new_length;
730  mutt_body_free(&body->parts);
731  }
732 
733  rc_attach_del = 0;
734 
735  attach_del_cleanup:
736  mutt_buffer_pool_release(&quoted_date);
737  return rc_attach_del;
738  }
739 
740  if (mutt_copy_header(fp_in, e, fp_out, chflags,
741  (chflags & CH_PREFIX) ? prefix : NULL, wraplen) == -1)
742  {
743  return -1;
744  }
745 
746  new_offset = ftello(fp_out);
747  }
748 
749  if (cmflags & MUTT_CM_DECODE)
750  {
751  /* now make a text/plain version of the message */
752  struct State s = { 0 };
753  s.fp_in = fp_in;
754  s.fp_out = fp_out;
755  if (cmflags & MUTT_CM_PREFIX)
756  s.prefix = prefix;
757  if (cmflags & MUTT_CM_DISPLAY)
758  {
759  s.flags |= MUTT_DISPLAY;
760  s.wraplen = wraplen;
761  }
762  if (cmflags & MUTT_CM_PRINTING)
763  s.flags |= MUTT_PRINTING;
764  if (cmflags & MUTT_CM_WEED)
765  s.flags |= MUTT_WEED;
766  if (cmflags & MUTT_CM_CHARCONV)
767  s.flags |= MUTT_CHARCONV;
768  if (cmflags & MUTT_CM_REPLYING)
769  s.flags |= MUTT_REPLYING;
770 
771  if ((WithCrypto != 0) && cmflags & MUTT_CM_VERIFY)
772  s.flags |= MUTT_VERIFY;
773 
774  rc = mutt_body_handler(body, &s);
775  }
776  else if ((WithCrypto != 0) && (cmflags & MUTT_CM_DECODE_CRYPT) && (e->security & SEC_ENCRYPT))
777  {
778  struct Body *cur = NULL;
779  FILE *fp = NULL;
780 
781  if (((WithCrypto & APPLICATION_PGP) != 0) && (cmflags & MUTT_CM_DECODE_PGP) &&
782  (e->security & APPLICATION_PGP) && (e->body->type == TYPE_MULTIPART))
783  {
784  if (crypt_pgp_decrypt_mime(fp_in, &fp, e->body, &cur))
785  return -1;
786  fputs("MIME-Version: 1.0\n", fp_out);
787  }
788 
789  if (((WithCrypto & APPLICATION_SMIME) != 0) && (cmflags & MUTT_CM_DECODE_SMIME) &&
791  {
792  if (crypt_smime_decrypt_mime(fp_in, &fp, e->body, &cur))
793  return -1;
794  }
795 
796  if (!cur)
797  {
798  mutt_error(_("No decryption engine available for message"));
799  return -1;
800  }
801 
802  mutt_write_mime_header(cur, fp_out, NeoMutt->sub);
803  fputc('\n', fp_out);
804 
805  if (!mutt_file_seek(fp, cur->offset, SEEK_SET))
806  return -1;
807  if (mutt_file_copy_bytes(fp, fp_out, cur->length) == -1)
808  {
809  mutt_file_fclose(&fp);
810  mutt_body_free(&cur);
811  return -1;
812  }
813  mutt_body_free(&cur);
814  mutt_file_fclose(&fp);
815  }
816  else
817  {
818  if (!mutt_file_seek(fp_in, body->offset, SEEK_SET))
819  return -1;
820  if (cmflags & MUTT_CM_PREFIX)
821  {
822  int c;
823  size_t bytes = body->length;
824 
825  fputs(prefix, fp_out);
826 
827  while (((c = fgetc(fp_in)) != EOF) && bytes--)
828  {
829  fputc(c, fp_out);
830  if (c == '\n')
831  {
832  fputs(prefix, fp_out);
833  }
834  }
835  }
836  else if (mutt_file_copy_bytes(fp_in, fp_out, body->length) == -1)
837  return -1;
838  }
839 
840  if ((cmflags & MUTT_CM_UPDATE) && ((cmflags & MUTT_CM_NOHEADER) == 0) &&
841  (new_offset != -1))
842  {
843  body->offset = new_offset;
844  mutt_body_free(&body->parts);
845  }
846 
847  return rc;
848 }
size_t mutt_buffer_len(const struct Buffer *buf)
Calculate the length of a Buffer.
Definition: buffer.c:356
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
static const char * mutt_buffer_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:77
int mutt_copy_header(FILE *fp_in, struct Email *e, FILE *fp_out, CopyHeaderFlags chflags, const char *prefix, int wraplen)
Copy Email header.
Definition: copy.c:414
#define MUTT_CM_WEED
Weed message/rfc822 attachment headers.
Definition: copy.h:41
#define MUTT_CM_REPLYING
Replying the message.
Definition: copy.h:44
#define MUTT_CM_PREFIX
Quote the header and body.
Definition: copy.h:37
#define MUTT_CM_UPDATE
Update structs on sync.
Definition: copy.h:40
#define MUTT_CM_VERIFY
Do signature verification.
Definition: copy.h:47
#define MUTT_CM_DECODE_PGP
Used for decoding PGP messages.
Definition: copy.h:45
#define MUTT_CM_DECODE
Decode the message body into text/plain.
Definition: copy.h:38
#define MUTT_CM_CHARCONV
Perform character set conversions.
Definition: copy.h:42
#define MUTT_CM_DECODE_SMIME
Used for decoding S/MIME messages.
Definition: copy.h:46
#define MUTT_CM_PRINTING
Printing the message - display light.
Definition: copy.h:43
#define MUTT_CM_DECODE_CRYPT
Definition: copy.h:48
#define MUTT_CM_NOHEADER
Don't copy the message header.
Definition: copy.h:36
#define MUTT_CM_DISPLAY
Output is displayed to the user.
Definition: copy.h:39
int crypt_smime_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **cur)
Wrapper for CryptModuleSpecs::decrypt_mime()
Definition: cryptglue.c:433
int crypt_pgp_decrypt_mime(FILE *fp_in, FILE **fp_out, struct Body *b, struct Body **cur)
Wrapper for CryptModuleSpecs::decrypt_mime()
Definition: cryptglue.c:211
void mutt_date_make_date(struct Buffer *buf, bool local)
Write a date in RFC822 format to a buffer.
Definition: date.c:378
void mutt_body_free(struct Body **ptr)
Free a Body.
Definition: body.c:57
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:152
#define MUTT_FORMAT_NO_FLAGS
No flags are set.
Definition: format_flags.h:30
#define mutt_error(...)
Definition: logging.h:87
int mutt_body_handler(struct Body *b, struct State *s)
Handler for the Body of an email.
Definition: handler.c:1597
void mutt_make_string(char *buf, size_t buflen, int cols, const char *s, struct Mailbox *m, int inpgr, struct Email *e, MuttFormatFlags flags, const char *progress)
Create formatted strings using mailbox expandos.
Definition: hdrline.c:1405
int mutt_write_mime_header(struct Body *a, FILE *fp, struct ConfigSubset *sub)
Create a MIME header.
Definition: header.c:760
struct Mailbox * get_current_mailbox(void)
Get the current Mailbox.
Definition: index.c:603
@ TYPE_MULTIPART
Type: 'multipart/*'.
Definition: mime.h:37
@ TYPE_APPLICATION
Type: 'application/*'.
Definition: mime.h:33
#define _(a)
Definition: message.h:28
#define MUTT_WEED
Weed headers even when not in display mode.
Definition: state.h:35
#define MUTT_VERIFY
Perform signature verification.
Definition: state.h:33
#define MUTT_CHARCONV
Do character set conversions.
Definition: state.h:36
#define MUTT_DISPLAY
Output is displayed to the user.
Definition: state.h:32
#define MUTT_REPLYING
Are we replying?
Definition: state.h:38
#define MUTT_PRINTING
Are we printing? - MUTT_DISPLAY "light".
Definition: state.h:37
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:90
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:91
#define SEC_ENCRYPT
Email is encrypted.
Definition: lib.h:78
#define WithCrypto
Definition: lib.h:116
void mutt_buffer_pool_release(struct Buffer **pbuf)
Free a Buffer from the pool.
Definition: pool.c:112
struct Buffer * mutt_buffer_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:101
#define NONULL(x)
Definition: string2.h:37
unsigned int type
content-type primary type, ContentType
Definition: body.h:40
String manipulation buffer.
Definition: buffer.h:34
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:41
bool attach_del
Has an attachment marked for deletion.
Definition: email.h:99
A mailbox.
Definition: mailbox.h:79
Keep track when processing files.
Definition: state.h:46
int wraplen
Width to wrap lines to (when flags & MUTT_DISPLAY)
Definition: state.h:51
StateFlags flags
Flags, e.g. MUTT_DISPLAY.
Definition: state.h:50
FILE * fp_out
File to write to.
Definition: state.h:48
char * prefix
String to add to the beginning of each output line.
Definition: state.h:49
FILE * fp_in
File to read from.
Definition: state.h:47
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_copy_message()

int mutt_copy_message ( FILE *  fp_out,
struct Email e,
struct Message msg,
CopyMessageFlags  cmflags,
CopyHeaderFlags  chflags,
int  wraplen 
)

Copy a message from a Mailbox.

Parameters
fp_outFILE pointer to write to
eEmail
msgMessage
cmflagsFlags, see CopyMessageFlags
chflagsFlags, see CopyHeaderFlags
wraplenWidth to wrap at (when chflags & CH_DISPLAY)
Return values
0Success
-1Failure

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

Definition at line 864 of file copy.c.

866 {
867  if (!msg || !e->body)
868  {
869  return -1;
870  }
871  if (fp_out == msg->fp)
872  {
873  mutt_debug(LL_DEBUG1, "trying to read/write from/to the same FILE*!\n");
874  return -1;
875  }
876 
877  int rc = mutt_copy_message_fp(fp_out, msg->fp, e, cmflags, chflags, wraplen);
878  if ((rc == 0) && (ferror(fp_out) || feof(fp_out)))
879  {
880  mutt_debug(LL_DEBUG1, "failed to detect EOF!\n");
881  rc = -1;
882  }
883  return rc;
884 }
int mutt_copy_message_fp(FILE *fp_out, FILE *fp_in, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags, int wraplen)
Make a copy of a message from a FILE pointer.
Definition: copy.c:640
FILE * fp
pointer to the message data
Definition: mxapi.h:44
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ append_message()

static int append_message ( struct Mailbox dest,
FILE *  fp_in,
struct Mailbox src,
struct Email e,
CopyMessageFlags  cmflags,
CopyHeaderFlags  chflags 
)
static

Appends a copy of the given message to a mailbox.

Parameters
destdestination mailbox
fp_inwhere to get input
srcsource mailbox
eEmail being copied
cmflagsFlags, see CopyMessageFlags
chflagsFlags, see CopyHeaderFlags
Return values
0Success
-1Error

Definition at line 897 of file copy.c.

899 {
900  char buf[256];
901  struct Message *msg = NULL;
902  int rc;
903 
904  if (!mutt_file_seek(fp_in, e->offset, SEEK_SET))
905  return -1;
906  if (!fgets(buf, sizeof(buf), fp_in))
907  return -1;
908 
909  msg = mx_msg_open_new(dest, e, is_from(buf, NULL, 0, NULL) ? MUTT_MSG_NO_FLAGS : MUTT_ADD_FROM);
910  if (!msg)
911  return -1;
912  if ((dest->type == MUTT_MBOX) || (dest->type == MUTT_MMDF))
913  chflags |= CH_FROM | CH_FORCE_FROM;
914  chflags |= ((dest->type == MUTT_MAILDIR) ? CH_NOSTATUS : CH_UPDATE);
915  rc = mutt_copy_message_fp(msg->fp, fp_in, e, cmflags, chflags, 0);
916  if (mx_msg_commit(dest, msg) != 0)
917  rc = -1;
918 
919 #ifdef USE_NOTMUCH
920  if (msg->committed_path && (dest->type == MUTT_MAILDIR) && (src->type == MUTT_NOTMUCH))
921  nm_update_filename(src, NULL, msg->committed_path, e);
922 #endif
923 
924  mx_msg_close(dest, &msg);
925  return rc;
926 }
bool is_from(const char *s, char *path, size_t pathlen, time_t *tp)
Is a string a 'From' header line?
Definition: from.c:48
@ MUTT_NOTMUCH
'Notmuch' (virtual) Mailbox type
Definition: mailbox.h:51
@ MUTT_MMDF
'mmdf' Mailbox type
Definition: mailbox.h:46
@ MUTT_MBOX
'mbox' Mailbox type
Definition: mailbox.h:45
@ MUTT_MAILDIR
'Maildir' Mailbox type
Definition: mailbox.h:48
int mx_msg_close(struct Mailbox *m, struct Message **msg)
Close a message.
Definition: mx.c:1193
int mx_msg_commit(struct Mailbox *m, struct Message *msg)
Commit a message to a folder - Wrapper for MxOps::msg_commit()
Definition: mx.c:1172
struct Message * mx_msg_open_new(struct Mailbox *m, const struct Email *e, MsgOpenFlags flags)
Open a new message.
Definition: mx.c:1057
#define MUTT_ADD_FROM
add a From_ line
Definition: mx.h:43
#define MUTT_MSG_NO_FLAGS
No flags are set.
Definition: mx.h:42
int nm_update_filename(struct Mailbox *m, const char *old_file, const char *new_file, struct Email *e)
Change the filename.
Definition: notmuch.c:1736
enum MailboxType type
Mailbox type.
Definition: mailbox.h:102
A local copy of an email.
Definition: mxapi.h:43
char * committed_path
the final path generated by mx_msg_commit()
Definition: mxapi.h:46
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ mutt_append_message()

int mutt_append_message ( struct Mailbox m_dst,
struct Mailbox m_src,
struct Email e,
struct Message msg,
CopyMessageFlags  cmflags,
CopyHeaderFlags  chflags 
)

Append a message.

Parameters
m_dstDestination Mailbox
m_srcSource Mailbox
eEmail
msgMessage
cmflagsFlags, see CopyMessageFlags
chflagsFlags, see CopyHeaderFlags
Return values
0Success
-1Failure

Definition at line 939 of file copy.c.

942 {
943  const bool own_msg = !msg;
944  if (own_msg && !(msg = mx_msg_open(m_src, e->msgno)))
945  {
946  return -1;
947  }
948 
949  int rc = append_message(m_dst, msg->fp, m_src, e, cmflags, chflags);
950  if (own_msg)
951  {
952  mx_msg_close(m_src, &msg);
953  }
954  return rc;
955 }
static int append_message(struct Mailbox *dest, FILE *fp_in, struct Mailbox *src, struct Email *e, CopyMessageFlags cmflags, CopyHeaderFlags chflags)
Appends a copy of the given message to a mailbox.
Definition: copy.c:897
struct Message * mx_msg_open(struct Mailbox *m, int msgno)
Return a stream pointer for a message.
Definition: mx.c:1147
int msgno
Number displayed to the user.
Definition: email.h:111
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ format_address_header()

static void format_address_header ( char **  h,
struct AddressList *  al 
)
static

Write address headers to a buffer.

Parameters
[out]hArray of header strings
[in]alAddressList

This function is the equivalent of mutt_addrlist_write_file(), but writes to a buffer instead of writing to a stream. mutt_addrlist_write_file could be re-used if we wouldn't store all the decoded headers in a huge array, first.

TODO fix that.

Definition at line 1037 of file copy.c.

1038 {
1039  char buf[8192];
1040  char cbuf[256];
1041  char c2buf[256];
1042  char *p = NULL;
1043  size_t linelen, buflen, plen;
1044 
1045  linelen = mutt_str_len(*h);
1046  plen = linelen;
1047  buflen = linelen + 3;
1048 
1049  mutt_mem_realloc(h, buflen);
1050  struct Address *first = TAILQ_FIRST(al);
1051  struct Address *a = NULL;
1052  TAILQ_FOREACH(a, al, entries)
1053  {
1054  *buf = '\0';
1055  *cbuf = '\0';
1056  *c2buf = '\0';
1057  const size_t l = mutt_addr_write(buf, sizeof(buf), a, false);
1058 
1059  if (a != first && (linelen + l > 74))
1060  {
1061  strcpy(cbuf, "\n\t");
1062  linelen = l + 8;
1063  }
1064  else
1065  {
1066  if (a->mailbox)
1067  {
1068  strcpy(cbuf, " ");
1069  linelen++;
1070  }
1071  linelen += l;
1072  }
1073  if (!a->group && TAILQ_NEXT(a, entries) && TAILQ_NEXT(a, entries)->mailbox)
1074  {
1075  linelen++;
1076  buflen++;
1077  strcpy(c2buf, ",");
1078  }
1079 
1080  const size_t cbuflen = mutt_str_len(cbuf);
1081  const size_t c2buflen = mutt_str_len(c2buf);
1082  buflen += l + cbuflen + c2buflen;
1083  mutt_mem_realloc(h, buflen);
1084  p = *h;
1085  strcat(p + plen, cbuf);
1086  plen += cbuflen;
1087  strcat(p + plen, buf);
1088  plen += l;
1089  strcat(p + plen, c2buf);
1090  plen += c2buflen;
1091  }
1092 
1093  /* Space for this was allocated in the beginning of this function. */
1094  strcat(p + plen, "\n");
1095 }
size_t mutt_addr_write(char *buf, size_t buflen, struct Address *addr, bool display)
Write a single Address to a buffer.
Definition: address.c:1025
#define TAILQ_FIRST(head)
Definition: queue.h:723
#define TAILQ_NEXT(elm, field)
Definition: queue.h:832
bool group
Group mailbox?
Definition: address.h:39
char * mailbox
Mailbox and host address.
Definition: address.h:38
+ Here is the call graph for this function:
+ Here is the caller graph for this function: