/*
* Copyright (c) 2006-2008 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mailprocd.h"
#include "pathnames.h"
static const char *mlOps[] = {
"admin", "bounces", "confirm", "join", "leave", "owner", "request",
"subscribe", "unsubscribe",
NULL
};
static int listsEnable, listsPostMaxDefault, listsSpamCheck;
static float listsSpamThreshold;
static char *listsProcessor, *listsMailmanPath, *listsMailmanGroup;
void
ML_Init(CFG_File *cf)
{
CFG_GetInt(cf, "lists.enable", &listsEnable, 1);
CFG_GetInt(cf, "lists.spam-check", &listsSpamCheck, 1);
CFG_GetFlt(cf, "lists.spam-threshold", &listsSpamThreshold, 8.0);
CFG_GetInt(cf, "lists.post-max-default", &listsPostMaxDefault, 65536);
CFG_GetStr(cf, "lists.processor", &listsProcessor, "mailman");
CFG_GetStr(cf, "lists.mailman.path", &listsMailmanPath, _PATH_MAILMAN);
CFG_GetStr(cf, "lists.mailman.group", &listsMailmanGroup, "_mailman");
}
void
ML_Destroy(void)
{
}
/*
* Search for a mailing list address matching the given recipient.
* Returns the owner of the list of owner is not NULL. The listName
* buffer must be at least ML_NAME_MAX bytes.
*/
ML_MailListReq *
ML_GetMailListReq(MPD_Recipient *rcpt)
{
char list_addr[ML_NAME_MAX+16];
ML_MailListReq *ml;
const char **op;
char *owner_name, *maxsize, *vp;
struct passwd *owner;
char *rcpt_op;
if (MPD_ParseRecipientParts(rcpt) == -1)
return (NULL);
/* Look for an exact match on the list address. */
if ((vp = TBL_Lookup(Tlists, rcpt->addr)) != NULL) {
owner_name = strsep(&vp, ":");
maxsize = strsep(&vp, ":");
if ((owner = getpwnam(owner_name)) == NULL) {
syslog(LOG_ERR, "Mailing list <%s>: Bad owner: %s "
"(table %s)",
rcpt->addr, owner_name, Tlists->path);
return (NULL);
}
ml = Malloc(sizeof(ML_MailListReq));
ml->owner = owner;
ml->maxSize = (maxsize != NULL) ? atoi(maxsize) : 0;
if (ml->maxSize == 0) { ml->maxSize = listsPostMaxDefault; }
Strlcpy(ml->op, "post", sizeof(ml->op));
Strlcpy(ml->name, rcpt->user_part, sizeof(ml->name));
Debug("Mailing list match (name=%s, op=POST, owner=%s(%d), "
"maxpost=%d)\n",
ml->name, ml->owner->pw_name, ml->owner->pw_uid,
(int)ml->maxSize);
return (ml);
}
/* Look for a mailing list operation address. */
if ((rcpt_op = strrchr(rcpt->user_part, '-')) == NULL ||
(rcpt_op[1] == '\0')) {
return (NULL);
}
for (op = mlOps; *op != NULL; op++) {
if (strcmp(&rcpt_op[1], *op) == 0)
break;
}
if (*op == NULL) {
return (NULL);
}
*rcpt_op = '\0';
Strlcpy(list_addr, rcpt->user_part, sizeof(list_addr));
Strlcat(list_addr, "@", sizeof(list_addr));
Strlcat(list_addr, rcpt->domain_part, sizeof(list_addr));
if ((vp = TBL_Lookup(Tlists, list_addr)) == NULL) {
return (NULL);
}
owner_name = strsep(&vp, ":");
maxsize = strsep(&vp, ":");
if ((owner = getpwnam(owner_name)) == NULL) {
syslog(LOG_ERR, "Bad %s owner: %s", rcpt->addr, owner_name);
return (NULL);
}
ml = Malloc(sizeof(ML_MailListReq));
ml->owner = owner;
ml->maxSize = (maxsize != NULL) ? atoi(maxsize) : 0;
if (ml->maxSize == 0) { ml->maxSize = listsPostMaxDefault; }
Strlcpy(ml->op, *op, sizeof(ml->op));
Strlcpy(ml->name, rcpt->user_part, sizeof(ml->name));
Debug("Mailing list match (name=%s, op=%s, owner=%s(%d), maxpost=%d)\n",
ml->name, ml->op, ml->owner->pw_name, ml->owner->pw_uid,
(int)ml->maxSize);
return (ml);
}
/* Mailing list message delivery routine. */
int
ML_MessageProcess(MPD_Message *msg, MPD_Recipient *rcpt)
{
char cmd[128+ML_NAME_MAX+16];
struct group *group;
uid_t uid;
gid_t gid;
if (LOCAL_GetDefaultRecipientUID(rcpt->addr, &uid, &gid) == -1) {
return (-1);
}
if ((group = getgrnam(listsMailmanGroup)) == NULL) {
MPD_SetError("No such group: %s", listsMailmanGroup);
return (1);
}
gid = group->gr_gid;
Debug("List <%s> operation %s (owner=%s)", rcpt->ml->name, rcpt->ml->op,
rcpt->ml->owner->pw_name);
#if 0
if (listsSpamCheck) {
/*
* Run the message through SpamAssassin if this is
* a "post" operation.
*/
if (strcmp(rcpt->ml->op, "post") == 0) {
if (SPAM_Check(msg, rcpt) == -1) {
syslog(LOG_ERR,
"List <%s>: spamcheck failed (%s)",
rcpt->addr, MPD_GetError());
msg->status.score = 0.0;
msg->status.req_score = 6.66;
msg->status.spam_status = 0;
}
Debug("ML spam score = %f", msg->status.score);
} else {
msg->status.score = 0.0;
msg->status.req_score = 6.66;
msg->status.spam_status = 0;
}
}
if (msg->status.score > listsSpamThreshold) {
Debug("Rejecting spam from <%s> to list %s",
msg->mail_from, rcpt->ml->name);
return (0);
}
#endif /* HAVE_SA */
Debug("List <%s> operation %s (owner=%s)", rcpt->ml->name,
rcpt->ml->op, rcpt->ml->owner->pw_name);
Strlcpy(cmd, listsMailmanPath, sizeof(cmd));
Strlcat(cmd, " ", sizeof(cmd));
Strlcat(cmd, rcpt->ml->op, sizeof(cmd));
Strlcat(cmd, " ", sizeof(cmd));
Strlcat(cmd, rcpt->ml->name, sizeof(cmd));
return LOCAL_FeedToPipe(msg, rcpt, cmd, rcpt->ml->op,
rcpt->ml->owner->pw_uid, gid);
}