4 * Implements the SEARCH command in IMAP.
5 * This command is way too convoluted. Marc Crispin is a fscking idiot.
7 * NOTE: this is a partial implementation. It is NOT FINISHED.
20 #include <sys/types.h>
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
27 # include <sys/time.h>
39 #include "sysdep_decls.h"
40 #include "citserver.h"
43 #include "serv_extensions.h"
50 #include "internet_addressing.h"
51 #include "serv_imap.h"
52 #include "imap_tools.h"
53 #include "imap_fetch.h"
54 #include "imap_search.h"
60 * imap_do_search() calls imap_do_search_msg() to search an individual
61 * message after it has been fetched from the disk. This function returns
62 * nonzero if there is a match.
64 int imap_do_search_msg(int seq, struct CtdlMessage *msg,
65 int num_items, char **itemlist, int is_uid) {
74 if (num_items == 0) return(0);
76 /* Initially we start at the beginning. */
79 /* Check for the dreaded NOT criterion. */
80 if (!strcasecmp(itemlist[0], "NOT")) {
85 /* Check for the dreaded OR criterion. */
86 if (!strcasecmp(itemlist[0], "OR")) {
91 /* Now look for criteria. */
92 if (!strcasecmp(itemlist[pos], "ALL")) {
97 else if (!strcasecmp(itemlist[pos], "ANSWERED")) {
98 if (IMAP->flags[seq-1] & IMAP_ANSWERED) {
104 else if (!strcasecmp(itemlist[pos], "BCC")) {
105 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Bcc");
106 if (fieldptr != NULL) {
107 if (bmstrstr(fieldptr, itemlist[pos+1], strncasecmp)) {
115 else if (!strcasecmp(itemlist[pos], "BEFORE")) {
116 if (msg->cm_fields['T'] != NULL) {
117 if (imap_datecmp(itemlist[pos+1],
118 atol(msg->cm_fields['T'])) < 0) {
125 else if (!strcasecmp(itemlist[pos], "BODY")) {
126 if (bmstrstr(msg->cm_fields['M'], itemlist[pos+1], strncasecmp)) {
132 else if (!strcasecmp(itemlist[pos], "CC")) {
133 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Cc");
134 if (fieldptr != NULL) {
135 if (bmstrstr(fieldptr, itemlist[pos+1], strncasecmp)) {
143 else if (!strcasecmp(itemlist[pos], "DELETED")) {
144 if (IMAP->flags[seq-1] & IMAP_DELETED) {
150 else if (!strcasecmp(itemlist[pos], "DRAFT")) {
151 if (IMAP->flags[seq-1] & IMAP_DRAFT) {
157 else if (!strcasecmp(itemlist[pos], "FLAGGED")) {
158 if (IMAP->flags[seq-1] & IMAP_FLAGGED) {
164 else if (!strcasecmp(itemlist[pos], "FROM")) {
165 if (bmstrstr(msg->cm_fields['A'], itemlist[pos+1], strncasecmp)) {
171 else if (!strcasecmp(itemlist[pos], "HEADER")) {
173 pos += 3; /* Yes, three */
176 else if (!strcasecmp(itemlist[pos], "KEYWORD")) {
181 else if (!strcasecmp(itemlist[pos], "LARGER")) {
182 if (strlen(msg->cm_fields['M']) > atoi(itemlist[pos+1])) {
188 else if (!strcasecmp(itemlist[pos], "NEW")) {
193 else if (!strcasecmp(itemlist[pos], "OLD")) {
198 else if (!strcasecmp(itemlist[pos], "ON")) {
199 if (msg->cm_fields['T'] != NULL) {
200 if (imap_datecmp(itemlist[pos+1],
201 atol(msg->cm_fields['T'])) == 0) {
208 else if (!strcasecmp(itemlist[pos], "RECENT")) {
213 else if (!strcasecmp(itemlist[pos], "SEEN")) {
214 if (IMAP->flags[seq-1] & IMAP_SEEN) {
220 else if (!strcasecmp(itemlist[pos], "SENTBEFORE")) {
221 if (msg->cm_fields['T'] != NULL) {
222 if (imap_datecmp(itemlist[pos+1],
223 atol(msg->cm_fields['T'])) < 0) {
230 else if (!strcasecmp(itemlist[pos], "SENTON")) {
231 if (msg->cm_fields['T'] != NULL) {
232 if (imap_datecmp(itemlist[pos+1],
233 atol(msg->cm_fields['T'])) == 0) {
240 else if (!strcasecmp(itemlist[pos], "SENTSINCE")) {
241 if (msg->cm_fields['T'] != NULL) {
242 if (imap_datecmp(itemlist[pos+1],
243 atol(msg->cm_fields['T'])) >= 0) {
250 else if (!strcasecmp(itemlist[pos], "SINCE")) {
251 if (msg->cm_fields['T'] != NULL) {
252 if (imap_datecmp(itemlist[pos+1],
253 atol(msg->cm_fields['T'])) >= 0) {
260 else if (!strcasecmp(itemlist[pos], "SMALLER")) {
261 if (strlen(msg->cm_fields['M']) < atoi(itemlist[pos+1])) {
267 else if (!strcasecmp(itemlist[pos], "SUBJECT")) {
268 if (bmstrstr(msg->cm_fields['U'], itemlist[pos+1], strncasecmp)) {
274 else if (!strcasecmp(itemlist[pos], "TEXT")) {
275 for (i='A'; i<='Z'; ++i) {
276 if (bmstrstr(msg->cm_fields[i], itemlist[pos+1], strncasecmp)) {
283 else if (!strcasecmp(itemlist[pos], "TO")) {
284 if (bmstrstr(msg->cm_fields['R'], itemlist[pos+1], strncasecmp)) {
290 else if (!strcasecmp(itemlist[pos], "UID")) {
291 if (is_msg_in_mset(itemlist[pos+1], IMAP->msgids[seq-1])) {
297 /* Now here come the 'UN' criteria. Why oh why do we have to
298 * implement *both* the 'UN' criteria *and* the 'NOT' keyword? Why
299 * can't there be *one* way to do things? Answer: because Mark
300 * Crispin is an idiot.
303 else if (!strcasecmp(itemlist[pos], "UNANSWERED")) {
304 if ((IMAP->flags[seq-1] & IMAP_ANSWERED) == 0) {
310 else if (!strcasecmp(itemlist[pos], "UNDELETED")) {
311 if ((IMAP->flags[seq-1] & IMAP_DELETED) == 0) {
317 else if (!strcasecmp(itemlist[pos], "UNDRAFT")) {
318 if ((IMAP->flags[seq-1] & IMAP_DRAFT) == 0) {
324 else if (!strcasecmp(itemlist[pos], "UNFLAGGED")) {
325 if ((IMAP->flags[seq-1] & IMAP_FLAGGED) == 0) {
331 else if (!strcasecmp(itemlist[pos], "UNKEYWORD")) {
336 else if (!strcasecmp(itemlist[pos], "UNSEEN")) {
337 if ((IMAP->flags[seq-1] & IMAP_SEEN) == 0) {
343 /* Remember to negate if we were told to */
348 /* Keep going if there are more criteria! */
349 if (pos < num_items) {
352 match = (match || imap_do_search_msg(seq, msg,
353 num_items - pos, &itemlist[pos], is_uid));
356 match = (match && imap_do_search_msg(seq, msg,
357 num_items - pos, &itemlist[pos], is_uid));
367 * imap_search() calls imap_do_search() to do its actual work, once it's
368 * validated and boiled down the request a bit.
370 void imap_do_search(int num_items, char **itemlist, int is_uid) {
372 struct CtdlMessage *msg;
374 cprintf("* SEARCH ");
375 if (IMAP->num_msgs > 0)
376 for (i = 0; i < IMAP->num_msgs; ++i)
377 if (IMAP->flags[i] && IMAP_SELECTED) {
378 msg = CtdlFetchMessage(IMAP->msgids[i]);
380 if (imap_do_search_msg(i+1, msg, num_items,
383 cprintf("%ld ", IMAP->msgids[i]);
389 CtdlFreeMessage(msg);
392 lprintf(CTDL_ERR, "SEARCH internal error\n");
400 * This function is called by the main command loop.
402 void imap_search(int num_parms, char *parms[]) {
406 cprintf("%s BAD invalid parameters\r\n", parms[0]);
410 for (i=1; i<num_parms; ++i) {
411 if (imap_is_message_set(parms[i])) {
412 imap_pick_range(parms[i], 0);
416 imap_do_search(num_parms-2, &parms[2], 0);
417 cprintf("%s OK SEARCH completed\r\n", parms[0]);
421 * This function is called by the main command loop.
423 void imap_uidsearch(int num_parms, char *parms[]) {
427 cprintf("%s BAD invalid parameters\r\n", parms[0]);
431 for (i=1; i<num_parms; ++i) {
432 if (imap_is_message_set(parms[i])) {
433 imap_pick_range(parms[i], 1);
437 imap_do_search(num_parms-3, &parms[3], 1);
438 cprintf("%s OK UID SEARCH completed\r\n", parms[0]);