1 /**
2  * @file
3  * Alternate address handling
4  *
5  * @authors
6  * Copyright (C) 2021 Richard Russon <rich@flatcap.org>
7  *
8  * @copyright
9  * This program is free software: you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation, either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /**
24  * @page neo_alternates Alternate address handling
25  *
26  * Alternate address handling
27  */
28 
29 #include "config.h"
30 #include <stdbool.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include "mutt/lib.h"
34 #include "address/lib.h"
35 #include "email/lib.h"
36 #include "core/lib.h"
37 #include "mutt.h"
38 #include "alternates.h"
39 #include "command_parse.h"
40 #include "init.h"
41 
42 struct RegexList Alternates = STAILQ_HEAD_INITIALIZER(Alternates); ///< List of regexes to match the user's alternate email addresses
43 struct RegexList UnAlternates = STAILQ_HEAD_INITIALIZER(UnAlternates); ///< List of regexes to blacklist false matches in Alternates
44 static struct Notify *AlternatesNotify = NULL;
45 
46 /**
47  * alternates_free - Free the alternates lists
48  */
alternates_free(void)49 void alternates_free(void)
50 {
51   notify_free(&AlternatesNotify);
52 
53   mutt_regexlist_free(&Alternates);
54   mutt_regexlist_free(&UnAlternates);
55 }
56 
57 /**
58  * alternates_init - Set up the alternates lists
59  */
alternates_init(void)60 void alternates_init(void)
61 {
62   if (AlternatesNotify)
63     return;
64 
65   AlternatesNotify = notify_new();
66   notify_set_parent(AlternatesNotify, NeoMutt->notify);
67 }
68 
69 /**
70  * mutt_alternates_reset - Clear the recipient valid flag of all emails
71  */
mutt_alternates_reset(struct Mailbox * m)72 void mutt_alternates_reset(struct Mailbox *m)
73 {
74   if (!m)
75     return;
76 
77   for (int i = 0; i < m->msg_count; i++)
78   {
79     struct Email *e = m->emails[i];
80     if (!e)
81       break;
82     e->recip_valid = false;
83   }
84 }
85 
86 /**
87  * parse_alternates - Parse the 'alternates' command - Implements Command::parse() - @ingroup command_parse
88  */
parse_alternates(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)89 enum CommandResult parse_alternates(struct Buffer *buf, struct Buffer *s,
90                                     intptr_t data, struct Buffer *err)
91 {
92   struct GroupList gl = STAILQ_HEAD_INITIALIZER(gl);
93 
94   do
95   {
96     mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
97 
98     if (parse_grouplist(&gl, buf, s, err) == -1)
99       goto bail;
100 
101     mutt_regexlist_remove(&UnAlternates, buf->data);
102 
103     if (mutt_regexlist_add(&Alternates, buf->data, REG_ICASE, err) != 0)
104       goto bail;
105 
106     if (mutt_grouplist_add_regex(&gl, buf->data, REG_ICASE, err) != 0)
107       goto bail;
108   } while (MoreArgs(s));
109 
110   mutt_grouplist_destroy(&gl);
111 
112   mutt_debug(LL_NOTIFY, "NT_ALTERN_ADD: %s\n", buf->data);
113   notify_send(AlternatesNotify, NT_ALTERN, NT_ALTERN_ADD, NULL);
114 
115   return MUTT_CMD_SUCCESS;
116 
117 bail:
118   mutt_grouplist_destroy(&gl);
119   return MUTT_CMD_ERROR;
120 }
121 
122 /**
123  * parse_unalternates - Parse the 'unalternates' command - Implements Command::parse() - @ingroup command_parse
124  */
parse_unalternates(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)125 enum CommandResult parse_unalternates(struct Buffer *buf, struct Buffer *s,
126                                       intptr_t data, struct Buffer *err)
127 {
128   do
129   {
130     mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
131     mutt_regexlist_remove(&Alternates, buf->data);
132 
133     if (!mutt_str_equal(buf->data, "*") &&
134         (mutt_regexlist_add(&UnAlternates, buf->data, REG_ICASE, err) != 0))
135     {
136       return MUTT_CMD_ERROR;
137     }
138 
139   } while (MoreArgs(s));
140 
141   mutt_debug(LL_NOTIFY, "NT_ALTERN_DELETE: %s\n", buf->data);
142   notify_send(AlternatesNotify, NT_ALTERN, NT_ALTERN_DELETE, NULL);
143 
144   return MUTT_CMD_SUCCESS;
145 }
146 
147 /**
148  * mutt_alternates_match - Compare an Address to the Un/Alternates lists
149  * @param addr Address to check
150  * @retval true Address matches
151  */
mutt_alternates_match(const char * addr)152 bool mutt_alternates_match(const char *addr)
153 {
154   if (!addr)
155     return false;
156 
157   if (mutt_regexlist_match(&Alternates, addr))
158   {
159     mutt_debug(LL_DEBUG5, "yes, %s matched by alternates\n", addr);
160     if (mutt_regexlist_match(&UnAlternates, addr))
161       mutt_debug(LL_DEBUG5, "but, %s matched by unalternates\n", addr);
162     else
163       return true;
164   }
165 
166   return false;
167 }
168