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