4 * Implements the SEARCH command in IMAP.
5 * You guessed it ... more gratuitous complexity in the protocol.
18 #include <sys/types.h>
20 #if TIME_WITH_SYS_TIME
21 # include <sys/time.h>
25 # include <sys/time.h>
37 #include "sysdep_decls.h"
38 #include "citserver.h"
41 #include "serv_extensions.h"
48 #include "internet_addressing.h"
49 #include "serv_imap.h"
50 #include "imap_tools.h"
51 #include "imap_fetch.h"
52 #include "imap_search.h"
58 * imap_do_search() calls imap_do_search_msg() to search an individual
59 * message after it has been fetched from the disk. This function returns
60 * nonzero if there is a match.
62 * supplied_msg MAY be used to pass a pointer to the message in memory,
63 * if for some reason it's already been loaded. If not, the message will
64 * be loaded only if one or more search criteria require it.
66 int imap_do_search_msg(int seq, struct CtdlMessage *supplied_msg,
67 int num_items, char **itemlist, int is_uid) {
75 struct CtdlMessage *msg = NULL;
76 int need_to_free_msg = 0;
83 /* Initially we start at the beginning. */
86 /* Check for the dreaded NOT criterion. */
87 if (!strcasecmp(itemlist[0], "NOT")) {
92 /* Check for the dreaded OR criterion. */
93 if (!strcasecmp(itemlist[0], "OR")) {
98 /* Now look for criteria. */
99 if (!strcasecmp(itemlist[pos], "ALL")) {
104 else if (!strcasecmp(itemlist[pos], "ANSWERED")) {
105 if (IMAP->flags[seq-1] & IMAP_ANSWERED) {
111 else if (!strcasecmp(itemlist[pos], "BCC")) {
113 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
114 need_to_free_msg = 1;
116 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Bcc");
117 if (fieldptr != NULL) {
118 if (bmstrstr(fieldptr, itemlist[pos+1], strncasecmp)) {
126 else if (!strcasecmp(itemlist[pos], "BEFORE")) {
128 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
129 need_to_free_msg = 1;
131 if (msg->cm_fields['T'] != NULL) {
132 if (imap_datecmp(itemlist[pos+1],
133 atol(msg->cm_fields['T'])) < 0) {
140 else if (!strcasecmp(itemlist[pos], "BODY")) {
142 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
143 need_to_free_msg = 1;
145 if (bmstrstr(msg->cm_fields['M'], itemlist[pos+1], strncasecmp)) {
151 else if (!strcasecmp(itemlist[pos], "CC")) {
153 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
154 need_to_free_msg = 1;
156 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Cc");
157 if (fieldptr != NULL) {
158 if (bmstrstr(fieldptr, itemlist[pos+1], strncasecmp)) {
166 else if (!strcasecmp(itemlist[pos], "DELETED")) {
167 if (IMAP->flags[seq-1] & IMAP_DELETED) {
173 else if (!strcasecmp(itemlist[pos], "DRAFT")) {
174 if (IMAP->flags[seq-1] & IMAP_DRAFT) {
180 else if (!strcasecmp(itemlist[pos], "FLAGGED")) {
181 if (IMAP->flags[seq-1] & IMAP_FLAGGED) {
187 else if (!strcasecmp(itemlist[pos], "FROM")) {
189 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
190 need_to_free_msg = 1;
192 if (bmstrstr(msg->cm_fields['A'], itemlist[pos+1], strncasecmp)) {
198 else if (!strcasecmp(itemlist[pos], "HEADER")) {
200 pos += 3; /* Yes, three */
203 else if (!strcasecmp(itemlist[pos], "KEYWORD")) {
208 else if (!strcasecmp(itemlist[pos], "LARGER")) {
210 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
211 need_to_free_msg = 1;
213 if (strlen(msg->cm_fields['M']) > atoi(itemlist[pos+1])) {
219 else if (!strcasecmp(itemlist[pos], "NEW")) {
220 if ( (IMAP->flags[seq-1] & IMAP_RECENT) && (!(IMAP->flags[seq-1] & IMAP_SEEN))) {
226 else if (!strcasecmp(itemlist[pos], "OLD")) {
227 if (!(IMAP->flags[seq-1] & IMAP_RECENT)) {
233 else if (!strcasecmp(itemlist[pos], "ON")) {
235 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
236 need_to_free_msg = 1;
238 if (msg->cm_fields['T'] != NULL) {
239 if (imap_datecmp(itemlist[pos+1],
240 atol(msg->cm_fields['T'])) == 0) {
247 else if (!strcasecmp(itemlist[pos], "RECENT")) {
248 if (IMAP->flags[seq-1] & IMAP_RECENT) {
254 else if (!strcasecmp(itemlist[pos], "SEEN")) {
255 if (IMAP->flags[seq-1] & IMAP_SEEN) {
261 else if (!strcasecmp(itemlist[pos], "SENTBEFORE")) {
263 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
264 need_to_free_msg = 1;
266 if (msg->cm_fields['T'] != NULL) {
267 if (imap_datecmp(itemlist[pos+1],
268 atol(msg->cm_fields['T'])) < 0) {
275 else if (!strcasecmp(itemlist[pos], "SENTON")) {
277 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
278 need_to_free_msg = 1;
280 if (msg->cm_fields['T'] != NULL) {
281 if (imap_datecmp(itemlist[pos+1],
282 atol(msg->cm_fields['T'])) == 0) {
289 else if (!strcasecmp(itemlist[pos], "SENTSINCE")) {
291 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
292 need_to_free_msg = 1;
294 if (msg->cm_fields['T'] != NULL) {
295 if (imap_datecmp(itemlist[pos+1],
296 atol(msg->cm_fields['T'])) >= 0) {
303 else if (!strcasecmp(itemlist[pos], "SINCE")) {
305 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
306 need_to_free_msg = 1;
308 if (msg->cm_fields['T'] != NULL) {
309 if (imap_datecmp(itemlist[pos+1],
310 atol(msg->cm_fields['T'])) >= 0) {
317 else if (!strcasecmp(itemlist[pos], "SMALLER")) {
319 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
320 need_to_free_msg = 1;
322 if (strlen(msg->cm_fields['M']) < atoi(itemlist[pos+1])) {
328 else if (!strcasecmp(itemlist[pos], "SUBJECT")) {
330 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
331 need_to_free_msg = 1;
333 if (bmstrstr(msg->cm_fields['U'], itemlist[pos+1], strncasecmp)) {
339 else if (!strcasecmp(itemlist[pos], "TEXT")) {
341 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
342 need_to_free_msg = 1;
344 for (i='A'; i<='Z'; ++i) {
345 if (bmstrstr(msg->cm_fields[i], itemlist[pos+1], strncasecmp)) {
352 else if (!strcasecmp(itemlist[pos], "TO")) {
354 msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1);
355 need_to_free_msg = 1;
357 if (bmstrstr(msg->cm_fields['R'], itemlist[pos+1], strncasecmp)) {
363 else if (!strcasecmp(itemlist[pos], "UID")) {
364 if (is_msg_in_mset(itemlist[pos+1], IMAP->msgids[seq-1])) {
370 /* Now here come the 'UN' criteria. Why oh why do we have to
371 * implement *both* the 'UN' criteria *and* the 'NOT' keyword? Why
372 * can't there be *one* way to do things? Answer: the design of
373 * IMAP suffers from gratuitous complexity.
376 else if (!strcasecmp(itemlist[pos], "UNANSWERED")) {
377 if ((IMAP->flags[seq-1] & IMAP_ANSWERED) == 0) {
383 else if (!strcasecmp(itemlist[pos], "UNDELETED")) {
384 if ((IMAP->flags[seq-1] & IMAP_DELETED) == 0) {
390 else if (!strcasecmp(itemlist[pos], "UNDRAFT")) {
391 if ((IMAP->flags[seq-1] & IMAP_DRAFT) == 0) {
397 else if (!strcasecmp(itemlist[pos], "UNFLAGGED")) {
398 if ((IMAP->flags[seq-1] & IMAP_FLAGGED) == 0) {
404 else if (!strcasecmp(itemlist[pos], "UNKEYWORD")) {
409 else if (!strcasecmp(itemlist[pos], "UNSEEN")) {
410 if ((IMAP->flags[seq-1] & IMAP_SEEN) == 0) {
416 /* Remember to negate if we were told to */
421 /* Keep going if there are more criteria! */
422 if (pos < num_items) {
425 match = (match || imap_do_search_msg(seq, msg,
426 num_items - pos, &itemlist[pos], is_uid));
429 match = (match && imap_do_search_msg(seq, msg,
430 num_items - pos, &itemlist[pos], is_uid));
435 if (need_to_free_msg) {
436 CtdlFreeMessage(msg);
443 * imap_search() calls imap_do_search() to do its actual work, once it's
444 * validated and boiled down the request a bit.
446 void imap_do_search(int num_items, char **itemlist, int is_uid) {
449 cprintf("* SEARCH ");
450 if (IMAP->num_msgs > 0)
451 for (i = 0; i < IMAP->num_msgs; ++i)
452 if (IMAP->flags[i] && IMAP_SELECTED) {
453 if (imap_do_search_msg(i+1, NULL, num_items, itemlist, is_uid)) {
455 cprintf("%ld ", IMAP->msgids[i]);
467 * This function is called by the main command loop.
469 void imap_search(int num_parms, char *parms[]) {
473 cprintf("%s BAD invalid parameters\r\n", parms[0]);
477 for (i=1; i<num_parms; ++i) {
478 if (imap_is_message_set(parms[i])) {
479 imap_pick_range(parms[i], 0);
483 imap_do_search(num_parms-2, &parms[2], 0);
484 cprintf("%s OK SEARCH completed\r\n", parms[0]);
488 * This function is called by the main command loop.
490 void imap_uidsearch(int num_parms, char *parms[]) {
494 cprintf("%s BAD invalid parameters\r\n", parms[0]);
498 for (i=1; i<num_parms; ++i) {
499 if (imap_is_message_set(parms[i])) {
500 imap_pick_range(parms[i], 1);
504 imap_do_search(num_parms-3, &parms[3], 1);
505 cprintf("%s OK UID SEARCH completed\r\n", parms[0]);