Coverage Report

Created: 2023-06-07 06:15

/src/neomutt/alias/alias.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 * Representation of a single alias to an email address
4
 *
5
 * @authors
6
 * Copyright (C) 1996-2002 Michael R. Elkins <me@mutt.org>
7
 * Copyright (C) 2019 Pietro Cerutti <gahr@gahr.ch>
8
 * Copyright (C) 2020 Richard Russon <rich@flatcap.org>
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 alias_alias Alias for an email address
27
 *
28
 * Representation of a single alias to an email address
29
 */
30
31
#include "config.h"
32
#include <stddef.h>
33
#include <pwd.h>
34
#include <stdbool.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <wchar.h>
39
#include <wctype.h>
40
#include "mutt/lib.h"
41
#include "address/lib.h"
42
#include "config/lib.h"
43
#include "email/lib.h"
44
#include "core/lib.h"
45
#include "gui/lib.h"
46
#include "mutt.h"
47
#include "alias.h"
48
#include "lib.h"
49
#include "enter/lib.h"
50
#include "question/lib.h"
51
#include "send/lib.h"
52
#include "alternates.h"
53
#include "globals.h" // IWYU pragma: keep
54
#include "maillist.h"
55
#include "muttlib.h"
56
#include "reverse.h"
57
58
struct AliasList Aliases = TAILQ_HEAD_INITIALIZER(Aliases); ///< List of all the user's email aliases
59
60
/**
61
 * write_safe_address - Defang malicious email addresses
62
 * @param fp File to write to
63
 * @param s  Email address to defang
64
 *
65
 * if someone has an address like
66
 *      From: John `/bin/rm -f ~` Doe <john.doe@example.com>
67
 * and the user creates an alias for this, NeoMutt could wind up executing
68
 * the backticks because it writes aliases like
69
 *      alias me John `/bin/rm -f ~` Doe <john.doe@example.com>
70
 * To avoid this problem, use a backslash (\) to quote any backticks.  We also
71
 * need to quote backslashes as well, since you could defeat the above by
72
 * doing
73
 *      From: John \`/bin/rm -f ~\` Doe <john.doe@example.com>
74
 * since that would get aliased as
75
 *      alias me John \\`/bin/rm -f ~\\` Doe <john.doe@example.com>
76
 * which still gets evaluated because the double backslash is not a quote.
77
 *
78
 * Additionally, we need to quote ' and " characters, otherwise neomutt will
79
 * interpret them on the wrong parsing step.
80
 *
81
 * $ wants to be quoted since it may indicate the start of an environment
82
 * variable.
83
 */
84
static void write_safe_address(FILE *fp, const char *s)
85
0
{
86
0
  while (*s)
87
0
  {
88
0
    if ((*s == '\\') || (*s == '`') || (*s == '\'') || (*s == '"') || (*s == '$'))
89
0
      fputc('\\', fp);
90
0
    fputc(*s, fp);
91
0
    s++;
92
0
  }
93
0
}
94
95
/**
96
 * expand_aliases_r - Expand aliases, recursively
97
 * @param[in]  al   Address List
98
 * @param[out] expn Alias List
99
 *
100
 * ListHead expn is used as temporary storage for already-expanded aliases.
101
 */
102
static void expand_aliases_r(struct AddressList *al, struct ListHead *expn)
103
0
{
104
0
  struct Address *a = TAILQ_FIRST(al);
105
0
  while (a)
106
0
  {
107
0
    if (!a->group && !a->personal && a->mailbox && !buf_find_char(a->mailbox, '@'))
108
0
    {
109
0
      struct AddressList *alias = alias_lookup(buf_string(a->mailbox));
110
0
      if (alias)
111
0
      {
112
0
        bool duplicate = false;
113
0
        struct ListNode *np = NULL;
114
0
        STAILQ_FOREACH(np, expn, entries)
115
0
        {
116
0
          if (mutt_str_equal(buf_string(a->mailbox), np->data)) /* alias already found */
117
0
          {
118
0
            mutt_debug(LL_DEBUG1, "loop in alias found for '%s'\n", a->mailbox);
119
0
            duplicate = true;
120
0
            break;
121
0
          }
122
0
        }
123
124
0
        if (duplicate)
125
0
        {
126
          // We've already seen this alias, so drop it
127
0
          struct Address *next = TAILQ_NEXT(a, entries);
128
0
          TAILQ_REMOVE(al, a, entries);
129
0
          mutt_addr_free(&a);
130
0
          a = next;
131
0
          continue;
132
0
        }
133
134
        // Keep a list of aliases that we've already seen
135
0
        mutt_list_insert_head(expn, buf_strdup(a->mailbox));
136
137
        /* The alias may expand to several addresses,
138
         * some of which may themselves be aliases.
139
         * Create a copy and recursively expand any aliases within. */
140
0
        struct AddressList copy = TAILQ_HEAD_INITIALIZER(copy);
141
0
        mutt_addrlist_copy(&copy, alias, false);
142
0
        expand_aliases_r(&copy, expn);
143
144
        /* Next, move the expanded addresses
145
         * from the copy into the original list (before the alias) */
146
0
        struct Address *a2 = NULL, *tmp = NULL;
147
0
        TAILQ_FOREACH_SAFE(a2, &copy, entries, tmp)
148
0
        {
149
0
          TAILQ_INSERT_BEFORE(a, a2, entries);
150
0
        }
151
0
        a = TAILQ_PREV(a, AddressList, entries);
152
        // Finally, remove the alias itself
153
0
        struct Address *next = TAILQ_NEXT(a, entries);
154
0
        TAILQ_REMOVE(al, next, entries);
155
0
        mutt_addr_free(&next);
156
0
      }
157
0
      else
158
0
      {
159
0
        struct passwd *pw = getpwnam(buf_string(a->mailbox));
160
0
        if (pw)
161
0
        {
162
0
          char namebuf[256] = { 0 };
163
164
0
          mutt_gecos_name(namebuf, sizeof(namebuf), pw);
165
0
          buf_strcpy(a->personal, namebuf);
166
0
        }
167
0
      }
168
0
    }
169
0
    a = TAILQ_NEXT(a, entries);
170
0
  }
171
172
0
  const char *fqdn = NULL;
173
0
  const bool c_use_domain = cs_subset_bool(NeoMutt->sub, "use_domain");
174
0
  if (c_use_domain && (fqdn = mutt_fqdn(true, NeoMutt->sub)))
175
0
  {
176
    /* now qualify all local addresses */
177
0
    mutt_addrlist_qualify(al, fqdn);
178
0
  }
179
0
}
180
181
/**
182
 * recode_buf - Convert some text between two character sets
183
 * @param buf    Buffer to convert
184
 * @param buflen Length of buffer
185
 *
186
 * The 'from' charset is controlled by the 'charset'        config variable.
187
 * The 'to'   charset is controlled by the 'config_charset' config variable.
188
 */
189
static void recode_buf(char *buf, size_t buflen)
190
0
{
191
0
  const char *const c_config_charset = cs_subset_string(NeoMutt->sub, "config_charset");
192
0
  if (!c_config_charset || !cc_charset())
193
0
    return;
194
195
0
  char *s = mutt_str_dup(buf);
196
0
  if (!s)
197
0
    return;
198
0
  if (mutt_ch_convert_string(&s, cc_charset(), c_config_charset, MUTT_ICONV_NO_FLAGS) == 0)
199
0
    mutt_str_copy(buf, s, buflen);
200
0
  FREE(&s);
201
0
}
202
203
/**
204
 * check_alias_name - Sanity-check an alias name
205
 * @param s       Alias to check
206
 * @param dest    Buffer for the result
207
 * @param destlen Length of buffer
208
 * @retval  0 Success
209
 * @retval -1 Error
210
 *
211
 * Only characters which are non-special to both the RFC822 and the neomutt
212
 * configuration parser are permitted.
213
 */
214
static int check_alias_name(const char *s, char *dest, size_t destlen)
215
0
{
216
0
  wchar_t wc = 0;
217
0
  mbstate_t mbstate = { 0 };
218
0
  size_t l;
219
0
  int rc = 0;
220
0
  bool dry = !dest || !destlen;
221
222
0
  if (!dry)
223
0
    destlen--;
224
0
  for (; s && *s && (dry || destlen) && (l = mbrtowc(&wc, s, MB_CUR_MAX, &mbstate)) != 0;
225
0
       s += l, destlen -= l)
226
0
  {
227
0
    bool bad = (l == ICONV_ILLEGAL_SEQ) || (l == ICONV_BUF_TOO_SMALL); /* conversion error */
228
0
    bad = bad || (!dry && l > destlen); /* too few room for mb char */
229
0
    if (l == 1)
230
0
      bad = bad || (!strchr("-_+=.", *s) && !iswalnum(wc));
231
0
    else
232
0
      bad = bad || !iswalnum(wc);
233
0
    if (bad)
234
0
    {
235
0
      if (dry)
236
0
        return -1;
237
0
      if (l == ICONV_ILLEGAL_SEQ)
238
0
        memset(&mbstate, 0, sizeof(mbstate_t));
239
0
      *dest++ = '_';
240
0
      rc = -1;
241
0
    }
242
0
    else if (!dry)
243
0
    {
244
0
      memcpy(dest, s, l);
245
0
      dest += l;
246
0
    }
247
0
  }
248
0
  if (!dry)
249
0
    *dest = '\0';
250
0
  return rc;
251
0
}
252
253
/**
254
 * string_is_address - Does an email address match a user and domain?
255
 * @param str    Address string to test
256
 * @param user   User name
257
 * @param domain Domain name
258
 * @retval true They match
259
 */
260
static bool string_is_address(const char *str, const char *user, const char *domain)
261
0
{
262
0
  char buf[1024] = { 0 };
263
264
0
  snprintf(buf, sizeof(buf), "%s@%s", NONULL(user), NONULL(domain));
265
0
  if (mutt_istr_equal(str, buf))
266
0
    return true;
267
268
0
  return false;
269
0
}
270
271
/**
272
 * alias_lookup - Find an Alias
273
 * @param name Alias name to find
274
 * @retval ptr  Address for the Alias
275
 * @retval NULL No such Alias
276
 *
277
 * @note The search is case-insensitive
278
 */
279
struct AddressList *alias_lookup(const char *name)
280
0
{
281
0
  struct Alias *a = NULL;
282
283
0
  TAILQ_FOREACH(a, &Aliases, entries)
284
0
  {
285
0
    if (mutt_istr_equal(name, a->name))
286
0
      return &a->addr;
287
0
  }
288
0
  return NULL;
289
0
}
290
291
/**
292
 * mutt_expand_aliases - Expand aliases in a List of Addresses
293
 * @param al AddressList
294
 *
295
 * Duplicate addresses are dropped
296
 */
297
void mutt_expand_aliases(struct AddressList *al)
298
0
{
299
0
  struct ListHead expn; /* previously expanded aliases to avoid loops */
300
301
0
  STAILQ_INIT(&expn);
302
0
  expand_aliases_r(al, &expn);
303
0
  mutt_list_free(&expn);
304
0
  mutt_addrlist_dedupe(al);
305
0
}
306
307
/**
308
 * mutt_expand_aliases_env - Expand aliases in all the fields of an Envelope
309
 * @param env Envelope to expand
310
 */
311
void mutt_expand_aliases_env(struct Envelope *env)
312
0
{
313
0
  mutt_expand_aliases(&env->from);
314
0
  mutt_expand_aliases(&env->to);
315
0
  mutt_expand_aliases(&env->cc);
316
0
  mutt_expand_aliases(&env->bcc);
317
0
  mutt_expand_aliases(&env->reply_to);
318
0
  mutt_expand_aliases(&env->mail_followup_to);
319
0
}
320
321
/**
322
 * mutt_get_address - Get an Address from an Envelope
323
 * @param[in]  env    Envelope to examine
324
 * @param[out] prefix Prefix for the Address, e.g. "To:"
325
 * @retval ptr AddressList in the Envelope
326
 *
327
 * @note The caller must NOT free the returned AddressList
328
 */
329
struct AddressList *mutt_get_address(struct Envelope *env, const char **prefix)
330
0
{
331
0
  struct AddressList *al = NULL;
332
0
  const char *pfx = NULL;
333
334
0
  if (mutt_addr_is_user(TAILQ_FIRST(&env->from)))
335
0
  {
336
0
    if (!TAILQ_EMPTY(&env->to) && !mutt_is_mail_list(TAILQ_FIRST(&env->to)))
337
0
    {
338
0
      pfx = "To";
339
0
      al = &env->to;
340
0
    }
341
0
    else
342
0
    {
343
0
      pfx = "Cc";
344
0
      al = &env->cc;
345
0
    }
346
0
  }
347
0
  else if (!TAILQ_EMPTY(&env->reply_to) && !mutt_is_mail_list(TAILQ_FIRST(&env->reply_to)))
348
0
  {
349
0
    pfx = "Reply-To";
350
0
    al = &env->reply_to;
351
0
  }
352
0
  else
353
0
  {
354
0
    al = &env->from;
355
0
    pfx = "From";
356
0
  }
357
358
0
  if (prefix)
359
0
    *prefix = pfx;
360
361
0
  return al;
362
0
}
363
364
/**
365
 * alias_create - Create a new Alias from an Address
366
 * @param al Address to use
367
 * @param sub Config items
368
 */
369
void alias_create(struct AddressList *al, const struct ConfigSubset *sub)
370
0
{
371
0
  struct Buffer *buf = buf_pool_get();
372
0
  struct Buffer *fixed = buf_pool_get();
373
0
  struct Buffer *prompt = NULL;
374
0
  struct Buffer *tmp = buf_pool_get();
375
376
0
  struct Address *addr = NULL;
377
0
  char *pc = NULL;
378
0
  char *err = NULL;
379
0
  FILE *fp_alias = NULL;
380
381
0
  if (al)
382
0
  {
383
0
    addr = TAILQ_FIRST(al);
384
0
    if (addr && addr->mailbox)
385
0
    {
386
0
      buf_copy(tmp, addr->mailbox);
387
0
      pc = strchr(buf_string(tmp), '@');
388
0
      if (pc)
389
0
        *pc = '\0';
390
0
    }
391
0
  }
392
393
  /* Don't suggest a bad alias name in the event of a strange local part. */
394
0
  check_alias_name(buf_string(tmp), buf->data, buf->dsize);
395
396
0
retry_name:
397
  /* L10N: prompt to add a new alias */
398
0
  if ((buf_get_field(_("Alias as: "), buf, MUTT_COMP_NO_FLAGS, false, NULL, NULL, NULL) != 0) ||
399
0
      buf_is_empty(buf))
400
0
  {
401
0
    goto done;
402
0
  }
403
404
  /* check to see if the user already has an alias defined */
405
0
  if (alias_lookup(buf_string(buf)))
406
0
  {
407
0
    mutt_error(_("You already have an alias defined with that name"));
408
0
    goto done;
409
0
  }
410
411
0
  if (check_alias_name(buf_string(buf), fixed->data, fixed->dsize))
412
0
  {
413
0
    switch (mutt_yesorno(_("Warning: This alias name may not work.  Fix it?"), MUTT_YES))
414
0
    {
415
0
      case MUTT_YES:
416
0
        buf_copy(buf, fixed);
417
0
        goto retry_name;
418
0
      case MUTT_ABORT:
419
0
        goto done;
420
0
      default:; // do nothing
421
0
    }
422
0
  }
423
424
0
  struct Alias *alias = alias_new();
425
0
  alias->name = buf_strdup(buf);
426
427
0
  mutt_addrlist_to_local(al);
428
429
0
  if (addr && addr->mailbox)
430
0
    buf_copy(buf, addr->mailbox);
431
0
  else
432
0
    buf_reset(buf);
433
434
0
  mutt_addrlist_to_intl(al, NULL);
435
436
0
  do
437
0
  {
438
0
    if ((buf_get_field(_("Address: "), buf, MUTT_COMP_NO_FLAGS, false, NULL, NULL, NULL) != 0) ||
439
0
        buf_is_empty(buf))
440
0
    {
441
0
      alias_free(&alias);
442
0
      goto done;
443
0
    }
444
445
0
    mutt_addrlist_parse(&alias->addr, buf_string(buf));
446
0
    if (TAILQ_EMPTY(&alias->addr))
447
0
      mutt_beep(false);
448
0
    if (mutt_addrlist_to_intl(&alias->addr, &err))
449
0
    {
450
0
      mutt_error(_("Bad IDN: '%s'"), err);
451
0
      FREE(&err);
452
0
      continue;
453
0
    }
454
0
  } while (TAILQ_EMPTY(&alias->addr));
455
456
0
  if (addr && addr->personal && !mutt_is_mail_list(addr))
457
0
    buf_copy(buf, addr->personal);
458
0
  else
459
0
    buf_reset(buf);
460
461
0
  if (buf_get_field(_("Personal name: "), buf, MUTT_COMP_NO_FLAGS, false, NULL,
462
0
                    NULL, NULL) != 0)
463
0
  {
464
0
    alias_free(&alias);
465
0
    goto done;
466
0
  }
467
0
  buf_copy(TAILQ_FIRST(&alias->addr)->personal, buf);
468
469
0
  buf_reset(buf);
470
0
  if (buf_get_field(_("Comment: "), buf, MUTT_COMP_NO_FLAGS, false, NULL, NULL, NULL) == 0)
471
0
  {
472
0
    mutt_str_replace(&alias->comment, buf_string(buf));
473
0
  }
474
475
0
  buf_reset(buf);
476
0
  mutt_addrlist_write(&alias->addr, buf, true);
477
0
  prompt = buf_pool_get();
478
0
  if (alias->comment)
479
0
  {
480
0
    buf_printf(prompt, "[%s = %s # %s] %s", alias->name, buf_string(buf),
481
0
               alias->comment, _("Accept?"));
482
0
  }
483
0
  else
484
0
  {
485
0
    buf_printf(prompt, "[%s = %s] %s", alias->name, buf_string(buf), _("Accept?"));
486
0
  }
487
0
  if (mutt_yesorno(buf_string(prompt), MUTT_YES) != MUTT_YES)
488
0
  {
489
0
    alias_free(&alias);
490
0
    goto done;
491
0
  }
492
493
0
  alias_reverse_add(alias);
494
0
  TAILQ_INSERT_TAIL(&Aliases, alias, entries);
495
496
0
  const char *const c_alias_file = cs_subset_path(sub, "alias_file");
497
0
  buf_strcpy(buf, c_alias_file);
498
499
0
  if (buf_get_field(_("Save to file: "), buf, MUTT_COMP_FILE | MUTT_COMP_CLEAR,
500
0
                    false, NULL, NULL, NULL) != 0)
501
0
  {
502
0
    goto done;
503
0
  }
504
0
  mutt_expand_path(buf->data, buf->dsize);
505
0
  fp_alias = fopen(buf_string(buf), "a+");
506
0
  if (!fp_alias)
507
0
  {
508
0
    mutt_perror(buf_string(buf));
509
0
    goto done;
510
0
  }
511
512
  /* terminate existing file with \n if necessary */
513
0
  if (!mutt_file_seek(fp_alias, 0, SEEK_END))
514
0
  {
515
0
    goto done;
516
0
  }
517
0
  if (ftell(fp_alias) > 0)
518
0
  {
519
0
    if (!mutt_file_seek(fp_alias, -1, SEEK_CUR))
520
0
    {
521
0
      goto done;
522
0
    }
523
0
    if (fread(buf->data, 1, 1, fp_alias) != 1)
524
0
    {
525
0
      mutt_perror(_("Error reading alias file"));
526
0
      goto done;
527
0
    }
528
0
    if (!mutt_file_seek(fp_alias, 0, SEEK_END))
529
0
    {
530
0
      goto done;
531
0
    }
532
0
    if (buf->data[0] != '\n')
533
0
      fputc('\n', fp_alias);
534
0
  }
535
536
0
  if (check_alias_name(alias->name, NULL, 0))
537
0
    mutt_file_quote_filename(alias->name, buf->data, buf->dsize);
538
0
  else
539
0
    buf_strcpy(buf, alias->name);
540
541
0
  recode_buf(buf->data, buf->dsize);
542
0
  fprintf(fp_alias, "alias %s ", buf_string(buf));
543
0
  buf_reset(buf);
544
545
0
  mutt_addrlist_write(&alias->addr, buf, false);
546
0
  recode_buf(buf->data, buf->dsize);
547
0
  write_safe_address(fp_alias, buf_string(buf));
548
0
  if (alias->comment)
549
0
    fprintf(fp_alias, " # %s", alias->comment);
550
0
  fputc('\n', fp_alias);
551
0
  if (mutt_file_fsync_close(&fp_alias) != 0)
552
0
    mutt_perror(_("Trouble adding alias"));
553
0
  else
554
0
    mutt_message(_("Alias added"));
555
556
0
done:
557
0
  mutt_file_fclose(&fp_alias);
558
0
  buf_pool_release(&buf);
559
0
  buf_pool_release(&fixed);
560
0
  buf_pool_release(&prompt);
561
0
  buf_pool_release(&tmp);
562
0
}
563
564
/**
565
 * mutt_addr_is_user - Does the address belong to the user
566
 * @param addr Address to check
567
 * @retval true The given address belongs to the user
568
 */
569
bool mutt_addr_is_user(const struct Address *addr)
570
0
{
571
0
  if (!addr)
572
0
  {
573
0
    mutt_debug(LL_DEBUG5, "no, NULL address\n");
574
0
    return false;
575
0
  }
576
0
  if (!addr->mailbox)
577
0
  {
578
0
    mutt_debug(LL_DEBUG5, "no, no mailbox\n");
579
0
    return false;
580
0
  }
581
582
0
  if (mutt_istr_equal(buf_string(addr->mailbox), Username))
583
0
  {
584
0
    mutt_debug(LL_DEBUG5, "#1 yes, %s = %s\n", buf_string(addr->mailbox), Username);
585
0
    return true;
586
0
  }
587
0
  if (string_is_address(buf_string(addr->mailbox), Username, ShortHostname))
588
0
  {
589
0
    mutt_debug(LL_DEBUG5, "#2 yes, %s = %s @ %s\n", buf_string(addr->mailbox),
590
0
               Username, ShortHostname);
591
0
    return true;
592
0
  }
593
0
  const char *fqdn = mutt_fqdn(false, NeoMutt->sub);
594
0
  if (string_is_address(buf_string(addr->mailbox), Username, fqdn))
595
0
  {
596
0
    mutt_debug(LL_DEBUG5, "#3 yes, %s = %s @ %s\n", buf_string(addr->mailbox),
597
0
               Username, NONULL(fqdn));
598
0
    return true;
599
0
  }
600
0
  fqdn = mutt_fqdn(true, NeoMutt->sub);
601
0
  if (string_is_address(buf_string(addr->mailbox), Username, fqdn))
602
0
  {
603
0
    mutt_debug(LL_DEBUG5, "#4 yes, %s = %s @ %s\n", buf_string(addr->mailbox),
604
0
               Username, NONULL(fqdn));
605
0
    return true;
606
0
  }
607
608
0
  const struct Address *c_from = cs_subset_address(NeoMutt->sub, "from");
609
0
  if (c_from && mutt_istr_equal(buf_string(c_from->mailbox), buf_string(addr->mailbox)))
610
0
  {
611
0
    mutt_debug(LL_DEBUG5, "#5 yes, %s = %s\n", buf_string(addr->mailbox), c_from->mailbox);
612
0
    return true;
613
0
  }
614
615
0
  if (mutt_alternates_match(buf_string(addr->mailbox)))
616
0
    return true;
617
618
0
  mutt_debug(LL_DEBUG5, "no, all failed\n");
619
0
  return false;
620
0
}
621
622
/**
623
 * alias_new - Create a new Alias
624
 * @retval ptr Newly allocated Alias
625
 *
626
 * Free the result with alias_free()
627
 */
628
struct Alias *alias_new(void)
629
0
{
630
0
  struct Alias *a = mutt_mem_calloc(1, sizeof(struct Alias));
631
0
  TAILQ_INIT(&a->addr);
632
0
  return a;
633
0
}
634
635
/**
636
 * alias_free - Free an Alias
637
 * @param[out] ptr Alias to free
638
 */
639
void alias_free(struct Alias **ptr)
640
0
{
641
0
  if (!ptr || !*ptr)
642
0
    return;
643
644
0
  struct Alias *alias = *ptr;
645
646
0
  mutt_debug(LL_NOTIFY, "NT_ALIAS_DELETE: %s\n", alias->name);
647
0
  struct EventAlias ev_a = { alias };
648
0
  notify_send(NeoMutt->notify, NT_ALIAS, NT_ALIAS_DELETE, &ev_a);
649
650
0
  FREE(&alias->name);
651
0
  FREE(&alias->comment);
652
0
  mutt_addrlist_clear(&(alias->addr));
653
0
  FREE(ptr);
654
0
}
655
656
/**
657
 * aliaslist_free - Free a List of Aliases
658
 * @param al AliasList to free
659
 */
660
void aliaslist_free(struct AliasList *al)
661
0
{
662
0
  if (!al)
663
0
    return;
664
665
0
  struct Alias *np = NULL, *tmp = NULL;
666
0
  TAILQ_FOREACH_SAFE(np, al, entries, tmp)
667
0
  {
668
0
    TAILQ_REMOVE(al, np, entries);
669
0
    alias_free(&np);
670
0
  }
671
0
  TAILQ_INIT(al);
672
0
}
673
674
/**
675
 * alias_init - Set up the Alias globals
676
 */
677
void alias_init(void)
678
0
{
679
0
  alias_reverse_init();
680
0
}
681
682
/**
683
 * alias_shutdown - Clean up the Alias globals
684
 */
685
void alias_shutdown(void)
686
0
{
687
0
  struct Alias *np = NULL;
688
0
  TAILQ_FOREACH(np, &Aliases, entries)
689
0
  {
690
0
    alias_reverse_delete(np);
691
0
  }
692
0
  aliaslist_free(&Aliases);
693
0
  alias_reverse_shutdown();
694
0
}