1 /**
2  * @file
3  * Subject Regex 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_subjrx Subject Regex handling
25  *
26  * Subject Regex handling
27  */
28 
29 #include "config.h"
30 #include <stddef.h>
31 #include <stdint.h>
32 #include "mutt/lib.h"
33 #include "email/lib.h"
34 #include "core/lib.h"
35 #include "mutt.h"
36 #include "subjectrx.h"
37 #include "init.h"
38 
39 static struct ReplaceList SubjectRegexList = STAILQ_HEAD_INITIALIZER(SubjectRegexList);
40 static struct Notify *SubjRxNotify = NULL;
41 
42 /**
43  * subjrx_free - Free the Subject Regex List
44  */
subjrx_free(void)45 void subjrx_free(void)
46 {
47   notify_free(&SubjRxNotify);
48   mutt_replacelist_free(&SubjectRegexList);
49 }
50 
51 /**
52  * subjrx_init - Create new Subject Regex List
53  */
subjrx_init(void)54 void subjrx_init(void)
55 {
56   if (SubjRxNotify)
57     return;
58 
59   SubjRxNotify = notify_new();
60   notify_set_parent(SubjRxNotify, NeoMutt->notify);
61 }
62 
63 /**
64  * parse_unreplace_list - Remove a string replacement rule - Implements Command::parse() - @ingroup command_parse
65  */
parse_unreplace_list(struct Buffer * buf,struct Buffer * s,struct ReplaceList * list,struct Buffer * err)66 static enum CommandResult parse_unreplace_list(struct Buffer *buf, struct Buffer *s,
67                                                struct ReplaceList *list, struct Buffer *err)
68 {
69   /* First token is a regex. */
70   if (!MoreArgs(s))
71   {
72     mutt_buffer_printf(err, _("%s: too few arguments"), "unsubjectrx");
73     return MUTT_CMD_WARNING;
74   }
75 
76   mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
77 
78   /* "*" is a special case. */
79   if (mutt_str_equal(buf->data, "*"))
80   {
81     mutt_replacelist_free(list);
82     return MUTT_CMD_SUCCESS;
83   }
84 
85   mutt_replacelist_remove(list, buf->data);
86   return MUTT_CMD_SUCCESS;
87 }
88 
89 /**
90  * parse_replace_list - Parse a string replacement rule - Implements Command::parse() - @ingroup command_parse
91  */
parse_replace_list(struct Buffer * buf,struct Buffer * s,struct ReplaceList * list,struct Buffer * err)92 static enum CommandResult parse_replace_list(struct Buffer *buf, struct Buffer *s,
93                                              struct ReplaceList *list, struct Buffer *err)
94 {
95   struct Buffer templ = mutt_buffer_make(0);
96 
97   /* First token is a regex. */
98   if (!MoreArgs(s))
99   {
100     mutt_buffer_printf(err, _("%s: too few arguments"), "subjectrx");
101     return MUTT_CMD_WARNING;
102   }
103   mutt_extract_token(buf, s, MUTT_TOKEN_NO_FLAGS);
104 
105   /* Second token is a replacement template */
106   if (!MoreArgs(s))
107   {
108     mutt_buffer_printf(err, _("%s: too few arguments"), "subjectrx");
109     return MUTT_CMD_WARNING;
110   }
111   mutt_extract_token(&templ, s, MUTT_TOKEN_NO_FLAGS);
112 
113   if (mutt_replacelist_add(list, buf->data, templ.data, err) != 0)
114   {
115     FREE(&templ.data);
116     return MUTT_CMD_ERROR;
117   }
118   FREE(&templ.data);
119 
120   return MUTT_CMD_SUCCESS;
121 }
122 
123 /**
124  * subjrx_apply_mods - Apply regex modifications to the subject
125  * @param env Envelope of Email
126  * @retval true Subject modified
127  */
subjrx_apply_mods(struct Envelope * env)128 bool subjrx_apply_mods(struct Envelope *env)
129 {
130   if (!env || !env->subject || (*env->subject == '\0'))
131     return false;
132 
133   if (env->disp_subj)
134     return true;
135 
136   if (STAILQ_EMPTY(&SubjectRegexList))
137     return false;
138 
139   env->disp_subj = mutt_replacelist_apply(&SubjectRegexList, NULL, 0, env->subject);
140   return true;
141 }
142 
143 /**
144  * subjrx_clear_mods - Clear out all modified email subjects
145  */
subjrx_clear_mods(struct Mailbox * m)146 void subjrx_clear_mods(struct Mailbox *m)
147 {
148   if (!m)
149     return;
150 
151   for (int i = 0; i < m->msg_count; i++)
152   {
153     struct Email *e = m->emails[i];
154     if (!e || !e->env)
155       continue;
156     FREE(&e->env->disp_subj);
157   }
158 }
159 
160 /**
161  * parse_subjectrx_list - Parse the 'subjectrx' command - Implements Command::parse() - @ingroup command_parse
162  */
parse_subjectrx_list(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)163 enum CommandResult parse_subjectrx_list(struct Buffer *buf, struct Buffer *s,
164                                         intptr_t data, struct Buffer *err)
165 {
166   enum CommandResult rc;
167 
168   rc = parse_replace_list(buf, s, &SubjectRegexList, err);
169   if (rc == MUTT_CMD_SUCCESS)
170   {
171     mutt_debug(LL_NOTIFY, "NT_SUBJRX_ADD: %s\n", buf->data);
172     notify_send(SubjRxNotify, NT_SUBJRX, NT_SUBJRX_ADD, NULL);
173   }
174   return rc;
175 }
176 
177 /**
178  * parse_unsubjectrx_list - Parse the 'unsubjectrx' command - Implements Command::parse() - @ingroup command_parse
179  */
parse_unsubjectrx_list(struct Buffer * buf,struct Buffer * s,intptr_t data,struct Buffer * err)180 enum CommandResult parse_unsubjectrx_list(struct Buffer *buf, struct Buffer *s,
181                                           intptr_t data, struct Buffer *err)
182 {
183   enum CommandResult rc;
184 
185   rc = parse_unreplace_list(buf, s, &SubjectRegexList, err);
186   if (rc == MUTT_CMD_SUCCESS)
187   {
188     mutt_debug(LL_NOTIFY, "NT_SUBJRX_DELETE: %s\n", buf->data);
189     notify_send(SubjRxNotify, NT_SUBJRX, NT_SUBJRX_DELETE, NULL);
190   }
191   return rc;
192 }
193