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