NeoMutt  2024-11-14-138-ge5ca67
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
mutt_header.c
Go to the documentation of this file.
1
31#include "config.h"
32#include <stdbool.h>
33#include <stddef.h>
34#include <stdint.h>
35#include <stdio.h>
36#include <sys/stat.h>
37#include <time.h>
38#include "mutt/lib.h"
39#include "email/lib.h"
40#include "core/lib.h"
41#include "alias/lib.h"
42#include "gui/lib.h"
43#include "mutt.h"
44#include "mutt_header.h"
45#include "complete/lib.h"
46#include "editor/lib.h"
47#include "history/lib.h"
48#include "index/lib.h"
49#include "ncrypt/lib.h"
50#include "postpone/lib.h"
51#include "send/lib.h"
52#include "globals.h"
53#include "muttlib.h"
54#include "mview.h"
55
61static void label_ref_dec(struct Mailbox *m, char *label)
62{
63 struct HashElem *he = mutt_hash_find_elem(m->label_hash, label);
64 if (!he)
65 return;
66
67 uintptr_t count = (uintptr_t) he->data;
68 if (count <= 1)
69 {
70 mutt_hash_delete(m->label_hash, label, NULL);
71 return;
72 }
73
74 count--;
75 he->data = (void *) count;
76}
77
83static void label_ref_inc(struct Mailbox *m, char *label)
84{
85 uintptr_t count;
86
87 struct HashElem *he = mutt_hash_find_elem(m->label_hash, label);
88 if (!he)
89 {
90 count = 1;
91 mutt_hash_insert(m->label_hash, label, (void *) count);
92 return;
93 }
94
95 count = (uintptr_t) he->data;
96 count++;
97 he->data = (void *) count;
98}
99
107static bool label_message(struct Mailbox *m, struct Email *e, char *new_label)
108{
109 if (!e)
110 return false;
111 if (mutt_str_equal(e->env->x_label, new_label))
112 return false;
113
114 if (e->env->x_label)
115 label_ref_dec(m, e->env->x_label);
116 if (mutt_str_replace(&e->env->x_label, new_label))
117 label_ref_inc(m, e->env->x_label);
118
119 e->changed = true;
121 return true;
122}
123
130int mutt_label_message(struct MailboxView *mv, struct EmailArray *ea)
131{
132 if (!mv || !mv->mailbox || !ea)
133 return 0;
134
135 struct Mailbox *m = mv->mailbox;
136
137 int changed = 0;
138 struct Buffer *buf = buf_pool_get();
139
140 struct Email **ep = ARRAY_GET(ea, 0);
141 if (ARRAY_SIZE(ea) == 1)
142 {
143 // If there's only one email, use its label as a template
144 struct Email *e = *ep;
145 if (e->env->x_label)
146 buf_strcpy(buf, e->env->x_label);
147 }
148
149 if (mw_get_field("Label: ", buf, MUTT_COMP_NO_FLAGS, HC_OTHER, &CompleteLabelOps, NULL) != 0)
150 {
151 goto done;
152 }
153
154 char *new_label = buf->data;
155 SKIPWS(new_label);
156 if (*new_label == '\0')
157 new_label = NULL;
158
159 ARRAY_FOREACH(ep, ea)
160 {
161 struct Email *e = *ep;
162 if (label_message(m, e, new_label))
163 {
164 changed++;
165 email_set_color(m, e);
166 }
167 }
168
169done:
170 buf_pool_release(&buf);
171 return changed;
172}
173
181void mutt_edit_headers(const char *editor, const char *body, struct Email *e,
182 struct Buffer *fcc)
183{
184 struct Buffer *path = buf_pool_get();
185 buf_mktemp(path);
186 FILE *fp_out = mutt_file_fopen(buf_string(path), "w");
187 if (!fp_out)
188 {
189 mutt_perror("%s", buf_string(path));
190 goto cleanup;
191 }
192
195 false, false, NeoMutt->sub);
196 fputc('\n', fp_out); /* tie off the header. */
197
198 /* now copy the body of the message. */
199 FILE *fp_in = mutt_file_fopen(body, "r");
200 if (!fp_in)
201 {
202 mutt_perror("%s", body);
203 mutt_file_fclose(&fp_out);
204 goto cleanup;
205 }
206
207 mutt_file_copy_stream(fp_in, fp_out);
208
209 mutt_file_fclose(&fp_in);
210 mutt_file_fclose(&fp_out);
211
212 struct stat st = { 0 };
213 if (stat(buf_string(path), &st) == -1)
214 {
215 mutt_perror("%s", buf_string(path));
216 goto cleanup;
217 }
218
219 time_t mtime = mutt_file_decrease_mtime(buf_string(path), &st);
220 if (mtime == (time_t) -1)
221 {
222 mutt_perror("%s", buf_string(path));
223 goto cleanup;
224 }
225
226 mutt_edit_file(editor, buf_string(path));
227 if ((stat(buf_string(path), &st) != 0) || (mtime == st.st_mtime))
228 {
229 mutt_debug(LL_DEBUG1, "temp file was not modified\n");
230 /* the file has not changed! */
232 goto cleanup;
233 }
234
235 mutt_file_unlink(body);
237
238 /* Read the temp file back in */
239 fp_in = mutt_file_fopen(buf_string(path), "r");
240 if (!fp_in)
241 {
242 mutt_perror("%s", buf_string(path));
243 goto cleanup;
244 }
245
246 fp_out = mutt_file_fopen(body, "w");
247 if (!fp_out)
248 {
249 /* intentionally leak a possible temporary file here */
250 mutt_file_fclose(&fp_in);
251 mutt_perror("%s", body);
252 goto cleanup;
253 }
254
255 struct Envelope *env_new = NULL;
256 char buf[1024] = { 0 };
257 env_new = mutt_rfc822_read_header(fp_in, NULL, true, false);
258 int bytes_read;
259 while ((bytes_read = fread(buf, 1, sizeof(buf), fp_in)) > 0)
260 fwrite(buf, 1, bytes_read, fp_out);
261 mutt_file_fclose(&fp_out);
262 mutt_file_fclose(&fp_in);
264
265 /* in case the user modifies/removes the In-Reply-To header with
266 * $edit_headers set, we remove References: as they're likely invalid;
267 * we can simply compare strings as we don't generate References for
268 * multiple Message-Ids in IRT anyways */
269 if (!OptNewsSend)
270 {
271 if (!STAILQ_EMPTY(&e->env->in_reply_to) &&
272 (STAILQ_EMPTY(&env_new->in_reply_to) ||
273 !mutt_str_equal(STAILQ_FIRST(&env_new->in_reply_to)->data,
274 STAILQ_FIRST(&e->env->in_reply_to)->data)))
275 {
277 }
278 }
279
280 /* restore old info. */
281 mutt_list_free(&env_new->references);
282 STAILQ_SWAP(&env_new->references, &e->env->references, ListNode);
283
284 mutt_env_free(&e->env);
285 e->env = env_new;
286 env_new = NULL;
287
289
290 /* search through the user defined headers added to see if
291 * fcc: or attach: or pgp: or smime: was specified */
292
293 struct ListNode *np = NULL, *tmp = NULL;
294 STAILQ_FOREACH_SAFE(np, &e->env->userhdrs, entries, tmp)
295 {
296 bool keep = true;
297 size_t plen = 0;
298
299 // Check for header names: most specific first
300 if (fcc && ((plen = mutt_istr_startswith(np->data, "X-Mutt-Fcc:")) ||
301 (plen = mutt_istr_startswith(np->data, "Mutt-Fcc:")) ||
302 (plen = mutt_istr_startswith(np->data, "fcc:"))))
303 {
304 const char *p = mutt_str_skip_email_wsp(np->data + plen);
305 if (*p)
306 {
307 buf_strcpy(fcc, p);
309 }
310 keep = false;
311 }
312 // Check for header names: most specific first
313 else if ((plen = mutt_istr_startswith(np->data, "X-Mutt-Attach:")) ||
314 (plen = mutt_istr_startswith(np->data, "Mutt-Attach:")) ||
315 (plen = mutt_istr_startswith(np->data, "attach:")))
316 {
317 struct Body *body2 = NULL;
318 struct Body *parts = NULL;
319
320 const char *p = mutt_str_skip_email_wsp(np->data + plen);
321 if (*p)
322 {
323 buf_reset(path);
324 for (; (p[0] != '\0') && (p[0] != ' ') && (p[0] != '\t'); p++)
325 {
326 if (p[0] == '\\')
327 {
328 if (p[1] == '\0')
329 break;
330 p++;
331 }
332 buf_addch(path, *p);
333 }
335
336 buf_expand_path(path);
338 if (body2)
339 {
340 body2->description = mutt_str_dup(p);
341 for (parts = e->body; parts->next; parts = parts->next)
342 ; // do nothing
343
344 parts->next = body2;
345 }
346 else
347 {
348 buf_pretty_mailbox(path);
349 mutt_error(_("%s: unable to attach file"), buf_string(path));
350 }
351 }
352 keep = false;
353 }
354 // Check for header names: most specific first
355 else if (((WithCrypto & APPLICATION_PGP) != 0) &&
356 ((plen = mutt_istr_startswith(np->data, "X-Mutt-PGP:")) ||
357 (plen = mutt_istr_startswith(np->data, "Mutt-PGP:")) ||
358 (plen = mutt_istr_startswith(np->data, "pgp:"))))
359 {
360 SecurityFlags sec = mutt_parse_crypt_hdr(np->data + plen, false, APPLICATION_PGP);
361 if (sec != SEC_NO_FLAGS)
362 sec |= APPLICATION_PGP;
363 if (sec != e->security)
364 {
365 e->security = sec;
367 }
368 keep = false;
369 }
370 // Check for header names: most specific first
371 else if (((WithCrypto & APPLICATION_SMIME) != 0) &&
372 ((plen = mutt_istr_startswith(np->data, "X-Mutt-SMIME:")) ||
373 (plen = mutt_istr_startswith(np->data, "Mutt-SMIME:")) ||
374 (plen = mutt_istr_startswith(np->data, "smime:"))))
375 {
377 if (sec != SEC_NO_FLAGS)
378 sec |= APPLICATION_SMIME;
379 if (sec != e->security)
380 {
381 e->security = sec;
383 }
384 keep = false;
385 }
386
387 if (!keep)
388 {
389 STAILQ_REMOVE(&e->env->userhdrs, np, ListNode, entries);
390 FREE(&np->data);
391 FREE(&np);
392 }
393 }
394
395cleanup:
396 buf_pool_release(&path);
397}
398
404{
405 /* 131 is just a rough prime estimate of how many distinct
406 * labels someone might have in a mailbox. */
408}
409
415void mutt_label_hash_add(struct Mailbox *m, struct Email *e)
416{
417 if (!m || !m->label_hash)
418 return;
419 if (e->env->x_label)
420 label_ref_inc(m, e->env->x_label);
421}
422
428void mutt_label_hash_remove(struct Mailbox *m, struct Email *e)
429{
430 if (!m || !m->label_hash)
431 return;
432 if (e->env->x_label)
433 label_ref_dec(m, e->env->x_label);
434}
Email Aliases.
void mutt_expand_aliases_env(struct Envelope *env)
Expand aliases in all the fields of an Envelope.
Definition: alias.c:309
#define ARRAY_FOREACH(elem, head)
Iterate over all elements of the array.
Definition: array.h:212
#define ARRAY_SIZE(head)
The number of elements stored.
Definition: array.h:87
#define ARRAY_GET(head, idx)
Return the element at index.
Definition: array.h:109
void buf_reset(struct Buffer *buf)
Reset an existing Buffer.
Definition: buffer.c:76
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_strcpy(struct Buffer *buf, const char *s)
Copy a string into a Buffer.
Definition: buffer.c:395
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
const struct CompleteOps CompleteLabelOps
Auto-Completion of Labels.
Definition: helpers.c:484
Auto-completion.
Convenience wrapper for the core headers.
void mutt_edit_file(const char *editor, const char *file)
Let the user edit a file.
Definition: curs_lib.c:116
void email_set_color(struct Mailbox *m, struct Email *e)
Select an Index colour for an Email.
Definition: dlg_index.c:1406
Edit a string.
Structs that make up an email.
struct Envelope * mutt_rfc822_read_header(FILE *fp, struct Email *e, bool user_hdrs, bool weed)
Parses an RFC822 header.
Definition: parse.c:1205
@ NT_EMAIL_CHANGE
Email has changed.
Definition: email.h:186
void mutt_env_free(struct Envelope **ptr)
Free an Envelope.
Definition: envelope.c:126
void mutt_env_to_local(struct Envelope *env)
Convert an Envelope's Address fields to local format.
Definition: envelope.c:317
#define MUTT_ENV_CHANGED_XLABEL
X-Label edited.
Definition: envelope.h:36
int mutt_file_copy_stream(FILE *fp_in, FILE *fp_out)
Copy the contents of one file into another.
Definition: file.c:287
time_t mutt_file_decrease_mtime(const char *fp, struct stat *st)
Decrease a file's modification time by 1 second.
Definition: file.c:1028
void mutt_file_unlink(const char *s)
Delete a file, carefully.
Definition: file.c:221
#define mutt_file_fclose(FP)
Definition: file.h:138
#define mutt_file_fopen(PATH, MODE)
Definition: file.h:137
bool OptNewsSend
(pseudo) used to change behavior when posting
Definition: globals.c:68
Global variables.
int mw_get_field(const char *prompt, struct Buffer *buf, CompletionFlags complete, enum HistoryClass hclass, const struct CompleteOps *comp_api, void *cdata)
Ask the user for a string -.
Definition: window.c:274
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
#define mutt_perror(...)
Definition: logging2.h:93
Convenience wrapper for the gui headers.
struct HashElem * mutt_hash_insert(struct HashTable *table, const char *strkey, void *data)
Add a new element to the Hash Table (with string keys)
Definition: hash.c:335
void mutt_hash_delete(struct HashTable *table, const char *strkey, const void *data)
Remove an element from a Hash Table.
Definition: hash.c:427
struct HashTable * mutt_hash_new(size_t num_elems, HashFlags flags)
Create a new Hash Table (with string keys)
Definition: hash.c:259
struct HashElem * mutt_hash_find_elem(const struct HashTable *table, const char *strkey)
Find the HashElem in a Hash Table element using a key.
Definition: hash.c:377
#define MUTT_HASH_STRDUP_KEYS
make a copy of the keys
Definition: hash.h:111
int mutt_rfc822_write_header(FILE *fp, struct Envelope *env, struct Body *b, enum MuttWriteHeaderMode mode, bool privacy, bool hide_protected_subject, struct ConfigSubset *sub)
Write out one RFC822 header line.
Definition: header.c:577
@ MUTT_WRITE_HEADER_EDITHDRS
"light" mode (used for edit_hdrs)
Definition: header.h:43
Read/write command history from/to a file.
@ HC_OTHER
Miscellaneous strings.
Definition: lib.h:58
GUI manage the main index (list of emails)
void mutt_list_free(struct ListHead *h)
Free a List AND its strings.
Definition: list.c:123
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
#define FREE(x)
Definition: memory.h:55
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
bool notify_send(struct Notify *notify, enum NotifyType event_type, int event_subtype, void *event_data)
Send out a notification message.
Definition: notify.c:173
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:253
char * mutt_str_skip_email_wsp(const char *s)
Skip over whitespace as defined by RFC5322.
Definition: string.c:608
bool mutt_str_equal(const char *a, const char *b)
Compare two strings.
Definition: string.c:660
size_t mutt_istr_startswith(const char *str, const char *prefix)
Check whether a string starts with a prefix, ignoring case.
Definition: string.c:242
char * mutt_str_replace(char **p, const char *s)
Replace one string with another.
Definition: string.c:280
Many unsorted constants and some structs.
#define MUTT_COMP_NO_FLAGS
No flags are set.
Definition: mutt.h:56
int mutt_label_message(struct MailboxView *mv, struct EmailArray *ea)
Let the user label a message.
Definition: mutt_header.c:130
void mutt_make_label_hash(struct Mailbox *m)
Create a Hash Table to store the labels.
Definition: mutt_header.c:403
void mutt_label_hash_remove(struct Mailbox *m, struct Email *e)
Remove a message's labels from the Hash Table.
Definition: mutt_header.c:428
static void label_ref_inc(struct Mailbox *m, char *label)
Increase the refcount of a label.
Definition: mutt_header.c:83
static bool label_message(struct Mailbox *m, struct Email *e, char *new_label)
Add an X-Label: field.
Definition: mutt_header.c:107
static void label_ref_dec(struct Mailbox *m, char *label)
Decrease the refcount of a label.
Definition: mutt_header.c:61
void mutt_edit_headers(const char *editor, const char *body, struct Email *e, struct Buffer *fcc)
Let the user edit the message header and body.
Definition: mutt_header.c:181
void mutt_label_hash_add(struct Mailbox *m, struct Email *e)
Add a message's labels to the Hash Table.
Definition: mutt_header.c:415
Representation of the email's header.
void buf_pretty_mailbox(struct Buffer *buf)
Shorten a mailbox path using '~' or '='.
Definition: muttlib.c:519
void buf_expand_path(struct Buffer *buf)
Create the canonical path.
Definition: muttlib.c:315
Some miscellaneous functions.
View of a Mailbox.
API for encryption/signing of emails.
uint16_t SecurityFlags
Flags, e.g. SEC_ENCRYPT.
Definition: lib.h:82
#define APPLICATION_PGP
Use PGP to encrypt/sign.
Definition: lib.h:96
#define APPLICATION_SMIME
Use SMIME to encrypt/sign.
Definition: lib.h:97
#define SEC_NO_FLAGS
No flags are set.
Definition: lib.h:83
#define WithCrypto
Definition: lib.h:122
@ NT_EMAIL
Email has changed, NotifyEmail, EventEmail.
Definition: notify_type.h:44
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
Postponed Emails.
SecurityFlags mutt_parse_crypt_hdr(const char *p, bool set_empty_signas, SecurityFlags crypt_app)
Parse a crypto header string.
Definition: postpone.c:204
#define STAILQ_REMOVE(head, elm, type, field)
Definition: queue.h:402
#define STAILQ_FIRST(head)
Definition: queue.h:350
#define STAILQ_EMPTY(head)
Definition: queue.h:348
#define STAILQ_FOREACH_SAFE(var, head, field, tvar)
Definition: queue.h:362
#define STAILQ_SWAP(head1, head2, type)
Definition: queue.h:428
Convenience wrapper for the send headers.
struct Body * mutt_make_file_attach(const char *path, struct ConfigSubset *sub)
Create a file attachment.
Definition: sendlib.c:607
#define SKIPWS(ch)
Definition: string2.h:45
The body of an email.
Definition: body.h:36
struct Body * parts
parts of a multipart or message/rfc822
Definition: body.h:73
char * description
content-description
Definition: body.h:55
struct Body * next
next attachment in the list
Definition: body.h:72
String manipulation buffer.
Definition: buffer.h:36
char * data
Pointer to data.
Definition: buffer.h:37
The envelope/body of an email.
Definition: email.h:39
struct Envelope * env
Envelope information.
Definition: email.h:68
SecurityFlags security
bit 0-10: flags, bit 11,12: application, bit 13: traditional pgp See: ncrypt/lib.h pgplib....
Definition: email.h:43
struct Body * body
List of MIME parts.
Definition: email.h:69
bool changed
Email has been edited.
Definition: email.h:77
struct Notify * notify
Notifications: NotifyEmail, EventEmail.
Definition: email.h:73
The header of an Email.
Definition: envelope.h:57
struct ListHead userhdrs
user defined headers
Definition: envelope.h:85
unsigned char changed
Changed fields, e.g. MUTT_ENV_CHANGED_SUBJECT.
Definition: envelope.h:90
struct ListHead references
message references (in reverse order)
Definition: envelope.h:83
struct ListHead in_reply_to
in-reply-to header content
Definition: envelope.h:84
char * x_label
X-Label.
Definition: envelope.h:76
The item stored in a Hash Table.
Definition: hash.h:43
void * data
User-supplied data.
Definition: hash.h:46
A List node for strings.
Definition: list.h:37
char * data
String.
Definition: list.h:38
View of a Mailbox.
Definition: mview.h:40
struct Mailbox * mailbox
Current Mailbox.
Definition: mview.h:51
A mailbox.
Definition: mailbox.h:79
bool changed
Mailbox has been modified.
Definition: mailbox.h:110
struct HashTable * label_hash
Hash Table: "x-labels" -> Email.
Definition: mailbox.h:125
Container for Accounts, Notifications.
Definition: neomutt.h:42
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:46
#define buf_mktemp(buf)
Definition: tmp.h:33