Coverage Report

Created: 2023-09-25 07:17

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