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