/src/neomutt/email/envelope.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * Representation of an email header (envelope) |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 2017-2023 Richard Russon <rich@flatcap.org> |
7 | | * Copyright (C) 2019-2021 Pietro Cerutti <gahr@gahr.ch> |
8 | | * Copyright (C) 2023 наб <nabijaczleweli@nabijaczleweli.xyz> |
9 | | * |
10 | | * @copyright |
11 | | * This program is free software: you can redistribute it and/or modify it under |
12 | | * the terms of the GNU General Public License as published by the Free Software |
13 | | * Foundation, either version 2 of the License, or (at your option) any later |
14 | | * version. |
15 | | * |
16 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
19 | | * details. |
20 | | * |
21 | | * You should have received a copy of the GNU General Public License along with |
22 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
23 | | */ |
24 | | |
25 | | /** |
26 | | * @page email_envelope Envelope (Email header) |
27 | | * |
28 | | * Representation of an email header (envelope) |
29 | | */ |
30 | | |
31 | | #include "config.h" |
32 | | #include <stdbool.h> |
33 | | #include <stddef.h> |
34 | | #include <string.h> |
35 | | #include "mutt/lib.h" |
36 | | #include "address/lib.h" |
37 | | #include "config/lib.h" |
38 | | #include "core/lib.h" |
39 | | #include "envelope.h" |
40 | | #include "email.h" |
41 | | |
42 | | /** |
43 | | * mutt_env_new - Create a new Envelope |
44 | | * @retval ptr New Envelope |
45 | | */ |
46 | | struct Envelope *mutt_env_new(void) |
47 | 153k | { |
48 | 153k | struct Envelope *env = MUTT_MEM_CALLOC(1, struct Envelope); |
49 | 153k | TAILQ_INIT(&env->return_path); |
50 | 153k | TAILQ_INIT(&env->from); |
51 | 153k | TAILQ_INIT(&env->to); |
52 | 153k | TAILQ_INIT(&env->cc); |
53 | 153k | TAILQ_INIT(&env->bcc); |
54 | 153k | TAILQ_INIT(&env->sender); |
55 | 153k | TAILQ_INIT(&env->reply_to); |
56 | 153k | TAILQ_INIT(&env->mail_followup_to); |
57 | 153k | TAILQ_INIT(&env->x_original_to); |
58 | 153k | STAILQ_INIT(&env->references); |
59 | 153k | STAILQ_INIT(&env->in_reply_to); |
60 | 153k | STAILQ_INIT(&env->userhdrs); |
61 | 153k | return env; |
62 | 153k | } |
63 | | |
64 | | /** |
65 | | * mutt_env_set_subject - Set both subject and real_subj to subj |
66 | | * @param env Envelope |
67 | | * @param subj Subject |
68 | | */ |
69 | | void mutt_env_set_subject(struct Envelope *env, const char *subj) |
70 | 124k | { |
71 | 124k | mutt_str_replace((char **) &env->subject, subj); |
72 | 124k | *(char **) &env->real_subj = NULL; |
73 | | |
74 | 124k | if (env->subject) |
75 | 3.61k | { |
76 | 3.61k | regmatch_t match; |
77 | 3.61k | const struct Regex *c_reply_regex = cs_subset_regex(NeoMutt->sub, "reply_regex"); |
78 | 3.61k | if (mutt_regex_capture(c_reply_regex, env->subject, 1, &match)) |
79 | 3.61k | { |
80 | 3.61k | if (env->subject[match.rm_eo] != '\0') |
81 | 3.41k | *(char **) &env->real_subj = env->subject + match.rm_eo; |
82 | 3.61k | } |
83 | 0 | else |
84 | 0 | { |
85 | 0 | *(char **) &env->real_subj = env->subject; |
86 | 0 | } |
87 | 3.61k | } |
88 | 124k | } |
89 | | |
90 | | #ifdef USE_AUTOCRYPT |
91 | | /** |
92 | | * mutt_autocrypthdr_new - Create a new AutocryptHeader |
93 | | * @retval ptr New AutocryptHeader |
94 | | */ |
95 | | struct AutocryptHeader *mutt_autocrypthdr_new(void) |
96 | | { |
97 | | return MUTT_MEM_CALLOC(1, struct AutocryptHeader); |
98 | | } |
99 | | |
100 | | /** |
101 | | * mutt_autocrypthdr_free - Free an AutocryptHeader |
102 | | * @param ptr AutocryptHeader to free |
103 | | */ |
104 | | void mutt_autocrypthdr_free(struct AutocryptHeader **ptr) |
105 | | { |
106 | | if (!ptr || !*ptr) |
107 | | return; |
108 | | |
109 | | struct AutocryptHeader *cur = NULL; |
110 | | |
111 | | while (*ptr) |
112 | | { |
113 | | cur = *ptr; |
114 | | *ptr = (*ptr)->next; |
115 | | FREE(&cur->addr); |
116 | | FREE(&cur->keydata); |
117 | | FREE(&cur); |
118 | | } |
119 | | } |
120 | | #endif |
121 | | |
122 | | /** |
123 | | * mutt_env_free - Free an Envelope |
124 | | * @param[out] ptr Envelope to free |
125 | | */ |
126 | | void mutt_env_free(struct Envelope **ptr) |
127 | 313k | { |
128 | 313k | if (!ptr || !*ptr) |
129 | 160k | return; |
130 | | |
131 | 153k | struct Envelope *env = *ptr; |
132 | | |
133 | 153k | mutt_addrlist_clear(&env->return_path); |
134 | 153k | mutt_addrlist_clear(&env->from); |
135 | 153k | mutt_addrlist_clear(&env->to); |
136 | 153k | mutt_addrlist_clear(&env->cc); |
137 | 153k | mutt_addrlist_clear(&env->bcc); |
138 | 153k | mutt_addrlist_clear(&env->sender); |
139 | 153k | mutt_addrlist_clear(&env->reply_to); |
140 | 153k | mutt_addrlist_clear(&env->mail_followup_to); |
141 | 153k | mutt_addrlist_clear(&env->x_original_to); |
142 | | |
143 | 153k | FREE(&env->list_post); |
144 | 153k | FREE(&env->list_subscribe); |
145 | 153k | FREE(&env->list_unsubscribe); |
146 | 153k | FREE((char **) &env->subject); |
147 | | /* real_subj is just an offset to subject and shouldn't be freed */ |
148 | 153k | FREE(&env->disp_subj); |
149 | 153k | FREE(&env->message_id); |
150 | 153k | FREE(&env->supersedes); |
151 | 153k | FREE(&env->date); |
152 | 153k | FREE(&env->x_label); |
153 | 153k | FREE(&env->organization); |
154 | 153k | FREE(&env->newsgroups); |
155 | 153k | FREE(&env->xref); |
156 | 153k | FREE(&env->followup_to); |
157 | 153k | FREE(&env->x_comment_to); |
158 | | |
159 | 153k | buf_dealloc(&env->spam); |
160 | | |
161 | 153k | mutt_list_free(&env->references); |
162 | 153k | mutt_list_free(&env->in_reply_to); |
163 | 153k | mutt_list_free(&env->userhdrs); |
164 | | |
165 | | #ifdef USE_AUTOCRYPT |
166 | | mutt_autocrypthdr_free(&env->autocrypt); |
167 | | mutt_autocrypthdr_free(&env->autocrypt_gossip); |
168 | | #endif |
169 | | |
170 | 153k | FREE(ptr); |
171 | 153k | } |
172 | | |
173 | | /** |
174 | | * mutt_env_notify_send - Send an Envelope change notification |
175 | | * @param e Email |
176 | | * @param type Notification type, e.g. #NT_ENVELOPE_SUBJECT |
177 | | * @retval true Successfully sent |
178 | | */ |
179 | | bool mutt_env_notify_send(struct Email *e, enum NotifyEnvelope type) |
180 | 0 | { |
181 | 0 | struct EventEmail ev_e = { 1, &e }; |
182 | 0 | return notify_send(e->notify, NT_ENVELOPE, type, &ev_e); |
183 | 0 | } |
184 | | |
185 | | /** |
186 | | * mutt_env_merge - Merge the headers of two Envelopes |
187 | | * @param[in] base Envelope destination for all the headers |
188 | | * @param[out] extra Envelope to copy from |
189 | | * |
190 | | * Any fields that are missing from base will be copied from extra. |
191 | | * extra will be freed afterwards. |
192 | | */ |
193 | | void mutt_env_merge(struct Envelope *base, struct Envelope **extra) |
194 | 0 | { |
195 | 0 | if (!base || !extra || !*extra) |
196 | 0 | return; |
197 | | |
198 | | /* copies each existing element if necessary, and sets the element |
199 | | * to NULL in the source so that mutt_env_free doesn't leave us |
200 | | * with dangling pointers. */ |
201 | 0 | #define MOVE_ELEM(member) \ |
202 | 0 | if (!base->member) \ |
203 | 0 | { \ |
204 | 0 | base->member = (*extra)->member; \ |
205 | 0 | (*extra)->member = NULL; \ |
206 | 0 | } |
207 | | |
208 | 0 | #define MOVE_STAILQ(member) \ |
209 | 0 | if (STAILQ_EMPTY(&base->member)) \ |
210 | 0 | { \ |
211 | 0 | STAILQ_SWAP(&base->member, &(*extra)->member, ListNode); \ |
212 | 0 | } |
213 | | |
214 | 0 | #define MOVE_ADDRESSLIST(member) \ |
215 | 0 | if (TAILQ_EMPTY(&base->member)) \ |
216 | 0 | { \ |
217 | 0 | TAILQ_SWAP(&base->member, &(*extra)->member, Address, entries); \ |
218 | 0 | } |
219 | | |
220 | 0 | #define MOVE_BUFFER(member) \ |
221 | 0 | if (buf_is_empty(&base->member)) \ |
222 | 0 | { \ |
223 | 0 | memcpy(&base->member, &(*extra)->member, sizeof(struct Buffer)); \ |
224 | 0 | buf_init(&(*extra)->member); \ |
225 | 0 | } |
226 | | |
227 | 0 | MOVE_ADDRESSLIST(return_path); |
228 | 0 | MOVE_ADDRESSLIST(from); |
229 | 0 | MOVE_ADDRESSLIST(to); |
230 | 0 | MOVE_ADDRESSLIST(cc); |
231 | 0 | MOVE_ADDRESSLIST(bcc); |
232 | 0 | MOVE_ADDRESSLIST(sender); |
233 | 0 | MOVE_ADDRESSLIST(reply_to); |
234 | 0 | MOVE_ADDRESSLIST(mail_followup_to); |
235 | 0 | MOVE_ELEM(list_post); |
236 | 0 | MOVE_ELEM(list_subscribe); |
237 | 0 | MOVE_ELEM(list_unsubscribe); |
238 | 0 | MOVE_ELEM(message_id); |
239 | 0 | MOVE_ELEM(supersedes); |
240 | 0 | MOVE_ELEM(date); |
241 | 0 | MOVE_ADDRESSLIST(x_original_to); |
242 | 0 | if (!(base->changed & MUTT_ENV_CHANGED_XLABEL)) |
243 | 0 | { |
244 | 0 | MOVE_ELEM(x_label); |
245 | 0 | } |
246 | 0 | if (!(base->changed & MUTT_ENV_CHANGED_REFS)) |
247 | 0 | { |
248 | 0 | MOVE_STAILQ(references); |
249 | 0 | } |
250 | 0 | if (!(base->changed & MUTT_ENV_CHANGED_IRT)) |
251 | 0 | { |
252 | 0 | MOVE_STAILQ(in_reply_to); |
253 | 0 | } |
254 | | |
255 | | /* real_subj is subordinate to subject */ |
256 | 0 | if (!base->subject) |
257 | 0 | { |
258 | 0 | *(char **) &base->subject = (*extra)->subject; |
259 | 0 | *(char **) &base->real_subj = (*extra)->real_subj; |
260 | 0 | base->disp_subj = (*extra)->disp_subj; |
261 | 0 | *(char **) &(*extra)->subject = NULL; |
262 | 0 | *(char **) &(*extra)->real_subj = NULL; |
263 | 0 | (*extra)->disp_subj = NULL; |
264 | 0 | } |
265 | | /* spam and user headers should never be hashed, and the new envelope may |
266 | | * have better values. Use new versions regardless. */ |
267 | 0 | buf_dealloc(&base->spam); |
268 | 0 | mutt_list_free(&base->userhdrs); |
269 | 0 | MOVE_BUFFER(spam); |
270 | 0 | MOVE_STAILQ(userhdrs); |
271 | 0 | #undef MOVE_ELEM |
272 | 0 | #undef MOVE_STAILQ |
273 | 0 | #undef MOVE_ADDRESSLIST |
274 | 0 | #undef MOVE_BUFFER |
275 | |
|
276 | 0 | mutt_env_free(extra); |
277 | 0 | } |
278 | | |
279 | | /** |
280 | | * mutt_env_cmp_strict - Strictly compare two Envelopes |
281 | | * @param e1 First Envelope |
282 | | * @param e2 Second Envelope |
283 | | * @retval true Envelopes are strictly identical |
284 | | */ |
285 | | bool mutt_env_cmp_strict(const struct Envelope *e1, const struct Envelope *e2) |
286 | 0 | { |
287 | 0 | if (e1 && e2) |
288 | 0 | { |
289 | 0 | if (!mutt_str_equal(e1->message_id, e2->message_id) || |
290 | 0 | !mutt_str_equal(e1->subject, e2->subject) || |
291 | 0 | !mutt_list_equal(&e1->references, &e2->references) || |
292 | 0 | !mutt_addrlist_equal(&e1->from, &e2->from) || |
293 | 0 | !mutt_addrlist_equal(&e1->sender, &e2->sender) || |
294 | 0 | !mutt_addrlist_equal(&e1->reply_to, &e2->reply_to) || |
295 | 0 | !mutt_addrlist_equal(&e1->to, &e2->to) || !mutt_addrlist_equal(&e1->cc, &e2->cc) || |
296 | 0 | !mutt_addrlist_equal(&e1->return_path, &e2->return_path)) |
297 | 0 | { |
298 | 0 | return false; |
299 | 0 | } |
300 | 0 | else |
301 | 0 | { |
302 | 0 | return true; |
303 | 0 | } |
304 | 0 | } |
305 | 0 | else |
306 | 0 | { |
307 | 0 | return (!e1 && !e2); |
308 | 0 | } |
309 | 0 | } |
310 | | |
311 | | /** |
312 | | * mutt_env_to_local - Convert an Envelope's Address fields to local format |
313 | | * @param env Envelope to modify |
314 | | * |
315 | | * Run mutt_addrlist_to_local() on each of the Address fields in the Envelope. |
316 | | */ |
317 | | void mutt_env_to_local(struct Envelope *env) |
318 | 0 | { |
319 | 0 | if (!env) |
320 | 0 | return; |
321 | | |
322 | 0 | mutt_addrlist_to_local(&env->return_path); |
323 | 0 | mutt_addrlist_to_local(&env->from); |
324 | 0 | mutt_addrlist_to_local(&env->to); |
325 | 0 | mutt_addrlist_to_local(&env->cc); |
326 | 0 | mutt_addrlist_to_local(&env->bcc); |
327 | 0 | mutt_addrlist_to_local(&env->reply_to); |
328 | 0 | mutt_addrlist_to_local(&env->mail_followup_to); |
329 | 0 | } |
330 | | |
331 | | /* Note that 'member' in the 'env->member' expression is macro argument, not |
332 | | * "real" name of an 'env' compound member. Real name will be substituted by |
333 | | * preprocessor at the macro-expansion time. |
334 | | * Note that #member escapes and double quotes the argument. |
335 | | */ |
336 | | #define H_TO_INTL(member) \ |
337 | 0 | if (mutt_addrlist_to_intl(&env->member, err) && (rc == 0)) \ |
338 | 0 | { \ |
339 | 0 | if (tag) \ |
340 | 0 | *tag = #member; \ |
341 | 0 | rc = 1; \ |
342 | 0 | err = NULL; \ |
343 | 0 | } |
344 | | |
345 | | /** |
346 | | * mutt_env_to_intl - Convert an Envelope's Address fields to Punycode format |
347 | | * @param[in] env Envelope to modify |
348 | | * @param[out] tag Name of the failed field |
349 | | * @param[out] err Failed address |
350 | | * @retval 0 Success, all addresses converted |
351 | | * @retval 1 Error, tag and err will be set |
352 | | * |
353 | | * Run mutt_addrlist_to_intl() on each of the Address fields in the Envelope. |
354 | | */ |
355 | | int mutt_env_to_intl(struct Envelope *env, const char **tag, char **err) |
356 | 0 | { |
357 | 0 | if (!env) |
358 | 0 | return 1; |
359 | | |
360 | 0 | int rc = 0; |
361 | 0 | H_TO_INTL(return_path); |
362 | 0 | H_TO_INTL(from); |
363 | 0 | H_TO_INTL(to); |
364 | 0 | H_TO_INTL(cc); |
365 | 0 | H_TO_INTL(bcc); |
366 | 0 | H_TO_INTL(reply_to); |
367 | 0 | H_TO_INTL(mail_followup_to); |
368 | 0 | return rc; |
369 | 0 | } |
370 | | |
371 | | #undef H_TO_INTL |