NeoMutt  2021-02-05-666-ge300cd
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
45 static int check_pattern_list(const struct PatternList *patterns);
46 
47 // fwd-decl, mutually recursive: compile_search, compile_search_children
48 static bool compile_search(const struct ImapAccountData *adata,
49  const struct Pattern *pat, struct Buffer *buf);
50 
57 static bool check_pattern(const struct Pattern *pat)
58 {
59  switch (pat->op)
60  {
61  case MUTT_PAT_BODY:
62  case MUTT_PAT_HEADER:
63  case MUTT_PAT_WHOLE_MSG:
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 
83 static 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 
104 static 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 
143 static bool compile_search_self(const struct ImapAccountData *adata,
144  const struct Pattern *pat, struct Buffer *buf)
145 {
146  char term[256];
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;
178  case MUTT_PAT_WHOLE_MSG:
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 
209 static 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 
229 bool 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 
246  struct ImapAccountData *adata = imap_adata_get(m);
247  const bool ok = compile_search(adata, SLIST_FIRST(pat), &buf) &&
249 
250  FREE(&buf.data);
251  return ok;
252 }
253 
259 void 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) < 0)
270  continue;
271  e = mutt_hash_int_find(mdata->uid_hash, uid);
272  if (e)
273  e->matched = true;
274  }
275 }
struct Email ** emails
Array of Emails.
Definition: mailbox.h:99
struct PatternList * child
Arguments to logical operation.
Definition: lib.h:82
int msg_count
Total number of messages.
Definition: mailbox.h:91
static bool check_pattern(const struct Pattern *pat)
Check whether a pattern can be searched server-side.
Definition: search.c:57
Pattern matches email&#39;s body.
Definition: lib.h:143
The envelope/body of an email.
Definition: email.h:37
Structs that make up an email.
#define mutt_error(...)
Definition: logging.h:88
static bool compile_search_children(const struct ImapAccountData *adata, const struct Pattern *pat, struct Buffer *buf)
Compile a search command for a pattern&#39;s children.
Definition: search.c:104
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
union Pattern::@3 p
String manipulation buffer.
Definition: buffer.h:33
#define _(a)
Definition: message.h:28
struct Mailbox * mailbox
Current selected mailbox.
Definition: adata.h:76
Imap-specific Account data.
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:384
A simple (non-regex) pattern.
Definition: lib.h:68
Imap command executed or queued successfully.
Definition: private.h:84
Pattern matches email&#39;s header.
Definition: lib.h:144
Server-side pattern matches.
Definition: lib.h:163
Imap-specific Mailbox data.
char * imap_next_word(char *s)
Find where the next IMAP word begins.
Definition: util.c:792
bool imap_search(struct Mailbox *m, const struct PatternList *pat)
Find messages in mailbox matching a pattern.
Definition: search.c:229
Log at debug level 2.
Definition: logging.h:41
Convenience wrapper for the core headers.
#define SKIPWS(ch)
Definition: string2.h:46
bool pat_not
Pattern should be inverted (not)
Definition: lib.h:71
bool string_match
Check a string for a match.
Definition: lib.h:73
Shared constants/structs that are private to IMAP.
void * mdata
Driver specific data.
Definition: mailbox.h:136
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:1250
size_t mutt_buffer_addstr(struct Buffer *buf, const char *s)
Add a string to a Buffer.
Definition: buffer.c:225
struct HashTable * uid_hash
Definition: mdata.h:58
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
#define SLIST_FIRST(head)
Definition: queue.h:229
A mailbox.
Definition: mailbox.h:81
void * adata
Private data (for Mailbox backends)
Definition: account.h:43
Match patterns to emails.
char * data
Pointer to data.
Definition: buffer.h:35
int mutt_str_atoui(const char *str, unsigned int *dst)
Convert ASCII string to an unsigned integer.
Definition: string.c:282
short op
Operation, e.g. MUTT_PAT_SCORE.
Definition: lib.h:70
ImapCapFlags capabilities
Definition: adata.h:55
#define IMAP_CMD_NO_FLAGS
No flags are set.
Definition: private.h:73
size_t mutt_buffer_addch(struct Buffer *buf, char c)
Add a single character to a Buffer.
Definition: buffer.c:240
struct ImapAccountData * imap_adata_get(struct Mailbox *m)
Get the Account data for this mailbox.
Definition: adata.c:90
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
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
IMAP-specific Account data -.
Definition: adata.h:39
IMAP-specific Mailbox data -.
Definition: mdata.h:38
char * str
String, if string_match is set.
Definition: lib.h:86
Either pattern can match.
Definition: lib.h:127
#define FREE(x)
Definition: memory.h:40
#define SLIST_FOREACH(var, head, field)
Definition: queue.h:231
Pattern matches raw email text.
Definition: lib.h:146
#define IMAP_CAP_X_GM_EXT_1
https://developers.google.com/gmail/imap/imap-extensions
Definition: private.h:141
struct Buffer * mutt_buffer_init(struct Buffer *buf)
Initialise a new Buffer.
Definition: buffer.c:46
Convenience wrapper for the library headers.
void imap_quote_string(char *dest, size_t dlen, const char *src, bool quote_backtick)
quote string according to IMAP rules
Definition: util.c:840
void cmd_parse_search(struct ImapAccountData *adata, const char *s)
store SEARCH response for later use
Definition: search.c:259
bool matched
Search matches this Email.
Definition: email.h:68