diff options
Diffstat (limited to 'imap/thread.c')
| -rw-r--r-- | imap/thread.c | 500 | 
1 files changed, 500 insertions, 0 deletions
| diff --git a/imap/thread.c b/imap/thread.c new file mode 100644 index 0000000..0670f22 --- /dev/null +++ b/imap/thread.c @@ -0,0 +1,500 @@ +/* +** Copyright 2000-2009 Double Precision, Inc. +** See COPYING for distribution information. +*/ + + +#include	"config.h" +#include	"imapd.h" +#include	"thread.h" +#include	"searchinfo.h" +#include	<stdio.h> +#include	<string.h> +#include	<stdlib.h> +#include	"imapwrite.h" +#include	"imaptoken.h" +#include	"imapscanclient.h" +#include	"rfc822/rfc822.h" +#include	"rfc822/rfc2047.h" +#include	"rfc822/imaprefs.h" +#include	"unicode/unicode.h" + +static void thread_os_callback(struct searchinfo *, struct searchinfo *, int, +	unsigned long, void *); +static void thread_ref_callback(struct searchinfo *, struct searchinfo *, int, +	unsigned long, void *); + +extern struct imapscaninfo current_maildir_info; + + +struct os_threadinfo { +	struct os_threadinfo *next; +	char *subj; +	time_t sent_date; +	unsigned long n; +	} ; + +struct os_threadinfo_list { +	struct os_threadinfo_list *next; +	size_t thread_start; +} ; + +struct os_struct { +	struct os_threadinfo *list; +	unsigned nmsgs; +	struct os_threadinfo **msgs; +	} ; + +static void os_init(struct os_struct *os) +{ +	memset(os, 0, sizeof(*os)); +} + +static void os_add(struct os_struct *os, unsigned long n, const char *s, +		   time_t sent_date) +{ +struct os_threadinfo *osi=(struct os_threadinfo *) +	malloc(sizeof(struct os_threadinfo)); + +	if (!osi)	write_error_exit(0); +	osi->subj=strdup(s); +			/* This decodes the MIME encoding */ +	if (!osi->subj)	write_error_exit(0); +	osi->sent_date=sent_date; +	osi->n=n; +	osi->next=os->list; +	os->list=osi; +	++os->nmsgs; +} + +static void os_free(struct os_struct *os) +{ +struct os_threadinfo *p; + +	while ((p=os->list) != 0) +	{ +		os->list=p->next; +		free(p->subj); +		free(p); +	} +	if (os->msgs) free(os->msgs); +} + +static int cmpsubjs(const void *a, const void *b) +{ +	const struct os_threadinfo *ap=*(const struct os_threadinfo **)a; +	const struct os_threadinfo *bp=*(const struct os_threadinfo **)b; +	int rc=strcmp( ap->subj, bp->subj); + +	if (rc)	return (rc); + +	return (ap->sent_date < bp->sent_date ? -1: +		ap->sent_date > bp->sent_date ? 1:0); +} + +/* Print the meat of the THREAD ORDEREDSUBJECT response */ + +static void printos(struct os_threadinfo **array, size_t cnt) +{ +	size_t	i; +	struct os_threadinfo_list *thread_list=NULL, *threadptr, **tptr; + +	/* +	** thread_list - indexes to start of each thread, sort indexes by +	** sent_date +	*/ + +	for (i=0; i<cnt; i++) +	{ +		/* Find start of next thread */ + +		if (i > 0 && strcmp(array[i-1]->subj, array[i]->subj) == 0) +			continue; + +		threadptr=malloc(sizeof(struct os_threadinfo_list)); +		if (!threadptr) +			write_error_exit(0); +		threadptr->thread_start=i; + +		/* Insert into the list, sorted by sent date */ + +		for (tptr= &thread_list; *tptr; tptr=&(*tptr)->next) +			if ( array[(*tptr)->thread_start]->sent_date +			     > array[i]->sent_date) +				break; + +		threadptr->next= *tptr; +		*tptr=threadptr; +	} + +	while ( (threadptr=thread_list) != NULL) +	{ +		size_t	i, j; +		const char *p; + +		thread_list=threadptr->next; + +		i=threadptr->thread_start; +		free(threadptr); + +		for (j=i+1; j<cnt; j++) +		{ +			if (strcmp(array[i]->subj, array[j]->subj)) +				break; +		} + +		p="("; +		while (i < j) +		{ +			writes(p); +			p=" "; +			writen(array[i]->n); +			++i; +		} +		writes(")"); +	} +} + +void dothreadorderedsubj(struct searchinfo *si, struct searchinfo *sihead, +			 const char *charset, int isuid) +{ +struct	os_struct	os; + +	os_init(&os); +	search_internal(si, sihead, charset, isuid, thread_os_callback, &os); + +	if (os.nmsgs > 0)	/* Found some messages */ +	{ +	size_t	i; +	struct os_threadinfo *o; + +		/* Convert it to an array */ + +		os.msgs= (struct os_threadinfo **) +			malloc(os.nmsgs * sizeof(struct os_threadinfo *)); +		if (!os.msgs)	write_error_exit(0); +		for (o=os.list, i=0; o; o=o->next, i++) +			os.msgs[i]=o; + +		/* Sort the array */ + +		qsort(os.msgs, os.nmsgs, sizeof(*os.msgs), cmpsubjs); + +		/* Print the array */ + +		printos(os.msgs, os.nmsgs); +	} +	os_free(&os); +} + +/* +This callback function is called once search finds a qualifying message. +We save its message number and subject in a link list. +*/ + +static void thread_os_callback(struct searchinfo *si, +			       struct searchinfo *sihead, +			       int isuid, unsigned long i, +			       void *voidarg) +{ +	if (sihead->type == search_orderedsubj) +		/* SHOULD BE ALWAYS TRUE */ +		os_add( (struct os_struct *)voidarg, +			isuid ? current_maildir_info.msgs[i].uid:i+1, +			sihead->as ? sihead->as:"", +			sihead->bs ? rfc822_parsedt(sihead->bs):0); +} + +static void printthread(struct imap_refmsg *, int); + +void dothreadreferences(struct searchinfo *si, struct searchinfo *sihead, +			const char *charset, +			int isuid) +{ +	struct imap_refmsgtable *reftable; +	struct imap_refmsg *root; + +	if (!(reftable=rfc822_threadalloc())) +	{ +		write_error_exit(0); +		return; +	} + +	search_internal(si, sihead, charset, 0, +			thread_ref_callback, reftable); + +	root=rfc822_thread(reftable); +	printthread(root, isuid); +	rfc822_threadfree(reftable); +} + +static void thread_ref_callback(struct searchinfo *si, +			       struct searchinfo *sihead, +			       int isuid, unsigned long i, +			       void *voidarg) +{ +	if (sihead->type == search_references1 && sihead->a && +	    sihead->a->type == search_references2 && sihead->a->a && +	    sihead->a->a->type == search_references3 && sihead->a->a->a && +	    sihead->a->a->a->type == search_references4) +	{ +		const char *ref, *inreplyto, *subject, *date, *msgid; + +		ref=sihead->as; +		inreplyto=sihead->bs; +		date=sihead->a->as; +		subject=sihead->a->a->as; +		msgid=sihead->a->a->a->as; + +#if 0 +		fprintf(stderr, "REFERENCES: ref=%s, inreplyto=%s, subject=%s, date=%s, msgid=%s\n", +			ref ? ref:"", +			inreplyto ? inreplyto:"", +			subject ? subject:"", +			date ? date:"", +			msgid ? msgid:""); +#endif + +		if (!rfc822_threadmsg( (struct imap_refmsgtable *)voidarg, +				       msgid, ref && *ref ? ref:inreplyto, +				       subject, date, 0, i)) +			write_error_exit(0); +	} +} + +static void printthread(struct imap_refmsg *msg, int isuid) +{ +	const char *pfix=""; + +	while (msg) +	{ +		if (!msg->isdummy) +		{ +			writes(pfix); +			writen(isuid ? +			       current_maildir_info.msgs[msg->seqnum].uid: +			       msg->seqnum+1); +			pfix=" "; +		} + +		if (msg->firstchild && (msg->firstchild->nextsib +					|| msg->firstchild->isdummy +					|| msg->parent == NULL)) +		{ +			writes(pfix); +			for (msg=msg->firstchild; msg; msg=msg->nextsib) +			{ +				struct imap_refmsg *msg2; + +				msg2=msg; + +				if (msg2->isdummy) +					msg2=msg2->firstchild; + +				for (; msg2; msg2=msg2->firstchild) +				{ +					if (!msg2->isdummy || +					    msg2->nextsib) +						break; +				} + +				if (msg2) +				{ +					writes("("); +					printthread(msg, isuid); +					writes(")"); +				} +			} +			break; +		} +		msg=msg->firstchild; +	} +} + +void free_temp_sort_stack(struct temp_sort_stack *t) +{ +	while (t) +	{ +	struct temp_sort_stack *u=t->next; + +		free(t); +		t=u; +	} +} + +/* ---------------------------------- SORT ---------------------------- */ + +/* sortmsginfo holds the sorting information for a message. */ + +struct sortmsginfo { +	struct sortmsginfo *next;	/* next msg */ +	unsigned long n;		/* msg number/uid */ +	char **sortfields;		/* array of sorting fields */ +	char *sortorder;		/* [x]=0 - normal, [x]=1 - reversed */ +	size_t nfields; +	} ; + +struct sortmsgs { +	struct sortmsginfo *list;	/* The actual list */ +	struct sortmsginfo **array;	/* In array form */ +	size_t	nmsgs;			/* Array size/count of msgs */ +	size_t	nfields;		/* From the SORT() arg */ +	} ; + +static void free_sortmsgs(struct sortmsgs *p) +{ +struct sortmsginfo *q; + +	if (p->array)	free(p->array); +	while ((q=p->list) != 0) +	{ +	size_t	i; + +		p->list=q->next; +		for (i=0; i<p->nfields; i++) +			if (q->sortfields[i]) +				free(q->sortfields[i]); +		if (q->sortfields)	free(q->sortfields); +		if (q->sortorder)	free(q->sortorder); +		free(q); +	} +} + +static void sort_callback(struct searchinfo *, struct searchinfo *, int, +	unsigned long, void *); + +static int cmpsort(const void *a, const void *b) +{ +const struct sortmsginfo *ap=*(const struct sortmsginfo **)a; +const struct sortmsginfo *bp=*(const struct sortmsginfo **)b; +size_t	i; + +	for (i=0; i<ap->nfields; i++) +	{ +	int	n=strcmp(ap->sortfields[i], bp->sortfields[i]); + +		if (n < 0) +			return (ap->sortorder[i] ? 1:-1); +		if (n > 0) +			return (ap->sortorder[i] ? -1:1); +	} +	return (0); +} + +void dosortmsgs(struct searchinfo *si, struct searchinfo *sihead, +		const char *charset, int isuid) +{ +struct	sortmsgs sm; +struct	searchinfo *p; + +	memset(&sm, 0, sizeof(sm)); +	for (p=sihead; p; p=p->a) +		switch (p->type)	{ +		case search_orderedsubj: +		case search_arrival: +		case search_cc: +		case search_date: +		case search_from: +		case search_size: +		case search_to: +			++sm.nfields; +			break; +		default: +			break; +		} +	search_internal(si, sihead, charset, isuid, sort_callback, &sm); +	if (sm.nmsgs > 0) +	{ +	size_t	i; +	struct sortmsginfo *o; + +		/* Convert it to an array */ + +		sm.array= (struct sortmsginfo **) +			malloc(sm.nmsgs * sizeof(struct sortmsginfo *)); +		if (!sm.array)	write_error_exit(0); +		for (o=sm.list, i=0; o; o=o->next, i++) +			sm.array[i]=o; + +		/* Sort the array */ + +		qsort(sm.array, sm.nmsgs, sizeof(*sm.array), cmpsort); + +		/* Print the array */ + +		for (i=0; i<sm.nmsgs; i++) +		{ +			writes(" "); +			writen(sm.array[i]->n); +		} +	} +	free_sortmsgs(&sm); +} + +static void sort_callback(struct searchinfo *si, struct searchinfo *sihead, +	int isuid, unsigned long n, void *voidarg) +{ +struct sortmsgs *sm=(struct sortmsgs *)voidarg; +struct sortmsginfo *msg=(struct sortmsginfo *) +		malloc(sizeof(struct sortmsginfo)); +struct searchinfo *ss; +int rev; +size_t	i; + +	if (msg)	memset(msg, 0, sizeof(*msg)); +	if (!msg || (sm->nfields && ((msg->sortfields=(char **) +				malloc(sizeof(char *)*sm->nfields)) == 0 || +					(msg->sortorder=(char *) +				malloc(sm->nfields)) == 0))) +		write_error_exit(0); + +	if (sm->nfields) +	{ +		memset(msg->sortfields, 0, sizeof(char *)*sm->nfields); +		memset(msg->sortorder, 0, sm->nfields); +	} + +	rev=0; +	i=0; + +/* fprintf(stderr, "--\n"); */ + +	for (ss=sihead; ss; ss=ss->a) +	{ +	char	*p; + +		if (i >= sm->nfields) +			break;	/* Something's fucked up, better handle it +				** gracefully, instead of dumping core. +				*/ +		switch (ss->type)	{ +		case search_reverse: +			rev=1-rev; +			continue; +		case search_orderedsubj: +		case search_arrival: +		case search_cc: +		case search_date: +		case search_from: +		case search_size: +		case search_to: +			p=ss->as; +			if (!p)	p=""; +			msg->sortfields[i]=my_strdup(p); +			msg->sortorder[i]=rev; +			/* fprintf(stderr, "%d %s\n", msg->sortorder[i], msg->sortfields[i]); */ +			++i; +			rev=0; +			continue; +		default: +			break; +		} +		break; +	} + +	msg->nfields=sm->nfields; +	msg->n=isuid ? current_maildir_info.msgs[n].uid:n+1; +	msg->next=sm->list; +	sm->list=msg; +	++sm->nmsgs; +} | 
