summaryrefslogtreecommitdiffstats
path: root/imap/searchinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'imap/searchinfo.c')
-rw-r--r--imap/searchinfo.c689
1 files changed, 689 insertions, 0 deletions
diff --git a/imap/searchinfo.c b/imap/searchinfo.c
new file mode 100644
index 0000000..0f43ad2
--- /dev/null
+++ b/imap/searchinfo.c
@@ -0,0 +1,689 @@
+/*
+** Copyright 1998 - 2010 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include "unicode/unicode.h"
+#include "searchinfo.h"
+#include "imapwrite.h"
+#include "imaptoken.h"
+
+
+struct searchinfo *alloc_search(struct searchinfo **head)
+{
+struct searchinfo *si=(struct searchinfo *)malloc(sizeof(**head));
+
+ if (si == 0) write_error_exit(0);
+ memset(si, 0, sizeof(*si));
+ maildir_search_init(&si->sei);
+ si->next= *head;
+ *head=si;
+ return (si);
+}
+
+void free_search(struct searchinfo *si)
+{
+struct searchinfo *p;
+
+ while (si)
+ {
+ p=si->next;
+ if (si->as) free(si->as);
+ if (si->bs) free(si->bs);
+ if (si->cs) free(si->cs);
+
+ maildir_search_destroy(&si->sei);
+
+ free(si);
+ si=p;
+ }
+}
+
+static struct searchinfo *alloc_search_andlist(struct searchinfo **);
+static struct searchinfo *alloc_search_notkey(struct searchinfo **);
+static struct searchinfo *alloc_search_key(struct searchinfo **);
+
+struct searchinfo *alloc_parsesearch(struct searchinfo **head)
+{
+struct searchinfo *si;
+
+ *head=0;
+ if ((si=alloc_search_andlist(head)) == 0)
+ {
+ free_search(*head);
+ return (0);
+ }
+ return (si);
+}
+
+struct searchinfo *alloc_searchextra(struct searchinfo *top,
+ struct searchinfo **head, search_type t)
+{
+ struct searchinfo *si;
+
+ if (t == search_references1)
+ {
+ /* Automatically add third and second dummy node */
+
+ top=alloc_searchextra(top, head, search_references4);
+ top=alloc_searchextra(top, head, search_references3);
+ top=alloc_searchextra(top, head, search_references2);
+ }
+ si=alloc_search(head);
+ si->type=t;
+ si->a=top;
+ return (si);
+}
+
+static struct searchinfo *alloc_search_andlist(struct searchinfo **head)
+{
+struct searchinfo *si, *a, *b;
+struct imaptoken *t;
+
+ si=alloc_search_notkey(head);
+ if (!si) return (0);
+ while ((t=currenttoken())->tokentype != IT_RPAREN && t->tokentype !=
+ IT_EOL)
+ {
+ if ((a=alloc_search_notkey(head)) == 0) return (0);
+ b=alloc_search(head);
+ b->type=search_and;
+ b->a=si;
+ b->b=a;
+ si=b;
+ }
+ return (si);
+}
+
+static struct searchinfo *alloc_search_notkey(struct searchinfo **head)
+{
+struct imaptoken *t=currenttoken();
+
+ if (t->tokentype == IT_ATOM && strcmp(t->tokenbuf, "NOT") == 0)
+ {
+ struct searchinfo *si=alloc_search(head);
+
+ si->type=search_not;
+ nexttoken();
+ if ((si->a=alloc_search_key(head)) == 0)
+ return (0);
+ return (si);
+ }
+ return (alloc_search_key(head));
+}
+
+static struct searchinfo *alloc_search_key(struct searchinfo **head)
+{
+struct imaptoken *t=currenttoken();
+struct searchinfo *si;
+const char *keyword;
+
+ if (t->tokentype == IT_LPAREN)
+ {
+ nexttoken();
+ if ((si=alloc_search_andlist(head)) == 0 ||
+ currenttoken()->tokentype != IT_RPAREN)
+ return (0);
+ nexttoken();
+ return (si);
+ }
+
+ if (t->tokentype != IT_ATOM && t->tokentype != IT_NUMBER)
+ return (0);
+
+ keyword=t->tokenbuf;
+
+ if (strcmp(keyword, "ALL") == 0)
+ {
+ struct searchinfo *si;
+
+ (si=alloc_search(head))->type=search_all;
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "OR") == 0)
+ {
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_or;
+ nexttoken();
+ if ((si->a=alloc_search_notkey(head)) == 0 ||
+ (si->b=alloc_search_notkey(head)) == 0) return (0);
+ return (si);
+ }
+
+ if (strcmp(keyword, "HEADER") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_header;
+ t=nexttoken_okbracket();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->cs=strdup(t->tokenbuf);
+ if (!si->cs)
+ write_error_exit(0);
+ t=nexttoken_okbracket();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "BCC") == 0 ||
+ strcmp(keyword, "CC") == 0 ||
+ strcmp(keyword, "FROM") == 0 ||
+ strcmp(keyword, "TO") == 0 ||
+ strcmp(keyword, "SUBJECT") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_header;
+ si->cs=my_strdup(keyword);
+ t=nexttoken_okbracket();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "BEFORE") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_before;
+ t=nexttoken();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "BODY") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_body;
+ t=nexttoken_okbracket();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+ if (strcmp(keyword, "LARGER") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_larger;
+ t=nexttoken();
+ if (t->tokentype != IT_NUMBER)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "ON") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_on;
+ t=nexttoken();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "SENTBEFORE") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_sentbefore;
+ t=nexttoken();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "SENTON") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_senton;
+ t=nexttoken();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(keyword);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "SENTSINCE") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_sentsince;
+ t=nexttoken();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "SINCE") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_since;
+ t=nexttoken();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "SMALLER") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_smaller;
+ t=nexttoken();
+ if (t->tokentype != IT_NUMBER)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "TEXT") == 0)
+ {
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_text;
+ t=nexttoken_okbracket();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "UID") == 0)
+ {
+ struct searchinfo *si;
+ struct imaptoken *t;
+
+ si=alloc_search(head);
+ si->type=search_uid;
+ t=nexttoken();
+ if (!ismsgset(t))
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "KEYWORD") == 0
+ || strcmp(keyword, "UNKEYWORD") == 0)
+ {
+ int isnot= *keyword == 'U';
+ struct imaptoken *t;
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_msgkeyword;
+ t=nexttoken_okbracket();
+ if (t->tokentype != IT_ATOM &&
+ t->tokentype != IT_NUMBER &&
+ t->tokentype != IT_QUOTED_STRING)
+ return (0);
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+
+ if (isnot)
+ {
+ struct searchinfo *si2=alloc_search(head);
+
+ si2->type=search_not;
+ si2->a=si;
+ si=si2;
+ }
+ return (si);
+ }
+ if (strcmp(keyword, "ANSWERED") == 0 ||
+ strcmp(keyword, "DELETED") == 0 ||
+ strcmp(keyword, "DRAFT") == 0 ||
+ strcmp(keyword, "FLAGGED") == 0 ||
+ strcmp(keyword, "RECENT") == 0 ||
+ strcmp(keyword, "SEEN") == 0)
+ {
+ struct searchinfo *si;
+
+ si=alloc_search(head);
+ si->type=search_msgflag;
+ if ((si->as=malloc(strlen(keyword)+2)) == 0)
+ write_error_exit(0);
+ si->as[0]='\\';
+ strcpy(si->as+1, keyword);
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "UNANSWERED") == 0 ||
+ strcmp(keyword, "UNDELETED") == 0 ||
+ strcmp(keyword, "UNDRAFT") == 0 ||
+ strcmp(keyword, "UNFLAGGED") == 0 ||
+ strcmp(keyword, "UNSEEN") == 0)
+ {
+ struct searchinfo *si;
+ struct searchinfo *si2;
+
+ si=alloc_search(head);
+ si->type=search_msgflag;
+ if ((si->as=malloc(strlen(keyword))) == 0)
+ write_error_exit(0);
+ si->as[0]='\\';
+ strcpy(si->as+1, keyword+2);
+ nexttoken();
+
+ si2=alloc_search(head);
+ si2->type=search_not;
+ si2->a=si;
+ return (si2);
+ }
+
+ if (strcmp(keyword, "NEW") == 0)
+ {
+ struct searchinfo *si, *si2;
+
+ si=alloc_search(head);
+ si->type=search_and;
+ si2=si->a=alloc_search(head);
+ si2->type=search_msgflag;
+ si2->as=my_strdup("\\RECENT");
+ si2=si->b=alloc_search(head);
+ si2->type=search_not;
+ si2=si2->a=alloc_search(head);
+ si2->type=search_msgflag;
+ si2->as=my_strdup("\\SEEN");
+ nexttoken();
+ return (si);
+ }
+
+ if (strcmp(keyword, "OLD") == 0)
+ {
+ struct searchinfo *si, *si2;
+
+ si=alloc_search(head);
+ si->type=search_not;
+ si2=si->a=alloc_search(head);
+ si2->type=search_msgflag;
+ si2->as=my_strdup("\\RECENT");
+ nexttoken();
+ return (si);
+ }
+
+ if (ismsgset(t))
+ {
+ si=alloc_search(head);
+ si->type=search_messageset;
+ si->as=my_strdup(t->tokenbuf);
+ nexttoken();
+ return (si);
+ }
+
+ return (0);
+}
+
+/*
+** We are about to search in charset 'textcharset'. Make sure that all
+** search_text nodes in the search string are in that character set.
+*/
+
+void search_set_charset_conv(struct searchinfo *si, const char *charset)
+{
+ for (; si; si=si->next)
+ {
+ if (si->type != search_text && si->type != search_body
+ && si->type != search_header)
+ continue;
+ if (si->value > 0)
+ continue; /* Already found, no need to do this again */
+
+ if (maildir_search_start_str_chset(&si->sei,
+ si->as ? si->as:"",
+ charset))
+ {
+ si->value=0;
+ continue;
+ }
+ }
+}
+
+
+#if 0
+
+void debug_search(struct searchinfo *si)
+{
+ if (!si) return;
+
+ switch (si->type) {
+ case search_messageset:
+ writes("MESSAGE SET: ");
+ writes(si->as);
+ return;
+ case search_all:
+ writes("ALL");
+ return;
+ case search_msgflag:
+ writes("FLAG \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_msgkeyword:
+ writes("KEYWORD \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_not:
+ writes("NOT (");
+ debug_search(si->a);
+ writes(")");
+ return;
+ case search_and:
+ writes("AND (");
+ debug_search(si->a);
+ writes(", ");
+ debug_search(si->b);
+ writes(")");
+ return;
+ case search_or:
+ writes("OR (");
+ debug_search(si->a);
+ writes(", ");
+ debug_search(si->b);
+ writes(")");
+ return;
+ case search_header:
+ writes("HEADER \"");
+ writeqs(si->cs);
+ writes("\" \"");
+ writeqs(si->bs);
+ writes("\"");
+ return;
+ case search_before:
+ writes("BEFORE \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_body:
+ writes("BODY \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_larger:
+ writes("LARGER \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_on:
+ writes("ON \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_sentbefore:
+ writes("SENTBEFORE \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_senton:
+ writes("SENTON \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_sentsince:
+ writes("SENTSINCE \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_since:
+ writes("SINCE \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_smaller:
+ writes("SMALLER \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_text:
+ writes("TEXT \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_uid:
+ writes("UID \"");
+ writeqs(si->as);
+ writes("\"");
+ return;
+ case search_orderedsubj:
+ writes("ORDEREDSUBJ ");
+ debug_search(si->a);
+ return;
+ case search_references1:
+ writes("REFERENCES[References/In-Reply-To]=");
+ writeqs(si->as ? si->as:"");
+ writes("/");
+ writeqs(si->bs ? si->bs:"");
+ writes(" ");
+ debug_search(si->a);
+ return;
+ case search_references2:
+ writes("REFERENCES[Date:]=");
+ writeqs(si->as ? si->as:"");
+ writes(" ");
+ debug_search(si->a);
+ return;
+ case search_references3:
+ writes("REFERENCES[Subject]=");
+ writeqs(si->as ? si->as:"");
+ writes(" ");
+ debug_search(si->a);
+ return;
+ case search_references4:
+ writes("REFERENCES[Message-ID]=");
+ writeqs(si->as ? si->as:"");
+ writes(" ");
+ debug_search(si->a);
+ return;
+ case search_arrival:
+ writes("ARRIVAL");
+ return;
+ case search_cc:
+ writes("CC");
+ return;
+ case search_date:
+ writes("DATE");
+ return;
+ case search_from:
+ writes("FROM");
+ return;
+ case search_reverse:
+ writes("REVERSE");
+ return;
+ case search_size:
+ writes("SIZE");
+ return;
+ case search_to:
+ writes("TO");
+ return;
+ }
+}
+
+#endif