NeoMutt  2024-10-02-37-gfa9146
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
search.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <stdbool.h>
32#include <string.h>
33#include "private.h"
34#include "mutt/lib.h"
35#include "email/lib.h"
36#include "core/lib.h"
37#include "lib.h"
38#include "pattern/lib.h"
39#include "adata.h"
40#include "mdata.h"
41
42// fwd decl, mutually recursive: check_pattern_list, check_pattern
43static int check_pattern_list(const struct PatternList *patterns);
44
45// fwd-decl, mutually recursive: compile_search, compile_search_children
46static bool compile_search(const struct ImapAccountData *adata,
47 const struct Pattern *pat, struct Buffer *buf);
48
55static bool check_pattern(const struct Pattern *pat)
56{
57 switch (pat->op)
58 {
59 case MUTT_PAT_BODY:
60 case MUTT_PAT_HEADER:
62 if (pat->string_match)
63 return true;
64 break;
66 return true;
67 break;
68 default:
69 if (pat->child && check_pattern_list(pat->child))
70 return true;
71 break;
72 }
73 return false;
74}
75
81static int check_pattern_list(const struct PatternList *patterns)
82{
83 int positives = 0;
84
85 const struct Pattern *pat = NULL;
86 SLIST_FOREACH(pat, patterns, entries)
87 {
88 positives += check_pattern(pat);
89 }
90
91 return positives;
92}
93
102static bool compile_search_children(const struct ImapAccountData *adata,
103 const struct Pattern *pat, struct Buffer *buf)
104{
105 int clauses = check_pattern_list(pat->child);
106 if (clauses == 0)
107 return true;
108
109 buf_addch(buf, '(');
110
111 struct Pattern *c;
112 SLIST_FOREACH(c, pat->child, entries)
113 {
114 if (!check_pattern(c))
115 continue;
116
117 if ((pat->op == MUTT_PAT_OR) && (clauses > 1))
118 buf_addstr(buf, "OR ");
119
120 if (!compile_search(adata, c, buf))
121 return false;
122
123 if (clauses > 1)
124 buf_addch(buf, ' ');
125
126 clauses--;
127 }
128
129 buf_addch(buf, ')');
130 return true;
131}
132
141static bool compile_search_self(const struct ImapAccountData *adata,
142 const struct Pattern *pat, struct Buffer *buf)
143{
144 char term[256] = { 0 };
145 char *delim = NULL;
146
147 switch (pat->op)
148 {
149 case MUTT_PAT_HEADER:
150 buf_addstr(buf, "HEADER ");
151
152 /* extract header name */
153 delim = strchr(pat->p.str, ':');
154 if (!delim)
155 {
156 mutt_error(_("Header search without header name: %s"), pat->p.str);
157 return false;
158 }
159 *delim = '\0';
160 imap_quote_string(term, sizeof(term), pat->p.str, false);
161 buf_addstr(buf, term);
162 buf_addch(buf, ' ');
163
164 /* and field */
165 *delim = ':';
166 delim++;
167 SKIPWS(delim);
168 imap_quote_string(term, sizeof(term), delim, false);
169 buf_addstr(buf, term);
170 break;
171 case MUTT_PAT_BODY:
172 buf_addstr(buf, "BODY ");
173 imap_quote_string(term, sizeof(term), pat->p.str, false);
174 buf_addstr(buf, term);
175 break;
177 buf_addstr(buf, "TEXT ");
178 imap_quote_string(term, sizeof(term), pat->p.str, false);
179 buf_addstr(buf, term);
180 break;
182 if (!(adata->capabilities & IMAP_CAP_X_GM_EXT_1))
183 {
184 mutt_error(_("Server-side custom search not supported: %s"), pat->p.str);
185 return false;
186 }
187 buf_addstr(buf, "X-GM-RAW ");
188 imap_quote_string(term, sizeof(term), pat->p.str, false);
189 buf_addstr(buf, term);
190 break;
191 }
192 return true;
193}
194
207static bool compile_search(const struct ImapAccountData *adata,
208 const struct Pattern *pat, struct Buffer *buf)
209{
210 if (!check_pattern(pat))
211 return true;
212
213 if (pat->pat_not)
214 buf_addstr(buf, "NOT ");
215
216 return pat->child ? compile_search_children(adata, pat, buf) :
217 compile_search_self(adata, pat, buf);
218}
219
227bool imap_search(struct Mailbox *m, const struct PatternList *pat)
228{
229 for (int i = 0; i < m->msg_count; i++)
230 {
231 struct Email *e = m->emails[i];
232 if (!e)
233 break;
234 e->matched = false;
235 }
236
237 if (check_pattern_list(pat) == 0)
238 return true;
239
240 struct Buffer *buf = buf_pool_get();
241 buf_addstr(buf, "UID SEARCH ");
242
244 const bool ok = compile_search(adata, SLIST_FIRST(pat), buf) &&
246
248 return ok;
249}
250
256void cmd_parse_search(struct ImapAccountData *adata, const char *s)
257{
258 unsigned int uid;
259 struct Email *e = NULL;
260 struct ImapMboxData *mdata = adata->mailbox->mdata;
261
262 mutt_debug(LL_DEBUG2, "Handling SEARCH\n");
263
264 while ((s = imap_next_word((char *) s)) && (*s != '\0'))
265 {
266 if (!mutt_str_atoui(s, &uid))
267 continue;
268 e = mutt_hash_int_find(mdata->uid_hash, uid);
269 if (e)
270 e->matched = true;
271 }
272}
const char * mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: atoi.c:214
size_t buf_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:241
size_t buf_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:226
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
Convenience wrapper for the core headers.
Structs that make up an email.
#define mutt_error(...)
Definition: logging2.h:92
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
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:123
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:1304
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:71
#define IMAP_CAP_X_GM_EXT_1
https://developers.google.com/gmail/imap/imap-extensions
Definition: private.h:140
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
Quote string according to IMAP rules.
Definition: util.c:887
@ IMAP_EXEC_SUCCESS
Imap command executed or queued successfully.
Definition: private.h:82
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:825
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
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:155
@ MUTT_PAT_WHOLE_MSG
Pattern matches raw email text.
Definition: lib.h:157
@ MUTT_PAT_OR
Either pattern can match.
Definition: lib.h:137
@ MUTT_PAT_SERVERSEARCH
Server-side pattern matches.
Definition: lib.h:174
@ MUTT_PAT_BODY
Pattern matches email's body.
Definition: lib.h:154
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
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:207
static int check_pattern_list(const struct PatternList *patterns)
Check how many patterns in a list can be searched server-side.
Definition: search.c:81
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:102
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:141
static bool check_pattern(const struct Pattern *pat)
Check whether a pattern can be searched server-side.
Definition: search.c:55
bool imap_search(struct Mailbox *m, const struct PatternList *pat)
Find messages in mailbox matching a pattern.
Definition: search.c:227
void cmd_parse_search(struct ImapAccountData *adata, const char *s)
Store SEARCH response for later use.
Definition: search.c:256
GUI display the mailboxes in a side panel.
Key value store.
#define SKIPWS(ch)
Definition: string2.h:45
void * adata
Private data (for Mailbox backends)
Definition: account.h:42
String manipulation buffer.
Definition: buffer.h:36
The envelope/body of an email.
Definition: email.h:39
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:40
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:76
union Pattern::@1 p
struct PatternList * child
Arguments to logical operation.
Definition: lib.h:89
bool string_match
Check a string for a match.
Definition: lib.h:80
char * str
String, if string_match is set.
Definition: lib.h:93
short op
Operation, e.g. MUTT_PAT_SCORE.
Definition: lib.h:77
bool pat_not
Pattern should be inverted (not)
Definition: lib.h:78