1 /**
2 * @file
3 * Manipulate an email's header
4 *
5 * @authors
6 * Copyright (C) 1996-2009,2012 Michael R. Elkins <me@mutt.org>
7 *
8 * @copyright
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 2 of the License, or (at your option) any later
12 * version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /**
24 * @page neo_mutt_header Manipulate an email's header
25 *
26 * Manipulate an email's header
27 */
28
29 #include "config.h"
30 #include <stddef.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/lib.h"
37 #include "email/lib.h"
38 #include "core/lib.h"
39 #include "alias/lib.h"
40 #include "gui/lib.h"
41 #include "mutt.h"
42 #include "mutt_header.h"
43 #include "index/lib.h"
44 #include "ncrypt/lib.h"
45 #include "send/lib.h"
46 #include "muttlib.h"
47 #include "options.h"
48 #include "protos.h"
49
50 /**
51 * label_ref_dec - Decrease the refcount of a label
52 * @param m Mailbox
53 * @param label Label
54 */
label_ref_dec(struct Mailbox * m,char * label)55 static void label_ref_dec(struct Mailbox *m, char *label)
56 {
57 struct HashElem *elem = mutt_hash_find_elem(m->label_hash, label);
58 if (!elem)
59 return;
60
61 uintptr_t count = (uintptr_t) elem->data;
62 if (count <= 1)
63 {
64 mutt_hash_delete(m->label_hash, label, NULL);
65 return;
66 }
67
68 count--;
69 elem->data = (void *) count;
70 }
71
72 /**
73 * label_ref_inc - Increase the refcount of a label
74 * @param m Mailbox
75 * @param label Label
76 */
label_ref_inc(struct Mailbox * m,char * label)77 static void label_ref_inc(struct Mailbox *m, char *label)
78 {
79 uintptr_t count;
80
81 struct HashElem *elem = mutt_hash_find_elem(m->label_hash, label);
82 if (!elem)
83 {
84 count = 1;
85 mutt_hash_insert(m->label_hash, label, (void *) count);
86 return;
87 }
88
89 count = (uintptr_t) elem->data;
90 count++;
91 elem->data = (void *) count;
92 }
93
94 /**
95 * label_message - Add an X-Label: field
96 * @param[in] m Mailbox
97 * @param[in] e Email
98 * @param[out] new_label Set to true if this is a new label
99 * @retval true The label was added
100 */
label_message(struct Mailbox * m,struct Email * e,char * new_label)101 static bool label_message(struct Mailbox *m, struct Email *e, char *new_label)
102 {
103 if (!e)
104 return false;
105 if (mutt_str_equal(e->env->x_label, new_label))
106 return false;
107
108 if (e->env->x_label)
109 label_ref_dec(m, e->env->x_label);
110 if (mutt_str_replace(&e->env->x_label, new_label))
111 label_ref_inc(m, e->env->x_label);
112
113 e->changed = true;
114 e->env->changed |= MUTT_ENV_CHANGED_XLABEL;
115 return true;
116 }
117
118 /**
119 * mutt_label_message - Let the user label a message
120 * @param m Mailbox
121 * @param el List of Emails to label
122 * @retval num Number of messages changed
123 */
mutt_label_message(struct Mailbox * m,struct EmailList * el)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_copy(buf, en->email->env->x_label, sizeof(buf));
137 }
138
139 if (mutt_get_field("Label: ", buf, sizeof(buf), MUTT_LABEL /* | MUTT_CLEAR */,
140 false, NULL, NULL) != 0)
141 {
142 return 0;
143 }
144
145 char *new_label = buf;
146 SKIPWS(new_label);
147 if (*new_label == '\0')
148 new_label = NULL;
149
150 int changed = 0;
151 STAILQ_FOREACH(en, el, entries)
152 {
153 if (label_message(m, en->email, new_label))
154 {
155 changed++;
156 mutt_set_header_color(m, en->email);
157 }
158 }
159
160 return changed;
161 }
162
163 /**
164 * mutt_edit_headers - Let the user edit the message header and body
165 * @param editor Editor command
166 * @param body File containing message body
167 * @param e Email
168 * @param fcc Buffer for the fcc field
169 */
mutt_edit_headers(const char * editor,const char * body,struct Email * e,struct Buffer * fcc)170 void mutt_edit_headers(const char *editor, const char *body, struct Email *e,
171 struct Buffer *fcc)
172 {
173 char buf[1024];
174 const char *p = NULL;
175 int i;
176 struct Envelope *n = NULL;
177 time_t mtime;
178 struct stat st = { 0 };
179
180 struct Buffer *path = mutt_buffer_pool_get();
181 mutt_buffer_mktemp(path);
182 FILE *fp_out = mutt_file_fopen(mutt_buffer_string(path), "w");
183 if (!fp_out)
184 {
185 mutt_perror(mutt_buffer_string(path));
186 goto cleanup;
187 }
188
189 mutt_env_to_local(e->env);
190 mutt_rfc822_write_header(fp_out, e->env, NULL, MUTT_WRITE_HEADER_EDITHDRS,
191 false, false, NeoMutt->sub);
192 fputc('\n', fp_out); /* tie off the header. */
193
194 /* now copy the body of the message. */
195 FILE *fp_in = fopen(body, "r");
196 if (!fp_in)
197 {
198 mutt_perror(body);
199 mutt_file_fclose(&fp_out);
200 goto cleanup;
201 }
202
203 mutt_file_copy_stream(fp_in, fp_out);
204
205 mutt_file_fclose(&fp_in);
206 mutt_file_fclose(&fp_out);
207
208 if (stat(mutt_buffer_string(path), &st) == -1)
209 {
210 mutt_perror(mutt_buffer_string(path));
211 goto cleanup;
212 }
213
214 mtime = mutt_file_decrease_mtime(mutt_buffer_string(path), &st);
215 if (mtime == (time_t) -1)
216 {
217 mutt_perror(mutt_buffer_string(path));
218 goto cleanup;
219 }
220
221 mutt_edit_file(editor, mutt_buffer_string(path));
222 if ((stat(mutt_buffer_string(path), &st) != 0) || (mtime == st.st_mtime))
223 {
224 mutt_debug(LL_DEBUG1, "temp file was not modified\n");
225 /* the file has not changed! */
226 mutt_file_unlink(mutt_buffer_string(path));
227 goto cleanup;
228 }
229
230 mutt_file_unlink(body);
231 mutt_list_free(&e->env->userhdrs);
232
233 /* Read the temp file back in */
234 fp_in = fopen(mutt_buffer_string(path), "r");
235 if (!fp_in)
236 {
237 mutt_perror(mutt_buffer_string(path));
238 goto cleanup;
239 }
240
241 fp_out = mutt_file_fopen(body, "w");
242 if (!fp_out)
243 {
244 /* intentionally leak a possible temporary file here */
245 mutt_file_fclose(&fp_in);
246 mutt_perror(body);
247 goto cleanup;
248 }
249
250 n = mutt_rfc822_read_header(fp_in, NULL, true, false);
251 while ((i = fread(buf, 1, sizeof(buf), fp_in)) > 0)
252 fwrite(buf, 1, i, fp_out);
253 mutt_file_fclose(&fp_out);
254 mutt_file_fclose(&fp_in);
255 mutt_file_unlink(mutt_buffer_string(path));
256
257 /* in case the user modifies/removes the In-Reply-To header with
258 * $edit_headers set, we remove References: as they're likely invalid;
259 * we can simply compare strings as we don't generate References for
260 * multiple Message-Ids in IRT anyways */
261 #ifdef USE_NNTP
262 if (!OptNewsSend)
263 #endif
264 {
265 if (!STAILQ_EMPTY(&e->env->in_reply_to) &&
266 (STAILQ_EMPTY(&n->in_reply_to) ||
267 !mutt_str_equal(STAILQ_FIRST(&n->in_reply_to)->data,
268 STAILQ_FIRST(&e->env->in_reply_to)->data)))
269 {
270 mutt_list_free(&e->env->references);
271 }
272 }
273
274 /* restore old info. */
275 mutt_list_free(&n->references);
276 STAILQ_SWAP(&n->references, &e->env->references, ListNode);
277
278 mutt_env_free(&e->env);
279 e->env = n;
280 n = NULL;
281
282 mutt_expand_aliases_env(e->env);
283
284 /* search through the user defined headers added to see if
285 * fcc: or attach: or pgp: was specified */
286
287 struct ListNode *np = NULL, *tmp = NULL;
288 STAILQ_FOREACH_SAFE(np, &e->env->userhdrs, entries, tmp)
289 {
290 bool keep = true;
291 size_t plen;
292
293 if (fcc && (plen = mutt_istr_startswith(np->data, "fcc:")))
294 {
295 p = mutt_str_skip_email_wsp(np->data + plen);
296 if (*p)
297 {
298 mutt_buffer_strcpy(fcc, p);
299 mutt_buffer_pretty_mailbox(fcc);
300 }
301 keep = false;
302 }
303 else if ((plen = mutt_istr_startswith(np->data, "attach:")))
304 {
305 struct Body *body2 = NULL;
306 struct Body *parts = NULL;
307
308 p = mutt_str_skip_email_wsp(np->data + plen);
309 if (*p)
310 {
311 mutt_buffer_reset(path);
312 for (; (p[0] != '\0') && (p[0] != ' ') && (p[0] != '\t'); p++)
313 {
314 if (p[0] == '\\')
315 {
316 if (p[1] == '\0')
317 break;
318 p++;
319 }
320 mutt_buffer_addch(path, *p);
321 }
322 p = mutt_str_skip_email_wsp(p);
323
324 mutt_buffer_expand_path(path);
325 body2 = mutt_make_file_attach(mutt_buffer_string(path), NeoMutt->sub);
326 if (body2)
327 {
328 body2->description = mutt_str_dup(p);
329 for (parts = e->body; parts->next; parts = parts->next)
330 ; // do nothing
331
332 parts->next = body2;
333 }
334 else
335 {
336 mutt_buffer_pretty_mailbox(path);
337 mutt_error(_("%s: unable to attach file"), mutt_buffer_string(path));
338 }
339 }
340 keep = false;
341 }
342 else if (((WithCrypto & APPLICATION_PGP) != 0) &&
343 (plen = mutt_istr_startswith(np->data, "pgp:")))
344 {
345 e->security = mutt_parse_crypt_hdr(np->data + plen, false, APPLICATION_PGP);
346 if (e->security)
347 e->security |= APPLICATION_PGP;
348 keep = false;
349 }
350
351 if (!keep)
352 {
353 STAILQ_REMOVE(&e->env->userhdrs, np, ListNode, entries);
354 FREE(&np->data);
355 FREE(&np);
356 }
357 }
358
359 cleanup:
360 mutt_buffer_pool_release(&path);
361 }
362
363 /**
364 * mutt_make_label_hash - Create a Hash Table to store the labels
365 * @param m Mailbox
366 */
mutt_make_label_hash(struct Mailbox * m)367 void mutt_make_label_hash(struct Mailbox *m)
368 {
369 /* 131 is just a rough prime estimate of how many distinct
370 * labels someone might have in a m. */
371 m->label_hash = mutt_hash_new(131, MUTT_HASH_STRDUP_KEYS);
372 }
373
374 /**
375 * mutt_label_hash_add - Add a message's labels to the Hash Table
376 * @param m Mailbox
377 * @param e Email
378 */
mutt_label_hash_add(struct Mailbox * m,struct Email * e)379 void mutt_label_hash_add(struct Mailbox *m, struct Email *e)
380 {
381 if (!m || !m->label_hash)
382 return;
383 if (e->env->x_label)
384 label_ref_inc(m, e->env->x_label);
385 }
386
387 /**
388 * mutt_label_hash_remove - Remove a message's labels from the Hash Table
389 * @param m Mailbox
390 * @param e Email
391 */
mutt_label_hash_remove(struct Mailbox * m,struct Email * e)392 void mutt_label_hash_remove(struct Mailbox *m, struct Email *e)
393 {
394 if (!m || !m->label_hash)
395 return;
396 if (e->env->x_label)
397 label_ref_dec(m, e->env->x_label);
398 }
399