/* * 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); }