NeoMutt  2018-07-16 +2481-68dcde
Teaching an old dog new tricks
DOXYGEN
mutt_header.c
Go to the documentation of this file.
1 
29 #include "config.h"
30 #include <limits.h>
31 #include <stdbool.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <sys/stat.h>
35 #include <time.h>
36 #include "mutt/mutt.h"
37 #include "email/lib.h"
38 #include "core/lib.h"
39 #include "mutt.h"
40 #include "alias.h"
41 #include "curs_lib.h"
42 #include "index.h"
43 #include "muttlib.h"
44 #include "ncrypt/ncrypt.h"
45 #include "options.h"
46 #include "protos.h"
47 #include "sendlib.h"
48 
54 static void label_ref_dec(struct Mailbox *m, char *label)
55 {
56  struct HashElem *elem = mutt_hash_find_elem(m->label_hash, label);
57  if (!elem)
58  return;
59 
60  uintptr_t count = (uintptr_t) elem->data;
61  if (count <= 1)
62  {
63  mutt_hash_delete(m->label_hash, label, NULL);
64  return;
65  }
66 
67  count--;
68  elem->data = (void *) count;
69 }
70 
76 static void label_ref_inc(struct Mailbox *m, char *label)
77 {
78  uintptr_t count;
79 
80  struct HashElem *elem = mutt_hash_find_elem(m->label_hash, label);
81  if (!elem)
82  {
83  count = 1;
84  mutt_hash_insert(m->label_hash, label, (void *) count);
85  return;
86  }
87 
88  count = (uintptr_t) elem->data;
89  count++;
90  elem->data = (void *) count;
91 }
92 
100 static bool label_message(struct Mailbox *m, struct Email *e, char *new_label)
101 {
102  if (!e)
103  return false;
104  if (mutt_str_strcmp(e->env->x_label, new_label) == 0)
105  return false;
106 
107  if (e->env->x_label)
108  label_ref_dec(m, e->env->x_label);
109  mutt_str_replace(&e->env->x_label, new_label);
110  if (e->env->x_label)
111  label_ref_inc(m, e->env->x_label);
112 
113  e->changed = true;
115  return true;
116 }
117 
124 int mutt_label_message(struct Mailbox *m, struct EmailList *el)
125 {
126  if (!m || !el)
127  return 0;
128 
129  char buf[1024] = { 0 };
130 
131  struct EmailNode *en = STAILQ_FIRST(el);
132  if (!STAILQ_NEXT(en, entries))
133  {
134  // If there's only one email, use its label as a template
135  if (en->email->env->x_label)
136  mutt_str_strfcpy(buf, en->email->env->x_label, sizeof(buf));
137  }
138 
139  if (mutt_get_field("Label: ", buf, sizeof(buf), MUTT_LABEL /* | MUTT_CLEAR */) != 0)
140  return 0;
141 
142  char *new_label = buf;
143  SKIPWS(new_label);
144  if (*new_label == '\0')
145  new_label = NULL;
146 
147  int changed = 0;
148  STAILQ_FOREACH(en, el, entries)
149  {
150  if (label_message(m, en->email, new_label))
151  {
152  changed++;
154  }
155  }
156 
157  return changed;
158 }
159 
168 void mutt_edit_headers(const char *editor, const char *body, struct Email *e,
169  char *fcc, size_t fcclen)
170 {
171  char path[PATH_MAX]; /* tempfile used to edit headers + body */
172  char buf[1024];
173  const char *p = NULL;
174  int i;
175  struct Envelope *n = NULL;
176  time_t mtime;
177  struct stat st;
178 
179  mutt_mktemp(path, sizeof(path));
180  FILE *fp_out = mutt_file_fopen(path, "w");
181  if (!fp_out)
182  {
183  mutt_perror(path);
184  return;
185  }
186 
188  mutt_rfc822_write_header(fp_out, e->env, NULL, MUTT_WRITE_HEADER_EDITHDRS, false, false);
189  fputc('\n', fp_out); /* tie off the header. */
190 
191  /* now copy the body of the message. */
192  FILE *fp_in = fopen(body, "r");
193  if (!fp_in)
194  {
195  mutt_perror(body);
196  mutt_file_fclose(&fp_out);
197  return;
198  }
199 
200  mutt_file_copy_stream(fp_in, fp_out);
201 
202  mutt_file_fclose(&fp_in);
203  mutt_file_fclose(&fp_out);
204 
205  if (stat(path, &st) == -1)
206  {
207  mutt_perror(path);
208  return;
209  }
210 
211  mtime = mutt_file_decrease_mtime(path, &st);
212 
213  mutt_edit_file(editor, path);
214  stat(path, &st);
215  if (mtime == st.st_mtime)
216  {
217  mutt_debug(LL_DEBUG1, "temp file was not modified\n");
218  /* the file has not changed! */
219  mutt_file_unlink(path);
220  return;
221  }
222 
223  mutt_file_unlink(body);
225 
226  /* Read the temp file back in */
227  fp_in = fopen(path, "r");
228  if (!fp_in)
229  {
230  mutt_perror(path);
231  return;
232  }
233 
234  fp_out = mutt_file_fopen(body, "w");
235  if (!fp_out)
236  {
237  /* intentionally leak a possible temporary file here */
238  mutt_file_fclose(&fp_in);
239  mutt_perror(body);
240  return;
241  }
242 
243  n = mutt_rfc822_read_header(fp_in, NULL, true, false);
244  while ((i = fread(buf, 1, sizeof(buf), fp_in)) > 0)
245  fwrite(buf, 1, i, fp_out);
246  mutt_file_fclose(&fp_out);
247  mutt_file_fclose(&fp_in);
248  mutt_file_unlink(path);
249 
250  /* in case the user modifies/removes the In-Reply-To header with
251  * $edit_headers set, we remove References: as they're likely invalid;
252  * we can simply compare strings as we don't generate References for
253  * multiple Message-Ids in IRT anyways */
254 #ifdef USE_NNTP
255  if (!OptNewsSend)
256 #endif
257  {
258  if (!STAILQ_EMPTY(&e->env->in_reply_to) &&
259  (STAILQ_EMPTY(&n->in_reply_to) ||
261  STAILQ_FIRST(&e->env->in_reply_to)->data) != 0)))
262  {
264  }
265  }
266 
267  /* restore old info. */
270 
271  mutt_env_free(&e->env);
272  e->env = n;
273  n = NULL;
274 
276 
277  /* search through the user defined headers added to see if
278  * fcc: or attach: or pgp: was specified */
279 
280  struct ListNode *np = NULL, *tmp = NULL;
281  STAILQ_FOREACH_SAFE(np, &e->env->userhdrs, entries, tmp)
282  {
283  bool keep = true;
284  size_t plen;
285 
286  if (fcc && (plen = mutt_str_startswith(np->data, "fcc:", CASE_IGNORE)))
287  {
288  p = mutt_str_skip_email_wsp(np->data + plen);
289  if (*p)
290  {
291  mutt_str_strfcpy(fcc, p, fcclen);
292  mutt_pretty_mailbox(fcc, fcclen);
293  }
294  keep = false;
295  }
296  else if ((plen = mutt_str_startswith(np->data, "attach:", CASE_IGNORE)))
297  {
298  struct Body *body2 = NULL;
299  struct Body *parts = NULL;
300 
301  p = mutt_str_skip_email_wsp(np->data + plen);
302  if (*p)
303  {
304  size_t l = 0;
305  for (; (p[0] != '\0') && (p[0] != ' ') && (p[0] != '\t'); p++)
306  {
307  if (p[0] == '\\')
308  {
309  if (p[1] == '\0')
310  break;
311  p++;
312  }
313  if (l < (sizeof(path) - 1))
314  path[l++] = *p;
315  }
317  path[l] = '\0';
318 
319  mutt_expand_path(path, sizeof(path));
320  body2 = mutt_make_file_attach(path);
321  if (body2)
322  {
323  body2->description = mutt_str_strdup(p);
324  for (parts = e->content; parts->next; parts = parts->next)
325  ;
326  parts->next = body2;
327  }
328  else
329  {
330  mutt_pretty_mailbox(path, sizeof(path));
331  mutt_error(_("%s: unable to attach file"), path);
332  }
333  }
334  keep = false;
335  }
336  else if (((WithCrypto & APPLICATION_PGP) != 0) &&
337  (plen = mutt_str_startswith(np->data, "pgp:", CASE_IGNORE)))
338  {
339  e->security = mutt_parse_crypt_hdr(np->data + plen, false, APPLICATION_PGP);
340  if (e->security)
342  keep = false;
343  }
344 
345  if (!keep)
346  {
347  STAILQ_REMOVE(&e->env->userhdrs, np, ListNode, entries);
348  FREE(&np->data);
349  FREE(&np);
350  }
351  }
352 }
353 
359 {
360  /* 131 is just a rough prime estimate of how many distinct
361  * labels someone might have in a m. */
363 }
364 
370 void mutt_label_hash_add(struct Mailbox *m, struct Email *e)
371 {
372  if (!m || !m->label_hash)
373  return;
374  if (e->env->x_label)
375  label_ref_inc(m, e->env->x_label);
376 }
377 
383 void mutt_label_hash_remove(struct Mailbox *m, struct Email *e)
384 {
385  if (!m || !m->label_hash)
386  return;
387  if (e->env->x_label)
388  label_ref_dec(m, e->env->x_label);
389 }
void mutt_hash_delete(struct Hash *table, const char *strkey, const void *data)
Remove an element from a Hash table.
Definition: hash.c:444
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:400
The envelope/body of an email.
Definition: email.h:39
#define mutt_perror(...)
Definition: logging.h:85
GUI miscellaneous curses (window drawing) routines.
Structs that make up an email.
GUI manage the main index (list of emails)
#define MUTT_LABEL
Do label completion.
Definition: mutt.h:73
void mutt_expand_aliases_env(struct Envelope *env)
Expand aliases in all the fields of an Envelope.
Definition: alias.c:318
static size_t plen
Length of cached packet.
Definition: pgppacket.c:38
struct Body * content
List of MIME parts.
Definition: email.h:92
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:192
struct ListHead userhdrs
user defined headers
Definition: envelope.h:83
#define _(a)
Definition: message.h:28
bool changed
Email has been edited.
Definition: email.h:50
struct Body * next
next attachment in the list
Definition: body.h:53
void mutt_label_hash_add(struct Mailbox *m, struct Email *e)
Add a message&#39;s labels to the Hash Table.
Definition: mutt_header.c:370
void mutt_label_hash_remove(struct Mailbox *m, struct Email *e)
Remove a message&#39;s labels from the Hash Table.
Definition: mutt_header.c:383
Representation of a single alias to an email address.
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:123
#define mutt_get_field(field, buf, buflen, complete)
Definition: curs_lib.h:86
The body of an email.
Definition: body.h:34
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *attach, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject)
Write out one RFC822 header line.
Definition: sendlib.c:2324
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:88
Some miscellaneous functions.
char * mutt_expand_path(char *buf, size_t buflen)
Create the canonical path.
Definition: muttlib.c:128
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:82
struct Body * mutt_make_file_attach(const char *path)
Create a file attachment.
Definition: sendlib.c:1620
int mutt_file_fclose(FILE **fp)
Close a FILE handle (and NULL the pointer)
Definition: file.c:150
struct Envelope * env
Envelope information.
Definition: email.h:91
Convenience wrapper for the core headers.
#define SKIPWS(ch)
Definition: string2.h:47
struct HashElem * mutt_hash_find_elem(const struct Hash *table, const char *strkey)
Find the HashElem in a Hash table element using a key.
Definition: hash.c:394
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:360
Prototypes for many functions.
#define MUTT_ENV_CHANGED_XLABEL
X-Label edited.
Definition: envelope.h:34
#define mutt_mktemp(buf, buflen)
Definition: muttlib.h:76
A mailbox.
Definition: mailbox.h:92
#define PATH_MAX
Definition: mutt.h:52
"light" mode (used for edit_hdrs)
Definition: sendlib.h:62
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:96
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:54
size_t mutt_str_strfcpy(char *dest, const char *src, size_t dsize)
Copy a string into a buffer (guaranteeing NUL-termination)
Definition: string.c:750
struct Hash * mutt_hash_new(size_t nelem, HashFlags flags)
Create a new Hash table (with string keys)
Definition: hash.c:276
Ignore case when comparing strings.
Definition: string2.h:68
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:776
API for encryption/signing of emails.
void mutt_pretty_mailbox(char *buf, size_t buflen)
Shorten a mailbox path using &#39;~&#39; or &#39;=&#39;.
Definition: muttlib.c:612
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:350
char * description
content-description
Definition: body.h:40
static void label_ref_dec(struct Mailbox *m, char *label)
Decrease the refcount of a label.
Definition: mutt_header.c:54
#define STAILQ_NEXT(elm, field)
Definition: queue.h:398
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/ncrypt.h pgplib.h, smime.h
Definition: email.h:41
void mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:453
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: ncrypt.h:134
size_t mutt_str_startswith(const char *str, const char *prefix, enum CaseSensitivity cs)
Check whether a string starts with a prefix.
Definition: string.c:168
void * data
Definition: hash.h:46
void mutt_set_header_color(struct Mailbox *m, struct Email *e)
Select a colour for a message.
Definition: index.c:3672
void mutt_make_label_hash(struct Mailbox *m)
Create a Hash Table to store the labels.
Definition: mutt_header.c:358
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope&#39;s Address fields to local format.
Definition: envelope.c:274
SecurityFlags mutt_parse_crypt_hdr(const char *p, bool set_empty_signas, SecurityFlags crypt_app)
Parse a crypto header string.
Definition: postpone.c:449
char * data
String.
Definition: list.h:35
int mutt_label_message(struct Mailbox *m, struct EmailList *el)
Let the user label a message.
Definition: mutt_header.c:124
Log at debug level 1.
Definition: logging.h:56
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:268
char * mutt_str_strdup(const char *str)
Copy a string, safely.
Definition: string.c:380
struct Email * email
Email in the list.
Definition: email.h:122
#define mutt_error(...)
Definition: logging.h:84
void mutt_edit_headers(const char *editor, const char *body, struct Email *e, char *fcc, size_t fcclen)
Let the user edit the message header and body.
Definition: mutt_header.c:168
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file&#39;s modification time by 1 second.
Definition: file.c:959
#define FREE(x)
Definition: memory.h:40
#define STAILQ_EMPTY(head)
Definition: queue.h:346
#define STAILQ_SWAP(head1, head2, type)
Definition: queue.h:426
The item stored in a Hash Table.
Definition: hash.h:42
List of Emails.
Definition: email.h:120
Handling of global boolean variables.
#define mutt_debug(LEVEL,...)
Definition: logging.h:81
Miscellaneous functions for sending an email.
WHERE bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: options.h:44
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition: hash.h:76
FILE * mutt_file_fopen(const char *path, const char *mode)
Call fopen() safely.
Definition: file.c:583
struct HashElem * mutt_hash_insert(struct Hash *table, const char *strkey, void *data)
Add a new element to the Hash table (with string keys)
Definition: hash.c:352
struct ListHead references
message references (in reverse order)
Definition: envelope.h:81
A List node for strings.
Definition: list.h:33
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
parses an RFC822 header
Definition: parse.c:1128
char * x_label
X-Label.
Definition: envelope.h:72
#define STAILQ_FIRST(head)
Definition: queue.h:348
#define WithCrypto
Definition: ncrypt.h:160
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:308
Convenience wrapper for the library headers.
struct Hash * label_hash
Hash table for x-labels.
Definition: mailbox.h:140
int mutt_str_strcmp(const char *a, const char *b)
Compare two strings, safely.
Definition: string.c:615
static bool label_message(struct Mailbox *m, struct Email *e, char *new_label)
add an X-Label: field
Definition: mutt_header.c:100
The header of an Email.
Definition: envelope.h:54
static void label_ref_inc(struct Mailbox *m, char *label)
Increase the refcount of a label.
Definition: mutt_header.c:76