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,"1215"); x++)
- switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
- case ' ': /* erase continuation newlines */
- case 't':
- memmove (x,v,strlen (v));
- break;
- default: /* tie off extraneous text */
- *x = x[1] = '';
- }
- /* skip leading whitespace */
- if (t) while ((*t == ' ') || (*t == 't')) t++;
- /* wrong thing before 3-Jan-1970 */
- if (!(t && mail_parse_date (&telt,t) &&
- (s->date = mail_longdate (&telt)))) s->date = 1;
- }
- break;
- case SORTFROM: /* sort by first from */
- if (!s->from) {
- if (env) s->from = env->from && env->from->mailbox ?
- cpystr (env->from->mailbox) : NIL;
- else if ((t = mail_fetch_header (stream,i,NIL,&mailfromline,NIL,
- FT_INTERNAL | FT_PEEK)) &&
- (t = strchr (t,':'))) {
- for (x = ++t; x = strpbrk (x,"1215"); x++)
- switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
- case ' ': /* erase continuation newlines */
- case 't':
- memmove (x,v,strlen (v));
- break;
- case 'f': /* continuation but with extra "From:" */
- case 'F':
- if (v = strchr (v,':')) {
- memmove (x,v+1,strlen (v+1));
- break;
- }
- default: /* tie off extraneous text */
- *x = x[1] = '';
- }
- if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
- s->from = adr->mailbox;
- adr->mailbox = NIL;
- mail_free_address (&adr);
- }
- }
- if (!s->from) s->from = cpystr ("");
- }
- break;
- case SORTTO: /* sort by first to */
- if (!s->to) {
- if (env) s->to = env->to && env->to->mailbox ?
- cpystr (env->to->mailbox) : NIL;
- else if ((t = mail_fetch_header (stream,i,NIL,&mailtonline,NIL,
- FT_INTERNAL | FT_PEEK)) &&
- (t = strchr (t,':'))) {
- for (x = ++t; x = strpbrk (x,"1215"); x++)
- switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
- case ' ': /* erase continuation newlines */
- case 't':
- memmove (x,v,strlen (v));
- break;
- case 't': /* continuation but with extra "To:" */
- case 'T':
- if (v = strchr (v,':')) {
- memmove (x,v+1,strlen (v+1));
- break;
- }
- default: /* tie off extraneous text */
- *x = x[1] = '';
- }
- if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
- s->to = adr->mailbox;
- adr->mailbox = NIL;
- mail_free_address (&adr);
- }
- }
- if (!s->to) s->to = cpystr ("");
- }
- break;
- case SORTCC: /* sort by first cc */
- if (!s->cc) {
- if (env) s->cc = env->cc && env->cc->mailbox ?
- cpystr (env->cc->mailbox) : NIL;
- else if ((t = mail_fetch_header (stream,i,NIL,&mailccline,NIL,
- FT_INTERNAL | FT_PEEK)) &&
- (t = strchr (t,':'))) {
- for (x = ++t; x = strpbrk (x,"1215"); x++)
- switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
- case ' ': /* erase continuation newlines */
- case 't':
- memmove (x,v,strlen (v));
- break;
- case 't': /* continuation but with extra "To:" */
- case 'T':
- if (v = strchr (v,':')) {
- memmove (x,v+1,strlen (v+1));
- break;
- }
- default: /* tie off extraneous text */
- *x = x[1] = '';
- }
- if (adr = rfc822_parse_address (&adr,adr,&t,BADHOST,0)) {
- s->cc = adr->mailbox;
- adr->mailbox = NIL;
- mail_free_address (&adr);
- }
- }
- if (!s->cc) s->cc = cpystr ("");
- }
- break;
- case SORTSUBJECT: /* sort by subject */
- if (!s->subject) {
- /* get subject from envelope if have one */
- if (env) t = env->subject ? env->subject : "";
- /* otherwise snarf from header text */
- else if ((t = mail_fetch_header (stream,i,NIL,&mailsubline,
- NIL,FT_INTERNAL | FT_PEEK)) &&
- (t = strchr (t,':')))
- for (x = ++t; x = strpbrk (x,"1215"); x++)
- switch (*(v = ((*x == '15') && (x[1] == '12')) ? x+2 : x+1)){
- case ' ': /* erase continuation newlines */
- case 't':
- memmove (x,v,strlen (v));
- break;
- default: /* tie off extraneous text */
- *x = x[1] = '';
- }
- else t = ""; /* empty subject */
- /* strip and cache subject */
- s->subject = mail_strip_subject (t);
- }
- break;
- default:
- fatal ("Unknown sort function");
- }
- }
- return sc;
- }
- /* Strip subjects of extra spaces and leading and trailing cruft for sorting
- * Accepts: unstripped subject
- * Returns: stripped subject, in cpystr form
- */
- char *mail_strip_subject (char *t)
- {
- char c,*s,*x;
- SIZEDTEXT src,dst;
- /* flush leading whitespace, [, and "re" */
- for (x = t; x; ) switch (*t) {
- case ' ': case 't':
- t++; /* leading whitespace */
- break;
- case '[': /* leading [ */
- for (x = ++t; x;) switch (*x) {
- case ']': /* closing ] */
- while (x) switch (*++x) {
- case '(': /* possible trailing (fwd) */
- if (((x[1] == 'F') || (x[1] == 'f')) &&
- ((x[2] == 'W') || (x[2] == 'w')) &&
- ((x[3] == 'D') || (x[3] == 'd')) && x[4] == ')') {
- x += 4; /* yes, skip past it */
- break; /* continue scan */
- }
- /* else fall through */
- default:
- t = x; /* found usable text */
- case '': /* end of string */
- x = NIL; /* terminate search here */
- case ' ': case 't': /* skip whitespace */
- break;
- }
- break;
- case '': case '[': /* end of string or nesting */
- x = NIL; /* just punt */
- break;
- default: /* any other character */
- x++; /* sniff at next */
- break;
- }
- x = t; /* finished handling [...] */
- break;
- case 'R': case 'r': /* possible "re" */
- if ((t[1] != 'E') && (t[1] != 'e')) x = NIL;
- else { /* found re, skip leading whitespace */
- for (x = t + 2; (*x == ' ') || (*x == 't'); x++);
- switch (*x++) { /* what comes after? */
- case ':': /* "re:" */
- t = x;
- break;
- case '[': /* possible "re[babble]:" */
- if (x = strchr (x,']')) {
- for (x++; ((*x == ' ') || (*x == 't')); x++);
- if (*x == ':') t = ++x;
- else x = NIL;
- }
- break;
- default: /* something significant starting with "re" */
- x = NIL; /* terminate the loop */
- break;
- }
- }
- break;
- case 'F': case 'f': /* possible "fw" or "fwd" */
- if ((t[1] != 'W') && (t[1] != 'w')) x = NIL;
- else { /* found fw, skip "D" and leading whitespace */
- for (x = ((t[2] == 'D') || (t[2] == 'd')) ? t + 3 : t + 2;
- (*x == ' ') || (*x == 't'); x++);
- switch (*x++) { /* what comes after? */
- case ':': /* "fwd:" */
- t = x;
- break;
- case '[': /* possible "fwd[babble]:" */
- if (x = strchr (x,']')) {
- for (x++; ((*x == ' ') || (*x == 't')); x++);
- if (*x == ':') t = ++x;
- else x = NIL;
- }
- break;
- default: /* something significant starting with "fwd" */
- x = NIL; /* terminate the loop */
- break;
- }
- }
- break;
- default: /* something significant */
- x = NIL; /* terminate the loop */
- break;
- }
- /* have an empty subject? */
- if (!(src.size = strlen (t))) return cpystr ("");
- src.data = (unsigned char *) t;
- /* make copy, convert MIME2 if needed */
- if (utf8_mime2text (&src,&dst) && (src.data != dst.data))
- t = (x = (char *) dst.data) + dst.size;
- else t = (x = cpystr ((char *) src.data)) + src.size;
- while (t > x) { /* flush trailing "(fwd)", ], and whitespace */
- while ((t[-1] == ' ') || (t[-1] == 't') || (t[-1] == ']')) t--;
- if ((t >= (x + 5)) && (t[-5] == '(') &&
- ((t[-4] == 'F') || (t[-4] == 'f')) &&
- ((t[-3] == 'W') || (t[-3] == 'w')) &&
- ((t[-2] == 'D') || (t[-2] == 'd')) && (t[-1] == ')')) t -= 5;
- else break;
- }
- *t = ''; /* tie off subject string */
- /* convert spaces to tab, strip extra spaces */
- for (s = t = x, c = 'x'; *t; t++) {
- if (c != ' ') c = *s++ = ((*t == 't') ? ' ' : *t);
- else if ((*t != 't') && (*t != ' ')) c = *s++ = *t;
- }
- *s = ''; /* tie off string again */
- return x; /* return stripped subject */
- }
- /* Sort compare messages
- * Accept: first message sort cache element
- * second message sort cache element
- * Returns: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
- */
- int mail_sort_compare (const void *a1,const void *a2)
- {
- int i = 0;
- SORTCACHE *s1 = *(SORTCACHE **) a1;
- SORTCACHE *s2 = *(SORTCACHE **) a2;
- SORTPGM *pgm = s1->pgm;
- if (!s1->sorted) { /* this one sorted yet? */
- s1->sorted = T;
- pgm->progress.sorted++; /* another sorted message */
- }
- if (!s2->sorted) { /* this one sorted yet? */
- s2->sorted = T;
- pgm->progress.sorted++; /* another sorted message */
- }
- do {
- switch (pgm->function) { /* execute search program */
- case SORTDATE: /* sort by date */
- i = mail_compare_ulong (s1->date,s2->date);
- break;
- case SORTARRIVAL: /* sort by arrival date */
- i = mail_compare_ulong (s1->arrival,s2->arrival);
- break;
- case SORTSIZE: /* sort by message size */
- i = mail_compare_ulong (s1->size,s2->size);
- break;
- case SORTFROM: /* sort by first from */
- i = mail_compare_cstring (s1->from,s2->from);
- break;
- case SORTTO: /* sort by first to */
- i = mail_compare_cstring (s1->to,s2->to);
- break;
- case SORTCC: /* sort by first cc */
- i = mail_compare_cstring (s1->cc,s2->cc);
- break;
- case SORTSUBJECT: /* sort by subject */
- i = mail_compare_cstring (s1->subject,s2->subject);
- break;
- }
- if (pgm->reverse) i = -i; /* flip results if necessary */
- }
- while (pgm = i ? NIL : pgm->next);
- return i ? i : (s1->num - s2->num);
- }
- /* Compare two unsigned longs
- * Accepts: first value
- * second value
- * Returns: -1 if l1 < l2, 0 if l1 == l2, 1 if l1 > l2
- */
- int mail_compare_ulong (unsigned long l1,unsigned long l2)
- {
- if (l1 < l2) return -1;
- if (l1 > l2) return 1;
- return 0;
- }
- /* Compare two case-independent strings
- * Accepts: first string
- * second string
- * Returns: -1 if s1 < s2, 0 if s1 == s2, 1 if s1 > s2
- */
- int mail_compare_cstring (char *s1,char *s2)
- {
- int i;
- if (!s1) return s2 ? -1 : 0; /* empty string cases */
- else if (!s2) return 1;
- for (; *s1 && *s2; s1++, s2++)
- if (i = (mail_compare_ulong (isupper (*s1) ? tolower (*s1) : *s1,
- isupper (*s2) ? tolower (*s2) : *s2)))
- return i; /* found a difference */
- if (*s1) return 1; /* first string is longer */
- return *s2 ? -1 : 0; /* second string longer : strings identical */
- }
- /* Return message date as an unsigned long seconds since time began
- * Accepts: message cache pointer
- * Returns: unsigned long of date
- */
- unsigned long mail_longdate (MESSAGECACHE *elt)
- {
- unsigned long yr = elt->year + BASEYEAR;
- /* number of days since time began */
- #ifndef USEORTHODOXCALENDAR /* Gregorian calendar */
- unsigned long ret = (elt->day - 1) + 30 * (elt->month - 1) +
- ((unsigned long) ((elt->month + (elt->month > 8))) / 2) +
- elt->year * 365 + (((unsigned long) (elt->year + (BASEYEAR % 4))) / 4) +
- ((yr / 400) - (BASEYEAR / 400)) - ((yr / 100) - (BASEYEAR / 100)) -
- #ifdef Y4KBUGFIX
- ((yr / 4000) - (BASEYEAR / 4000)) -
- #endif
- ((elt->month < 3) ? !(yr % 4) && ((yr % 100) || !(yr % 400)) : 2);
- #else /* Orthodox calendar */
- unsigned long ret = (elt->day - 1) + 30 * (elt->month - 1) +
- ((unsigned long) ((elt->month + (elt->month > 8))) / 2) +
- elt->year * 365 + (((unsigned long) (elt->year + (BASEYEAR % 4))) / 4) +
- ((2*(yr / 900)) - (2*(BASEYEAR / 900))) +
- (((yr % 900) >= 200) - ((BASEYEAR % 900) >= 200)) +
- (((yr % 900) >= 600) - ((BASEYEAR % 900) >= 600)) -
- ((yr / 100) - (BASEYEAR / 100)) -
- ((elt->month < 3) ? !(yr % 4) &&
- ((yr % 100) || ((yr % 900) == 200) || ((yr % 900) == 600)) :
- 2);
- #endif
- ret *= 24; ret += elt->hours; /* date value in hours */
- ret *= 60; ret +=elt->minutes;/* date value in minutes */
- yr = (elt->zhours * 60) + elt->zminutes;
- if (elt->zoccident) ret += yr;/* cretinous TinyFlaccid C compiler! */
- else ret -= yr;
- ret *= 60; ret += elt->seconds;
- return ret;
- }
- /* Mail thread messages
- * Accepts: mail stream
- * thread type
- * character set
- * search program
- * option flags
- * Returns: thread node tree
- */
- THREADNODE *mail_thread (MAILSTREAM *stream,char *type,char *charset,
- SEARCHPGM *spg,long flags)
- {
- THREADNODE *ret = NIL;
- if (stream->dtb) /* must have a live driver */
- ret = stream->dtb->thread ? /* do driver's action if available */
- (*stream->dtb->thread) (stream,type,charset,spg,flags) :
- mail_thread_msgs (stream,type,charset,spg,flags,mail_sort_msgs);
- /* flush search/sort programs if requested */
- if (spg && (flags & SE_FREE)) mail_free_searchpgm (&spg);
- return ret;
- }
- /* Mail thread messages
- * Accepts: mail stream
- * thread type
- * character set
- * search program
- * option flags
- * sorter routine
- * Returns: thread node tree
- */
- THREADNODE *mail_thread_msgs (MAILSTREAM *stream,char *type,char *charset,
- SEARCHPGM *spg,long flags,sorter_t sorter)
- {
- THREADER *t;
- for (t = &mailthreadlist; t; t = t->next) if (!strcmp (type,t->name)) {
- THREADNODE *ret = (*t->dispatch) (stream,charset,spg,flags,sorter);
- if (mailthreadresults) (*mailthreadresults) (stream,ret);
- return ret;
- }
- mm_log ("No such thread type",ERROR);
- return NIL;
- }
- /* Mail thread ordered subject
- * Accepts: mail stream
- * character set
- * search program
- * option flags
- * sorter routine
- * Returns: thread node tree
- */
- THREADNODE *mail_thread_orderedsubject (MAILSTREAM *stream,char *charset,
- SEARCHPGM *spg,long flags,
- sorter_t sorter)
- {
- THREADNODE *thr = NIL;
- THREADNODE *cur,*top,**tc;
- SORTPGM pgm,pgm2;
- SORTCACHE *s;
- unsigned long i,j,*lst,*ls;
- /* sort by subject+date */
- memset (&pgm,0,sizeof (SORTPGM));
- memset (&pgm2,0,sizeof (SORTPGM));
- pgm.function = SORTSUBJECT;
- pgm.next = &pgm2;
- pgm2.function = SORTDATE;
- if (lst = (*sorter) (stream,charset,spg,&pgm,flags & ~(SE_FREE | SE_UID))){
- if (*(ls = lst)) { /* create thread */
- /* note first subject */
- cur = top = thr = mail_newthreadnode
- ((SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE));
- /* note its number */
- cur->num = (flags & SE_UID) ? mail_uid (stream,*lst) : *lst;
- i = 1; /* number of threads */
- while (*ls) { /* build tree */
- /* subjects match? */
- s = (SORTCACHE *) (*mailcache) (stream,*ls++,CH_SORTCACHE);
- if (mail_compare_cstring (top->sc->subject,s->subject)) {
- i++; /* have a new thread */
- top = top->branch = cur = mail_newthreadnode (s);
- }
- /* another node on this branch */
- else cur = cur->next = mail_newthreadnode (s);
- /* set to msgno or UID as needed */
- cur->num = (flags & SE_UID) ? mail_uid (stream,s->num) : s->num;
- }
- /* size of threadnode cache */
- j = (i + 1) * sizeof (THREADNODE *);
- /* load threadnode cache */
- tc = (THREADNODE **) memset (fs_get ((size_t) j),0,(size_t) j);
- for (j = 0, cur = thr; cur; cur = cur->branch) tc[j++] = cur;
- if (i != j) fatal ("Threadnode cache confusion");
- qsort ((void *) tc,i,sizeof (THREADNODE *),mail_thread_compare_date);
- for (j = 0; j < i; j++) tc[j]->branch = tc[j+1];
- thr = tc[0]; /* head of data */
- fs_give ((void **) &tc);
- }
- fs_give ((void **) &lst);
- }
- return thr;
- }
- /* Thread compare date
- * Accept: first message sort cache element
- * second message sort cache element
- * Returns: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
- */
- int mail_thread_compare_date (const void *a1,const void *a2)
- {
- return mail_compare_ulong ((*(THREADNODE **) a1)->sc->date,
- (*(THREADNODE **) a2)->sc->date);
- }
- /* Mail parse sequence
- * Accepts: mail stream
- * sequence to parse
- * Returns: T if parse successful, else NIL
- */
- long mail_sequence (MAILSTREAM *stream,char *sequence)
- {
- unsigned long i,j,x;
- 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 */
- if (stream->nmsgs) i = stream->nmsgs;
- else {
- mm_log ("No messages, so no maximum message number",ERROR);
- return NIL;
- }
- sequence++; /* skip past * */
- }
- /* parse and validate message number */
- else if (!(i = strtoul ((const char *) sequence,&sequence,10)) ||
- (i > stream->nmsgs)) {
- mm_log ("Sequence invalid",ERROR);
- return NIL;
- }
- switch (*sequence) { /* see what the delimiter is */
- case ':': /* sequence range */
- if (*++sequence == '*') { /* maximum message */
- if (stream->nmsgs) j = stream->nmsgs;
- else {
- mm_log ("No messages, so no maximum message number",ERROR);
- return NIL;
- }
- sequence++; /* skip past * */
- }
- /* parse end of range */
- else if (!(j = strtoul ((const char *) sequence,&sequence,10)) ||
- (j > stream->nmsgs)) {
- mm_log ("Sequence range invalid",ERROR);
- return NIL;
- }
- if (*sequence && *sequence++ != ',') {
- mm_log ("Sequence range syntax error",ERROR);
- return NIL;
- }
- if (i > j) { /* swap the range if backwards */
- x = i; i = j; j = x;
- }
- /* mark each item in the sequence */
- while (i <= j) mail_elt (stream,j--)->sequence = T;
- break;
- case ',': /* single message */
- ++sequence; /* skip the delimiter, fall into end case */
- case '': /* end of sequence, mark this message */
- mail_elt (stream,i)->sequence = T;
- break;
- default: /* anything else is a syntax error! */
- mm_log ("Sequence syntax error",ERROR);
- return NIL;
- }
- }
- return T; /* successfully parsed sequence */
- }
- /* Parse flag list
- * Accepts: MAIL stream
- * flag list as a character string
- * pointer to user flags to return
- * Returns: system flags
- */
- long mail_parse_flags (MAILSTREAM *stream,char *flag,unsigned long *uf)
- {
- char *t,*n,*s,tmp[MAILTMPLEN],flg[MAILTMPLEN],key[MAILTMPLEN];
- short f = 0;
- long i;
- short j;
- *uf = 0; /* initially no user flags */
- if (flag && *flag) { /* no-op if no flag string */
- /* check if a list and make sure valid */
- if (((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) ||
- (strlen (flag) >= MAILTMPLEN)) {
- mm_log ("Bad flag list",ERROR);
- return NIL;
- }
- /* copy the flag string w/o list construct */
- strncpy (n = tmp,flag+i,(j = strlen (flag) - (2*i)));
- tmp[j] = '';
- while ((t = n) && *t) { /* parse the flags */
- i = 0; /* flag not known yet */
- /* find end of flag */
- if (n = strchr (t,' ')) *n++ = '';
- ucase (strcpy (flg,t));
- if (flg[0] == '\') { /* system flag? */
- switch (flg[1]) { /* dispatch based on first character */
- case 'S': /* possible Seen flag */
- if (flg[2] == 'E' && flg[3] == 'E' && flg[4] == 'N' && !flg[5])
- i = fSEEN;
- break;
- case 'D': /* possible Deleted or Draft flag */
- if (flg[2] == 'E' && flg[3] == 'L' && flg[4] == 'E' &&
- flg[5] == 'T' && flg[6] == 'E' && flg[7] == 'D' && !flg[8])
- i = fDELETED;
- else if (flg[2] == 'R' && flg[3] == 'A' && flg[4] == 'F' &&
- flg[5] == 'T' && !flg[6]) i = fDRAFT;
- break;
- case 'F': /* possible Flagged flag */
- if (flg[2] == 'L' && flg[3] == 'A' && flg[4] == 'G' &&
- flg[5] == 'G' && flg[6] == 'E' && flg[7] == 'D' && !flg[8])
- i = fFLAGGED;
- break;
- case 'A': /* possible Answered flag */
- if (flg[2] == 'N' && flg[3] == 'S' && flg[4] == 'W' &&
- flg[5] == 'E' && flg[6] == 'R' && flg[7] == 'E' &&
- flg[8] == 'D' && !flg[9]) i = fANSWERED;
- break;
- default: /* unknown */
- break;
- }
- if (i) f |= i; /* add flag to flags list */
- }
- /* user flag, search through table */
- else for (j = 0; !i && j < NUSERFLAGS && (s=stream->user_flags[j]); ++j){
- sprintf (key,"%.900s",s);
- if (!strcmp (flg,ucase (key))) *uf |= i = 1 << j;
- }
- if (!i) { /* didn't find a matching flag? */
- /* can we create it? */
- if (stream->kwd_create && (j < NUSERFLAGS)) {
- *uf |= 1 << j; /* set the bit */
- stream->user_flags[j] = cpystr (t);
- /* if out of user flags */
- if (j == NUSERFLAGS - 1) stream->kwd_create = NIL;
- }
- else {
- sprintf (key,"Unknown flag: %.80s",t);
- mm_log (key,ERROR);
- }
- }
- }
- }
- return f;
- }
- /* Mail check network stream for usability with new name
- * Accepts: MAIL stream
- * candidate new name
- * Returns: T if stream can be used, NIL otherwise
- */
- long mail_usable_network_stream (MAILSTREAM *stream,char *name)
- {
- NETMBX smb,nmb;
- return (stream && stream->dtb && !(stream->dtb->flags & DR_LOCAL) &&
- mail_valid_net_parse (name,&nmb) &&
- mail_valid_net_parse (stream->mailbox,&smb) &&
- !strcmp (lcase (smb.host),lcase (tcp_canonical (nmb.host))) &&
- !strcmp (smb.service,nmb.service) &&
- (!nmb.port || (smb.port == nmb.port)) &&
- (nmb.anoflag == stream->anonymous) &&
- (!nmb.user[0] || !strcmp (smb.user,nmb.user))) ? LONGT : NIL;
- }
- /* Mail data structure instantiation routines */
- /* Mail instantiate cache elt
- * Accepts: initial message number
- * Returns: new cache elt
- */
- MESSAGECACHE *mail_new_cache_elt (unsigned long msgno)
- {
- MESSAGECACHE *elt = (MESSAGECACHE *) memset (fs_get (sizeof (MESSAGECACHE)),
- 0,sizeof (MESSAGECACHE));
- elt->lockcount = 1; /* initially only cache references it */
- elt->msgno = msgno; /* message number */
- return elt;
- }
- /* Mail instantiate envelope
- * Returns: new envelope
- */
- ENVELOPE *mail_newenvelope (void)
- {
- return (ENVELOPE *) memset (fs_get (sizeof (ENVELOPE)),0,sizeof (ENVELOPE));
- }
- /* Mail instantiate address
- * Returns: new address
- */
- ADDRESS *mail_newaddr (void)
- {
- return (ADDRESS *) memset (fs_get (sizeof (ADDRESS)),0,sizeof (ADDRESS));
- }
- /* Mail instantiate body
- * Returns: new body
- */
- BODY *mail_newbody (void)
- {
- return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
- }
- /* Mail initialize body
- * Accepts: body
- * Returns: body
- */
- BODY *mail_initbody (BODY *body)
- {
- memset ((void *) body,0,sizeof (BODY));
- body->type = TYPETEXT; /* content type */
- body->encoding = ENC7BIT; /* content encoding */
- return body;
- }
- /* Mail instantiate body parameter
- * Returns: new body part
- */
- PARAMETER *mail_newbody_parameter (void)
- {
- return (PARAMETER *) memset (fs_get (sizeof(PARAMETER)),0,sizeof(PARAMETER));
- }
- /* Mail instantiate body part
- * Returns: new body part
- */
- PART *mail_newbody_part (void)
- {
- PART *part = (PART *) memset (fs_get (sizeof (PART)),0,sizeof (PART));
- mail_initbody (&part->body); /* initialize the body */
- return part;
- }
- /* Mail instantiate body message part
- * Returns: new body message part
- */
- MESSAGE *mail_newmsg (void)
- {
- return (MESSAGE *) memset (fs_get (sizeof (MESSAGE)),0,sizeof (MESSAGE));
- }
- /* Mail instantiate string list
- * Returns: new string list
- */
- STRINGLIST *mail_newstringlist (void)
- {
- return (STRINGLIST *) memset (fs_get (sizeof (STRINGLIST)),0,
- sizeof (STRINGLIST));
- }
- /* Mail instantiate new search program
- * Returns: new search program
- */
- SEARCHPGM *mail_newsearchpgm (void)
- {
- return (SEARCHPGM *) memset (fs_get (sizeof(SEARCHPGM)),0,sizeof(SEARCHPGM));
- }
- /* Mail instantiate new search program
- * Accepts: header line name
- * Returns: new search program
- */
- SEARCHHEADER *mail_newsearchheader (char *line,char *text)
- {
- SEARCHHEADER *hdr = (SEARCHHEADER *) memset (fs_get (sizeof (SEARCHHEADER)),
- 0,sizeof (SEARCHHEADER));
- hdr->line.size = strlen ((char *) (hdr->line.data =
- (unsigned char *) cpystr (line)));
- hdr->text.size = strlen ((char *) (hdr->text.data =
- (unsigned char *) cpystr (text)));
- return hdr;
- }
- /* Mail instantiate new search set
- * Returns: new search set
- */
- SEARCHSET *mail_newsearchset (void)
- {
- return (SEARCHSET *) memset (fs_get (sizeof(SEARCHSET)),0,sizeof(SEARCHSET));
- }
- /* Mail instantiate new search or
- * Returns: new search or
- */
- SEARCHOR *mail_newsearchor (void)
- {
- SEARCHOR *or = (SEARCHOR *) memset (fs_get (sizeof (SEARCHOR)),0,
- sizeof (SEARCHOR));
- or->first = mail_newsearchpgm ();
- or->second = mail_newsearchpgm ();
- return or;
- }
- /* Mail instantiate new searchpgmlist
- * Returns: new searchpgmlist
- */
- SEARCHPGMLIST *mail_newsearchpgmlist (void)
- {
- SEARCHPGMLIST *pgl = (SEARCHPGMLIST *)
- memset (fs_get (sizeof (SEARCHPGMLIST)),0,sizeof (SEARCHPGMLIST));
- pgl->pgm = mail_newsearchpgm ();
- return pgl;
- }
- /* Mail instantiate new sortpgm
- * Returns: new sortpgm
- */
- SORTPGM *mail_newsortpgm (void)
- {
- return (SORTPGM *) memset (fs_get (sizeof (SORTPGM)),0,sizeof (SORTPGM));
- }
- /* Mail instantiate new threadnode
- * Accepts: sort cache for thread node
- * Returns: new threadnode
- */
- THREADNODE *mail_newthreadnode (SORTCACHE *sc)
- {
- THREADNODE *thr = (THREADNODE *) memset (fs_get (sizeof (THREADNODE)),0,
- sizeof (THREADNODE));
- if (sc) thr->sc = sc; /* initialize sortcache */
- return thr;
- }
- /* Mail garbage collection routines */
- /* Mail garbage collect body
- * Accepts: pointer to body pointer
- */
- void mail_free_body (BODY **body)
- {
- if (*body) { /* only free if exists */
- mail_free_body_data (*body);/* free its data */
- fs_give ((void **) body); /* return body to free storage */
- }
- }
- /* Mail garbage collect body data
- * Accepts: body pointer
- */
- void mail_free_body_data (BODY *body)
- {
- switch (body->type) { /* free contents */
- case TYPEMULTIPART: /* multiple part */
- mail_free_body_part (&body->nested.part);
- break;
- case TYPEMESSAGE: /* encapsulated message */
- if (body->subtype && !strcmp (body->subtype,"RFC822")) {
- mail_free_stringlist (&body->nested.msg->lines);
- mail_gc_msg (body->nested.msg,GC_ENV | GC_TEXTS);
- fs_give ((void **) &body->nested.msg);
- }
- break;
- default:
- break;
- }
- if (body->subtype) fs_give ((void **) &body->subtype);
- mail_free_body_parameter (&body->parameter);
- if (body->id) fs_give ((void **) &body->id);
- if (body->description) fs_give ((void **) &body->description);
- if (body->disposition.type) fs_give ((void **) &body->disposition);
- if (body->disposition.parameter)
- mail_free_body_parameter (&body->disposition.parameter);
- if (body->language) mail_free_stringlist (&body->language);
- if (body->mime.text.data) fs_give ((void **) &body->mime.text.data);
- if (body->contents.text.data) fs_give ((void **) &body->contents.text.data);
- if (body->md5) fs_give ((void **) &body->md5);
- }
- /* Mail garbage collect body parameter
- * Accepts: pointer to body parameter pointer
- */
- void mail_free_body_parameter (PARAMETER **parameter)
- {
- if (*parameter) { /* only free if exists */
- if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
- if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
- /* run down the list as necessary */
- mail_free_body_parameter (&(*parameter)->next);
- /* return body part to free storage */
- fs_give ((void **) parameter);
- }
- }
- /* Mail garbage collect body part
- * Accepts: pointer to body part pointer
- */
- void mail_free_body_part (PART **part)
- {
- if (*part) { /* only free if exists */
- mail_free_body_data (&(*part)->body);
- /* run down the list as necessary */
- mail_free_body_part (&(*part)->next);
- fs_give ((void **) part); /* return body part to free storage */
- }
- }
- /* Mail garbage collect message cache
- * Accepts: mail stream
- *
- * The message cache is set to NIL when this function finishes.
- */
- void mail_free_cache (MAILSTREAM *stream)
- {
- /* do driver specific stuff first */
- mail_gc (stream,GC_ELT | GC_ENV | GC_TEXTS);
- /* flush the cache */
- (*mailcache) (stream,(long) 0,CH_INIT);
- }
- /* Mail garbage collect cache element
- * Accepts: pointer to cache element pointer
- */
- void mail_free_elt (MESSAGECACHE **elt)
- {
- /* only free if exists and no sharers */
- if (*elt && !--(*elt)->lockcount) {
- mail_gc_msg (&(*elt)->private.msg,GC_ENV | GC_TEXTS);
- fs_give ((void **) elt);
- }
- else *elt = NIL; /* else simply drop pointer */
- }
- /* Mail garbage collect envelope
- * Accepts: pointer to envelope pointer
- */
- void mail_free_envelope (ENVELOPE **env)
- {
- if (*env) { /* only free if exists */
- if ((*env)->remail) fs_give ((void **) &(*env)->remail);
- mail_free_address (&(*env)->return_path);
- if ((*env)->date) fs_give ((void **) &(*env)->date);
- mail_free_address (&(*env)->from);
- mail_free_address (&(*env)->sender);
- mail_free_address (&(*env)->reply_to);
- if ((*env)->subject) fs_give ((void **) &(*env)->subject);
- mail_free_address (&(*env)->to);
- mail_free_address (&(*env)->cc);
- mail_free_address (&(*env)->bcc);
- if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
- if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
- if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
- if ((*env)->followup_to) fs_give ((void **) &(*env)->followup_to);
- if ((*env)->references) fs_give ((void **) &(*env)->references);
- fs_give ((void **) env); /* return envelope to free storage */
- }
- }
- /* Mail garbage collect address
- * Accepts: pointer to address pointer
- */
- void mail_free_address (ADDRESS **address)
- {
- if (*address) { /* only free if exists */
- if ((*address)->personal) fs_give ((void **) &(*address)->personal);
- if ((*address)->adl) fs_give ((void **) &(*address)->adl);
- if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
- if ((*address)->host) fs_give ((void **) &(*address)->host);
- if ((*address)->error) fs_give ((void **) &(*address)->error);
- mail_free_address (&(*address)->next);
- fs_give ((void **) address);/* return address to free storage */
- }
- }
- /* Mail garbage collect stringlist
- * Accepts: pointer to stringlist pointer
- */
- void mail_free_stringlist (STRINGLIST **string)
- {
- if (*string) { /* only free if exists */
- if ((*string)->text.data) fs_give ((void **) &(*string)->text.data);
- mail_free_stringlist (&(*string)->next);
- fs_give ((void **) string); /* return string to free storage */
- }
- }
- /* Mail garbage collect searchpgm
- * Accepts: pointer to searchpgm pointer
- */
- void mail_free_searchpgm (SEARCHPGM **pgm)
- {
- if (*pgm) { /* only free if exists */
- mail_free_searchset (&(*pgm)->msgno);
- mail_free_searchset (&(*pgm)->uid);
- mail_free_searchor (&(*pgm)->or);
- mail_free_searchpgmlist (&(*pgm)->not);
- mail_free_searchheader (&(*pgm)->header);
- mail_free_stringlist (&(*pgm)->bcc);
- mail_free_stringlist (&(*pgm)->body);
- mail_free_stringlist (&(*pgm)->cc);
- mail_free_stringlist (&(*pgm)->from);
- mail_free_stringlist (&(*pgm)->keyword);
- mail_free_stringlist (&(*pgm)->subject);
- mail_free_stringlist (&(*pgm)->text);
- mail_free_stringlist (&(*pgm)->to);
- fs_give ((void **) pgm); /* return program to free storage */
- }
- }
- /* Mail garbage collect searchheader
- * Accepts: pointer to searchheader pointer
- */
- void mail_free_searchheader (SEARCHHEADER **hdr)
- {
- if (*hdr) { /* only free if exists */
- if ((*hdr)->line.data) fs_give ((void **) &(*hdr)->line.data);
- if ((*hdr)->text.data) fs_give ((void **) &(*hdr)->text.data);
- mail_free_searchheader (&(*hdr)->next);
- fs_give ((void **) hdr); /* return header to free storage */
- }
- }
- /* Mail garbage collect searchset
- * Accepts: pointer to searchset pointer
- */
- void mail_free_searchset (SEARCHSET **set)
- {
- if (*set) { /* only free if exists */
- mail_free_searchset (&(*set)->next);
- fs_give ((void **) set); /* return set to free storage */
- }
- }
- /* Mail garbage collect searchor
- * Accepts: pointer to searchor pointer
- */
- void mail_free_searchor (SEARCHOR **orl)
- {
- if (*orl) { /* only free if exists */
- mail_free_searchpgm (&(*orl)->first);
- mail_free_searchpgm (&(*orl)->second);
- mail_free_searchor (&(*orl)->next);
- fs_give ((void **) orl); /* return searchor to free storage */
- }
- }
- /* Mail garbage collect search program list
- * Accepts: pointer to searchpgmlist pointer
- */
- void mail_free_searchpgmlist (SEARCHPGMLIST **pgl)
- {
- if (*pgl) { /* only free if exists */
- mail_free_searchpgm (&(*pgl)->pgm);
- mail_free_searchpgmlist (&(*pgl)->next);
- fs_give ((void **) pgl); /* return searchpgmlist to free storage */
- }
- }
- /* Mail garbage collect namespace
- * Accepts: poiner to namespace
- */
- void mail_free_namespace (NAMESPACE **n)
- {
- if (*n) {
- fs_give ((void **) &(*n)->name);
- mail_free_namespace (&(*n)->next);
- mail_free_body_parameter (&(*n)->param);
- fs_give ((void **) n); /* return namespace to free storage */
- }
- }
- /* Mail garbage collect sort program
- * Accepts: pointer to sortpgm pointer
- */
- void mail_free_sortpgm (SORTPGM **pgm)
- {
- if (*pgm) { /* only free if exists */
- mail_free_sortpgm (&(*pgm)->next);
- fs_give ((void **) pgm); /* return sortpgm to free storage */
- }
- }
- /* Mail garbage collect thread node
- * Accepts: pointer to threadnode pointer
- */
- void mail_free_threadnode (THREADNODE **thr)
- {
- if (*thr) { /* only free if exists */
- mail_free_threadnode (&(*thr)->branch);
- mail_free_threadnode (&(*thr)->next);
- fs_give ((void **) thr); /* return threadnode to free storage */
- }
- }
- /* Link authenicator
- * Accepts: authenticator to add to list
- */
- void auth_link (AUTHENTICATOR *auth)
- {
- if (!auth->valid || (*auth->valid) ()) {
- AUTHENTICATOR **a = &mailauthenticators;
- while (*a) a = &(*a)->next; /* find end of list of authenticators */
- *a = auth; /* put authenticator at the end */
- auth->next = NIL; /* this authenticator is the end of the list */
- }
- }
- /* Authenticate access
- * Accepts: mechanism name
- * responder function
- * argument count
- * argument vector
- * Returns: authenticated user name or NIL
- */
- char *mail_auth (char *mechanism,authresponse_t resp,int argc,char *argv[])
- {
- char tmp[MAILTMPLEN];
- AUTHENTICATOR *auth;
- /* cretins still haven't given up */
- if (strlen (mechanism) >= MAILTMPLEN)
- syslog (LOG_ALERT|LOG_AUTH,"System break-in attempt, host=%.80s",
- tcp_clienthost ());
- else { /* make upper case copy of mechanism name */
- ucase (strcpy (tmp,mechanism));
- for (auth = mailauthenticators; auth; auth = auth->next)
- if (auth->server && !strcmp (auth->name,tmp))
- return (*auth->server) (resp,argc,argv);
- }
- return NIL; /* no authenticator found */
- }
- /* Lookup authenticator index
- * Accepts: authenticator index
- * Returns: authenticator, or 0 if not found
- */
- AUTHENTICATOR *mail_lookup_auth (unsigned long i)
- {
- AUTHENTICATOR *auth = mailauthenticators;
- while (auth && --i) auth = auth->next;
- return auth;
- }
- /* Lookup authenticator name
- * Accepts: authenticator name
- * security flag (non-zero to ignore non-secure authenticators)
- * Returns: index in authenticator chain, or 0 if not found
- */
- unsigned int mail_lookup_auth_name (char *mechanism,long secflag)
- {
- char tmp[MAILTMPLEN];
- int i;
- AUTHENTICATOR *auth;
- if (strlen (mechanism) < MAILTMPLEN) {
- /* make upper case copy of mechanism name */
- ucase (strcpy (tmp,mechanism));
- for (i = 1, auth = mailauthenticators; auth; i++, auth = auth->next)
- if (auth->client && (!secflag || auth->secflag) &&
- !strcmp (auth->name,tmp)) return i;
- }
- return 0;
- }
- /* Standard TCP/IP network driver */
- static NETDRIVER tcpdriver = {
- tcp_open, /* open connection */
- tcp_aopen, /* open preauthenticated connection */
- tcp_getline, /* get a line */
- tcp_getbuffer, /* get a buffer */
- tcp_soutr, /* output pushed data */
- tcp_sout, /* output string */
- tcp_close, /* close connection */
- tcp_host, /* return host name */
- tcp_remotehost, /* return remote host name */
- tcp_port, /* return port number */
- tcp_localhost /* return local host name */
- };
- /* Network open
- * Accepts: NETMBX specifier to open
- * default network driver
- * default port
- * alternate network driver
- * alternate driver service name
- * alternate driver port
- * Returns: Network stream if success, else NIL
- */
- NETSTREAM *net_open (NETMBX *mb,NETDRIVER *dv,unsigned long port,
- NETDRIVER *altd,char *alts,unsigned long altp)
- {
- NETSTREAM *stream = NIL;
- char tmp[MAILTMPLEN];
- if (strlen (mb->host) >= NETMAXHOST) {
- sprintf (tmp,"Invalid host name: %.80s",mb->host);
- mm_log (tmp,ERROR);
- }
- /* use designated driver if given */
- else if (dv) stream = net_open_work (dv,mb->host,mb->service,port,mb->port,
- NIL);
- else if (mb->altflag && altd) /* use alt if altflag lit */
- stream = net_open_work (altd,mb->host,alts,altp,mb->port,NIL);
- /* if tryaltfirst and can open alt... */
- else if ((mb->tryaltflag || tryaltfirst) && altd &&
- (stream = net_open_work (altd,mb->host,alts,altp,mb->port,T)))
- mb->altflag = T; /* ...light altflag */
- /* default to TCP driver */
- else stream = net_open_work (&tcpdriver,mb->host,mb->service,port,mb->port,
- NIL);
- return stream;
- }
- /* Network open worker routine
- * Accepts: network driver
- * host name
- * service name to look up port
- * port number if service name not found
- * port number to override service name
- * flags (non-zero means silent failure)
- * Returns: Network stream if success, else NIL
- */
- NETSTREAM *net_open_work (NETDRIVER *dv,char *host,char *service,
- unsigned long port,unsigned long portoverride,
- long flags)
- {
- NETSTREAM *stream = NIL;
- void *tstream;
- if (portoverride) { /* explicit port number? */
- service = NIL; /* yes, override service name */
- port = portoverride; /* use that instead of default port */
- }
- if (flags) port |= 0x80000000;/* want silent open? */
- if (tstream = (*dv->open) (host,service,port)) {
- stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
- stream->stream = tstream;
- stream->dtb = dv;
- }
- return stream;
- }
- /* Network authenticated open
- * Accepts: network driver
- * NETMBX specifier
- * service specifier
- * return user name buffer
- * Returns: Network stream if success else NIL
- */
- NETSTREAM *net_aopen (NETDRIVER *dv,NETMBX *mb,char *service,char *user)
- {
- NETSTREAM *stream = NIL;
- void *tstream;
- if (!dv) dv = &tcpdriver; /* default to TCP driver */
- if (tstream = (*dv->aopen) (mb,service,user)) {
- stream = (NETSTREAM *) fs_get (sizeof (NETSTREAM));
- stream->stream = tstream;
- stream->dtb = dv;
- }
- return stream;
- }
- /* Network receive line
- * Accepts: Network stream
- * Returns: text line string or NIL if failure
- */
- char *net_getline (NETSTREAM *stream)
- {
- return (*stream->dtb->getline) (stream->stream);
- }
- /* Network receive buffer
- * Accepts: Network stream (must be void * for use as readfn_t)
- * size in bytes
- * buffer to read into
- * Returns: T if success, NIL otherwise
- */
- long net_getbuffer (void *st,unsigned long size,char *buffer)
- {
- NETSTREAM *stream = (NETSTREAM *) st;
- return (*stream->dtb->getbuffer) (stream->stream,size,buffer);
- }
- /* Network send null-terminated string
- * Accepts: Network stream
- * string pointer
- * Returns: T if success else NIL
- */
- long net_soutr (NETSTREAM *stream,char *string)
- {
- return (*stream->dtb->soutr) (stream->stream,string);
- }
- /* Network send string
- * Accepts: Network stream
- * string pointer
- * byte count
- * Returns: T if success else NIL
- */
- long net_sout (NETSTREAM *stream,char *string,unsigned long size)
- {
- return (*stream->dtb->sout) (stream->stream,string,size);
- }
- /* Network close
- * Accepts: Network stream
- */
- void net_close (NETSTREAM *stream)
- {
- (*stream->dtb->close) (stream->stream);
- fs_give ((void **) &stream);
- }
- /* Network get host name
- * Accepts: Network stream
- * Returns: host name for this stream
- */
- char *net_host (NETSTREAM *stream)
- {
- return (*stream->dtb->host) (stream->stream);
- }
- /* Network get remote host name
- * Accepts: Network stream
- * Returns: host name for this stream
- */
- char *net_remotehost (NETSTREAM *stream)
- {
- return (*stream->dtb->remotehost) (stream->stream);
- }
- /* Network return port for this stream
- * Accepts: Network stream
- * Returns: port number for this stream
- */
- unsigned long net_port (NETSTREAM *stream)
- {
- return (*stream->dtb->port) (stream->stream);
- }
- /* Network get local host name
- * Accepts: Network stream
- * Returns: local host name
- */
- char *net_localhost (NETSTREAM *stream)
- {
- return (*stream->dtb->localhost) (stream->stream);
- }