Mercurial > hg > mlmmj
view src/listcontrol.c @ 484:2855bbfb74fe
Allow bounces with no From:
author | mmj |
---|---|
date | Thu, 28 Apr 2005 20:04:11 +1000 |
parents | e47f5d7b8ddd |
children | 89a256e8f1d3 |
line wrap: on
line source
/* Copyright (C) 2003 Mads Martin Joergensen <mmj at mmj.dk> * * $Id$ * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <ctype.h> #include "mlmmj.h" #include "listcontrol.h" #include "find_email_adr.h" #include "strgen.h" #include "send_help.h" #include "send_list.h" #include "log_error.h" #include "statctrl.h" #include "mygetline.h" #include "chomp.h" #include "memory.h" #include "log_oper.h" #include "ctrlvalues.h" enum ctrl_e { CTRL_SUBSCRIBE_DIGEST, CTRL_SUBSCRIBE_NOMAIL, CTRL_SUBSCRIBE, CTRL_CONFSUB_DIGEST, CTRL_CONFSUB_NOMAIL, CTRL_CONFSUB, CTRL_UNSUBSCRIBE_DIGEST, CTRL_UNSUBSCRIBE_NOMAIL, CTRL_UNSUBSCRIBE, CTRL_CONFUNSUB_DIGEST, CTRL_CONFUNSUB_NOMAIL, CTRL_CONFUNSUB, CTRL_BOUNCES, CTRL_MODERATE, CTRL_HELP, CTRL_GET, CTRL_LIST, CTRL_END /* end marker, must be last */ }; struct ctrl_command { char *command; unsigned int accepts_parameter; }; /* Must match the enum. CAREFUL when using commands that are substrings * of other commands. In that case the longest one have to be listed * first to match correctly. */ static struct ctrl_command ctrl_commands[] = { { "subscribe-digest", 0 }, { "subscribe-nomail", 0 }, { "subscribe", 0 }, { "confsub-digest", 1 }, { "confsub-nomail", 1 }, { "confsub", 1 }, { "unsubscribe-digest", 0 }, { "unsubscribe-nomail", 0 }, { "unsubscribe", 0 }, { "confunsub-digest", 1 }, { "confunsub-nomail", 1 }, { "confunsub", 1 }, { "bounces", 1 }, { "moderate", 1 }, { "help", 0 }, { "get", 1 }, { "list", 0 } }; int listcontrol(struct email_container *fromemails, const char *listdir, const char *controladdr, const char *mlmmjsub, const char *mlmmjunsub, const char *mlmmjsend, const char *mlmmjbounce, const char *mailname) { char *atsign, *recipdelimsign, *bouncenr, *tmpstr; char *controlstr, *param, *conffilename, *moderatefilename; char *c, *archivefilename, *sendfilename; const char *subswitch; size_t len; struct stat stbuf; int closedlist, nosubconfirm, tmpfd, noget, i; size_t cmdlen; unsigned int ctrl; struct strlist *owners; /* A closed list doesn't allow subscribtion and unsubscription */ closedlist = statctrl(listdir, "closedlist"); nosubconfirm = statctrl(listdir, "nosubconfirm"); if(nosubconfirm) subswitch = "-c"; else subswitch = "-C"; recipdelimsign = index(controladdr, RECIPDELIM); MY_ASSERT(recipdelimsign); atsign = index(controladdr, '@'); MY_ASSERT(atsign); len = atsign - recipdelimsign; controlstr = mymalloc(len); MY_ASSERT(controlstr); snprintf(controlstr, len, "%s", recipdelimsign + 1); #if 0 log_error(LOG_ARGS, "controlstr = [%s]\n", controlstr); log_error(LOG_ARGS, "fromemails->emaillist[0] = [%s]\n", fromemails->emaillist[0]); #endif for (ctrl=0; ctrl<CTRL_END; ctrl++) { cmdlen = strlen(ctrl_commands[ctrl].command); if (strncmp(controlstr, ctrl_commands[ctrl].command, cmdlen) == 0) { if (ctrl_commands[ctrl].accepts_parameter && (controlstr[cmdlen] == '-')) { param = mystrdup(controlstr + cmdlen + 1); MY_ASSERT(param); if (strchr(param, '/')) { errno = 0; log_error(LOG_ARGS, "Slash (/) in" " list control request," " discarding mail"); exit(EXIT_SUCCESS); } } else if (!ctrl_commands[ctrl].accepts_parameter && (controlstr[cmdlen] == '\0')) { param = NULL; } else { /* malformed request, ignore and clean up */ unlink(mailname); exit(EXIT_SUCCESS); } myfree(controlstr); break; } } /* Only allow mails with bad From: header to be bounce mails */ if(fromemails->emailcount != 1 && ctrl != CTRL_BOUNCES) { log_error(LOG_ARGS, "Discarding mail with invalid From: " "which was not a bounce"); unlink(mailname); exit(EXIT_SUCCESS); } switch (ctrl) { /* listname+subscribe-digest@domain.tld */ case CTRL_SUBSCRIBE_DIGEST: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); if (!strchr(fromemails->emaillist[0], '@')) /* Not a valid From: address, silently ignore */ exit(EXIT_SUCCESS); log_oper(listdir, OPLOGFNAME, "mlmmj-sub: request for digest" " subscription from %s", fromemails->emaillist[0]); execlp(mlmmjsub, mlmmjsub, "-L", listdir, "-a", fromemails->emaillist[0], "-d", subswitch, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); break; /* listname+subscribe-nomail@domain.tld */ case CTRL_SUBSCRIBE_NOMAIL: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); if (!strchr(fromemails->emaillist[0], '@')) /* Not a valid From: address, silently ignore */ exit(EXIT_SUCCESS); log_oper(listdir, OPLOGFNAME, "mlmmj-sub: request for nomail" " subscription from %s", fromemails->emaillist[0]); execlp(mlmmjsub, mlmmjsub, "-L", listdir, "-a", fromemails->emaillist[0], "-n", subswitch, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); break; /* listname+subscribe@domain.tld */ case CTRL_SUBSCRIBE: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); if (!strchr(fromemails->emaillist[0], '@')) /* Not a valid From: address, silently ignore */ exit(EXIT_SUCCESS); log_oper(listdir, OPLOGFNAME, "mlmmj-sub: request for regular" " subscription from %s", fromemails->emaillist[0]); execlp(mlmmjsub, mlmmjsub, "-L", listdir, "-a", fromemails->emaillist[0], subswitch, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); break; /* listname+subconf-digest-COOKIE@domain.tld */ case CTRL_CONFSUB_DIGEST: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); conffilename = concatstr(3, listdir, "/subconf/", param); myfree(param); if((tmpfd = open(conffilename, O_RDONLY)) < 0) { /* invalid COOKIE, silently ignore */ exit(EXIT_SUCCESS); } tmpstr = mygetline(tmpfd); chomp(tmpstr); close(tmpfd); unlink(conffilename); log_oper(listdir, OPLOGFNAME, "mlmmj-sub: %s confirmed" " subscription to digest", tmpstr); execlp(mlmmjsub, mlmmjsub, "-L", listdir, "-a", tmpstr, "-d", "-c", (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); break; /* listname+subconf-nomail-COOKIE@domain.tld */ case CTRL_CONFSUB_NOMAIL: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); conffilename = concatstr(3, listdir, "/subconf/", param); myfree(param); if((tmpfd = open(conffilename, O_RDONLY)) < 0) { /* invalid COOKIE, silently ignore */ exit(EXIT_SUCCESS); } tmpstr = mygetline(tmpfd); chomp(tmpstr); close(tmpfd); unlink(conffilename); log_oper(listdir, OPLOGFNAME, "mlmmj-sub: %s confirmed" " subscription to nomail", tmpstr); execlp(mlmmjsub, mlmmjsub, "-L", listdir, "-a", tmpstr, "-n", "-c", (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); break; /* listname+subconf-COOKIE@domain.tld */ case CTRL_CONFSUB: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); conffilename = concatstr(3, listdir, "/subconf/", param); myfree(param); if((tmpfd = open(conffilename, O_RDONLY)) < 0) { /* invalid COOKIE, silently ignore */ exit(EXIT_SUCCESS); } tmpstr = mygetline(tmpfd); chomp(tmpstr); close(tmpfd); unlink(conffilename); log_oper(listdir, OPLOGFNAME, "mlmmj-sub: %s confirmed" " subscription to regular list", tmpstr); execlp(mlmmjsub, mlmmjsub, "-L", listdir, "-a", tmpstr, "-c", (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); break; /* listname+unsubscribe-digest@domain.tld */ case CTRL_UNSUBSCRIBE_DIGEST: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); if (!strchr(fromemails->emaillist[0], '@')) /* Not a valid From: address, silently ignore */ exit(EXIT_SUCCESS); log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s requests" " unsubscribe from digest", fromemails->emaillist[0]); execlp(mlmmjunsub, mlmmjunsub, "-L", listdir, "-a", fromemails->emaillist[0], "-d", subswitch, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjunsub); exit(EXIT_FAILURE); break; /* listname+unsubscribe-nomail@domain.tld */ case CTRL_UNSUBSCRIBE_NOMAIL: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); if (!strchr(fromemails->emaillist[0], '@')) /* Not a valid From: address, silently ignore */ exit(EXIT_SUCCESS); log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s requests" " unsubscribe from nomail", fromemails->emaillist[0]); execlp(mlmmjunsub, mlmmjunsub, "-L", listdir, "-a", fromemails->emaillist[0], "-n", subswitch, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjunsub); exit(EXIT_FAILURE); break; /* listname+unsubscribe@domain.tld */ case CTRL_UNSUBSCRIBE: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); if (!strchr(fromemails->emaillist[0], '@')) /* Not a valid From: address, silently ignore */ exit(EXIT_SUCCESS); log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s requests" " unsubscribe from regular list", fromemails->emaillist[0]); execlp(mlmmjunsub, mlmmjunsub, "-L", listdir, "-a", fromemails->emaillist[0], subswitch, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjunsub); exit(EXIT_FAILURE); break; /* listname+unsubconf-digest-COOKIE@domain.tld */ case CTRL_CONFUNSUB_DIGEST: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); conffilename = concatstr(3, listdir, "/unsubconf/", param); myfree(param); if((tmpfd = open(conffilename, O_RDONLY)) < 0) { /* invalid COOKIE, silently ignore */ exit(EXIT_SUCCESS); } tmpstr = mygetline(tmpfd); close(tmpfd); chomp(tmpstr); unlink(conffilename); log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s confirmed" " unsubscribe from digest", tmpstr); execlp(mlmmjunsub, mlmmjunsub, "-L", listdir, "-a", tmpstr, "-d", "-c", (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjunsub); exit(EXIT_FAILURE); break; /* listname+unsubconf-nomail-COOKIE@domain.tld */ case CTRL_CONFUNSUB_NOMAIL: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); conffilename = concatstr(3, listdir, "/unsubconf/", param); myfree(param); if((tmpfd = open(conffilename, O_RDONLY)) < 0) { /* invalid COOKIE, silently ignore */ exit(EXIT_SUCCESS); } tmpstr = mygetline(tmpfd); close(tmpfd); chomp(tmpstr); unlink(conffilename); log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s confirmed" " unsubscribe from nomail", tmpstr); execlp(mlmmjunsub, mlmmjunsub, "-L", listdir, "-a", tmpstr, "-n", "-c", (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjunsub); exit(EXIT_FAILURE); break; /* listname+unsubconf-COOKIE@domain.tld */ case CTRL_CONFUNSUB: unlink(mailname); if (closedlist) exit(EXIT_SUCCESS); conffilename = concatstr(3, listdir, "/unsubconf/", param); myfree(param); if((tmpfd = open(conffilename, O_RDONLY)) < 0) { /* invalid COOKIE, silently ignore */ exit(EXIT_SUCCESS); } tmpstr = mygetline(tmpfd); close(tmpfd); chomp(tmpstr); unlink(conffilename); log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s confirmed" " unsubscribe from regular list", tmpstr); execlp(mlmmjunsub, mlmmjunsub, "-L", listdir, "-a", tmpstr, "-c", (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjunsub); exit(EXIT_FAILURE); break; /* listname+bounces-INDEX-user=example.tld@domain.tld */ case CTRL_BOUNCES: bouncenr = param; c = strchr(param, '-'); if (!c) { /* Exec with dsn parsing, since the addr is missing */ execlp(mlmmjbounce, mlmmjbounce, "-L", listdir, "-m", mailname, "-n", bouncenr, "-d", (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjbounce); exit(EXIT_FAILURE); } *c++ = '\0'; execlp(mlmmjbounce, mlmmjbounce, "-L", listdir, "-a", c, "-m", mailname, "-n", bouncenr, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjbounce); exit(EXIT_FAILURE); break; /* listname+moderate-COOKIE@domain.tld */ case CTRL_MODERATE: /* TODO Add accept/reject parameter to moderate */ unlink(mailname); moderatefilename = concatstr(3, listdir, "/moderation/", param); sendfilename = concatstr(2, moderatefilename, ".sending"); myfree(param); if(stat(moderatefilename, &stbuf) < 0) { myfree(moderatefilename); exit(EXIT_SUCCESS); /* just exit, no mail to moderate */ } /* Rename it to avoid mail being sent twice */ if(rename(moderatefilename, sendfilename) < 0) { log_error(LOG_ARGS, "Could not rename to .sending"); exit(EXIT_FAILURE); } log_oper(listdir, OPLOGFNAME, "%s moderated %s", fromemails->emaillist[0], moderatefilename); myfree(moderatefilename); execlp(mlmmjsend, mlmmjsend, "-L", listdir, "-m", sendfilename, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend); exit(EXIT_FAILURE); break; /* listname+help@domain.tld */ case CTRL_HELP: unlink(mailname); if(strchr(fromemails->emaillist[0], '@')) { log_oper(listdir, OPLOGFNAME, "%s requested help", fromemails->emaillist[0]); send_help(listdir, fromemails->emaillist[0], mlmmjsend); } break; /* listname+get-INDEX@domain.tld */ case CTRL_GET: unlink(mailname); noget = statctrl(listdir, "noget"); if(noget) exit(EXIT_SUCCESS); /* sanity check--is it all digits? */ for(c = param; *c != '\0'; c++) { if(!isdigit((int)*c)) exit(EXIT_SUCCESS); } archivefilename = concatstr(3, listdir, "/archive/", param); if(stat(archivefilename, &stbuf) < 0) exit(EXIT_SUCCESS); log_oper(listdir, OPLOGFNAME, "%s got archive/%s", fromemails->emaillist[0], archivefilename); execlp(mlmmjsend, mlmmjsend, "-T", fromemails->emaillist[0], "-L", listdir, "-l", "6", "-m", archivefilename, "-a", "-D", (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend); exit(EXIT_FAILURE); break; /* listname+list@domain.tld */ case CTRL_LIST: unlink(mailname); owners = ctrlvalues(listdir, "owner"); for(i = 0; i < owners->count; i++) { if(strcmp(fromemails->emaillist[0], owners->strs[i]) == 0) { log_oper(listdir, OPLOGFNAME, "%s requested sub list", fromemails->emaillist[0]); send_list(listdir, owners->strs[i], mlmmjsend); } } break; } unlink(mailname); return 0; }