]> code.citadel.org Git - citadel.git/blobdiff - citadel/imap_search.c
* Use syslog-compatible logging levels in lprintf(); the loglevel chosen
[citadel.git] / citadel / imap_search.c
index c55ba28c69098bb65f53a53c8f31d5a15770db05..894a96e952158cadd51c9bdf0bc6cbcffbfb2d8c 100644 (file)
@@ -4,6 +4,8 @@
  * Implements the SEARCH command in IMAP.
  * This command is way too convoluted.  Marc Crispin is a fscking idiot.
  *
+ * NOTE: this is a partial implementation.  It is NOT FINISHED.
+ *
  */
 
 
@@ -38,7 +40,7 @@
 #include "citserver.h"
 #include "support.h"
 #include "config.h"
-#include "dynloader.h"
+#include "serv_extensions.h"
 #include "room_ops.h"
 #include "user_ops.h"
 #include "policy.h"
 
 
 
-
-
-
 /*
- * imap_do_search() calls imap_do_search_msg() to output the deta of an
- * individual message, once it has been successfully loaded from disk.
+ * imap_do_search() calls imap_do_search_msg() to search an individual
+ * message after it has been fetched from the disk.  This function returns
+ * nonzero if there is a match.
  */
-void imap_do_search_msg(int seq, struct CtdlMessage *msg,
+int imap_do_search_msg(int seq, struct CtdlMessage *msg,
                        int num_items, char **itemlist, int is_uid) {
 
-       int is_valid = 0;
+       int match = 0;
+       int is_not = 0;
+       int is_or = 0;
+       int pos = 0;
+       int i;
+       char *fieldptr;
+
+       if (num_items == 0) return(0);
+
+       /* Initially we start at the beginning. */
+       pos = 0;
+
+       /* Check for the dreaded NOT criterion. */
+       if (!strcasecmp(itemlist[0], "NOT")) {
+               is_not = 1;
+               pos = 1;
+       }
+
+       /* Check for the dreaded OR criterion. */
+       if (!strcasecmp(itemlist[0], "OR")) {
+               is_or = 1;
+               pos = 1;
+       }
+
+       /* Now look for criteria. */
+       if (!strcasecmp(itemlist[pos], "ALL")) {
+               match = 1;
+               ++pos;
+       }
+       
+       else if (!strcasecmp(itemlist[pos], "ANSWERED")) {
+               if (IMAP->flags[seq-1] & IMAP_ANSWERED) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "BCC")) {
+               fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Bcc");
+               if (fieldptr != NULL) {
+                       if (bmstrstr(fieldptr, itemlist[pos+1], strncasecmp)) {
+                               match = 1;
+                       }
+                       phree(fieldptr);
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "BEFORE")) {
+               if (msg->cm_fields['T'] != NULL) {
+                       if (imap_datecmp(itemlist[pos+1],
+                                       atol(msg->cm_fields['T'])) < 0) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "BODY")) {
+               if (bmstrstr(msg->cm_fields['M'], itemlist[pos+1], strncasecmp)) {
+                       match = 1;
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "CC")) {
+               fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Cc");
+               if (fieldptr != NULL) {
+                       if (bmstrstr(fieldptr, itemlist[pos+1], strncasecmp)) {
+                               match = 1;
+                       }
+                       phree(fieldptr);
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "DELETED")) {
+               if (IMAP->flags[seq-1] & IMAP_DELETED) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "DRAFT")) {
+               if (IMAP->flags[seq-1] & IMAP_DRAFT) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "FLAGGED")) {
+               if (IMAP->flags[seq-1] & IMAP_FLAGGED) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "FROM")) {
+               if (bmstrstr(msg->cm_fields['A'], itemlist[pos+1], strncasecmp)) {
+                       match = 1;
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "HEADER")) {
+               /* FIXME */
+               pos += 3;       /* Yes, three */
+       }
+
+       else if (!strcasecmp(itemlist[pos], "KEYWORD")) {
+               /* FIXME */
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "LARGER")) {
+               if (strlen(msg->cm_fields['M']) > atoi(itemlist[pos+1])) {
+                       match = 1;
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "NEW")) {
+               /* FIXME */
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "OLD")) {
+               /* FIXME */
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "ON")) {
+               if (msg->cm_fields['T'] != NULL) {
+                       if (imap_datecmp(itemlist[pos+1],
+                                       atol(msg->cm_fields['T'])) == 0) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "RECENT")) {
+               /* FIXME */
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SEEN")) {
+               if (IMAP->flags[seq-1] & IMAP_SEEN) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SENTBEFORE")) {
+               if (msg->cm_fields['T'] != NULL) {
+                       if (imap_datecmp(itemlist[pos+1],
+                                       atol(msg->cm_fields['T'])) < 0) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SENTON")) {
+               if (msg->cm_fields['T'] != NULL) {
+                       if (imap_datecmp(itemlist[pos+1],
+                                       atol(msg->cm_fields['T'])) == 0) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SENTSINCE")) {
+               if (msg->cm_fields['T'] != NULL) {
+                       if (imap_datecmp(itemlist[pos+1],
+                                       atol(msg->cm_fields['T'])) >= 0) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SINCE")) {
+               if (msg->cm_fields['T'] != NULL) {
+                       if (imap_datecmp(itemlist[pos+1],
+                                       atol(msg->cm_fields['T'])) >= 0) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SMALLER")) {
+               if (strlen(msg->cm_fields['M']) < atoi(itemlist[pos+1])) {
+                       match = 1;
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "SUBJECT")) {
+               if (bmstrstr(msg->cm_fields['U'], itemlist[pos+1], strncasecmp)) {
+                       match = 1;
+               }
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "TEXT")) {
+               for (i='A'; i<='Z'; ++i) {
+                       if (bmstrstr(msg->cm_fields[i], itemlist[pos+1], strncasecmp)) {
+                               match = 1;
+                       }
+               }
+               pos += 2;
+       }
 
-       is_valid = 1;  /* FIXME ... replace with a real test */
+       else if (!strcasecmp(itemlist[pos], "TO")) {
+               if (bmstrstr(msg->cm_fields['R'], itemlist[pos+1], strncasecmp)) {
+                       match = 1;
+               }
+               pos += 2;
+       }
 
-       /*
-        * If the message meets the specified search criteria, output its
-        * sequence number *or* UID, depending on what the client wants.
+       else if (!strcasecmp(itemlist[pos], "UID")) {
+               if (is_msg_in_mset(itemlist[pos+1], IMAP->msgids[seq-1])) {
+                       match = 1;
+               }
+               pos += 2;
+       }
+
+       /* Now here come the 'UN' criteria.  Why oh why do we have to
+        * implement *both* the 'UN' criteria *and* the 'NOT' keyword?  Why
+        * can't there be *one* way to do things?  Answer: because Mark
+        * Crispin is an idiot.
         */
-       if (is_valid) {
-               if (is_uid)     cprintf("%ld ", IMAP->msgids[seq-1]);
-               else            cprintf("%d ", seq);
+
+       else if (!strcasecmp(itemlist[pos], "UNANSWERED")) {
+               if ((IMAP->flags[seq-1] & IMAP_ANSWERED) == 0) {
+                       match = 1;
+               }
+               ++pos;
        }
 
-}
+       else if (!strcasecmp(itemlist[pos], "UNDELETED")) {
+               if ((IMAP->flags[seq-1] & IMAP_DELETED) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNDRAFT")) {
+               if ((IMAP->flags[seq-1] & IMAP_DRAFT) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNFLAGGED")) {
+               if ((IMAP->flags[seq-1] & IMAP_FLAGGED) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNKEYWORD")) {
+               /* FIXME */
+               pos += 2;
+       }
+
+       else if (!strcasecmp(itemlist[pos], "UNSEEN")) {
+               if ((IMAP->flags[seq-1] & IMAP_SEEN) == 0) {
+                       match = 1;
+               }
+               ++pos;
+       }
+
+       /* Remember to negate if we were told to */
+       if (is_not) {
+               match = !match;
+       }
+
+       /* Keep going if there are more criteria! */
+       if (pos < num_items) {
+
+               if (is_or) {
+                       match = (match || imap_do_search_msg(seq, msg,
+                               num_items - pos, &itemlist[pos], is_uid));
+               }
+               else {
+                       match = (match && imap_do_search_msg(seq, msg,
+                               num_items - pos, &itemlist[pos], is_uid));
+               }
+
+       }
 
+       return(match);
+}
 
 
 /*
@@ -95,12 +377,19 @@ void imap_do_search(int num_items, char **itemlist, int is_uid) {
          if (IMAP->flags[i] && IMAP_SELECTED) {
                msg = CtdlFetchMessage(IMAP->msgids[i]);
                if (msg != NULL) {
-                       imap_do_search_msg(i+1, msg, num_items,
-                                       itemlist, is_uid);
+                       if (imap_do_search_msg(i+1, msg, num_items,
+                          itemlist, is_uid)) {
+                               if (is_uid) {
+                                       cprintf("%ld ", IMAP->msgids[i]);
+                               }
+                               else {
+                                       cprintf("%d ", i+1);
+                               }
+                       }
                        CtdlFreeMessage(msg);
                }
                else {
-                       lprintf(1, "SEARCH internal error\n");
+                       lprintf(CTDL_ERR, "SEARCH internal error\n");
                }
        }
        cprintf("\r\n");
@@ -111,9 +400,6 @@ void imap_do_search(int num_items, char **itemlist, int is_uid) {
  * This function is called by the main command loop.
  */
 void imap_search(int num_parms, char *parms[]) {
-       char items[1024];
-       char *itemlist[256];
-       int num_items;
        int i;
 
        if (num_parms < 3) {
@@ -127,19 +413,7 @@ void imap_search(int num_parms, char *parms[]) {
                }
        }
 
-       strcpy(items, "");
-       for (i=2; i<num_parms; ++i) {
-               strcat(items, parms[i]);
-               if (i < (num_parms-1)) strcat(items, " ");
-       }
-
-       num_items = imap_extract_data_items(itemlist, items);
-       if (num_items < 1) {
-               cprintf("%s BAD invalid data item list\r\n", parms[0]);
-               return;
-       }
-
-       imap_do_search(num_items, itemlist, 0);
+       imap_do_search(num_parms-2, &parms[2], 0);
        cprintf("%s OK SEARCH completed\r\n", parms[0]);
 }
 
@@ -147,9 +421,6 @@ void imap_search(int num_parms, char *parms[]) {
  * This function is called by the main command loop.
  */
 void imap_uidsearch(int num_parms, char *parms[]) {
-       char items[1024];
-       char *itemlist[256];
-       int num_items;
        int i;
 
        if (num_parms < 4) {
@@ -163,19 +434,7 @@ void imap_uidsearch(int num_parms, char *parms[]) {
                }
        }
 
-       strcpy(items, "");
-       for (i=4; i<num_parms; ++i) {
-               strcat(items, parms[i]);
-               if (i < (num_parms-1)) strcat(items, " ");
-       }
-
-       num_items = imap_extract_data_items(itemlist, items);
-       if (num_items < 1) {
-               cprintf("%s BAD invalid data item list\r\n", parms[0]);
-               return;
-       }
-
-       imap_do_search(num_items, itemlist, 1);
+       imap_do_search(num_parms-3, &parms[3], 1);
        cprintf("%s OK UID SEARCH completed\r\n", parms[0]);
 }