/* ** Copyright (C) 1994, 1995 Enterprise Integration Technologies Corp. ** VeriFone Inc./Hewlett-Packard. All Rights Reserved. ** Kevin Hughes, kev@kevcom.com 3/11/94 ** Kent Landfield, kent@landfield.com 4/6/97 ** ** This program and library is free software; you can redistribute it and/or ** modify it under the terms of the GNU (Library) General Public License ** as published by the Free Software Foundation; either version 2 ** of the License, or any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU (Library) General Public License for more details. ** ** You should have received a copy of the GNU (Library) General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /* Define the following define to enable better date debugging */ /*#define PH_DATE_DEBUG */ int rbs = 0; int rbs_bigtime = 0; #include "hypermail.h" #include "dmatch.h" #include "setup.h" #include "struct.h" #include "parse.h" #define HAVE_PCRE #ifdef HAVE_PCRE #ifdef __LCC__ #include "../lcc/pcre.h" #else #include #endif #endif struct body *hashnumlookup(int, struct emailinfo **); /* ** Ladies and germs - the hash function. */ unsigned hash(char *s) { unsigned hashval; /* We MUST deal with NULL pointers passed to this */ if (!s) return HASHSIZE - 1; /* (Daniel) I think a high value is fun ;-) */ for (hashval = 0; *s; s++) hashval = *s + 31 * hashval; return (hashval % HASHSIZE); } void reinit_structs() { int i; for (i = 0; i < HASHSIZE; ++i) { if (etable[i]) { free(etable[i]); etable[i] = NULL; } } } void fill_email_dates(struct emailinfo *e, char *date, char *fromdate, char *isodate, char *isofromdate) { bool fromdate_valid = 1, date_valid = 1; /* fromdate may be empty (zero length string rather than NULL) * date will always have something in it - either a date or * the NODATE string "(no date)" */ if (isodate != NULL && isofromdate != NULL) { e->date = iso_to_secs(isodate); e->fromdate = iso_to_secs(isofromdate); e->fromdatestr = strsav(fromdate); e->datestr = strsav(date); } else { e->date = e->fromdate = -1; if (date == NULL) date = NODATE; if (!strcmp(date, NODATE)) { date_valid = 0; } else { if ((e->date = convtoyearsecs(date)) == -1) date_valid = 0; } if (!*fromdate) { fromdate_valid = 0; } else { if ((e->fromdate = convtoyearsecs(fromdate)) == -1) fromdate_valid = 0; } if (fromdate_valid && !date_valid) { #ifdef PH_DATE_DEBUG fprintf(stderr, "%d: %s: using fromdate '%s' for both (date '%s')\n", num, msgid, fromdate, date); #endif e->fromdatestr = strsav(fromdate); e->datestr = strsav(fromdate); e->date = e->fromdate; } else if (!fromdate_valid && date_valid) { #ifdef PH_DATE_DEBUG fprintf(stderr, "%d: %s: using date '%s' for both (fromdate '%s')\n", num, msgid, date, fromdate); #endif e->fromdatestr = strsav(date); e->datestr = strsav(date); e->fromdate = e->date; } else if (!fromdate_valid && !date_valid) { #ifdef PH_DATE_DEBUG fprintf(stderr, "%d: %s: fromdate '%s' and date '%s' both bad\n", num, msgid, fromdate, date); #endif e->fromdatestr = strsav(fromdate); e->datestr = strsav(date); } else { e->fromdatestr = strsav(fromdate); e->datestr = strsav(date); #ifdef PH_DATE_DEBUG if (e->date > e->fromdate) { rbs++; if ((e->date - e->fromdate) > 4200) { /* if more than 1:10 off */ char fromdate_parsed[26]; char date_parsed[26]; strcpy(fromdate_parsed, ctime(&(e->fromdate))); strcpy(date_parsed, ctime(&(e->date))); date_parsed[24] = '\0'; fromdate_parsed[24] = '\0'; rbs_bigtime++; fprintf(stderr, "%d [%d:%d]: %s: received before sent\n %-38.38s %-30.38s\n %-38.38s %-38.38s\n", num, rbs, rbs_bigtime, msgid, fromdate, date, fromdate_parsed, date_parsed); } } #endif } } } /* ** The structure most of everything else depends on. ** Hashes a message - header info, pointer to a list of body lines - ** by number, message ID, date, and subject, so an article can be ** handily looked up and retrieved using any of these criteria. */ struct emailinfo *addhash(int num, char *date, char *name, char *email, char *msgid, char *subject, char *inreply, char *fromdate, char *charset, char *isodate, char *isofromdate, struct body *sp) { struct emailinfo *e; struct hashemail *h; unsigned hashval; char numstr[NUMSTRLEN]; bool msgid_dup = 0; bool msgid_missing = 0; static int freedummy = 0; char *newmsgid; if (!msgid) { /* SEVERE ERROR, all mails MUST have a Message-ID, ignore it! */ if (set_require_msgids) { if (set_warn_surpressions) printf("Message-ID is missing, ignoring message with subject '%s'.\n", subject ? subject : ""); return NULL; } else { msgid_missing = 1; } } if (!msgid_missing) { h = (struct hashemail *)etable[hash(msgid)]; while (h) { if (!strcmp(h->data->msgid, msgid)) { msgid_dup = 1; break; } h = h->next; } } if (msgid_dup && set_discard_dup_msgids) { if (set_showprogress) printf("%d: Message-ID collision, '%s' already present - discarding message.\n", num, msgid); return NULL; } if (msgid_dup || msgid_missing) { /* * This is broken. It needs to be corrected so that * the original message id is a component of the * generated message-id instead of a total unrelated * dummy msg-id. Good enough for now though... */ newmsgid = emalloc(MSGDSTRLEN); do { msgid_dup = 0; sprintf(newmsgid, "%d.%4.4d@hypermail.dummy", time(NULL), freedummy); h = (struct hashemail *)etable[hash(newmsgid)]; while (h) { if (!strcmp(h->data->msgid, newmsgid)) { msgid_dup = 1; break; } h = h->next; } freedummy++; } while (msgid_dup && (freedummy < 1000)); if (!msgid_dup) { if (set_showprogress) { if (msgid_missing) printf("\n%d Message-ID missing, using %s instead.\n", num, newmsgid); else printf("\n%d Message-ID collision, '%s' already present - using %s instead.\n", num, msgid, newmsgid); } msgid = newmsgid; } else { if (set_showprogress) { if (msgid_missing) printf("\n%d Message-ID missing, failed to find free id - dropping message.\n", num); else printf("\n%d Message-ID collision, '%s' already present - failed to find free id - dropping message.\n", num, msgid); } return NULL; } } e = (struct emailinfo *)emalloc(sizeof(struct emailinfo)); #ifdef FASTREPLYCODE e->replylist = NULL; e->isreply = 0; #endif e->msgnum = num; if (num > max_msgnum) max_msgnum = num; e->emailaddr = strsav(email); if ((name == NULL) || (*name == '\0')) e->name = strsav(email); else e->name = strsav(name); fill_email_dates(e, date, fromdate, isodate, isofromdate); e->subdir = msg_subdir(e->msgnum, set_use_sender_date ? e->date : e->fromdate); if (e->subdir && set_increment != -1) { if (!e->subdir->first_email) e->subdir->first_email = e; e->subdir->last_email = e; ++e->subdir->count; } e->msgid = strsav(msgid); e->subject = strsav(subject); e->unre_subject = unre(subject); e->inreplyto = strsav(inreply); e->charset = strsav(charset); e->flags = 0; e->is_deleted = 0; e->deletion_completed = -1; e->exp_time = -1; e->bodylist = sp; e->initial_next_in_thread = -1; /* Added by Daniel 1999-03-19, we need this hash later to find the mail we replied to */ h = (struct hashemail *)emalloc(sizeof(struct hashemail)); hashval = hash(inreply); h->next = etable[hashval]; h->data = e; etable[hashval] = h; h = (struct hashemail *)emalloc(sizeof(struct hashemail)); hashval = hash(date); h->next = etable[hashval]; h->data = e; etable[hashval] = h; h = (struct hashemail *)emalloc(sizeof(struct hashemail)); #if 0 printf("ADD msgid %s to HASH!\n", msgid); #endif hashval = hash(msgid); h->next = etable[hashval]; h->data = e; etable[hashval] = h; h = (struct hashemail *)emalloc(sizeof(struct hashemail)); hashval = hash(subject); h->next = etable[hashval]; h->data = e; etable[hashval] = h; h = (struct hashemail *)emalloc(sizeof(struct hashemail)); sprintf(numstr, "%d", num); hashval = hash(numstr); h->next = etable[hashval]; h->data = e; etable[hashval] = h; return e; /* the actual mail struct pointer */ } int insert_in_lists(struct emailinfo *emp, const bool * require_filter, int rlen) { int i; if (set_delete_msgnum) { char num_str[32]; sprintf(num_str, "%d", emp->msgnum); if (inlist(set_delete_msgnum, num_str)) { if (!emp->is_deleted) { if (emp->subdir) --emp->subdir->count; } emp->is_deleted = FILTERED_DELETE; } } for (i = 0; i < rlen; ++i) { if (!require_filter[i]) { emp->is_deleted = FILTERED_REQUIRED; } } if (emp->is_deleted) { struct hashemail *h; h = (struct hashemail *)malloc(sizeof(struct hashemail)); h->data = emp; h->next = deletedlist; deletedlist = h; if (set_warn_surpressions && require_filter) { const char *option = "??"; switch (emp->is_deleted) { case FILTERED_DELETE: option = "deleted or delete_msgnum"; break; case FILTERED_EXPIRE: option = "expires"; break; case FILTERED_OUT: option = "filter_out or filter_out_full_body"; break; case FILTERED_REQUIRED: option = "filter_require or filter_require_full_body"; break; case FILTERED_OLD: option = "delete_older"; break; case FILTERED_NEW: option = "delete_newer"; break; } printf("message %d deleted under option %s. msgid: %s\n", emp->msgnum + 1, option, emp->msgid); } } else { authorlist = addheader(authorlist, emp, 1, 0); subjectlist = addheader(subjectlist, emp, 0, 0); } datelist = addheader(datelist, emp, 2, 0); return !emp->is_deleted; } /* * Given an "in-reply-to:" field and a message number, this function * retrieves information about the message that this message is a * reply to. * If all else fails but a reply is * found by comparing subjects, issubjmatch is set to 1. * * Daniel 1999-03-19. Changed return type and parameters. */ #ifdef USE_OBSOLETE_HASHREPLYLOOKUP struct emailinfo *hashreplylookup(int msgnum, char *inreply, int *issubjmatch) { struct hashemail *ep; #if DEBUG_THREAD fprintf(stderr, "hashreplylookup(%d, '%s'...)\n", msgnum, inreply); #endif *issubjmatch = 0; ep = etable[hash(inreply)]; while (ep) { if ((msgnum != ep->data->msgnum) && (strcmp(inreply, ep->data->msgid) == 0)) { #if DEBUG_THREAD fprintf(stderr, "match on msgid [%d]\n", ep->data->msgnum); #endif return ep->data; } ep = ep->next; } ep = etable[hash(inreply)]; while (ep != NULL) { if ((msgnum != ep->data->msgnum) && (strstr(inreply, ep->data->datestr))) { #if DEBUG_THREAD fprintf(stderr, "match on date [%d]\n", ep->data->msgnum); #endif return ep->data; } ep = ep->next; } ep = etable[hash(inreply)]; while (ep != NULL) { if ((msgnum != ep->data->msgnum) && (strcmp(inreply, ep->data->subject) == 0)) { *issubjmatch = 1; #if DEBUG_THREAD fprintf(stderr, "match on subject [%d]\n", ep->data->msgnum); #endif return ep->data; } ep = ep->next; } return NULL; } #endif /* * Given an "message-id:" field, this tries to retrieve information * about an article that has replied. If all else fails but a reply is * found by comparing subjects, issubjmatch is set to 1. * * Daniel 1999-03-24. New. */ struct emailinfo *hashmsgidlookup(char *msgid, int *issubjmatch) { struct hashemail *ep; *issubjmatch = 0; ep = etable[hash(msgid)]; while (ep) { if (strcmp(msgid, ep->data->inreplyto) == 0) { return ep->data; } ep = ep->next; } return NULL; } /* * Given an "in-reply-to:" field and a message number, this function * retrieves information about the message that this message is a * reply to. * If all else fails but a reply is * found by comparing subjects, maybereply is set to 1. */ struct emailinfo *hashreplylookup(int msgnum, char *inreply, char *subject, int *maybereply) { struct hashemail *ep; #if DEBUG_THREAD > 1 fprintf(stderr, "hashreplynumlookup(%d, '%s'...)\n", msgnum, (inreply == NULL) ? "" : inreply); #endif *maybereply = 0; if ((inreply != NULL) && *inreply) { ep = etable[hash(inreply)]; while (ep) { if (strcmp(inreply, ep->data->msgid) == 0) { #if DEBUG_THREAD fprintf(stderr, "match on msgid %4d %4d\n", msgnum, ep->data->msgnum); #endif return ep->data; } ep = ep->next; } ep = etable[hash(inreply)]; while (ep) { if ((msgnum != ep->data->msgnum) && (strstr(inreply, ep->data->datestr))) { #if DEBUG_THREAD fprintf(stderr, "match on date %4d %4d\n", msgnum, ep->data->msgnum); #endif return ep->data; } ep = ep->next; } ep = etable[hash(inreply)]; while (ep != NULL) { if ((msgnum != ep->data->msgnum) && (strcmp(inreply, ep->data->subject) == 0)) { *maybereply = 1; #if DEBUG_THREAD fprintf(stderr, "match on subject %4d %4d\n", msgnum, ep->data->msgnum); #endif return ep->data; } ep = ep->next; } } /* end of matching on inreply */ /* No match so far. Now try matching on the subject, removing * one instance of "re: " from the front of the subject each * time round the loop. */ { #define MAX_SUBJ_LEN 300 char *s, *saved_s; struct emailinfo *lowest_so_far = NULL; int match = 0; size_t subj_len; subj_len = strlen(subject) > MAX_SUBJ_LEN ? MAX_SUBJ_LEN : strlen(subject); s = emalloc(subj_len + 1); saved_s = strncpy(s, subject, subj_len); s[subj_len] = '\0'; #ifdef NOTDEF /* AUDIT biege: can we trigger a DoS here by using a very long "Subject"? */ s = emalloc(strlen(subject) + 1); saved_s = strcpy(s, subject); #endif if (isre(s, NULL)) do { #if DEBUG_THREAD > 1 fprintf(stderr, "extra %s\n", s); #endif ep = etable[hash(s)]; while (ep != NULL) { if ((strcasecmp(s, ep->data->subject) == 0) && (msgnum != ep->data->msgnum)) { match = 1; if (lowest_so_far == NULL || ep->data->msgnum < lowest_so_far->msgnum) lowest_so_far = ep->data; } ep = ep->next; } s = oneunre(s); } while (s != NULL); free(saved_s); if (match) { *maybereply = 1; if (lowest_so_far != NULL && lowest_so_far->msgnum < msgnum) { #if DEBUG_THREAD fprintf(stderr, "match on extra %4d %4d\n", msgnum, lowest_so_far->msgnum); #endif return lowest_so_far; } else { #if DEBUG_THREAD fprintf(stderr, "match on extra %4d %4d discarded - less than %d\n", msgnum, lowest_so_far ? lowest_so_far->msgnum : -1, msgnum); #endif return NULL; } } } #if DEBUG_THREAD fprintf(stderr, "match NO MATCH %4d\n", msgnum); #endif return NULL; } /* ** Same as the above function, but only returns the article number. */ int hashreplynumlookup(int msgnum, char *inreply, char *subject, int *maybereply) { struct emailinfo *email = hashreplylookup(msgnum, inreply, subject, maybereply); return email != NULL ? email->msgnum : -1; } /* * From an article's number, retrieve all information associated with * that article. */ struct body *hashnumlookup(int num, struct emailinfo **emailp) { struct hashemail *ep; struct body *lp_tmp; char numstr[NUMSTRLEN]; sprintf(numstr, "%d", num); for (ep = etable[hash(numstr)]; ep != NULL; ep = ep->next) { if (ep->data && (num == ep->data->msgnum)) { /* return a mere pointer to it! */ *emailp = ep->data; if (!ep->data->bodylist) ep->data->bodylist = addbody(NULL, &lp_tmp, "\n", 0); return ep->data->bodylist; } } return NULL; } /* * returns info about the first message associated with the given msgid. */ struct emailinfo *hashlookupbymsgid(char *msgid) { struct hashemail *ep; if (!msgid || !*msgid) return NULL; ep = etable[hash(msgid)]; while (ep) { if (strcmp(msgid, ep->data->msgid) == 0) { return ep->data; } ep = ep->next; } return NULL; } int insert_older_msgs(int num) { int i; int num_added = 0; for(i = set_startmsgnum; i < num; ++i) { struct emailinfo *emp; if (hashnumlookup(i, &emp)) { authorlist = addheader(authorlist, emp, 1, 0); subjectlist = addheader(subjectlist, emp, 0, 0); datelist = addheader(datelist, emp, 2, 0); ++num_added; } } return num_added; } /* * Find the nearest non-deleted email to num by adding direction to num. */ struct emailinfo *neighborlookup(int num, int direction) { struct hashemail *ep; char numstr[NUMSTRLEN]; num += direction; while (num >= 0 && num <= max_msgnum) { sprintf(numstr, "%d", num); for (ep = etable[hash(numstr)]; ep != NULL; ep = ep->next) if (ep->data && (num == ep->data->msgnum)) { if (ep->data->is_deleted) break; /* return a mere pointer to it! */ return ep->data; } num += direction; } return NULL; } /* ** Add a line to a linked list that makes up a boundary stack. This new one ** should be the new "active" boundary. ** ** "Adding" a NULL will retrieve the formerly used boundary pointer. */ struct boundary *bound(struct boundary *bnd, char *line) { struct boundary *newnode = NULL; if (line) { newnode = (struct boundary *)emalloc(sizeof(struct boundary)); newnode->line = strsav(line); newnode->next = NULL; newnode->prev = bnd; if (bnd) bnd->next = newnode; bnd = newnode; } else { if (bnd->prev) { /* go back to the previous */ bnd = bnd->prev; /* free the latest one */ free(bnd->next->line); free(bnd->next); } else { /* this is the last node */ free(bnd->line); free(bnd); bnd = NULL; } } return bnd; /* the new "active" boundary */ } /* ** Add a line to a linked list that makes up an article's body. */ struct body *addbody(struct body *bp, struct body **lp, /* points to the last pointer */ char *line, int flags) { struct body *tempnode; struct body *newnode = NULL; if (!(flags & BODY_CONTINUE)) { newnode = (struct body *)emalloc(sizeof(struct body)); memset(newnode, 0, sizeof(struct body)); newnode->line = strsav(line); newnode->html = (flags & BODY_HTMLIZED) ? 1 : 0; newnode->header = (flags & BODY_HEADER) ? 1 : 0; newnode->attached = (flags & BODY_ATTACHED) ? 1 : 0; newnode->next = NULL; } if (bp == NULL) { bp = newnode; *lp = bp; } else { tempnode = *lp; /* get pointer right away */ if (flags & BODY_CONTINUE) { /* this is a continuation of the previous line */ int newlen; char *newbuf; /* get the new size + 1 for the terminating zero */ newlen = strlen(tempnode->line) + strlen(line) + 1; /* extend the former memory area: */ newbuf = (char *)realloc(tempnode->line, newlen); /* if successful, continue */ if (newbuf) { /* remove LF from the first part: */ char *lf; lf = strchr(newbuf, '\n'); if (lf) *lf = 0; /* append the new part */ strcat(newbuf, line); /* point out the new buffer instead */ tempnode->line = newbuf; } } else { tempnode->next = newnode; *lp = newnode; } } return bp; } /* ** Remove the last empty lines, if any, from an article body's linked list. */ int rmlastlines(struct body *bp) { struct body *tempnode; if (NULL == bp) return 0; for (tempnode = bp; tempnode->next != NULL && (tempnode->next->line)[0] != '\0'; tempnode = tempnode->next); if ((tempnode->line)[0] == '\n') { (tempnode->line)[0] = '\0'; return 1; } return 0; } /* copy bp on to the end of origbp, deallocate bp */ struct body *append_body(struct body *origbp, struct body **origlp, struct body *bp) { while (bp) { struct body *next; origbp = addbody(origbp, origlp, bp->line, (bp->header ? BODY_HEADER : 0) | (bp->html ? BODY_HTMLIZED : 0) | (bp->attached ? BODY_ATTACHED : 0)); next = bp->next; free(bp->line); free(bp); bp = next; } return origbp; } void free_body(struct body *bp) { while (bp != NULL) { struct body *cp = bp->next; if (bp->line) free(bp->line); free(bp); bp = cp; } } /* ** If a message is a reply to another, that message's number and the number of ** the message it may be referring to is put in this list. */ struct reply *addreply(struct reply *rp, int fromnum, struct emailinfo *email, int maybereply, struct reply **last_node) { struct reply *tempnode, *newnode; newnode = (struct reply *)emalloc(sizeof(struct reply)); newnode->frommsgnum = fromnum; if (email == NULL) newnode->msgnum = -1; else { newnode->msgnum = email->msgnum; #ifdef FASTREPLYCODE email->isreply = 1; #endif } newnode->data = email; newnode->maybereply = maybereply; newnode->next = NULL; if (!rp) { rp = newnode; #ifdef FASTREPLYCODE if (!threadlist && threadlist_by_msgnum && email) threadlist_by_msgnum[email->msgnum] = rp; #endif } else { #ifdef FASTREPLYCODE for (tempnode = (last_node ? *last_node : rp); tempnode->next != NULL; tempnode = tempnode->next); tempnode->next = newnode; if (rp == threadlist && threadlist_by_msgnum && email) threadlist_by_msgnum[email->msgnum] = tempnode; #else for (tempnode = rp; tempnode->next != NULL; tempnode = tempnode->next); tempnode->next = newnode; #endif } if (last_node) *last_node = newnode; return rp; } struct reply *addreply2(struct reply *rp, struct emailinfo *from_email, struct emailinfo *email, int maybereply, struct reply **last_node) { #ifdef FASTREPLYCODE struct reply *tempnode; for (tempnode = rp; tempnode != NULL; tempnode = tempnode->next) { if (tempnode->msgnum == email->msgnum) { /* duplicate? */ if (tempnode->maybereply) tempnode->maybereply = maybereply; return rp; /* don't add 2nd time */ } } from_email->replylist = addreply(from_email->replylist, from_email->msgnum, email, maybereply, NULL); #endif return addreply(rp, from_email->msgnum, email, maybereply, last_node); } /* ** Mark an article number as having been printed. */ struct printed *markasprinted(struct printed *pp, int num) { struct printed *tempnode, *newnode; newnode = (struct printed *)emalloc(sizeof(struct printed)); newnode->msgnum = num; newnode->next = NULL; if (pp == NULL) pp = newnode; else { for (tempnode = pp; tempnode->next != NULL; tempnode = tempnode->next); tempnode->next = newnode; } return pp; } /* ** Has an article already been printed? */ int wasprinted(struct printed *list, int num) { struct printed *pp; for (pp = list; pp != NULL; pp = pp->next) if (pp->msgnum == num) return 1; return 0; } /* ** Add article header information to a binary tree and sort by date, ** subject, or author. This is necessary for printing the index files. */ struct header *addheader(struct header *hp, struct emailinfo *email, int sorttype, int depth) { int isbigger; long yearsecs; static int max_depth, count_d; isbigger = 0; if (hp == NULL) { max_depth = depth; hp = (struct header *)emalloc(sizeof(struct header)); if (!hp) return NULL; hp->data = email; if (sorttype == 2) { yearsecs = hp->data->datenum = email->fromdate; if (set_use_sender_date) yearsecs = hp->data->datenum = email->date; if (!firstdatenum || yearsecs < firstdatenum) firstdatenum = yearsecs; if (yearsecs > lastdatenum) lastdatenum = yearsecs; } else hp->data->datenum = 0; hp->left = hp->right = NULL; return hp; } switch (sorttype) { case 1: isbigger = (strcasecmp(email->name, hp->data->name) > 0) ? 0 : 1; break; case 0: isbigger = (strcasecmp(email->unre_subject, hp->data->unre_subject) > 0) ? 0 : 1; break; case 2: yearsecs = email->fromdate; if (set_reverse) isbigger = (yearsecs < hp->data->datenum) ? 0 : 1; else isbigger = (yearsecs >= hp->data->datenum) ? 0 : 1; if (!firstdatenum || yearsecs < firstdatenum) firstdatenum = yearsecs; if (yearsecs > lastdatenum) lastdatenum = yearsecs; break; } if (isbigger) hp->left = addheader(hp->left, email, sorttype, depth + 1); else hp->right = addheader(hp->right, email, sorttype, depth + 1); if (sorttype == 2 && depth < max_depth / 2 && !(++count_d % 3)) { /* semi-random rebalancing */ struct header **hpp = (set_reverse ? &hp->left : &hp->right); struct header *hp1 = *hpp; if (hp1 != NULL && (hp1->right != NULL || hp1->left != NULL)) { if (hp1->right != NULL && (hp1->left == NULL || (count_d & 1))) { struct header *p = hp1->right; hp1->right = p->left; p->left = hp1; *hpp = p; } else { struct header *p = hp1->left; hp1->left = p->right; p->right = hp1; *hpp = p; } } } return hp; } struct emailsubdir *new_subdir(char *subdir, struct emailsubdir *last_subdir, char *description, time_t date) { struct emailsubdir *sd = folders; struct emailsubdir *new_sd; struct emailsubdir *insert_point = NULL; int count; char *p; while (sd) { if (!strcmp(subdir, sd->subdir)) return sd; if (sd->a_date < date && (!insert_point || sd->a_date > insert_point->a_date)) insert_point = sd; if (sd->next_subdir == NULL) break; sd = sd->next_subdir; } new_sd = (struct emailsubdir *)emalloc(sizeof(struct emailsubdir)); new_sd->next_subdir = NULL; new_sd->prior_subdir = NULL; if (insert_point == NULL) insert_point = sd; if (insert_point == NULL) folders = (set_increment == -1 ? NULL : new_sd); else if (date < folders->a_date) { new_sd->next_subdir = folders; folders->prior_subdir = new_sd; folders = new_sd; } else { new_sd->next_subdir = insert_point->next_subdir; insert_point->next_subdir = new_sd; new_sd->prior_subdir = insert_point; if (new_sd->next_subdir) new_sd->next_subdir->prior_subdir = new_sd; } new_sd->first_email = NULL; new_sd->last_email = NULL; new_sd->count = 0; new_sd->subdir = strsav(subdir); new_sd->description = description; new_sd->a_date = date; if (set_base_url != NULL) { if (set_base_url[strlen(set_base_url) - 1] != '/') trio_asprintf(&new_sd->rel_path_to_top, "%s/", set_base_url); else new_sd->rel_path_to_top = strsav(set_base_url); } else { new_sd->rel_path_to_top = strsav("../"); count = 0; for (p = subdir; *p; ++p) count += (*p == '/'); while (--count > 0) { trio_asprintf(&p, "../%s", new_sd->rel_path_to_top); free(new_sd->rel_path_to_top); new_sd->rel_path_to_top = p; } if (set_latest_folder != NULL && set_folder_by_date != NULL && strchr(set_folder_by_date, '/')) { static int warned = 0; if (!warned) { fprintf(stderr, "Warning: the latest_folder option combined with " "a folder_by_date option that includes a '/' will produce " "some invalid links unless you use the base_url option!!.\n"); warned = 1; } } } trio_asprintf(&new_sd->full_path, "%s%s", set_dir, subdir); if (!isdir(new_sd->full_path)) { checkdir(new_sd->full_path); /* make subdir(s) if needed */ if (set_latest_folder) latest_folder_path = new_sd->subdir; } return new_sd; } /* ** And Now The List Addition Routines! */ void print_list(char *listname, struct hmlist *list) { struct hmlist *tlist; int set = 0; int len = 0; int line = 0; /* Daniel remade Nov 23 1998. Use a single line as far as possible */ for (tlist = list; tlist != NULL; tlist = tlist->next) { set++; if (!line++) { printf("%s = ", listname); len = strlen(listname) + 3; } else { if (len + strlen(tlist->val) > 78) { printf("\n%s = ", listname); len = strlen(listname) + 3; } else { printf(","); len++; } } printf("%s", tlist->val); len += strlen(tlist->val); } if (!set) printf("%s: %s\n", listname, lang[MSG_NO_ELEMENTS]); else printf("\n"); } /* ** Is the requested element in the list ? */ int inlist(struct hmlist *listname, char *wildcard) { struct hmlist *tlist; for (tlist = listname; tlist != NULL; tlist = tlist->next) { /* wildcard checks enabled! */ if (Match(wildcard, tlist->val)) return 1; } return 0; } /* ** What is the position of the requested element in the list ? */ int inlist_pos(struct hmlist *listname, char *wildcard) { struct hmlist *tlist; int i; for (i = 0, tlist = listname; tlist != NULL; i++, tlist = tlist->next) { /* wildcard checks enabled! */ if (Match(wildcard, tlist->val)) return i; } return -1; } static int regex_index(struct hmlist *listname, int index) { static int start[5] = { -1, -1, -1, -1, -1 }; if (start[0] == -1) { struct hmlist *tlist; start[1] = start[0] = 0; for (tlist = set_filter_out; tlist != NULL; tlist = tlist->next) ++start[1]; start[2] = start[1]; for (tlist = set_filter_require; tlist != NULL; tlist = tlist->next) ++start[2]; start[3] = start[2]; for (tlist = set_filter_out_full_body; tlist != NULL; tlist = tlist->next) ++start[3]; start[4] = start[3]; for (tlist = set_filter_require_full_body; tlist != NULL; tlist = tlist->next) ++start[4]; } if (index == -1) return start[4]; if (listname == set_filter_out) return start[0] + index; if (listname == set_filter_require) return start[1] + index; if (listname == set_filter_out_full_body) return start[2] + index; if (listname == set_filter_require_full_body) return start[3] + index; return -1; } /* ** like inlist_pos, but does regex search */ int inlist_regex_pos(struct hmlist *listname, char *str) { struct hmlist *tlist; int i; for (i = 0, tlist = listname; tlist != NULL; i++, tlist = tlist->next) { #ifdef HAVE_PCRE int r; const char *errptr; int epos; int index = regex_index(listname, i); static pcre *p, **pcre_list; static pcre_extra *extra, **extra_list; if (!pcre_list) { int n = regex_index(NULL, -1); int i; pcre_list = (pcre **) emalloc(n * sizeof(pcre *)); extra_list = (pcre_extra **) emalloc(n * sizeof(pcre_extra *)); for (i = 0; i < n; ++i) { pcre_list[i] = NULL; extra_list[i] = NULL; } } if ((p = pcre_list[index]) == NULL) { p = pcre_compile(tlist->val, 0, &errptr, &epos, NULL); if (!p) { snprintf(errmsg, sizeof(errmsg), "Error at position %d of regular expression '%s': %s", epos, tlist->val, errptr); progerr(errmsg); } extra = pcre_study(p, 0, &errptr); if (errptr) { snprintf(errmsg, sizeof(errmsg), "Error studying regular expression '%s': %s", tlist->val, errptr); progerr(errmsg); } pcre_list[index] = p; extra_list[index] = extra; } extra = extra_list[index]; r = pcre_exec(p, extra, str, strlen(str), 0, 0, NULL, 0); if (r >= 0) return i; #else static int warned = 0; if (set_showprogress && !warned) { warned = 1; printf("warning - regex not available\n"); } if (strstr(tlist->val, str)) return i; #endif } return -1; } /* ** Add element to the specified list */ struct hmlist *add_2_list(struct hmlist *listname, char *value) { struct hmlist *tnode, *newnode; newnode = (struct hmlist *)emalloc(sizeof(struct hmlist)); newnode->val = strsav(value); newnode->next = NULL; if (listname == NULL) listname = newnode; else { for (tnode = listname; tnode->next != NULL; tnode = tnode->next); tnode->next = newnode; } return listname; } struct hmlist *add_list(struct hmlist *listname, char *value) { register char *cp; register char *valp; /* * This needs to support lists specified on a single line, such as * inline = image/jpeg image/gif ... * inline = image/jpeg,image/gif ... * as well as lists specified on multiple lines. */ valp = value; cp = valp; while (*cp) { if (*cp == ' ' || *cp == ',') { if (*valp != ' ' && *valp != ',') { *cp = '\0'; listname = add_2_list(listname, valp); } valp = cp + 1; } cp++; } if (*valp) listname = add_2_list(listname, valp); return listname; }