Coverage Report

Created: 2025-04-22 06:17

/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