static char rcsid[] = "@(#)$Id: thread.c,v 1.6.2.2 2007/08/25 07:45:27 hurtta Exp $"; /****************************************************************************** * The Elm (ME+) Mail System - $Revision: 1.6.2.2 $ $State: Exp $ * * Author: Kari Hurtta * or Kari Hurtta *****************************************************************************/ #include "def_messages.h" DEBUG_VAR(Debug,__FILE__,"messages"); #if ANSI_C #define S_(x) static x; #else #define S_(x) #endif static unsigned char *s2us P_((char *str)); static unsigned char *s2us(str) char *str; { return (unsigned char *)str; } #define THREAD_NAME_magic 0xF50A static struct thread_name { unsigned short magic; /* THREAD_NAME_magic */ struct string * thread_title; struct thread_name * smaller; /* LEFT */ struct thread_name * bigger; /* RIGHT */ struct thread_name * bad; /* NO COMPARE */ int * thread_list; int thread_list_len; } * give_thread_name P_((struct thread_name **root, const struct string * thread_title)); static struct thread_name * give_thread_name(root, thread_title) struct thread_name **root; const struct string * thread_title; { struct thread_name *ptr = *root; int r; if (!ptr) { ptr = safe_malloc(sizeof(*ptr)); bzero((void *)ptr, sizeof(*ptr)); ptr->magic = THREAD_NAME_magic; ptr->thread_title = dup_string(thread_title); ptr->smaller = NULL; ptr->bigger = NULL; ptr->bad = NULL; ptr->thread_list = NULL; ptr->thread_list_len = 0; *root = ptr; return ptr; } if (ptr->magic != THREAD_NAME_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_name", "Bad magic number",0); r = string_cmp(thread_title,ptr->thread_title, 999 /* == Values not comparable */); switch(r) { case 0: return ptr; case -1: return give_thread_name(& (ptr->bigger), thread_title); case 1: return give_thread_name(& (ptr->smaller), thread_title); case 999: return give_thread_name(& (ptr->bad), thread_title); } panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_name", "Unexpected return from string_cmp()",0); return NULL; } static void free_thread_names P_((struct thread_name **root)); static void free_thread_names(root) struct thread_name **root; { struct thread_name *ptr = *root; if (ptr->magic != THREAD_NAME_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"free_thread_names", "Bad magic number",0); if (ptr->smaller) free_thread_names(& (ptr->smaller)); if (ptr->bigger) free_thread_names(& (ptr->bigger)); if (ptr->bad) free_thread_names(& (ptr->bad)); if (ptr->thread_title) { free_string(& (ptr->thread_title)); } if (ptr->thread_list) { free(ptr->thread_list); ptr->thread_list = NULL; } ptr->thread_list_len = 0; ptr->magic = 0; /* Invalidate */ free(ptr); *root = NULL; } struct thread { struct thread_info *t; int * msg_list; int msg_list_len; }; #define THREADVIEW_magic 0xF509 struct ThreadView { unsigned short magic; /* THREADVIEW_magic */ struct thread_name * name_root; struct thread * thread; int thread_len; }; static void free_thread_info P_((struct thread_info **t)); static void free_thread_info(t) struct thread_info **t; { if ((*t)->thread_subject) free_string(& ((*t)->thread_subject)); free(*t); *t = NULL; } static struct thread_info *malloc_thread_info P_((void)); static struct thread_info *malloc_thread_info() { struct thread_info * ret= safe_malloc(sizeof (*ret)); bzero((void *)ret, sizeof(*ret)); ret->thread_subject = NULL; return ret; } void free_thread_view(t) struct ThreadView **t; { if (THREADVIEW_magic != (*t)->magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"free_thread_view", "Bad magic number",0); if ((*t)->thread) { int i; for (i = 0; i < (*t)->thread_len; i++) { if ((*t)->thread[i].t) free_thread_info(& (*t)->thread[i].t); if ((*t)->thread[i].msg_list) { free((*t)->thread[i].msg_list); (*t)->thread[i].msg_list = NULL; } (*t)->thread[i].msg_list_len = 0; } free((*t)->thread); (*t)->thread = NULL; } (*t)->thread_len = 0; if ((*t)->name_root) free_thread_names(& ((*t)->name_root)); (*t)->magic = 0; /* Invalidate */ free(*t); *t = NULL; } static struct ThreadView * malloc_mailbox_thread P_((void)); static struct ThreadView * malloc_mailbox_thread() { struct ThreadView * ret; ret = safe_malloc(sizeof (*ret)); /* bzero is defined hdrs/defs.h */ bzero((void *)ret,sizeof (*ret)); ret->magic = THREADVIEW_magic; ret->name_root = NULL; ret->thread = NULL; ret->thread_len = 0; return ret; } static struct string *normalize_subject P_((const struct string *subject)); static struct string *normalize_subject(subject) const struct string *subject; { struct string * temp = skip_ascii_head_from_string(subject, s2us("Re: "),1); struct string * result = collapse_spaces_from_string(temp); free_string (&temp); return result; } struct thread_sort_data { struct MailboxView *mailbox; int index; }; static int compare_thread P_((struct thread_sort_data *X1, struct thread_sort_data *X2)); static int compare_thread(X1,X2) struct thread_sort_data *X1; struct thread_sort_data *X2; { struct header_rec *hdr1 = X1->mailbox->mailbox_type-> mt_give_header_it(X1->mailbox,X1->index, & X1->mailbox->view[X1->index]); struct header_rec *hdr2 = X2->mailbox->mailbox_type-> mt_give_header_it(X2->mailbox,X2->index, & X2->mailbox->view[X2->index]); struct string * from1, * from2; int ret; if (!hdr1 || !hdr2) return 0; if (!hdr1->subject && hdr2->subject) return -1; if (hdr1->subject && !hdr2->subject) return 1; if (!hdr1->subject && !hdr2->subject) return 0; from1 = normalize_subject(hdr1->subject); from2 = normalize_subject(hdr2->subject); ret = string_cmp(from1,from2, 0 /* == Values not comparable */ ); free_string(&from1); free_string(&from2); if (0 == ret) { long diff; diff = hdr1->time_sent - hdr2->time_sent; if ( diff < 0 ) ret = -1; else if ( diff > 0 ) ret = 1; else ret = 0; } return ret; } void update_mailbox_threads(mailbox) struct MailboxView *mailbox; { int i; int not_added = 0; struct ThreadView * TV; if (mailbox->magic != MAILBOXVIEW_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Bad magic number",0); if (! mailbox->thread_view) mailbox->thread_view = malloc_mailbox_thread(); if (mailbox->thread_view->magic != THREADVIEW_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Bad thread view magic number",0); TV = mailbox->thread_view; for (i = 0; i < TV->thread_len; i++) { /* Just reset counters -- msg_list_len is realloced later */ TV->thread[i].msg_list_len = 0; } if (mailbox->mailbox_type->magic != MAILBOXTYPE_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Bad type magic number",0); /* Read numbers */ for (i = 0; i < mailbox->view_len; i++) { if (-1 == mailbox->view[i].thread_number) not_added++; else { int index = mailbox->view[i].thread_number; struct string * subj1 = NULL; if (index < 0 || index >= TV->thread_len) { DPRINT(Debug,1,(&Debug, "update_mailbox_threads: [%d] bad thread index %d\n", i,index)); mailbox->view[i].thread_number = -1; not_added++; } else { struct header_rec *hdr = mailbox->mailbox_type-> mt_give_header_it(mailbox,i, & mailbox->view[i]); struct thread_info * T = TV->thread[index].t; if (!hdr) { DPRINT(Debug,7,(&Debug, "update_mailbox_threads: [%d] thread index %d -- no header available\n", i,index)); mailbox->view[i].thread_number = -1; not_added++; continue; } if (!hdr->subject) { DPRINT(Debug,7,(&Debug, "update_mailbox_threads: [%d] thread index %d -- no subject\n", i,index)); subj1 = new_string(system_charset); } else { subj1 = normalize_subject(hdr->subject); } if (0 != string_cmp(T->thread_subject,subj1, 999 /* == Values not comparable */)) { DPRINT(Debug,7,(&Debug, "update_mailbox_threads: [%d] thread index %d -- subject not match\n", i,index)); mailbox->view[i].thread_number = -1; not_added++; DPRINT(Debug,7,(&Debug," thread %d=%S\n", index,T->thread_subject)); DPRINT(Debug,7,(&Debug," mail %d=%S\n", i,subj1)); free_string(&subj1); continue; } if (hdr->time_sent < T->time_sent_first - (long) sort_thread_max_time * 24 * 60 * 60 || hdr->time_sent > T->time_sent_last + (long) sort_thread_max_time * 24 * 60 * 60) { DPRINT(Debug,7,(&Debug, "update_mailbox_threads: [%d] thread index %d -- time not match\n", i,index)); mailbox->view[i].thread_number = -1; not_added++; DPRINT(Debug,7,(&Debug," thread %d= first time=%ld last time=%ld\n", index, (long) T->time_sent_first, (long) T->time_sent_last)); DPRINT(Debug,7,(&Debug," mail %d= time sent=%ld\n", i, hdr->time_sent)); free_string(&subj1); continue; } TV->thread[index].msg_list = safe_realloc(TV->thread[index].msg_list, (TV->thread[index].msg_list_len+1) * sizeof (TV->thread[index].msg_list[0])); TV->thread[index].msg_list[TV->thread[index].msg_list_len] = i; TV->thread[index].msg_list_len++; free_string(&subj1); } } } for (i = 0; i < TV->thread_len; i++) { TV->thread[i].t->num_messages = TV->thread[i].msg_list_len; } DPRINT(Debug,7,(&Debug, "update_mailbox_threads: not_added=%d\n",not_added)); if (not_added > 0) { struct thread_sort_data * sort_data = safe_malloc(not_added * sizeof(sort_data[0])); int idx = 0; int outer, inner = 0; typedef int (*compar) P_((const void *, const void *)); compar X = (compar) compare_thread; for (i = 0; i < mailbox->view_len; i++) { if (-1 == mailbox->view[i].thread_number) { if (idx >= not_added) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Overflow",0); sort_data[idx].mailbox = mailbox; sort_data[idx].index = i; idx++; } } /* Sort with subject and sent_time */ qsort(sort_data,idx,sizeof (sort_data[0]), X); for (outer = 0; outer < idx; outer = inner) { int index = sort_data[outer].index; struct header_rec *hdr; struct string * subj1 = NULL; time_t thread_first; time_t thread_last; int thread_idx; int scan_idx; int count; int j; struct thread_name * thread_name = NULL; if (index < 0 || index >= mailbox->view_len) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Bad index",0); hdr = mailbox->mailbox_type-> mt_give_header_it(mailbox,index, & mailbox->view[index]); if (!hdr) { DPRINT(Debug,7,(&Debug, "update_mailbox_threads: [%d] -- no header available\n", index)); inner = outer+1; continue; } if (!hdr->subject) { DPRINT(Debug,7,(&Debug, "update_mailbox_threads: [%d] -- no subject\n", index)); subj1 = new_string(system_charset); } else { subj1 = normalize_subject(hdr->subject); } thread_first = hdr->time_sent ; thread_last = hdr->time_sent ; for (inner = outer+1; inner < idx; inner++) { struct header_rec *hdr2; int index2 = sort_data[inner].index; struct string * subj2 = NULL; if (index2 < 0 || index2 >= mailbox->view_len) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Bad index",0); hdr2 = mailbox->mailbox_type-> mt_give_header_it(mailbox,index2, & mailbox->view[index2]); if (!hdr2) { DPRINT(Debug,7,(&Debug, "update_mailbox_threads: [%d] -- no header available\n", index2)); break; } if (!hdr2->subject) { DPRINT(Debug,7,(&Debug, "update_mailbox_threads: [%d] -- no subject\n", index2)); subj2 = new_string(system_charset); } else { subj2 = normalize_subject(hdr2->subject); } if (0 != string_cmp(subj2,subj1, 999 /* == Values not comparable */)) { free_string(& subj2); /* NO MATCH */ break; } if (hdr2->time_sent < thread_first - (long) sort_thread_max_time * 24 * 60 * 60 || hdr2->time_sent > thread_last + (long) sort_thread_max_time * 24 * 60 * 60) { free_string(& subj2); /* NO MATCH */ break; } /* OK */ if (hdr2->time_sent < thread_first) thread_first = hdr2->time_sent; if (hdr2->time_sent > thread_last) thread_last = hdr2->time_sent; free_string(& subj2); } thread_name = give_thread_name(& (TV->name_root), subj1); for (scan_idx = 0; scan_idx < thread_name->thread_list_len; scan_idx++) { struct thread_info * T; thread_idx = thread_name->thread_list[scan_idx]; if (thread_idx < 0 || thread_idx >= TV->thread_len) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Bad thread index",0); T = TV->thread[thread_idx].t; if (0 != string_cmp(T->thread_subject,subj1, 999 /* == Values not comparable */)) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Bad thread index",0); if (thread_last > T->time_sent_first - (long) sort_thread_max_time * 24 * 60 * 60 && thread_first < T->time_sent_last + (long) sort_thread_max_time * 24 * 60 * 60) { if (thread_first < T->time_sent_first) T->time_sent_first = thread_first; if (thread_last > T->time_sent_last) T->time_sent_last = thread_last; goto found_thread; } } TV->thread = safe_realloc(TV->thread, ( TV->thread_len+1) * sizeof (TV->thread[0])); thread_idx = TV->thread_len; bzero((void *)& (TV->thread[thread_idx]), sizeof(TV->thread[thread_idx])); TV->thread_len++; TV->thread[thread_idx].t = malloc_thread_info(); TV->thread[thread_idx].t->time_sent_first = thread_first; TV->thread[thread_idx].t->time_sent_last = thread_last; TV->thread[thread_idx].t->thread_subject = dup_string(subj1); TV->thread[thread_idx].msg_list = NULL; TV->thread[thread_idx].msg_list_len = 0; DPRINT(Debug,7,(&Debug, "update_mailbox_threads: Adding thread #%d to thread subject: %S\n", thread_idx,thread_name->thread_title)); thread_name->thread_list = safe_realloc(thread_name->thread_list, (thread_name->thread_list_len +1) * sizeof(thread_name->thread_list[0])); thread_name->thread_list[thread_name->thread_list_len] = thread_idx; thread_name->thread_list_len++; found_thread: count = inner-outer; DPRINT(Debug,7,(&Debug, "update_mailbox_threads: Adding %d messages to thread %d: %S\n", count,thread_idx, TV->thread[thread_idx].t->thread_subject)); DPRINT(Debug,7,(&Debug, " messages sent time %ld - %ld\n", thread_first, thread_last)); DPRINT(Debug,7,(&Debug, " thread sent time %ld - %ld\n", TV->thread[thread_idx].t->time_sent_first, TV->thread[thread_idx].t->time_sent_last)); if (!count) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Empty count",0); count += TV->thread[thread_idx].msg_list_len; TV->thread[thread_idx].msg_list = safe_realloc(TV->thread[thread_idx].msg_list, count * sizeof(TV->thread[thread_idx].msg_list[0])); for (j = outer; j < inner; j++) { int index = sort_data[j].index; if (TV->thread[thread_idx].msg_list_len >= count) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Overflow",0); TV->thread[thread_idx].msg_list[TV->thread[thread_idx].msg_list_len] = index; TV->thread[thread_idx].msg_list_len++; if (index < 0 || index >= mailbox->view_len) panic("MBX VIEW PANIC",__FILE__,__LINE__,"update_mailbox_threads", "Bad index",0); mailbox->view[index].thread_number = thread_idx; } TV->thread[thread_idx].t->num_messages = TV->thread[thread_idx].msg_list_len; free_string(&subj1); } free(sort_data); } } CONST struct thread_info * give_thread_info_s(s) struct sort_data *s; { int idx; if (s->sort_data_type->magic != SORTDATATYPE_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_info_s", "Bad magic number",0); if (THREADVIEW_magic != s->t->magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_info_s", "Bad threadview magic number",0); idx = s->w.thread_number; if (-1 == idx) return NULL; /* No thread information */ if (idx < 0 || idx >= s->t->thread_len) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_info_s", "Bad thread index",0); return s->t->thread[idx].t; } int get_thread_count(mailbox, create) struct MailboxView *mailbox; int create; { if (mailbox->magic != MAILBOXVIEW_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"get_thread_count", "Bad magic number",0); if (! mailbox->thread_view) { if (!create) return -1; update_mailbox_threads(mailbox); } if (mailbox->thread_view->magic != THREADVIEW_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"get_thread_count", "Bad thread view magic number",0); return mailbox->thread_view->thread_len; } CONST struct thread_info * give_thread_info(mailbox,idx) struct MailboxView * mailbox; int idx; { if (mailbox->magic != MAILBOXVIEW_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_info", "Bad magic number",0); if (! mailbox->thread_view) return NULL; if (mailbox->thread_view->magic != THREADVIEW_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_info", "Bad thread view magic number",0); if (idx < 0 || idx >= mailbox->thread_view->thread_len) return NULL; return mailbox->thread_view->thread[idx].t; } /* caller must free result */ int * give_thread_message_list (mailbox,idx,reslen) struct MailboxView * mailbox; int idx; int *reslen; { int * res = NULL; int i; if (mailbox->magic != MAILBOXVIEW_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_message_list", "Bad magic number",0); *reslen = 0; if (! mailbox->thread_view) return NULL; if (mailbox->thread_view->magic != THREADVIEW_magic) panic("MBX VIEW PANIC",__FILE__,__LINE__,"give_thread_info", "Bad thread view magic number",0); if (idx < 0 || idx >= mailbox->thread_view->thread_len) return NULL; if (mailbox->thread_view->thread[idx].msg_list_len < 1) return NULL; *reslen = mailbox->thread_view->thread[idx].msg_list_len; res = safe_malloc((*reslen) * sizeof(res[0])); for (i = 0; i < *reslen; i++) res[i] = mailbox->thread_view->thread[idx].msg_list[i]; return res; } /* * Local Variables: * mode:c * c-basic-offset:4 * buffer-file-coding-system: iso-8859-1 * End: */