NeoMutt  2022-04-29-215-gc12b98
Teaching an old dog new tricks
DOXYGEN
search.c
Go to the documentation of this file.
1
32#include "config.h"
33#include <stdbool.h>
34#include <string.h>
35#include "private.h"
36#include "mutt/lib.h"
37#include "email/lib.h"
38#include "core/lib.h"
39#include "lib.h"
40#include "pattern/lib.h"
41#include "adata.h"
42#include "mdata.h"
43
44// fwd decl, mutually recursive: check_pattern_list, check_pattern
45static int check_pattern_list(const struct PatternList *patterns);
46
47// fwd-decl, mutually recursive: compile_search, compile_search_children
48static bool compile_search(const struct ImapAccountData *adata,
49 const struct Pattern *pat, struct Buffer *buf);
50
57static bool check_pattern(const struct Pattern *pat)
58{
59 switch (pat->op)
60 {
61 case MUTT_PAT_BODY:
62 case MUTT_PAT_HEADER:
64 if (pat->string_match)
65 return true;
66 break;
68 return true;
69 break;
70 default:
71 if (pat->child && check_pattern_list(pat->child))
72 return true;
73 break;
74 }
75 return false;
76}
77
83static int check_pattern_list(const struct PatternList *patterns)
84{
85 int positives = 0;
86
87 const struct Pattern *pat = NULL;
88 SLIST_FOREACH(pat, patterns, entries)
89 {
90 positives += check_pattern(pat);
91 }
92
93 return positives;
94}
95
104static bool compile_search_children(const struct ImapAccountData *adata,
105 const struct Pattern *pat, struct Buffer *buf)
106{
107 int clauses = check_pattern_list(pat->child);
108 if (clauses == 0)
109 return true;
110
111 mutt_buffer_addch(buf, '(');
112
113 struct Pattern *c;
114 SLIST_FOREACH(c, pat->child, entries)
115 {
116 if (!check_pattern(c))
117 continue;
118
119 if ((pat->op == MUTT_PAT_OR) && (clauses > 1))
120 mutt_buffer_addstr(buf, "OR ");
121
122 if (!compile_search(adata, c, buf))
123 return false;
124
125 if (clauses > 1)
126 mutt_buffer_addch(buf, ' ');
127
128 clauses--;
129 }
130
131 mutt_buffer_addch(buf, ')');
132 return true;
133}
134
143static bool compile_search_self(const struct ImapAccountData *adata,
144 const struct Pattern *pat, struct Buffer *buf)
145{
146 char term[256] = { 0 };
147 char *delim = NULL;
148
149 switch (pat->op)
150 {
151 case MUTT_PAT_HEADER:
152 mutt_buffer_addstr(buf, "HEADER ");
153
154 /* extract header name */
155 delim = strchr(pat->p.str, ':');
156 if (!delim)
157 {
158 mutt_error(_("Header search without header name: %s"), pat->p.str);
159 return false;
160 }
161 *delim = '\0';
162 imap_quote_string(term, sizeof(term), pat->p.str, false);
163 mutt_buffer_addstr(buf, term);
164 mutt_buffer_addch(buf, ' ');
165
166 /* and field */
167 *delim = ':';
168 delim++;
169 SKIPWS(delim);
170 imap_quote_string(term, sizeof(term), delim, false);
171 mutt_buffer_addstr(buf, term);
172 break;
173 case MUTT_PAT_BODY:
174 mutt_buffer_addstr(buf, "BODY ");
175 imap_quote_string(term, sizeof(term), pat->p.str, false);
176 mutt_buffer_addstr(buf, term);
177 break;
179 mutt_buffer_addstr(buf, "TEXT ");
180 imap_quote_string(term, sizeof(term), pat->p.str, false);
181 mutt_buffer_addstr(buf, term);
182 break;
184 if (!(adata->capabilities & IMAP_CAP_X_GM_EXT_1))
185 {
186 mutt_error(_("Server-side custom search not supported: %s"), pat->p.str);
187 return false;
188 }
189 mutt_buffer_addstr(buf, "X-GM-RAW ");
190 imap_quote_string(term, sizeof(term), pat->p.str, false);
191 mutt_buffer_addstr(buf, term);
192 break;
193 }
194 return true;
195}
196
209static bool compile_search(const struct ImapAccountData *adata,
210 const struct Pattern *pat, struct Buffer *buf)
211{
212 if (!check_pattern(pat))
213 return true;
214
215 if (pat->pat_not)
216 mutt_buffer_addstr(buf, "NOT ");
217
218 return pat->child ? compile_search_children(adata, pat, buf) :
219 compile_search_self(adata, pat, buf);
220}
221
229bool imap_search(struct Mailbox *m, const struct PatternList *pat)
230{
231 for (int i = 0; i < m->msg_count; i++)
232 {
233 struct Email *e = m->emails[i];
234 if (!e)
235 break;
236 e->matched = false;
237 }
238
239 if (check_pattern_list(pat) == 0)
240 return true;
241
242 struct Buffer buf;
243 mutt_buffer_init(&buf);
244 mutt_buffer_addstr(&buf, "UID SEARCH ");
245
247 const bool ok = compile_search(adata, SLIST_FIRST(pat), &buf) &&
249
250 FREE(&buf.data);
251 return ok;
252}
253
259void cmd_parse_search(struct ImapAccountData *adata, const char *s)
260{
261 unsigned int uid;
262 struct Email *e = NULL;
263 struct ImapMboxData *mdata = adata->mailbox->mdata;
264
265 mutt_debug(LL_DEBUG2, "Handling SEARCH\n");
266
267 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
268 {
269 if (!mutt_str_atoui(s, &uid))
270 continue;
271 e = mutt_hash_int_find(mdata->uid_hash, uid);
272 if (e)
273 e->matched = true;
274 }
275}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:202
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:52
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:248
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:233
Convenience wrapper for the core headers.
Structs that make up an email.
#define mutt_error(...)
Definition: logging.h:87
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
void * mutt_hash_int_find(const struct HashTable *table, unsigned int intkey)
Find the HashElem data in a Hash Table element using a key.
Definition: hash.c:392
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:89
int imap_exec(struct ImapAccountData *adata, const char *cmdstr, ImapCmdFlags flags)
Execute a command and wait for the response from the server.
Definition: command.c:1247
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
#define IMAP_CAP_X_GM_EXT_1
https://developers.google.com/gmail/imap/imap-extensions
Definition: private.h:142
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:837
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:84
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:789
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
#define FREE(x)
Definition: memory.h:43
Convenience wrapper for the library headers.
#define _(a)
Definition: message.h:28
Notmuch-specific Mailbox data.
Match patterns to emails.
@ MUTT_PAT_HEADER
Pattern matches email's header.
Definition: lib.h:148
@ MUTT_PAT_WHOLE_MSG
Pattern matches raw email text.
Definition: lib.h:150
@ MUTT_PAT_OR
Either pattern can match.
Definition: lib.h:131
@ MUTT_PAT_SERVERSEARCH
Server-side pattern matches.
Definition: lib.h:167
@ MUTT_PAT_BODY
Pattern matches email's body.
Definition: lib.h:147
Pop-specific Account data.
#define SLIST_FOREACH(var, head, field)
Definition: queue.h:231
#define SLIST_FIRST(head)
Definition: queue.h:229
static bool compile_search(const struct ImapAccountData *adata, const struct Pattern *pat, struct Buffer *buf)
Convert NeoMutt pattern to IMAP search.
Definition: search.c:209
static int check_pattern_list(const struct PatternList *patterns)
Check how many patterns in a list can be searched server-side.
Definition: search.c:83
static bool compile_search_children(const struct ImapAccountData *adata, const struct Pattern *pat, struct Buffer *buf)
Compile a search command for a pattern's children.
Definition: search.c:104
static bool compile_search_self(const struct ImapAccountData *adata, const struct Pattern *pat, struct Buffer *buf)
Compile a search command for a pattern.
Definition: search.c:143
static bool check_pattern(const struct Pattern *pat)
Check whether a pattern can be searched server-side.
Definition: search.c:57
bool imap_search(struct Mailbox *m, const struct PatternList *pat)
Find messages in mailbox matching a pattern.
Definition: search.c:229
void cmd_parse_search(struct ImapAccountData *adata, const char *s)
Store SEARCH response for later use.
Definition: search.c:259
GUI display the mailboxes in a side panel.
Key value store.
#define SKIPWS(ch)
Definition: string2.h:46
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
String manipulation buffer.
Definition: buffer.h:34
The envelope/body of an email.
Definition: email.h:37
bool matched
Search matches this Email.
Definition: email.h:102
IMAP-specific Account data -.
Definition: adata.h:40
ImapCapFlags capabilities
Capability flags.
Definition: adata.h:55
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
char * buf
Definition: adata.h:59
IMAP-specific Mailbox data -.
Definition: mdata.h:39
A mailbox.
Definition: mailbox.h:79
int msg_count
Total number of messages.
Definition: mailbox.h:88
void * mdata
Driver specific data.
Definition: mailbox.h:132
struct Email ** emails
Array of Emails.
Definition: mailbox.h:96
A simple (non-regex) pattern.
Definition: lib.h:70
union Pattern::@1 p
struct PatternList * child
Arguments to logical operation.
Definition: lib.h:83
bool string_match
Check a string for a match.
Definition: lib.h:74
char * str
String, if string_match is set.
Definition: lib.h:87
short op
Operation, e.g. MUTT_PAT_SCORE.
Definition: lib.h:71
bool pat_not
Pattern should be inverted (not)
Definition: lib.h:72