/* Central switching station for email on its
 * way to be delivered. From here we call out
 * to the sorting module, if applicable, to
 * give additional information on what to do
 * with a message.
 *
 * (c) 2004-2006 Aaron Stone <aaron@serendpity.cx>
 *
 * $Id: $
 */

#include "dbmail.h"

/* Figure out where to deliver the message, then deliver it.
 * */
dsn_class_t sort_and_deliver(struct DbmailMessage *message,
		const char *destination, u64_t useridnr,
		const char *mailbox, mailbox_source_t source)
{
	int cancelkeep = 0;
	int reject = 0;
	dsn_class_t ret;
	field_t val;
	
	/* This is the only condition when called from pipe.c, actually. */
	if (! mailbox) {
		mailbox = "INBOX";
		source = BOX_DEFAULT;
	}

	/* Subaddress. */
	config_get_value("SUBADDRESS", "DELIVERY", val);
	if (strcasecmp(val, "yes") == 0) {
		int res;
		size_t sublen, subpos;
		char *subaddress;
		// FIXME: Where can I get access to the address?
		res = find_bounded((char *)destination, '+', '@', &subaddress, &sublen, &subpos);
		if (res == 0 && sublen > 0) {
			// FIXME: I forget who frees the mailbox.
			mailbox = subaddress;
			source = BOX_ADDRESSPART;
			trace(TRACE_INFO, "%s, %s: Setting BOX_ADDRESSPART mailbox to [%s]",
					__FILE__, __func__, mailbox);
		}
	}

	/* Give Sieve access to the envelope recipient. */
	dbmail_message_set_envelope_recipient(message, destination);

	/* Sieve. */
	config_get_value("SIEVE", "DELIVERY", val);
	if (strcasecmp(val, "yes") == 0
	&& db_check_sievescript_active(useridnr) == 0) {
		trace(TRACE_INFO, "%s, %s: Calling for a Sieve sort",
				__FILE__, __func__);
		sort_result_t *sort_result;
		sort_result = sort_process(useridnr, message);
		if (sort_result) {
			cancelkeep = sort_get_cancelkeep(sort_result);
			reject = sort_get_reject(sort_result);
			sort_free_result(sort_result);
		}
	}

	/* Sieve actions:
	 * (m = must implement, s = should implement, e = extension)
	 * m Keep - implicit default action.
	 * m Discard - requires us to skip the default action.
	 * m Redirect - add to the forwarding list.
	 * s Fileinto - change the destination mailbox.
	 * s Reject - nope, sorry. we killed bounce().
	 * e Vacation - share with the auto reply code.
	 */

	if (cancelkeep) {
		// The implicit keep has been cancelled.
		// This may necessarily imply that the message
		// is being discarded -- dropped flat on the floor.
		ret = DSN_CLASS_OK;
		trace(TRACE_INFO, "%s, %s: Keep was cancelled. Message may be discarded.",
				__FILE__, __func__);
	} else {
		ret = sort_deliver_to_mailbox(message, useridnr, mailbox, source, NULL);
		trace(TRACE_INFO, "%s, %s: Keep was not cancelled. Message will be delivered by default.",
				__FILE__, __func__);
	}

	/* Reject probably implies cancelkeep,
	 * but we'll not assume that and instead
	 * just test this as a separate block. */
	if (reject) {
		trace(TRACE_INFO, "%s, %s: Message will be rejected.",
				__FILE__, __func__);
		ret = DSN_CLASS_FAIL;
	}

	return ret;
}

dsn_class_t sort_deliver_to_mailbox(struct DbmailMessage *message,
		u64_t useridnr, const char *mailbox, mailbox_source_t source,
		int *msgflags)
{
	u64_t mboxidnr, newmsgidnr;
	size_t msgsize = (u64_t)dbmail_message_get_size(message, FALSE);

	trace(TRACE_INFO,"%s,%s: useridnr [%llu] mailbox [%s]",
			__FILE__, __func__, useridnr, mailbox);

	if (db_find_create_mailbox(mailbox, source, useridnr, &mboxidnr) != 0) {
		trace(TRACE_ERROR, "%s,%s: mailbox [%s] not found",
				__FILE__, __func__,
				mailbox);
		return DSN_CLASS_FAIL;
	}

	switch (db_copymsg(message->id, mboxidnr, useridnr, &newmsgidnr)) {
	case -2:
		trace(TRACE_DEBUG, "%s, %s: error copying message to user [%llu],"
				"maxmail exceeded", 
				__FILE__, __func__, 
				useridnr);
		return DSN_CLASS_QUOTA;
	case -1:
		trace(TRACE_ERROR, "%s, %s: error copying message to user [%llu]", 
				__FILE__, __func__, useridnr);
		return DSN_CLASS_TEMP;
	default:
		trace(TRACE_MESSAGE, "%s, %s: message id=%llu, size=%d is inserted", 
				__FILE__, __func__, newmsgidnr, msgsize);
		if (msgflags) {
			trace(TRACE_MESSAGE, "%s, %s: message id=%llu, setting imap flags", 
				__FILE__, __func__, newmsgidnr);
			db_set_msgflag(newmsgidnr, mboxidnr, msgflags, IMAPFA_ADD);
		}
		message->id = newmsgidnr;
		return DSN_CLASS_OK;
	}
}



syntax highlighted by Code2HTML, v. 0.9.1