1 #ifndef MAIL_SEARCH_H
2 #define MAIL_SEARCH_H
3 
4 #include "seq-range-array.h"
5 #include "mail-types.h"
6 #include "mail-thread.h"
7 
8 struct mail_search_mime_part;
9 
10 enum mail_search_arg_type {
11 	SEARCH_OR,
12 	SEARCH_SUB,
13 
14 	/* sequence sets */
15 	SEARCH_ALL,
16 	SEARCH_SEQSET,
17 	SEARCH_UIDSET,
18 
19 	/* flags */
20 	SEARCH_FLAGS,
21 	SEARCH_KEYWORDS,
22 
23 	/* dates (date_type required) */
24 	SEARCH_BEFORE,
25 	SEARCH_ON, /* time must point to beginning of the day */
26 	SEARCH_SINCE,
27 
28 	/* sizes */
29 	SEARCH_SMALLER,
30 	SEARCH_LARGER,
31 
32 	/* headers */
33 	SEARCH_HEADER,
34 	SEARCH_HEADER_ADDRESS,
35 	SEARCH_HEADER_COMPRESS_LWSP,
36 
37 	/* body */
38 	SEARCH_BODY,
39 	SEARCH_TEXT,
40 
41 	/* extensions */
42 	SEARCH_MODSEQ,
43 	SEARCH_SAVEDATESUPPORTED,
44 	SEARCH_INTHREAD,
45 	SEARCH_GUID,
46 	SEARCH_MAILBOX,
47 	SEARCH_MAILBOX_GUID,
48 	SEARCH_MAILBOX_GLOB,
49 	SEARCH_REAL_UID,
50 	SEARCH_MIMEPART
51 };
52 
53 enum mail_search_date_type {
54 	MAIL_SEARCH_DATE_TYPE_SENT = 1,
55 	MAIL_SEARCH_DATE_TYPE_RECEIVED,
56 	MAIL_SEARCH_DATE_TYPE_SAVED
57 };
58 
59 enum mail_search_arg_flag {
60 	/* Used by *BEFORE/SINCE/ON searches.
61 
62 	   When NOT set: Adjust search timestamps so that the email's timezone
63 	   is included in the comparisons. For example
64 	   "04-Nov-2016 00:00:00 +0200" would match 4th day. This allows
65 	   searching for mails with dates from the email sender's point of
66 	   view. For received/saved dates there is no known timezone, and
67 	   without this flag the dates are compared using the server's local
68 	   timezone.
69 
70 	   When set: Compare the timestamp as UTC. For example
71 	   "04-Nov-2016 00:00:00 +0200" would be treated as
72 	   "03-Nov-2016 22:00:00 UTC" and would match 3rd day. This allows
73 	   searching for mails within precise time interval. Since imap-dates
74 	   don't allow specifying timezone this isn't really possible with IMAP
75 	   protocol, except using OLDER/YOUNGER searches. */
76 	MAIL_SEARCH_ARG_FLAG_UTC_TIMES	= 0x01,
77 };
78 
79 enum mail_search_modseq_type {
80 	MAIL_SEARCH_MODSEQ_TYPE_ANY = 0,
81 	MAIL_SEARCH_MODSEQ_TYPE_PRIVATE,
82 	MAIL_SEARCH_MODSEQ_TYPE_SHARED
83 };
84 
85 struct mail_search_modseq {
86 	uint64_t modseq;
87 	enum mail_search_modseq_type type;
88 };
89 
90 struct mail_search_arg {
91 	/* NOTE: when adding new fields, make sure mail_search_arg_dup_one()
92 	   and mail_search_arg_one_equals() are updated. */
93 	struct mail_search_arg *next;
94 
95 	enum mail_search_arg_type type;
96 	struct {
97 		struct mail_search_arg *subargs;
98 		ARRAY_TYPE(seq_range) seqset;
99 		const char *str;
100 		time_t time;
101 		uoff_t size;
102 		enum mail_flags flags;
103 		enum mail_search_arg_flag search_flags;
104 		enum mail_search_date_type date_type;
105 		enum mail_thread_type thread_type;
106 		struct mail_search_modseq *modseq;
107 		struct mail_search_result *search_result;
108 		struct mail_search_mime_part *mime_part;
109 	} value;
110 	/* set by mail_search_args_init(): */
111 	struct {
112 		struct mail_search_args *search_args;
113 		/* Note that initialized keywords may be empty if the keyword
114 		   wasn't valid in this mailbox. */
115 		struct mail_keywords *keywords;
116 		struct imap_match_glob *mailbox_glob;
117 	} initialized;
118 
119         void *context;
120 	const char *hdr_field_name; /* for SEARCH_HEADER* */
121 	bool match_not:1; /* result = !result */
122 	bool match_always:1; /* result = 1 always */
123 	bool nonmatch_always:1; /* result = 0 always */
124 	bool fuzzy:1; /* use fuzzy matching for this arg */
125 	bool no_fts:1; /* do NOT call FTS */
126 
127 	int result; /* -1 = unknown, 0 = unmatched, 1 = matched */
128 };
129 
130 struct mail_search_args {
131 	/* There are two types of refcount:
132 
133 	1) The normal refcount tracks the lifetime of the struct itself.
134 	This allows using the same args for multiple search queries, even
135 	across different mailboxes.
136 
137 	2) The init_refcount tracks how many times mail_search_args_init() has
138 	been called. This can happen when the same mail_search_args have been
139 	shared by referencing them in different parts of the code. Only after
140 	each one of them has called mail_search_args_deinit() the init_refcount
141 	drops to 0 and it can really be deinitialized.
142 
143 	Note that all of the inits must be within the same mailbox - attempting
144 	to init the same args in different mailboxes at the same time will
145 	result in assert-crash. */
146 	int refcount, init_refcount;
147 
148 	pool_t pool;
149 	struct mailbox *box;
150 	struct mail_search_arg *args;
151 
152 	bool simplified:1;
153 	bool have_inthreads:1;
154 	/* Stop mail_search_next() when finding a non-matching mail.
155 	   (Could be useful when wanting to find only the oldest mails.) */
156 	bool stop_on_nonmatch:1;
157 	/* fts plugin has already expanded the search args - no need to do
158 	   it again. */
159 	bool fts_expanded:1;
160 };
161 
162 #define ARG_SET_RESULT(arg, res) \
163 	STMT_START { \
164 		(arg)->result = !(arg)->match_not ? (res) : \
165 			((res) == -1 ? -1 : ((res) == 0 ? 1 : 0)); \
166 	} STMT_END
167 
168 typedef void mail_search_foreach_callback_t(struct mail_search_arg *arg,
169 					    void *context);
170 
171 /* Fully initialize and optimize the args for searching within the specified
172    mailbox. This should always be called before the args are actually used
173    for searching. After search is finished, the args must be deinitialized.
174    It's possible to initialize the same args multiple times, as long as it's
175    done within the same mailbox. This would allow multiple concurrent searches
176    to be done within the shared search args.
177 
178    This will implicitly call mail_search_args_simplify() if it wasn't called
179    yet. It also allocates any necessary per-mailbox data like keywords.
180 
181    If change_sets is TRUE, change uidsets to seqsets and convert "*" in seqsets
182    to the current highest message sequence. */
183 void mail_search_args_init(struct mail_search_args *args,
184 			   struct mailbox *box, bool change_sets,
185 			   const ARRAY_TYPE(seq_range) *search_saved_uidset)
186 	ATTR_NULL(4);
187 /* Initialize arg and its children. args is used for getting mailbox and
188    pool. */
189 void mail_search_arg_init(struct mail_search_args *args,
190 			  struct mail_search_arg *arg);
191 /* Free memory allocated by mail_search_args_init(). The args can initialized
192    afterwards again if needed. The args can be reused for other queries after
193    calling this. */
194 void mail_search_args_deinit(struct mail_search_args *args);
195 /* Free arg and its siblings and children. */
196 void mail_search_arg_deinit(struct mail_search_arg *arg);
197 /* Free arg and its children, but not its siblings. */
198 void mail_search_arg_one_deinit(struct mail_search_arg *arg);
199 /* Convert sequence sets in args to UIDs. */
200 void mail_search_args_seq2uid(struct mail_search_args *args);
201 /* Returns TRUE if the two search arguments are fully compatible.
202    Always returns FALSE if there are seqsets, since they may point to different
203    messages depending on when the search is run. */
204 bool mail_search_args_equal(const struct mail_search_args *args1,
205 			    const struct mail_search_args *args2);
206 /* Same as mail_search_args_equal(), but for individual mail_search_arg
207    structs. All the siblings of arg1 and arg2 are also compared. */
208 bool mail_search_arg_equals(const struct mail_search_arg *arg1,
209 			    const struct mail_search_arg *arg2);
210 /* Same as mail_search_arg_equals(), but don't compare siblings. */
211 bool mail_search_arg_one_equals(const struct mail_search_arg *arg1,
212 				const struct mail_search_arg *arg2);
213 
214 void mail_search_args_ref(struct mail_search_args *args);
215 void mail_search_args_unref(struct mail_search_args **args);
216 
217 struct mail_search_args *
218 mail_search_args_dup(const struct mail_search_args *args);
219 struct mail_search_arg *
220 mail_search_arg_dup(pool_t pool, const struct mail_search_arg *arg);
221 
222 /* Reset the results in search arguments. match_always is reset only if
223    full_reset is TRUE. */
224 void mail_search_args_reset(struct mail_search_arg *args, bool full_reset);
225 
226 /* goes through arguments in list that don't have a result yet.
227    Returns 1 = search matched, 0 = search unmatched, -1 = don't know yet */
228 int mail_search_args_foreach(struct mail_search_arg *args,
229 			     mail_search_foreach_callback_t *callback,
230 			     void *context) ATTR_NULL(3);
231 #define mail_search_args_foreach(args, callback, context) \
232 	  mail_search_args_foreach(args - \
233 		CALLBACK_TYPECHECK(callback, void (*)( \
234 			struct mail_search_arg *, typeof(context))), \
235 		(mail_search_foreach_callback_t *)callback, context)
236 
237 /* Fills have_headers and have_body based on if such search argument exists
238    that needs to be checked. Returns the headers that we're searching for, or
239    NULL if we're searching for TEXT. */
240 const char *const *
241 mail_search_args_analyze(struct mail_search_arg *args,
242 			 bool *have_headers, bool *have_body);
243 
244 /* Returns FALSE if search query contains MAILBOX[_GLOB] args such that the
245    query can never match any messages in the given mailbox. */
246 bool mail_search_args_match_mailbox(struct mail_search_args *args,
247 				    const char *vname, char sep);
248 
249 /* Simplify/optimize search arguments. Afterwards all OR/SUB args are
250    guaranteed to have match_not=FALSE. */
251 void mail_search_args_simplify(struct mail_search_args *args);
252 
253 /* Append all args as IMAP SEARCH AND-query to the dest string and returns TRUE.
254    If some search arg can't be written as IMAP SEARCH parameter, error_r is set
255    and FALSE is returned. */
256 bool mail_search_args_to_imap(string_t *dest, const struct mail_search_arg *args,
257 			      const char **error_r);
258 /* Like mail_search_args_to_imap(), but append only a single arg. */
259 bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg,
260 			     const char **error_r);
261 /* Write all args to dest string as cmdline/human compatible input. */
262 void mail_search_args_to_cmdline(string_t *dest,
263 				 const struct mail_search_arg *args);
264 
265 /* Serialization for search args' results. */
266 void mail_search_args_result_serialize(const struct mail_search_args *args,
267 				       buffer_t *dest);
268 void mail_search_args_result_deserialize(struct mail_search_args *args,
269 					 const unsigned char *data,
270 					 size_t size);
271 
272 #endif
273