/* ** Copyright 1998 - 2002 Double Precision, Inc. ** See COPYING for distribution information. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "unicode/unicode.h" #include "searchinfo.h" #include "imapwrite.h" #include "imaptoken.h" static const char rcsid[]="$Id: searchinfo.c,v 1.13 2003/07/16 23:09:29 mrsam Exp $"; 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=(*unicode_ISO8859_1.toupper_func) (&unicode_ISO8859_1, t->tokenbuf, 0); 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 struct unicode_info *mycharset, const struct unicode_info *textcharset) { if (textcharset->search_chset) textcharset=textcharset->search_chset; for (; si; si=si->next) { char *p; 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 (si->bs_charset && strcmp(si->bs_charset->chset, textcharset->chset) == 0) continue; /* Already converted to this charset */ si->bs_charset=0; if (si->bs) free(si->bs); si->bs=0; p=unicode_convert(si->as ? si->as:"", mycharset, textcharset); if (!p && errno != EINVAL) write_error_exit(0); if (p) { si->bs=(*textcharset->toupper_func)(textcharset, p, 0); free(p); if (!si->bs || maildir_search_start(&si->sei, si->bs)) write_error_exit(0); } si->bs_charset=textcharset; } } #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