mail.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:153k
- }
- /* Mail message n is expunged
- * Accepts: mail stream
- * message #
- */
- void mail_expunged (MAILSTREAM *stream,unsigned long msgno)
- {
- MESSAGECACHE *elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
- /* notify main program of change */
- if (!stream->silent) mm_expunged (stream,msgno);
- if (elt) { /* if an element is there */
- elt->msgno = 0; /* invalidate its message number and free */
- (*mailcache) (stream,msgno,CH_FREE);
- (*mailcache) (stream,msgno,CH_FREESORTCACHE);
- }
- /* expunge the slot */
- (*mailcache) (stream,msgno,CH_EXPUNGE);
- --stream->nmsgs; /* update stream status */
- if (stream->msgno) { /* have stream pointers? */
- /* make sure the short cache is nuked */
- if (stream->scache) mail_gc (stream,GC_ENV | GC_TEXTS);
- else stream->msgno = 0; /* make sure invalidated in any case */
- }
- }
- /* Mail stream status routines */
- /* Mail lock stream
- * Accepts: mail stream
- */
- void mail_lock (MAILSTREAM *stream)
- {
- if (stream->lock) fatal ("Lock when already locked");
- else stream->lock = T; /* lock stream */
- }
- /* Mail unlock stream
- * Accepts: mail stream
- */
-
- void mail_unlock (MAILSTREAM *stream)
- {
- if (!stream->lock) fatal ("Unlock when not locked");
- else stream->lock = NIL; /* unlock stream */
- }
- /* Mail turn on debugging telemetry
- * Accepts: mail stream
- */
- void mail_debug (MAILSTREAM *stream)
- {
- stream->debug = T; /* turn on debugging telemetry */
- }
- /* Mail turn off debugging telemetry
- * Accepts: mail stream
- */
- void mail_nodebug (MAILSTREAM *stream)
- {
- stream->debug = NIL; /* turn off debugging telemetry */
- }
- /* Mail parse UID sequence
- * Accepts: mail stream
- * sequence to parse
- * Returns: T if parse successful, else NIL
- */
- long mail_uid_sequence (MAILSTREAM *stream,char *sequence)
- {
- unsigned long i,j,k,x,y;
- for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
- while (sequence && *sequence){/* while there is something to parse */
- if (*sequence == '*') { /* maximum message */
- i = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
- sequence++; /* skip past * */
- }
- /* parse and validate message number */
- else if (!(i = strtoul ((const char *) sequence,&sequence,10))) {
- mm_log ("UID sequence invalid",ERROR);
- return NIL;
- }
- switch (*sequence) { /* see what the delimiter is */
- case ':': /* sequence range */
- if (*++sequence == '*') { /* maximum message */
- j = stream->nmsgs ? mail_uid (stream,stream->nmsgs) : stream->uid_last;
- sequence++; /* skip past * */
- }
- /* parse end of range */
- else if (!(j = strtoul ((const char *) sequence,&sequence,10))) {
- mm_log ("UID sequence range invalid",ERROR);
- return NIL;
- }
- if (*sequence && *sequence++ != ',') {
- mm_log ("UID sequence range syntax error",ERROR);
- return NIL;
- }
- if (i > j) { /* swap the range if backwards */
- x = i; i = j; j = x;
- }
- x = mail_msgno (stream,i);/* get msgnos */
- y = mail_msgno (stream,j);/* for both UIDS (don't && it) */
- /* easy if both UIDs valid */
- if (x && y) while (x <= y) mail_elt (stream,x++)->sequence = T;
- /* start UID valid, end is not */
- else if (x) while ((x <= stream->nmsgs) && (mail_uid (stream,x) <= j))
- mail_elt (stream,x++)->sequence = T;
- /* end UID valid, start is not */
- else if (y) for (x = 1; x <= y; x++) {
- if (mail_uid (stream,x) >= i) mail_elt (stream,x)->sequence = T;
- }
- /* neither is valid, ugh */
- else for (x = 1; x <= stream->nmsgs; x++)
- if (((k = mail_uid (stream,x)) >= i) && (k <= j))
- mail_elt (stream,x)->sequence = T;
- break;
- case ',': /* single message */
- ++sequence; /* skip the delimiter, fall into end case */
- case ' ': /* end of sequence, mark this message */
- if (x = mail_msgno (stream,i)) mail_elt (stream,x)->sequence = T;
- break;
- default: /* anything else is a syntax error! */
- mm_log ("UID sequence syntax error",ERROR);
- return NIL;
- }
- }
- return T; /* successfully parsed sequence */
- }
- /* Mail see if line list matches that in cache
- * Accepts: candidate line list
- * cached line list
- * matching flags
- * Returns: T if match, NIL if no match
- */
- long mail_match_lines (STRINGLIST *lines,STRINGLIST *msglines,long flags)
- {
- unsigned long i;
- unsigned char *s,*t;
- STRINGLIST *m;
- if (!msglines) return T; /* full header is in cache */
- /* need full header but filtered in cache */
- if ((flags & FT_NOT) || !lines) return NIL;
- do { /* make sure all present & accounted for */
- for (m = msglines; m; m = m->next) if (lines->text.size == m->text.size) {
- for (s = lines->text.data,t = m->text.data,i = lines->text.size;
- i && ((islower (*s) ? (*s-('a'-'A')) : *s) ==
- (islower (*t) ? (*t-('a'-'A')) : *t)); s++,t++,i--);
- if (!i) break; /* this line matches */
- }
- if (!m) return NIL; /* didn't find in the list */
- }
- while (lines = lines->next);
- return T; /* all lines found */
- }
- /* Mail filter text by header lines
- * Accepts: text to filter
- * length of text
- * list of lines
- * fetch flags
- * Returns: new text size
- */
- unsigned long mail_filter (char *text,unsigned long len,STRINGLIST *lines,
- long flags)
- {
- STRINGLIST *hdrs;
- int notfound;
- unsigned long i;
- char c,*s,*e,*t,tmp[MAILTMPLEN],tst[MAILTMPLEN];
- char *src = text;
- char *dst = src;
- char *end = text + len;
- while (src < end) { /* process header */
- /* slurp header line name */
- for (s = src,e = s + MAILTMPLEN - 1,e = (e < end ? e : end),t = tmp;
- (s < e) && ((c = *s) != ':') &&
- ((c > ' ') ||
- ((c != ' ') && (c != 't') && (c != ' 15') && (c != ' 12')));
- *t++ = *s++);
- *t = ' '; /* tie off */
- notfound = T; /* not found yet */
- if (i = t - ucase (tmp)) /* see if found in header */
- for (hdrs = lines,tst[i] = ' '; hdrs && notfound; hdrs = hdrs->next)
- if ((hdrs->text.size == i) &&
- !strncmp (tmp,ucase (strncpy (tst,(char *) hdrs->text.data,
- (size_t) i)),(size_t) i))
- notfound = NIL;
- /* skip header line if not wanted */
- if (i && ((flags & FT_NOT) ? !notfound : notfound))
- while (((*src++ != ' 12') && (*src++ != ' 12') && (*src++ != ' 12') &&
- (*src++ != ' 12') && (*src++ != ' 12') && (*src++ != ' 12') &&
- (*src++ != ' 12') && (*src++ != ' 12') && (*src++ != ' 12') &&
- (*src++ != ' 12')) || ((*src == ' ') || (*src == 't')));
- else if (src == dst) { /* copy to self */
- while (((*src++ != ' 12') && (*src++ != ' 12') && (*src++ != ' 12') &&
- (*src++ != ' 12') && (*src++ != ' 12') && (*src++ != ' 12') &&
- (*src++ != ' 12') && (*src++ != ' 12') && (*src++ != ' 12') &&
- (*src++ != ' 12')) || ((*src == ' ') || (*src == 't')));
- dst = src; /* update destination */
- }
- else /* copy line and any continuation line */
- while ((((*dst++ = *src++) != ' 12') && ((*dst++ = *src++) != ' 12') &&
- ((*dst++ = *src++) != ' 12') && ((*dst++ = *src++) != ' 12') &&
- ((*dst++ = *src++) != ' 12') && ((*dst++ = *src++) != ' 12') &&
- ((*dst++ = *src++) != ' 12') && ((*dst++ = *src++) != ' 12') &&
- ((*dst++ = *src++) != ' 12') && ((*dst++ = *src++) != ' 12'))||
- ((*src == ' ') || (*src == 't')));
- }
- *dst = ' '; /* tie off destination */
- return dst - text;
- }
- /* Local mail search message
- * Accepts: MAIL stream
- * message number
- * optional section specification
- * search program
- * Returns: T if found, NIL otherwise
- */
- long mail_search_msg (MAILSTREAM *stream,unsigned long msgno,char *section,
- SEARCHPGM *pgm)
- {
- unsigned short d;
- char tmp[MAILTMPLEN];
- MESSAGECACHE *elt = mail_elt (stream,msgno);
- SEARCHHEADER *hdr;
- SEARCHOR *or;
- SEARCHPGMLIST *not;
- if (pgm->msgno || pgm->uid) { /* message set searches */
- SEARCHSET *set;
- /* message sequences */
- if (set = pgm->msgno) { /* must be inside this sequence */
- while (set) { /* run down until find matching range */
- if (set->last ? ((msgno < set->first) || (msgno > set->last)) :
- msgno != set->first) set = set->next;
- else break;
- }
- if (!set) return NIL; /* not found within sequence */
- }
- if (set = pgm->uid) { /* must be inside this sequence */
- unsigned long uid = mail_uid (stream,msgno);
- while (set) { /* run down until find matching range */
- if (set->last ? ((uid < set->first) || (uid > set->last)) :
- uid != set->first) set = set->next;
- else break;
- }
- if (!set) return NIL; /* not found within sequence */
- }
- }
- /* Fast data searches */
- /* need to fetch fast data? */
- if ((!elt->rfc822_size && (pgm->larger || pgm->smaller)) ||
- (!elt->year && (pgm->before || pgm->on || pgm->since)) ||
- (!elt->valid && (pgm->answered || pgm->unanswered ||
- pgm->deleted || pgm->undeleted ||
- pgm->draft || pgm->undraft ||
- pgm->flagged || pgm->unflagged ||
- pgm->recent || pgm->old ||
- pgm->seen || pgm->unseen ||
- pgm->keyword || pgm->unkeyword))) {
- sprintf (tmp,"%lu",elt->msgno);
- mail_fetch_fast (stream,tmp,NIL);
- }
- /* size ranges */
- if ((pgm->larger && (elt->rfc822_size <= pgm->larger)) ||
- (pgm->smaller && (elt->rfc822_size >= pgm->smaller))) return NIL;
- /* message flags */
- if ((pgm->answered && !elt->answered) ||
- (pgm->unanswered && elt->answered) ||
- (pgm->deleted && !elt->deleted) ||
- (pgm->undeleted && elt->deleted) ||
- (pgm->draft && !elt->draft) ||
- (pgm->undraft && elt->draft) ||
- (pgm->flagged && !elt->flagged) ||
- (pgm->unflagged && elt->flagged) ||
- (pgm->recent && !elt->recent) ||
- (pgm->old && elt->recent) ||
- (pgm->seen && !elt->seen) ||
- (pgm->unseen && elt->seen)) return NIL;
- /* keywords */
- if ((pgm->keyword && !mail_search_keyword (stream,elt,pgm->keyword)) ||
- (pgm->unkeyword && mail_search_keyword (stream,elt,pgm->unkeyword)))
- return NIL;
- /* internal date ranges */
- if (pgm->before || pgm->on || pgm->since) {
- d = (elt->year << 9) + (elt->month << 5) + elt->day;
- if (pgm->before && (d >= pgm->before)) return NIL;
- if (pgm->on && (d != pgm->on)) return NIL;
- if (pgm->since && (d < pgm->since)) return NIL;
- }
- /* envelope searches */
- if (pgm->sentbefore || pgm->senton || pgm->sentsince ||
- pgm->bcc || pgm->cc || pgm->from || pgm->to || pgm->subject ||
- pgm->return_path || pgm->sender || pgm->reply_to || pgm->in_reply_to ||
- pgm->message_id || pgm->newsgroups || pgm->followup_to ||
- pgm->references) {
- ENVELOPE *env;
- MESSAGECACHE delt;
- if (section) { /* use body part envelope */
- BODY *body = mail_body (stream,msgno,section);
- env = (body && (body->type == TYPEMESSAGE) && body->subtype &&
- !strcmp (body->subtype,"RFC822")) ? body->nested.msg->env : NIL;
- }
- /* use top level envelope if no section */
- else env = mail_fetchenvelope (stream,msgno);
- if (!env) return NIL; /* no envelope obtained */
- /* sent date ranges */
- if ((pgm->sentbefore || pgm->senton || pgm->sentsince) &&
- (!mail_parse_date (&delt,env->date) ||
- !(d = (delt.year << 9) + (delt.month << 5) + delt.day) ||
- (pgm->sentbefore && (d >= pgm->sentbefore)) ||
- (pgm->senton && (d != pgm->senton)) ||
- (pgm->sentsince && (d < pgm->sentsince)))) return NIL;
- /* search headers */
- if ((pgm->bcc && !mail_search_addr (env->bcc,pgm->bcc)) ||
- (pgm->cc && !mail_search_addr (env->cc,pgm->cc)) ||
- (pgm->from && !mail_search_addr (env->from,pgm->from)) ||
- (pgm->to && !mail_search_addr (env->to,pgm->to)) ||
- (pgm->subject && !mail_search_header_text (env->subject,pgm->subject)))
- return NIL;
- /* These criteria are not supported by IMAP and have to be emulated */
- if ((pgm->return_path &&
- !mail_search_addr (env->return_path,pgm->return_path)) ||
- (pgm->sender && !mail_search_addr (env->sender,pgm->sender)) ||
- (pgm->reply_to && !mail_search_addr (env->reply_to,pgm->reply_to)) ||
- (pgm->in_reply_to &&
- !mail_search_header_text (env->in_reply_to,pgm->in_reply_to)) ||
- (pgm->message_id &&
- !mail_search_header_text (env->message_id,pgm->message_id)) ||
- (pgm->newsgroups &&
- !mail_search_header_text (env->newsgroups,pgm->newsgroups)) ||
- (pgm->followup_to &&
- !mail_search_header_text (env->followup_to,pgm->followup_to)) ||
- (pgm->references &&
- !mail_search_header_text (env->references,pgm->references)))
- return NIL;
- }
- /* search header lines */
- for (hdr = pgm->header; hdr; hdr = hdr->next) {
- char *t,*e,*v;
- SIZEDTEXT s;
- STRINGLIST sth,stc;
- sth.next = stc.next = NIL; /* only one at a time */
- sth.text.data = hdr->line.data;
- sth.text.size = hdr->line.size;
- /* get the header text */
- if ((t = mail_fetch_header (stream,msgno,NIL,&sth,&s.size,
- FT_INTERNAL | FT_PEEK)) && strchr (t,':')) {
- if (hdr->text.size) { /* anything matches empty search string */
- /* non-empty, copy field data */
- s.data = (unsigned char *) fs_get (s.size + 1);
- /* for each line */
- for (v = (char *) s.data, e = t + s.size; t < e;) switch (*t) {
- default: /* non-continuation, skip leading field name */
- while ((t < e) && (*t++ != ':'));
- if ((t < e) && (*t == ':')) t++;
- case 't': case ' ': /* copy field data */
- while ((t < e) && (*t != ' 15') && (*t != ' 12')) *v++ = *t++;
- *v++ = 'n'; /* tie off line */
- while (((*t == ' 15') || (*t == ' 12')) && (t < e)) t++;
- }
- /* calculate true size */
- s.size = v - (char *) s.data;
- *v = ' '; /* tie off results */
- stc.text.data = hdr->text.data;
- stc.text.size = hdr->text.size;
- /* search header */
- if (mail_search_header (&s,&stc)) fs_give ((void **) &s.data);
- else { /* search failed */
- fs_give ((void **) &s.data);
- return NIL;
- }
- }
- }
- else return NIL; /* no matching header text */
- }
- /* search strings */
- if ((pgm->text && !mail_search_text (stream,msgno,section,pgm->text,LONGT))||
- (pgm->body && !mail_search_text (stream,msgno,section,pgm->body,NIL)))
- return NIL;
- /* logical conditions */
- for (or = pgm->or; or; or = or->next)
- if (!(mail_search_msg (stream,msgno,section,or->first) ||
- mail_search_msg (stream,msgno,section,or->second))) return NIL;
- for (not = pgm->not; not; not = not->next)
- if (mail_search_msg (stream,msgno,section,not->pgm)) return NIL;
- return T;
- }
- /* Mail search message header null-terminated text
- * Accepts: header text
- * strings to search
- * Returns: T if search found a match
- */
- long mail_search_header_text (char *s,STRINGLIST *st)
- {
- SIZEDTEXT h;
- /* have any text? */
- if (h.data = (unsigned char *) s) {
- h.size = strlen (s); /* yes, get its size */
- return mail_search_header (&h,st);
- }
- return NIL;
- }
- /* Mail search message header
- * Accepts: header as sized text
- * strings to search
- * Returns: T if search found a match
- */
- long mail_search_header (SIZEDTEXT *hdr,STRINGLIST *st)
- {
- SIZEDTEXT h;
- long ret = LONGT;
- utf8_mime2text (hdr,&h); /* make UTF-8 version of header */
- while (h.size && ((h.data[h.size-1]==' 15') || (h.data[h.size-1]==' 12')))
- --h.size; /* slice off trailing newlines */
- do if (h.size ? /* search non-empty string */
- !search (h.data,h.size,st->text.data,st->text.size) : st->text.size)
- ret = NIL;
- while (ret && (st = st->next));
- if (h.data != hdr->data) fs_give ((void **) &h.data);
- return ret;
- }
- /* Mail search message body
- * Accepts: MAIL stream
- * message number
- * optional section specification
- * string list
- * flags
- * Returns: T if search found a match
- */
- long mail_search_text (MAILSTREAM *stream,unsigned long msgno,char *section,
- STRINGLIST *st,long flags)
- {
- BODY *body;
- long ret = NIL;
- STRINGLIST *s = mail_newstringlist ();
- mailgets_t omg = mailgets;
- if (stream->dtb->flags & DR_LOWMEM) mailgets = mail_search_gets;
- /* strings to search */
- for (stream->private.search.string = s; st;) {
- s->text.data = st->text.data;
- s->text.size = st->text.size;
- if (st = st->next) s = s->next = mail_newstringlist ();
- }
- stream->private.search.text = NIL;
- if (flags) { /* want header? */
- SIZEDTEXT s,t;
- s.data = (unsigned char *)
- mail_fetch_header (stream,msgno,section,NIL,&s.size,FT_INTERNAL|FT_PEEK);
- utf8_mime2text (&s,&t);
- ret = mail_search_string (&t,"UTF-8",&stream->private.search.string);
- if (t.data != s.data) fs_give ((void **) &t.data);
- }
- if (!ret) { /* still looking for match? */
- /* no section, get top-level body */
- if (!section) mail_fetchstructure (stream,msgno,&body);
- /* get body of nested message */
- else if ((body = mail_body (stream,msgno,section)) &&
- (body->type == TYPEMULTIPART) && body->subtype &&
- !strcmp (body->subtype,"RFC822")) body = body->nested.msg->body;
- if (body) ret = mail_search_body (stream,msgno,body,NIL,1,flags);
- }
- mailgets = omg; /* restore former gets routine */
- /* clear searching */
- for (s = stream->private.search.string; s; s = s->next) s->text.data = NIL;
- mail_free_stringlist (&stream->private.search.string);
- stream->private.search.text = NIL;
- return ret;
- }
- /* Mail search message body text parts
- * Accepts: MAIL stream
- * message number
- * current body pointer
- * hierarchical level prefix
- * position at current hierarchical level
- * string list
- * flags
- * Returns: T if search found a match
- */
- long mail_search_body (MAILSTREAM *stream,unsigned long msgno,BODY *body,
- char *prefix,unsigned long section,long flags)
- {
- long ret = NIL;
- unsigned long i;
- char *s,*t,sect[MAILTMPLEN];
- SIZEDTEXT st,h;
- PART *part;
- PARAMETER *param;
- if (prefix && (strlen (prefix) > (MAILTMPLEN - 20))) return NIL;
- sprintf (sect,"%s%lu",prefix ? prefix : "",section++);
- if (flags && prefix) { /* want to search MIME header too? */
- st.data = (unsigned char *) mail_fetch_mime (stream,msgno,sect,&st.size,
- FT_INTERNAL | FT_PEEK);
- if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
- else {
- utf8_mime2text (&st,&h); /* make UTF-8 version of header */
- ret = mail_search_string (&h,"UTF-8",&stream->private.search.string);
- if (h.data != st.data) fs_give ((void **) &h.data);
- }
- }
- if (!ret) switch (body->type) {
- case TYPEMULTIPART:
- /* extend prefix if not first time */
- s = prefix ? strcat (sect,".") : "";
- for (i = 1,part = body->nested.part; part && !ret; i++,part = part->next)
- ret = mail_search_body (stream,msgno,&part->body,s,i,flags);
- break;
- case TYPEMESSAGE:
- if (flags) { /* want to search nested message header? */
- st.data = (unsigned char *)
- mail_fetch_header (stream,msgno,sect,NIL,&st.size,
- FT_INTERNAL | FT_PEEK);
- if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
- else {
- utf8_mime2text (&st,&h);/* make UTF-8 version of header */
- ret = mail_search_string (&h,"UTF-8",&stream->private.search.string);
- if (h.data != st.data) fs_give ((void **) &h.data);
- }
- }
- if (body = body->nested.msg->body)
- ret = (body->type == TYPEMULTIPART) ?
- mail_search_body (stream,msgno,body,(prefix ? prefix : ""),section - 1,
- flags) :
- mail_search_body (stream,msgno,body,strcat (sect,"."),1,flags);
- break;
- case TYPETEXT:
- s = mail_fetch_body (stream,msgno,sect,&i,FT_INTERNAL | FT_PEEK);
- if (stream->dtb->flags & DR_LOWMEM) ret = stream->private.search.result;
- else {
- for (t = NIL,param = body->parameter; param && !t; param = param->next)
- if (!strcmp (param->attribute,"CHARSET")) t = param->value;
- switch (body->encoding) { /* what encoding? */
- case ENCBASE64:
- if (st.data = (unsigned char *)
- rfc822_base64 ((unsigned char *) s,i,&st.size)) {
- ret = mail_search_string (&st,t,&stream->private.search.string);
- fs_give ((void **) &st.data);
- }
- break;
- case ENCQUOTEDPRINTABLE:
- if (st.data = rfc822_qprint ((unsigned char *) s,i,&st.size)) {
- ret = mail_search_string (&st,t,&stream->private.search.string);
- fs_give ((void **) &st.data);
- }
- break;
- default:
- st.data = (unsigned char *) s;
- st.size = i;
- ret = mail_search_string (&st,t,&stream->private.search.string);
- break;
- }
- }
- break;
- }
- return ret;
- }
- /* Mail search text
- * Accepts: sized text to search
- * character set of sized text
- * string list of search keys
- * Returns: T if search found a match
- */
- long mail_search_string (SIZEDTEXT *s,char *charset,STRINGLIST **st)
- {
- void *t;
- SIZEDTEXT u;
- STRINGLIST **sc = st;
- if (utf8_text (s,charset,&u,NIL)) {
- while (*sc) { /* run down criteria list */
- if (search (u.data,u.size,(*sc)->text.data,(*sc)->text.size)) {
- t = (void *) (*sc); /* found one, need to flush this */
- *sc = (*sc)->next; /* remove it from the list */
- fs_give (&t); /* flush the buffer */
- }
- else sc = &(*sc)->next; /* move to next in list */
- }
- if (u.data != s->data) fs_give ((void **) &u.data);
- }
- return *st ? NIL : LONGT;
- }
- /* Mail search keyword
- * Accepts: MAIL stream
- * elt to get flags from
- * keyword list
- * Returns: T if search found a match
- */
- long mail_search_keyword (MAILSTREAM *stream,MESSAGECACHE *elt,STRINGLIST *st)
- {
- int i;
- char tmp[MAILTMPLEN],tmp2[MAILTMPLEN];
- do { /* get uppercase form of flag */
- sprintf (tmp,"%.900s",(char *) st->text.data);
- ucase (tmp);
- for (i = 0;; ++i) { /* check each possible keyword */
- if (i >= NUSERFLAGS || !stream->user_flags[i]) return NIL;
- if (elt->user_flags & (1 << i)) {
- sprintf (tmp2,"%.901s",stream->user_flags[i]);
- if (!strcmp (tmp,ucase (tmp2))) break;
- }
- }
- }
- while (st = st->next); /* try next keyword */
- return T;
- }
- /* Mail search an address list
- * Accepts: address list
- * string list
- * Returns: T if search found a match
- */
- #define SEARCHBUFLEN (size_t) 2000
- #define SEARCHBUFSLOP (size_t) 5
- long mail_search_addr (ADDRESS *adr,STRINGLIST *st)
- {
- ADDRESS *a,tadr;
- SIZEDTEXT txt;
- char tmp[MAILTMPLEN];
- size_t i = SEARCHBUFLEN;
- size_t k;
- long ret = NIL;
- if (adr) {
- txt.data = (unsigned char *) fs_get (i + SEARCHBUFSLOP);
- /* never an error or next */
- tadr.error = NIL,tadr.next = NIL;
- /* write address list */
- for (txt.size = 0,a = adr; a; a = a->next) {
- k = (tadr.mailbox = a->mailbox) ? 2*strlen (a->mailbox) : 3;
- if (tadr.personal = a->personal) k += 3 + 2*strlen (a->personal);
- if (tadr.adl = a->adl) k += 1 + 2*strlen (a->adl);
- if (tadr.host = a->host) k += 1 + 2*strlen (a->host);
- if (k < MAILTMPLEN) { /* ignore ridiculous addresses */
- tmp[0] = ' ';
- rfc822_write_address (tmp,&tadr);
- /* resize buffer if necessary */
- if (((k = strlen (tmp)) + txt.size) > i)
- fs_resize ((void **) &txt.data,SEARCHBUFSLOP + (i += SEARCHBUFLEN));
- /* add new address */
- memcpy (txt.data + txt.size,tmp,k);
- txt.size += k;
- /* another address follows */
- if (a->next) txt.data[txt.size++] = ',';
- }
- }
- txt.data[txt.size] = ' '; /* tie off string */
- ret = mail_search_header (&txt,st);
- fs_give ((void **) &txt.data);
- }
- return ret;
- }
- /* Get string for low-memory searching
- * Accepts: readin function pointer
- * stream to use
- * number of bytes
- * gets data packet
- * mail stream
- * message number
- * descriptor string
- * option flags
- * Returns: NIL, always
- */
- #define SEARCHSLOP 128
- char *mail_search_gets (readfn_t f,void *stream,unsigned long size,
- GETS_DATA *md)
- {
- unsigned long i;
- char tmp[MAILTMPLEN+SEARCHSLOP+1];
- SIZEDTEXT st;
- /* better not be called unless searching */
- if (!md->stream->private.search.string) {
- sprintf (tmp,"Search botch, mbx = %.80s, %s = %lu[%.80s]",
- md->stream->mailbox,
- (md->flags & FT_UID) ? "UID" : "msg",md->msgno,md->what);
- fatal (tmp);
- }
- /* initially no match for search */
- md->stream->private.search.result = NIL;
- /* make sure buffer clear */
- memset (st.data = (unsigned char *) tmp,' ',
- (size_t) MAILTMPLEN+SEARCHSLOP+1);
- /* read first buffer */
- (*f) (stream,st.size = i = min (size,(long) MAILTMPLEN),tmp);
- /* search for text */
- if (mail_search_string (&st,NIL,&md->stream->private.search.string))
- md->stream->private.search.result = T;
- else if (size -= i) { /* more to do, blat slop down */
- memmove (tmp,tmp+MAILTMPLEN-SEARCHSLOP,(size_t) SEARCHSLOP);
- do { /* read subsequent buffers one at a time */
- (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp+SEARCHSLOP);
- st.size = i + SEARCHSLOP;
- if (mail_search_string (&st,NIL,&md->stream->private.search.string))
- md->stream->private.search.result = T;
- else memmove (tmp,tmp+MAILTMPLEN,(size_t) SEARCHSLOP);
- }
- while ((size -= i) && !md->stream->private.search.result);
- }
- if (size) { /* toss out everything after that */
- do (*f) (stream,i = min (size,(long) MAILTMPLEN),tmp);
- while (size -= i);
- }
- return NIL;
- }
- /* Mail parse search criteria
- * Accepts: criteria
- * Returns: search program if parse successful, else NIL
- */
- SEARCHPGM *mail_criteria (char *criteria)
- {
- SEARCHPGM *pgm;
- char tmp[MAILTMPLEN];
- int f = NIL;
- if (!criteria) return NIL; /* return if no criteria */
- pgm = mail_newsearchpgm (); /* make a basic search program */
- /* for each criterion */
- for (criteria = strtok (criteria," "); criteria;
- (criteria = strtok (NIL," "))) {
- f = NIL; /* init then scan the criterion */
- switch (*ucase (criteria)) {
- case 'A': /* possible ALL, ANSWERED */
- if (!strcmp (criteria+1,"LL")) f = T;
- else if (!strcmp (criteria+1,"NSWERED")) f = pgm->answered = T;
- break;
- case 'B': /* possible BCC, BEFORE, BODY */
- if (!strcmp (criteria+1,"CC")) f = mail_criteria_string (&pgm->bcc);
- else if (!strcmp (criteria+1,"EFORE"))
- f = mail_criteria_date (&pgm->before);
- else if (!strcmp (criteria+1,"ODY")) f=mail_criteria_string (&pgm->body);
- break;
- case 'C': /* possible CC */
- if (!strcmp (criteria+1,"C")) f = mail_criteria_string (&pgm->cc);
- break;
- case 'D': /* possible DELETED */
- if (!strcmp (criteria+1,"ELETED")) f = pgm->deleted = T;
- break;
- case 'F': /* possible FLAGGED, FROM */
- if (!strcmp (criteria+1,"LAGGED")) f = pgm->flagged = T;
- else if (!strcmp (criteria+1,"ROM")) f=mail_criteria_string (&pgm->from);
- break;
- case 'K': /* possible KEYWORD */
- if (!strcmp (criteria+1,"EYWORD"))
- f = mail_criteria_string (&pgm->keyword);
- break;
- case 'N': /* possible NEW */
- if (!strcmp (criteria+1,"EW")) f = pgm->recent = pgm->unseen = T;
- break;
- case 'O': /* possible OLD, ON */
- if (!strcmp (criteria+1,"LD")) f = pgm->old = T;
- else if (!strcmp (criteria+1,"N")) f = mail_criteria_date (&pgm->on);
- break;
- case 'R': /* possible RECENT */
- if (!strcmp (criteria+1,"ECENT")) f = pgm->recent = T;
- break;
- case 'S': /* possible SEEN, SINCE, SUBJECT */
- if (!strcmp (criteria+1,"EEN")) f = pgm->seen = T;
- else if (!strcmp (criteria+1,"INCE")) f=mail_criteria_date (&pgm->since);
- else if (!strcmp (criteria+1,"UBJECT"))
- f = mail_criteria_string (&pgm->subject);
- break;
- case 'T': /* possible TEXT, TO */
- if (!strcmp (criteria+1,"EXT")) f = mail_criteria_string (&pgm->text);
- else if (!strcmp (criteria+1,"O")) f = mail_criteria_string (&pgm->to);
- break;
- case 'U': /* possible UN* */
- if (criteria[1] == 'N') {
- if (!strcmp (criteria+2,"ANSWERED")) f = pgm->unanswered = T;
- else if (!strcmp (criteria+2,"DELETED")) f = pgm->undeleted = T;
- else if (!strcmp (criteria+2,"FLAGGED")) f = pgm->unflagged = T;
- else if (!strcmp (criteria+2,"KEYWORD"))
- f = mail_criteria_string (&pgm->unkeyword);
- else if (!strcmp (criteria+2,"SEEN")) f = pgm->unseen = T;
- }
- break;
- default: /* we will barf below */
- break;
- }
- if (!f) { /* if can't determine any criteria */
- sprintf (tmp,"Unknown search criterion: %.30s",criteria);
- mm_log (tmp,ERROR);
- mail_free_searchpgm (&pgm);
- break;
- }
- }
- return pgm;
- }
- /* Parse a date
- * Accepts: pointer to date integer to return
- * Returns: T if successful, else NIL
- */
- int mail_criteria_date (unsigned short *date)
- {
- STRINGLIST *s = NIL;
- MESSAGECACHE elt;
- /* parse the date and return fn if OK */
- int ret = (mail_criteria_string (&s) &&
- mail_parse_date (&elt,(char *) s->text.data) &&
- (*date = (elt.year << 9) + (elt.month << 5) + elt.day)) ?
- T : NIL;
- if (s) mail_free_stringlist (&s);
- return ret;
- }
- /* Parse a string
- * Accepts: pointer to stringlist
- * Returns: T if successful, else NIL
- */
- int mail_criteria_string (STRINGLIST **s)
- {
- unsigned long n;
- char e,*d,*end = " ",*c = strtok (NIL,"");
- if (!c) return NIL; /* missing argument */
- switch (*c) { /* see what the argument is */
- case '{': /* literal string */
- n = strtoul (c+1,&d,10); /* get its length */
- if ((*d++ == '}') && (*d++ == ' 15') && (*d++ == ' 12') &&
- (!(*(c = d + n)) || (*c == ' '))) {
- e = *--c; /* store old delimiter */
- *c = DELIM; /* make sure not a space */
- strtok (c," "); /* reset the strtok mechanism */
- *c = e; /* put character back */
- break;
- }
- case ' ': /* catch bogons */
- case ' ':
- return NIL;
- case '"': /* quoted string */
- if (strchr (c+1,'"')) end = """;
- else return NIL; /* falls through */
- default: /* atomic string */
- if (d = strtok (c,end)) n = strlen (d);
- else return NIL;
- break;
- }
- while (*s) s = &(*s)->next; /* find tail of list */
- *s = mail_newstringlist (); /* make new entry */
- /* return the data */
- (*s)->text.data = (unsigned char *) cpystr (d);
- (*s)->text.size = n;
- return T;
- }
- /* Mail sort messages
- * Accepts: mail stream
- * character set
- * search program
- * sort program
- * option flags
- * Returns: vector of sorted message sequences or NIL if error
- */
- unsigned long *mail_sort (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
- SORTPGM *pgm,long flags)
- {
- unsigned long *ret = NIL;
- if (stream->dtb) /* do the driver's action */
- ret = (*(stream->dtb->sort ? stream->dtb->sort : mail_sort_msgs))
- (stream,charset,spg,pgm,flags);
- /* flush search/sort programs if requested */
- if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
- if (flags & SO_FREE) mail_free_sortpgm (&pgm);
- return ret;
- }
- /* Mail sort messages work routine
- * Accepts: mail stream
- * character set
- * search program
- * sort program
- * option flags
- * Returns: vector of sorted message sequences or NIL if error
- */
- unsigned long *mail_sort_msgs (MAILSTREAM *stream,char *charset,SEARCHPGM *spg,
- SORTPGM *pgm,long flags)
- {
- unsigned long i;
- SORTCACHE **sc;
- unsigned long *ret = NIL;
- if (spg) { /* only if a search needs to be done */
- int silent = stream->silent;
- stream->silent = T; /* don't pass up mm_searched() events */
- /* search for messages */
- mail_search_full (stream,charset,spg,NIL);
- stream->silent = silent; /* restore silence state */
- }
- /* initialize progress counters */
- pgm->nmsgs = pgm->progress.cached = 0;
- /* pass 1: count messages to sort */
- for (i = 1; i <= stream->nmsgs; ++i)
- if (mail_elt (stream,i)->searched) pgm->nmsgs++;
- if (pgm->nmsgs) { /* pass 2: sort cache */
- sc = mail_sort_loadcache (stream,pgm);
- /* pass 3: sort messages */
- if (!pgm->abort) ret = mail_sort_cache (stream,pgm,sc,flags);
- fs_give ((void **) &sc); /* don't need sort vector any more */
- }
- /* empty sort results */
- else ret = (unsigned long *) memset (fs_get (sizeof (unsigned long)),0,
- sizeof (unsigned long));
- /* also return via callback if requested */
- if (mailsortresults) (*mailsortresults) (stream,ret,pgm->nmsgs);
- return ret; /* return sort results */
- }
- /* Mail sort sortcache vector
- * Accepts: mail stream
- * sort program
- * sortcache vector
- * option flags
- * Returns: vector of sorted message sequences or NIL if error
- */
- unsigned long *mail_sort_cache (MAILSTREAM *stream,SORTPGM *pgm,SORTCACHE **sc,
- long flags)
- {
- unsigned long i,*ret;
- /* pass 3: sort messages */
- qsort ((void *) sc,pgm->nmsgs,sizeof (SORTCACHE *),mail_sort_compare);
- /* optional post sorting */
- if (pgm->postsort) (*pgm->postsort) ((void *) sc);
- /* pass 4: return results */
- ret = (unsigned long *) fs_get ((pgm->nmsgs+1) * sizeof (unsigned long));
- if (flags & SE_UID) /* UID or msgno? */
- for (i = 0; i < pgm->nmsgs; i++) ret[i] = mail_uid (stream,sc[i]->num);
- else for (i = 0; i < pgm->nmsgs; i++) ret[i] = sc[i]->num;
- ret[pgm->nmsgs] = 0; /* tie off message list */
- return ret;
- }
- /* Mail load sortcache
- * Accepts: mail stream, already searched
- * sort program
- * Returns: vector of sortcache pointers matching search
- */
- static STRINGLIST maildateline = {{(unsigned char *) "date",4},NIL};
- static STRINGLIST mailrnfromline = {{(unsigned char *) ">from",5},NIL};
- static STRINGLIST mailfromline = {{(unsigned char *) "from",4},
- &mailrnfromline};
- static STRINGLIST mailtonline = {{(unsigned char *) "to",2},NIL};
- static STRINGLIST mailccline = {{(unsigned char *) "cc",2},NIL};
- static STRINGLIST mailsubline = {{(unsigned char *) "subject",7},NIL};
- SORTCACHE **mail_sort_loadcache (MAILSTREAM *stream,SORTPGM *pgm)
- {
- char *t,*v,*x,tmp[MAILTMPLEN];
- SORTPGM *pg;
- SORTCACHE *s,**sc;
- MESSAGECACHE *elt,telt;
- ENVELOPE *env;
- ADDRESS *adr = NIL;
- unsigned long i = (pgm->nmsgs) * sizeof (SORTCACHE *);
- sc = (SORTCACHE **) memset (fs_get ((size_t) i),0,(size_t) i);
- /* see what needs to be loaded */
- for (i = 1; !pgm->abort && (i <= stream->nmsgs); i++)
- if ((elt = mail_elt (stream,i))->searched) {
- sc[pgm->progress.cached++] =
- s = (SORTCACHE *) (*mailcache) (stream,i,CH_SORTCACHE);
- s->pgm = pgm; /* note sort program */
- s->num = i;
- /* get envelope if cached */
- if (stream->scache) env = (i == stream->msgno) ? stream->env : NIL;
- else env = elt->private.msg.env;
- for (pg = pgm; pg; pg = pg->next) switch (pg->function) {
- case SORTARRIVAL: /* sort by arrival date */
- if (!s->arrival) {
- if (!elt->day) { /* if date unknown */
- sprintf (tmp,"%lu",i);
- mail_fetch_fast (stream,tmp,NIL);
- }
- /* wrong thing before 3-Jan-1970 */
- s->arrival = elt->day ? mail_longdate (elt) : 1;
- }
- break;
- case SORTSIZE: /* sort by message size */
- if (!s->size) {
- if (!elt->rfc822_size) {
- sprintf (tmp,"%lu",i);
- mail_fetch_fast (stream,tmp,NIL);
- }
- s->size = elt->rfc822_size ? elt->rfc822_size : 1;
- }
- break;
- case SORTDATE: /* sort by date */
- if (!s->date) {
- if (env) t = env->date;
- else if ((t = mail_fetch_header (stream,i,NIL,&maildateline,NIL,
- FT_INTERNAL | FT_PEEK)) &&
- (t = strchr (t,':')))
- for (x = ++t; x = strpbrk (x,"