From: Baptiste Daroussin Date: Thu, 6 Jul 2023 11:07:37 +0000 (+0200) Subject: mlmmj-sub: move all subcription code into a reusable function X-Git-Tag: RELEASE_1_4_0rc1~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f9da8d132f4098de90aacf8c651f862f67b2d0c9;p=thirdparty%2Fmlmmj.git mlmmj-sub: move all subcription code into a reusable function This codes allows to remove a execl code from listcontrol, allow to deduplicate plenty of code and remove the need for mlmmmj-process to know about mlmmj-sub command path --- diff --git a/include/listcontrol.h b/include/listcontrol.h index 0256ec01..98d53ce7 100644 --- a/include/listcontrol.h +++ b/include/listcontrol.h @@ -64,7 +64,7 @@ struct ctrl_command { struct ctrl_command *get_ctrl_command(const char *controlstr, char **param); int listcontrol(strlist *fromemails, struct ml *ml, - const char *controlstr, const char *mlmmjsub, - const char *mlmmjsend, const char *mailname); + const char *controlstr, const char *mlmmjsend, + const char *mailname); #endif /* LISTCONTROL_H */ diff --git a/include/subscriberfuncs.h b/include/subscriberfuncs.h index 8c39bceb..69e8b1f0 100644 --- a/include/subscriberfuncs.h +++ b/include/subscriberfuncs.h @@ -25,6 +25,18 @@ #include +struct subscription { + bool send_welcome_email; + const char *modstr; + enum subreason reasonsub; + enum subtype typesub; + bool gensubscribed; + bool subconfirm; + bool force; + bool quiet; + const char *mlmmjsend; +}; + bool find_subscriber(int fd, const char *address); int is_subbed_in(int fd, const char *subddirname, const char *address); enum subtype is_subbed(int listfd, const char *address, bool both); @@ -37,3 +49,4 @@ void generate_subconfirm(struct ml *ml, const char *subaddr, enum subtype typesu void send_confirmation_mail(struct ml *ml, const char *subaddr, enum subtype typesub, enum subreason reasonsub, bool sub); bool do_unsubscribe(struct ml *ml, const char *addr, enum subtype typesub, enum subreason reasonsub, bool inform_not_subscribed, bool confirm_unsubscription, bool quiet, bool send_goodbye_mail); void mod_get_addr_and_type(struct ml *ml, const char *modstr, char **addrptr, enum subtype *subtypeptr); +bool do_subscribe(struct ml *ml, struct subscription *sub, const char *addr); diff --git a/src/listcontrol.c b/src/listcontrol.c index 4cfd9150..acd6eb6a 100644 --- a/src/listcontrol.c +++ b/src/listcontrol.c @@ -85,8 +85,8 @@ static struct ctrl_command ctrl_commands[] = { struct listcontrol { const char *fromemail; - const char *mlmmjsub; - bool nosubconfirm; + const char *mlmmjsend; + bool subconfirm; }; struct sub { @@ -94,7 +94,7 @@ struct sub { const char *ctrlstr; const char *type; const char *typedeny; - const char *arg; + enum subtype typesub; }; static bool @@ -116,7 +116,7 @@ lc_sub(struct listcontrol *lc, struct ml *ml, struct sub *sub) if (!is_valid_email(lc->fromemail, sub->typereq)) exit(EXIT_FAILURE); - if (statctrl(ml->ctrlfd, sub->ctrlstr)) { + if (sub->ctrlstr != NULL && statctrl(ml->ctrlfd, sub->ctrlstr)) { char *queuefilename; text *txt; errno = 0; @@ -134,9 +134,13 @@ lc_sub(struct listcontrol *lc, struct ml *ml, struct sub *sub) } log_oper(ml->fd, OPLOGFNAME, "mlmmj-sub: request for %s" " subscription from %s", sub->type, lc->fromemail); - exec_or_die(lc->mlmmjsub, "-L", ml->dir, "-a", - lc->fromemail, sub->arg, "-r", "-c", - (lc->nosubconfirm ? NULL : "-C"), NULL); + struct subscription subc = { 0 }; + subc.subconfirm = lc->subconfirm; + subc.send_welcome_email = true; + subc.reasonsub = SUB_REQUEST; + subc.typesub = sub->typesub; + subc.gensubscribed = true; + exit(do_subscribe(ml, &subc, lc->fromemail) ? EXIT_SUCCESS : EXIT_FAILURE); } struct ctrl_command * @@ -186,9 +190,8 @@ get_ctrl_command(const char *controlstr, char **param) return (NULL); } -int listcontrol(strlist *fromemails, struct ml *ml, - const char *controlstr, const char *mlmmjsub, - const char *mlmmjsend, const char *mailname) +int listcontrol(strlist *fromemails, struct ml *ml, const char *controlstr, + const char *mlmmjsend, const char *mailname) { char *bouncenr, *tmpstr; char *param = NULL, *moderatefilename, *gatekeepfilename; @@ -198,14 +201,14 @@ int listcontrol(strlist *fromemails, struct ml *ml, text *txt; char *queuefilename; enum subtype ts = SUB_NONE; - const char *subtype = NULL; const char *subtypename = NULL; bounce_t bret; struct ctrl_command *cc; struct listcontrol lc = { 0 }; struct sub sub = { 0 }; + struct subscription subc = { 0 }; - lc.nosubconfirm = statctrl(ml->ctrlfd, "nosubconfirm"); + lc.subconfirm = !statctrl(ml->ctrlfd, "nosubconfirm"); #if 0 log_error(LOG_ARGS, "controlstr = [%s]\n", controlstr); @@ -225,7 +228,7 @@ int listcontrol(strlist *fromemails, struct ml *ml, if (tll_length(*fromemails) >= 1) lc.fromemail = tll_front(*fromemails); - lc.mlmmjsub = mlmmjsub; + lc.mlmmjsend = mlmmjsend; /* We only need the control mail when bouncing, to save bounced msg */ if (cc->type != CTRL_BOUNCES) @@ -251,17 +254,17 @@ int listcontrol(strlist *fromemails, struct ml *ml, sub.typereq = "A subscribe-digest"; sub.ctrlstr = "nodigestsub"; sub.type = "digest"; + sub.typesub = SUB_DIGEST; sub.typedeny = "sub-deny-digest"; - sub.arg = "-d"; __attribute__ ((fallthrough)); /* listname+subscribe-nomail@domain.tld */ case CTRL_SUBSCRIBE_NOMAIL: if (sub.typereq == NULL) { sub.typereq = "A subscribe-nomail"; sub.ctrlstr = "nomailsub"; + sub.typesub = SUB_NOMAIL; sub.type = "nomail"; sub.typedeny = "sub-deny-nomail"; - sub.arg = "-n"; } __attribute__ ((fallthrough)); /* listname+subscribe-both@domain.tld */ @@ -269,59 +272,47 @@ int listcontrol(strlist *fromemails, struct ml *ml, if (sub.typereq == NULL) { sub.typereq = "A subscribe-both"; sub.ctrlstr = "nodigestsub"; + sub.typesub = SUB_BOTH; sub.type = "both"; sub.typedeny = "sub-deny-digest"; - sub.arg = "-b"; } - lc_sub(&lc, ml, &sub); - break; - + __attribute__ ((fallthrough)); /* listname+subscribe@domain.tld */ case CTRL_SUBSCRIBE: - if (!is_valid_email(lc.fromemail, "A subscribe")) - return -1; - log_oper(ml->fd, OPLOGFNAME, "mlmmj-sub: request for regular" - " subscription from %s", lc.fromemail); - exec_or_die(mlmmjsub, "-L", ml->dir, "-a", - lc.fromemail, "-r", "-c", - (lc.nosubconfirm ? NULL : "-C"), NULL); + if (sub.typereq == NULL) { + sub.typereq = "A subcribe"; + sub.ctrlstr = NULL; + sub.type = "regular"; + sub.typesub = SUB_NORMAL; + } + lc_sub(&lc, ml, &sub); break; /* listname+subconf-digest-COOKIE@domain.tld */ case CTRL_CONFSUB_DIGEST: - subtype = "-d"; + subc.typesub = SUB_DIGEST; subtypename = "digest"; __attribute__ ((fallthrough)); /* listname+subconf-nomail-COOKIE@domain.tld */ case CTRL_CONFSUB_NOMAIL: - if (subtype == NULL) { - subtype = "-n"; + if (subtypename == NULL) { + subc.typesub = SUB_NOMAIL; subtypename = "nomail"; } __attribute__ ((fallthrough)); /* listname+subconf-both-COOKIE@domain.tld */ case CTRL_CONFSUB_BOTH: - if (subtype == NULL) { - subtype = "-b"; + if (subtypename == NULL) { + subc.typesub = SUB_BOTH; subtypename = "both"; } - tmpstr = get_subcookie_content(ml->fd, false, param); - if (tmpstr == NULL) { - /* invalid COOKIE */ - errno = 0; - log_error(LOG_ARGS, "A subconf request was" - " sent with a mismatching cookie." - " Ignoring mail"); - return -1; - } - log_oper(ml->fd, OPLOGFNAME, "mlmmj-sub: %s confirmed" - " subscription to %s", tmpstr, subtypename); - exec_or_die(mlmmjsub, "-L", ml->dir, "-a", tmpstr, subtype, - "-R", "-c", NULL); - break; - + __attribute__ ((fallthrough)); /* listname+subconf-COOKIE@domain.tld */ case CTRL_CONFSUB: + if (subtypename == NULL) { + subc.typesub = SUB_NORMAL; + subtypename = "regular list"; + } tmpstr = get_subcookie_content(ml->fd, false, param); if (tmpstr == NULL) { /* invalid COOKIE */ @@ -331,11 +322,13 @@ int listcontrol(strlist *fromemails, struct ml *ml, " Ignoring mail"); return -1; } + subc.send_welcome_email = true; + subc.reasonsub = SUB_CONFIRM; + subc.gensubscribed = true; + subc.mlmmjsend = lc.mlmmjsend; log_oper(ml->fd, OPLOGFNAME, "mlmmj-sub: %s confirmed" - " subscription to regular list", tmpstr); - exec_or_die(mlmmjsub, "-L", ml->dir, "-a", tmpstr, - "-R", "-c", NULL); - break; + " subscription to %s", tmpstr, subtypename); + exit(do_subscribe(ml, &subc, tmpstr) ? EXIT_SUCCESS : EXIT_FAILURE); /* DEPRECATED: listname+unsubscribe-digest@domain.tld */ case CTRL_UNSUBSCRIBE_DIGEST: @@ -348,7 +341,7 @@ int listcontrol(strlist *fromemails, struct ml *ml, log_oper(ml->fd, OPLOGFNAME, "mlmmj-unsub: %s requests" " unsubscribe", lc.fromemail); do_unsubscribe(ml, lc.fromemail, SUB_ALL, SUB_REQUEST, - false, !lc.nosubconfirm, false, lc.nosubconfirm); + false, lc.subconfirm, false, lc.subconfirm); exit(EXIT_SUCCESS); break; @@ -495,7 +488,12 @@ permit: } log_oper(ml->fd, OPLOGFNAME, "%s permitted %s", lc.fromemail, param); - exec_or_die(mlmmjsub, "-L", ml->dir, "-m", param, "-c", NULL); + subc.modstr = param; + subc.send_welcome_email = true; + subc.gensubscribed = true; + subc.reasonsub = SUB_PERMIT; + subc.mlmmjsend = lc.mlmmjsend; + exit(do_subscribe(ml, &subc, tmpstr) ? EXIT_SUCCESS : EXIT_FAILURE); break; /* listname+obstruct-COOKIE@domain.tld */ diff --git a/src/mlmmj-process.c b/src/mlmmj-process.c index 6e579523..1b35410d 100644 --- a/src/mlmmj-process.c +++ b/src/mlmmj-process.c @@ -382,7 +382,7 @@ int main(int argc, char **argv) bool subonlypost, modonlypost, modnonsubposts, foundaddr = false; char *mailfile = NULL, *donemailname = NULL; char *randomstr = NULL, *mqueuename, *omitfilename; - char *mlmmjsend, *mlmmjsub; + char *mlmmjsend; char *bindir, *subjectprefix, *discardname; text *txt; char *queuefilename, *recipextra = NULL, *owner = NULL; @@ -421,7 +421,6 @@ int main(int argc, char **argv) bindir = mydirname(argv[0]); xasprintf(&mlmmjsend, "%s/mlmmj-send", bindir); - xasprintf(&mlmmjsub, "%s/mlmmj-sub", bindir); free(bindir); while ((opt = getopt(argc, argv, "hVPm:L:")) != -1) { @@ -658,7 +657,7 @@ int main(int argc, char **argv) else testfrom = &fromemails; listcontrol(testfrom, &ml, recipextra, - mlmmjsub, mlmmjsend, donemailname); + mlmmjsend, donemailname); return EXIT_SUCCESS; } diff --git a/src/mlmmj-sub.c b/src/mlmmj-sub.c index 178466be..a72c123e 100644 --- a/src/mlmmj-sub.c +++ b/src/mlmmj-sub.c @@ -52,151 +52,6 @@ extern char *subtypes[]; -static void moderate_sub(struct ml *ml, const char *subaddr, - const char *mlmmjsend, enum subtype typesub, - enum subreason reasonsub) -{ - int fd, status; - text *txt; - memory_lines_state *mls; - char *a = NULL, *queuefilename, *from; - char *modfilename, *mods, *to, *replyto, *moderators = NULL; - char *cookie, *obstruct; - strlist *submods; - const char *type; - pid_t childpid, pid; - xstring *str = NULL; - int modfd = -1; - - type = subtypes[typesub]; - - for (;;) { - cookie = random_str(); - xasprintf(&modfilename, "moderation/subscribe%s", cookie); - fd = openat(ml->fd, modfilename, O_RDWR|O_CREAT|O_EXCL, - S_IRUSR|S_IWUSR); - if (fd < 0) { - if (errno == EEXIST) { - free(cookie); - free(modfilename); - continue; - } - log_error(LOG_ARGS, "could not create %s" - "ignoring request for: %s", subaddr); - exit(EXIT_FAILURE); - } - break; - } - - if (dprintf(fd, "%s\n%s\n", subaddr, type) < 0) { - log_error(LOG_ARGS, "could not write to %s" - "ignoring request for: %s", subaddr); - exit(EXIT_FAILURE); - } - close(fd); - - submods = ctrlvalues(ml->ctrlfd, "submod"); - if (submods == NULL) - return; - /* check to see if there's adresses in the submod control file */ - tll_foreach(*submods, it) - a = strchr(it->item, '@'); - - /* no addresses in submod control file, use owner */ - if(a == NULL) { - /* free the submods struct from above */ - tll_free_and_free(*submods, free); - free(submods); - submods = ctrlvalues(ml->ctrlfd, "owner"); - modfd = openat(ml->ctrlfd, "owner", O_RDONLY); - } - if (modfd == -1) - modfd = openat(ml->ctrlfd, "submod", O_RDONLY); - - gen_addr(from, ml, "owner"); - xasprintf(&to, "%s-moderators@%s", ml->name, ml->fqdn); - gen_addr_cookie(replyto, ml, "permit-", cookie); - gen_addr_cookie(obstruct, ml, "obstruct-", cookie); - free(cookie); - tll_foreach(*submods, sm) { - if (str == NULL) - str = xstring_new(); - fprintf(str->fp, "%s\n", sm->item); - } - moderators = xstring_get(str); - mls = init_memory_lines(moderators); - free(moderators); - - txt = open_text(ml->fd, - "gatekeep", "sub", - subreason_strs[reasonsub], subtype_strs[typesub], - "submod-moderator"); - MY_ASSERT(txt); - register_default_unformatted(txt, ml); - register_unformatted(txt, "subaddr", subaddr); - register_unformatted(txt, "moderateaddr", replyto); /* DEPRECATED */ - register_unformatted(txt, "permitaddr", replyto); - register_unformatted(txt, "obstructaddr", obstruct); - register_unformatted(txt, "moderators", "%gatekeepers%"); /* DEPRECATED */ - register_formatted(txt, "gatekeepers", - rewind_memory_lines, get_memory_line, mls); - queuefilename = prepstdreply(txt, ml, "$listowner$", to, replyto); - MY_ASSERT(queuefilename); - close_text(txt); - - /* we might need to exec more than one mlmmj-send */ - - if (statctrl(ml->ctrlfd, "nosubmodmails")) - childpid = -1; - else { - childpid = fork(); - if(childpid < 0) - log_error(LOG_ARGS, "Could not fork; requester not notified"); - } - - if(childpid != 0) { - if(childpid > 0) { - do /* Parent waits for the child */ - pid = waitpid(childpid, &status, 0); - while(pid == -1 && errno == EINTR); - } - finish_memory_lines(mls); - xasprintf(&mods, "%d", modfd); - execl(mlmmjsend, mlmmjsend, - "-a", - "-l", "4", - "-L", ml->dir, - "-s", mods, - "-F", from, - "-R", replyto, - "-m", queuefilename, (char *)NULL); - log_error(LOG_ARGS, "execl() of '%s' failed", mlmmjsend); - exit(EXIT_FAILURE); - } - - free(to); - free(replyto); - - /* send mail to requester that the list is submod'ed */ - - txt = open_text(ml->fd, - "wait", "sub", - subreason_strs[reasonsub], subtype_strs[typesub], - "submod-requester"); - MY_ASSERT(txt); - register_default_unformatted(txt, ml); - register_unformatted(txt, "subaddr", subaddr); - register_unformatted(txt, "moderators", "%gatekeepers"); /* DEPRECATED */ - register_formatted(txt, "gatekeepers", - rewind_memory_lines, get_memory_line, mls); - queuefilename = prepstdreply(txt, ml, "$listowner$", subaddr, NULL); - MY_ASSERT(queuefilename); - close_text(txt); - - finish_memory_lines(mls); - send_help(ml, queuefilename, subaddr); -} - static void print_help(const char *prg) { printf("Usage: %s -L /path/to/list {-a john@doe.org | -m str}\n" @@ -220,54 +75,16 @@ static void print_help(const char *prg) exit(EXIT_SUCCESS); } -static void subscribe_type(int listfd, char *address, enum subtype typesub) { - int dirfd;; - char chstr[2]; - const char *subdir; - int groupwritable = 0, subfilefd; - struct stat st; - - dirfd = open_subscriber_directory(listfd, typesub, &subdir); - if (dirfd == -1) - err(EXIT_FAILURE, "cannot open(%s)", subdir); - if (fstat(dirfd, &st) == 0) { - if(st.st_mode & S_IWGRP) { - groupwritable = S_IRGRP|S_IWGRP; - umask(S_IWOTH); - setgid(st.st_gid); - } - } - - chstr[0] = address[0]; - chstr[1] = '\0'; - - subfilefd = openat(dirfd, chstr, O_RDWR|O_CREAT|O_APPEND, - S_IRUSR|S_IWUSR|groupwritable); - if(subfilefd == -1 && !lock(subfilefd, true)) { - log_error(LOG_ARGS, "Could not open '%s/%s'", subdir, chstr); - exit(EXIT_FAILURE); - } - - dprintf(subfilefd, "%s\n", address); - close(dirfd); - close(subfilefd); -} int main(int argc, char **argv) { char *mlmmjsend, *bindir; - char *address = NULL, *modstr = NULL; - bool send_welcome_mail = false; + char *address = NULL; + struct subscription sub = { 0 }; int opt; - bool subconfirm = false, notifysub; bool changeuid = true, digest = false, nomail = false, both = false; - bool nogensubscribed = false; - bool force = false, quiet = false; - enum subtype subbed; struct stat st; uid_t uid; - enum subtype typesub = SUB_NORMAL; - enum subreason reasonsub = SUB_ADMIN; struct ml ml; ml_init(&ml); @@ -278,6 +95,10 @@ int main(int argc, char **argv) bindir = mydirname(argv[0]); xasprintf(&mlmmjsend, "%s/mlmmj-send", bindir); free(bindir); + sub.reasonsub = SUB_ADMIN; + sub.typesub = SUB_NORMAL; + sub.gensubscribed = true; + sub.mlmmjsend = mlmmjsend; while ((opt = getopt(argc, argv, "hbcCdfm:nsVUL:a:qrR")) != -1) { switch(opt) { @@ -291,16 +112,16 @@ int main(int argc, char **argv) both = true; break; case 'c': - send_welcome_mail = true; + sub.send_welcome_email = true; break; case 'C': - subconfirm = true; + sub.subconfirm = true; break; case 'd': digest = true; break; case 'f': - force = true; + sub.force = true; break; case 'h': print_help(argv[0]); @@ -309,22 +130,22 @@ int main(int argc, char **argv) ml.dir = optarg; break; case 'm': - modstr = optarg; + sub.modstr = optarg; break; case 'n': nomail = true; break; case 'q': - quiet = true; + sub.quiet = true; break; case 'r': - reasonsub = SUB_REQUEST; + sub.reasonsub = SUB_REQUEST; break; case 'R': - reasonsub = SUB_CONFIRM; + sub.reasonsub = SUB_CONFIRM; break; case 's': - nogensubscribed = true; + sub.gensubscribed = false; break; case 'U': changeuid = false; @@ -342,7 +163,7 @@ int main(int argc, char **argv) if (!ml_open(&ml, false)) exit(EXIT_FAILURE); - if(address == NULL && modstr == NULL) { + if(address == NULL && sub.modstr == NULL) { errx(EXIT_FAILURE, "You have to specify -a or -m\n" "%s -h for help", argv[0]); } @@ -353,30 +174,17 @@ int main(int argc, char **argv) } if(digest) - typesub = SUB_DIGEST; + sub.typesub = SUB_DIGEST; if(nomail) - typesub = SUB_NOMAIL; + sub.typesub = SUB_NOMAIL; if(both) - typesub = SUB_BOTH; + sub.typesub = SUB_BOTH; - if(reasonsub == SUB_CONFIRM && subconfirm) { + if(sub.reasonsub == SUB_CONFIRM && sub.subconfirm) { errx(EXIT_FAILURE, "Cannot specify both -C and -R\n" "%s -h for help", argv[0]); } - if(modstr) { - mod_get_addr_and_type(&ml, modstr, &address, &typesub); - reasonsub = SUB_PERMIT; - } - - /* Make the address lowercase */ - address = lowercase(address); - - if(strncasecmp(ml.addr, address, strlen(ml.addr)) == 0) { - free(address); - errx(EXIT_FAILURE, "Cannot subscribe the list address to the list"); - } - if(changeuid) { uid = getuid(); if(!uid && fstat(ml.fd, &st) == 0 && uid != st.st_uid) { @@ -390,60 +198,7 @@ int main(int argc, char **argv) } } - subbed = is_subbed(ml.fd, address, 1); - - if(subbed == typesub) { - if(!nogensubscribed) - generate_subscription(&ml, address, typesub, true); - return EXIT_SUCCESS; - } else if(subbed != SUB_NONE) { - reasonsub = SUB_SWITCH; - /* If we want to subscribe to both, we can just subscribe the - * missing version, so don't unsub. */ - if (!(typesub == SUB_BOTH && - subbed != SUB_NOMAIL)) { - enum subtype ts = SUB_ALL; - if (subbed == SUB_BOTH) { - if (typesub == SUB_NORMAL) ts = SUB_DIGEST; - if (typesub == SUB_DIGEST) ts = SUB_NORMAL; - } - if (!unsubscribe(ml.fd, address, ts)) - log_error(LOG_ARGS, "not unsubscribed from " - "current version"); - } - } - - if(subbed == SUB_NONE && subconfirm) - generate_subconfirm(&ml, address, typesub, reasonsub, true); - - if(modstr == NULL && subbed == SUB_NONE && !force && - statctrl(ml.ctrlfd, "submod")) { - moderate_sub(&ml, address, mlmmjsend, typesub, reasonsub); - } - - if (typesub == SUB_BOTH) { - if (subbed != SUB_NORMAL) { - subscribe_type(ml.fd, address, SUB_NORMAL); - } - if (subbed != SUB_DIGEST) { - subscribe_type(ml.fd, address, SUB_DIGEST); - } - } else if (!(subbed == SUB_BOTH && typesub != SUB_NOMAIL)) { - subscribe_type(ml.fd, address, typesub); - } - - if(send_welcome_mail) - send_confirmation_mail(&ml, address, typesub, reasonsub, true); - - notifysub = !quiet && reasonsub != SUB_SWITCH && - statctrl(ml.ctrlfd, "notifysub"); - - /* Notify list owner about subscription */ - if (notifysub) - notify_sub(&ml, address, typesub, reasonsub, true); - - free(address); - free(mlmmjsend); - - return EXIT_SUCCESS; + if (do_subscribe(&ml, &sub, address)) + exit(EXIT_SUCCESS); + exit(EXIT_FAILURE); } diff --git a/src/subscriberfuncs.c b/src/subscriberfuncs.c index a30171d8..9fab8b66 100644 --- a/src/subscriberfuncs.c +++ b/src/subscriberfuncs.c @@ -22,12 +22,14 @@ */ #include +#include #include #include #include #include #include +#include #include "mlmmj.h" #include "subscriberfuncs.h" @@ -40,6 +42,8 @@ #include "strgen.h" #include "send_help.h" #include "statctrl.h" +#include "xstring.h" +#include "ctrlvalues.h" char *subtype_strs[] = { "normal", @@ -462,3 +466,259 @@ mod_get_addr_and_type(struct ml *ml, const char *modstr, char **addrptr, enum su close(dfd); free(modfilename); } + +static void moderate_sub(struct ml *ml, const char *subaddr, + struct subscription *sub) +{ + int fd, status; + text *txt; + memory_lines_state *mls; + char *a = NULL, *queuefilename, *from; + char *modfilename, *mods, *to, *replyto, *moderators = NULL; + char *cookie, *obstruct; + strlist *submods; + const char *type; + pid_t childpid, pid; + xstring *str = NULL; + int modfd = -1; + + type = subtypes[sub->typesub]; + + for (;;) { + cookie = random_str(); + xasprintf(&modfilename, "moderation/subscribe%s", cookie); + fd = openat(ml->fd, modfilename, O_RDWR|O_CREAT|O_EXCL, + S_IRUSR|S_IWUSR); + if (fd < 0) { + if (errno == EEXIST) { + free(cookie); + free(modfilename); + continue; + } + log_error(LOG_ARGS, "could not create %s" + "ignoring request for: %s", subaddr); + exit(EXIT_FAILURE); + } + break; + } + + if (dprintf(fd, "%s\n%s\n", subaddr, type) < 0) { + log_error(LOG_ARGS, "could not write to %s" + "ignoring request for: %s", subaddr); + exit(EXIT_FAILURE); + } + close(fd); + + submods = ctrlvalues(ml->ctrlfd, "submod"); + if (submods == NULL) + return; + /* check to see if there's adresses in the submod control file */ + tll_foreach(*submods, it) + a = strchr(it->item, '@'); + + /* no addresses in submod control file, use owner */ + if(a == NULL) { + /* free the submods struct from above */ + tll_free_and_free(*submods, free); + free(submods); + submods = ctrlvalues(ml->ctrlfd, "owner"); + modfd = openat(ml->ctrlfd, "owner", O_RDONLY); + } + if (modfd == -1) + modfd = openat(ml->ctrlfd, "submod", O_RDONLY); + + gen_addr(from, ml, "owner"); + xasprintf(&to, "%s-moderators@%s", ml->name, ml->fqdn); + gen_addr_cookie(replyto, ml, "permit-", cookie); + gen_addr_cookie(obstruct, ml, "obstruct-", cookie); + free(cookie); + tll_foreach(*submods, sm) { + if (str == NULL) + str = xstring_new(); + fprintf(str->fp, "%s\n", sm->item); + } + moderators = xstring_get(str); + mls = init_memory_lines(moderators); + free(moderators); + + txt = open_text(ml->fd, + "gatekeep", "sub", + subreason_strs[sub->reasonsub], subtype_strs[sub->typesub], + "submod-moderator"); + MY_ASSERT(txt); + register_default_unformatted(txt, ml); + register_unformatted(txt, "subaddr", subaddr); + register_unformatted(txt, "moderateaddr", replyto); /* DEPRECATED */ + register_unformatted(txt, "permitaddr", replyto); + register_unformatted(txt, "obstructaddr", obstruct); + register_unformatted(txt, "moderators", "%gatekeepers%"); /* DEPRECATED */ + register_formatted(txt, "gatekeepers", + rewind_memory_lines, get_memory_line, mls); + queuefilename = prepstdreply(txt, ml, "$listowner$", to, replyto); + MY_ASSERT(queuefilename); + close_text(txt); + + /* we might need to exec more than one mlmmj-send */ + + if (statctrl(ml->ctrlfd, "nosubmodmails")) + childpid = -1; + else { + childpid = fork(); + if(childpid < 0) + log_error(LOG_ARGS, "Could not fork; requester not notified"); + } + + if(childpid != 0) { + if(childpid > 0) { + do /* Parent waits for the child */ + pid = waitpid(childpid, &status, 0); + while(pid == -1 && errno == EINTR); + } + finish_memory_lines(mls); + xasprintf(&mods, "%d", modfd); + execl(sub->mlmmjsend, sub->mlmmjsend, + "-a", + "-l", "4", + "-L", ml->dir, + "-s", mods, + "-F", from, + "-R", replyto, + "-m", queuefilename, (char *)NULL); + log_error(LOG_ARGS, "execl() of '%s' failed", sub->mlmmjsend); + exit(EXIT_FAILURE); + } + + free(to); + free(replyto); + + /* send mail to requester that the list is submod'ed */ + + txt = open_text(ml->fd, + "wait", "sub", + subreason_strs[sub->reasonsub], subtype_strs[sub->typesub], + "submod-requester"); + MY_ASSERT(txt); + register_default_unformatted(txt, ml); + register_unformatted(txt, "subaddr", subaddr); + register_unformatted(txt, "moderators", "%gatekeepers"); /* DEPRECATED */ + register_formatted(txt, "gatekeepers", + rewind_memory_lines, get_memory_line, mls); + queuefilename = prepstdreply(txt, ml, "$listowner$", subaddr, NULL); + MY_ASSERT(queuefilename); + close_text(txt); + + finish_memory_lines(mls); + send_help(ml, queuefilename, subaddr); +} + +static void +subscribe_type(int listfd, char *address, enum subtype typesub) +{ + int dirfd;; + char chstr[2]; + const char *subdir; + int groupwritable = 0, subfilefd; + struct stat st; + + dirfd = open_subscriber_directory(listfd, typesub, &subdir); + if (dirfd == -1) + err(EXIT_FAILURE, "cannot open(%s)", subdir); + if (fstat(dirfd, &st) == 0) { + if(st.st_mode & S_IWGRP) { + groupwritable = S_IRGRP|S_IWGRP; + umask(S_IWOTH); + setgid(st.st_gid); + } + } + + chstr[0] = address[0]; + chstr[1] = '\0'; + + subfilefd = openat(dirfd, chstr, O_RDWR|O_CREAT|O_APPEND, + S_IRUSR|S_IWUSR|groupwritable); + if(subfilefd == -1 && !lock(subfilefd, true)) { + log_error(LOG_ARGS, "Could not open '%s/%s'", subdir, chstr); + exit(EXIT_FAILURE); + } + + dprintf(subfilefd, "%s\n", address); + close(dirfd); + close(subfilefd); +} + +bool +do_subscribe(struct ml *ml, struct subscription *sub, const char *addr) +{ + char *address = NULL; + enum subtype subbed; + + if (addr != NULL) + address = lowercase(addr); + + if (sub->modstr != NULL) { + mod_get_addr_and_type(ml, sub->modstr, &address, &sub->typesub); + sub->reasonsub = SUB_PERMIT; + } + + if(strncasecmp(ml->addr, address, strlen(ml->addr)) == 0) + errx(EXIT_FAILURE, "Cannot subscribe the list address to the list"); + + subbed = is_subbed(ml->fd, address, 1); + + if (subbed == sub->typesub) { + if (sub->gensubscribed) + generate_subscription(ml, address, sub->typesub, true); + free(address); + return (true); + } + if (subbed != SUB_NONE) { + sub->reasonsub = SUB_SWITCH; + /* If we want to subscribe to both, we can just subscribe the + * missing version, so don't unsub-> */ + if (!(sub->typesub == SUB_BOTH && + subbed != SUB_NOMAIL)) { + enum subtype ts = SUB_ALL; + if (subbed == SUB_BOTH) { + if (sub->typesub == SUB_NORMAL) ts = SUB_DIGEST; + if (sub->typesub == SUB_DIGEST) ts = SUB_NORMAL; + } + if (!unsubscribe(ml->fd, address, ts)) + log_error(LOG_ARGS, "not unsubscribed from " + "current version"); + } + } + + if (subbed == SUB_NONE && sub->subconfirm) + generate_subconfirm(ml, address, sub->typesub, sub->reasonsub, + true); + + if(sub->modstr == NULL && subbed == SUB_NONE && !sub->force && + statctrl(ml->ctrlfd, "submod")) { + moderate_sub(ml, address, sub); + } + + if (sub->typesub == SUB_BOTH) { + if (subbed != SUB_NORMAL) { + subscribe_type(ml->fd, address, SUB_NORMAL); + } + if (subbed != SUB_DIGEST) { + subscribe_type(ml->fd, address, SUB_DIGEST); + } + } else if (!(subbed == SUB_BOTH && sub->typesub != SUB_NOMAIL)) { + subscribe_type(ml->fd, address, sub->typesub); + } + + if (sub->send_welcome_email) + send_confirmation_mail(ml, address, sub->typesub, + sub->reasonsub, true); + + bool notifysub = !sub->quiet && sub->reasonsub != SUB_SWITCH && + statctrl(ml->ctrlfd, "notifysub"); + + /* Notify list owner about subscription */ + if (notifysub) + notify_sub(ml, address, sub->typesub, sub->reasonsub, true); + + free(address); + return (true); +} diff --git a/tests/mlmmj.c b/tests/mlmmj.c index d228adfc..8ea6db6f 100644 --- a/tests/mlmmj.c +++ b/tests/mlmmj.c @@ -2454,20 +2454,20 @@ ATF_TC_BODY(listcontrol, tc) strlist fromemails = tll_init(); ATF_REQUIRE(ml_open(&ml, false)); - ATF_REQUIRE_EQ(listcontrol(NULL, &ml, "plop", NULL, NULL, "meh"), -1); + ATF_REQUIRE_EQ(listcontrol(NULL, &ml, "plop", NULL, "meh"), -1); - ATF_REQUIRE_EQ(listcontrol(&fromemails, &ml, "help", NULL, NULL, "meh"), -1); + ATF_REQUIRE_EQ(listcontrol(&fromemails, &ml, "help", NULL, "meh"), -1); atf_utils_create_file("list/control/closedlist", ""); atf_utils_create_file("meh", "mail"); tll_push_back(fromemails, "plop@test"); - ATF_REQUIRE_EQ(listcontrol(&fromemails, &ml, "unsubscribe", NULL, NULL, "meh"), -1); + ATF_REQUIRE_EQ(listcontrol(&fromemails, &ml, "unsubscribe", NULL, "meh"), -1); if (atf_utils_file_exists("me")) atf_tc_fail("mail should have been removed"); unlink("list/control/closedlist"); atf_utils_create_file("list/control/closedlistsub", ""); - ATF_REQUIRE_EQ(listcontrol(&fromemails, &ml, "subscribe", NULL, NULL, "meh"), -1); + ATF_REQUIRE_EQ(listcontrol(&fromemails, &ml, "subscribe", NULL, "meh"), -1); } ATF_TC_BODY(send_help, tc)