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 <hurtta+elm@posti.FMI.FI>
* or Kari Hurtta <elm@elmme-mailer.org>
*****************************************************************************/
#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:
*/
syntax highlighted by Code2HTML, v. 0.9.1