/*
Copyright (C) 2004-2006 Aaron Stone aaron@serendipity.cx
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
*
* 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.
*
* Upstream: pipe.c, insert_messages calls sort_and_deliver.
* Way upstream: main.c and lmtp.c call insert_messages.
*
*/
#include "dbmail.h"
#define THIS_MODULE "sort"
/* 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;
char *subaddress = NULL;
/* Catch the brute force delivery right away.
* We skip the Sieve scripts, and down the call
* chain we don't check permissions on the mailbox. */
if (source == BOX_BRUTEFORCE) {
TRACE(TRACE_MESSAGE, "Beginning brute force delivery for user [%llu] to mailbox [%s].",
useridnr, mailbox);
return sort_deliver_to_mailbox(message, useridnr, mailbox, source, NULL);
}
TRACE(TRACE_INFO, "Destination [%s] useridnr [%llu], mailbox [%s], source [%d]",
destination, useridnr, mailbox, source);
/* 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;
res = find_bounded((char *)destination, '+', '@', &subaddress, &sublen, &subpos);
if (res == 0 && sublen > 0) {
/* We'll free this towards the end of the function. */
mailbox = subaddress;
source = BOX_ADDRESSPART;
TRACE(TRACE_INFO, "Setting BOX_ADDRESSPART mailbox to [%s]", 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, "Calling for a Sieve sort");
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);
}
}
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, "Keep was cancelled. Message may be discarded.");
} else {
ret = sort_deliver_to_mailbox(message, useridnr, mailbox, source, NULL);
TRACE(TRACE_INFO, "Keep was not cancelled. Message will be delivered by default.");
}
/* Might have been allocated by the subaddress calculation. NULL otherwise. */
g_free(subaddress);
/* Reject probably implies cancelkeep,
* but we'll not assume that and instead
* just test this as a separate block. */
if (reject) {
TRACE(TRACE_INFO, "Message will be rejected.");
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;
field_t val;
size_t msgsize = (u64_t)dbmail_message_get_size(message, FALSE);
TRACE(TRACE_INFO,"useridnr [%llu] mailbox [%s]", useridnr, mailbox);
if (db_find_create_mailbox(mailbox, source, useridnr, &mboxidnr) != 0) {
TRACE(TRACE_ERROR, "mailbox [%s] not found", mailbox);
return DSN_CLASS_FAIL;
}
if (source == BOX_BRUTEFORCE) {
TRACE(TRACE_INFO, "Brute force delivery; skipping ACL checks on mailbox.");
} else {
// Check ACL's on the mailbox. It must be read-write,
// it must not be no_select, and it may require an ACL for
// the user whose Sieve script this is, since it's possible that
// we've looked up a #Public or a #Users mailbox.
TRACE(TRACE_DEBUG, "Checking if we have the right to post incoming messages");
mailbox_t mbox;
memset(&mbox, 0, sizeof(mbox));
mbox.uid = mboxidnr;
switch (acl_has_right(&mbox, useridnr, ACL_RIGHT_POST)) {
case -1:
TRACE(TRACE_MESSAGE, "error retrieving right for [%llu] to deliver mail to [%s]",
useridnr, mailbox);
return DSN_CLASS_TEMP;
case 0:
// No right.
TRACE(TRACE_MESSAGE, "user [%llu] does not have right to deliver mail to [%s]",
useridnr, mailbox);
// Switch to INBOX.
if (strcmp(mailbox, "INBOX") == 0) {
// Except if we've already been down this path.
TRACE(TRACE_MESSAGE, "already tried to deliver to INBOX");
return DSN_CLASS_FAIL;
}
return sort_deliver_to_mailbox(message, useridnr, "INBOX", BOX_DEFAULT, msgflags);
case 1:
// Has right.
TRACE(TRACE_INFO, "user [%llu] has right to deliver mail to [%s]",
useridnr, mailbox);
break;
default:
TRACE(TRACE_ERROR, "invalid return value from acl_has_right");
return DSN_CLASS_FAIL;
}
}
// if the mailbox already holds this message we're done
GETCONFIGVALUE("suppress_duplicates", "DELIVERY", val);
if (strcasecmp(val,"yes")==0) {
const char *messageid = dbmail_message_get_header(message, "message-id");
if ( messageid && ((db_mailbox_has_message_id(mboxidnr, messageid)) > 0) ) {
TRACE(TRACE_MESSAGE, "suppress_duplicate: [%s]", messageid);
return DSN_CLASS_OK;
}
}
// Ok, we have the ACL right, time to deliver the message.
switch (db_copymsg(message->id, mboxidnr, useridnr, &newmsgidnr)) {
case -2:
TRACE(TRACE_DEBUG, "error copying message to user [%llu],"
"maxmail exceeded", useridnr);
return DSN_CLASS_QUOTA;
case -1:
TRACE(TRACE_ERROR, "error copying message to user [%llu]",
useridnr);
return DSN_CLASS_TEMP;
default:
TRACE(TRACE_MESSAGE, "message id=%llu, size=%zd is inserted",
newmsgidnr, msgsize);
if (msgflags) {
TRACE(TRACE_MESSAGE, "message id=%llu, setting imap flags",
newmsgidnr);
db_set_msgflag(newmsgidnr, mboxidnr, msgflags, IMAPFA_ADD);
}
message->id = newmsgidnr;
return DSN_CLASS_OK;
}
}
syntax highlighted by Code2HTML, v. 0.9.1