From 425c5650055f6efaa6ee408709951e7c7bd32ec2 Mon Sep 17 00:00:00 2001 From: Ben Schmidt Date: Wed, 25 Jan 2012 22:34:57 +1100 Subject: [PATCH] Add ability to subscribe to 'both' and avoid a deadlock when switching. - 'both' means normal and digest versions simultaneously; information about this feature is not included in the default list texts, but a few power users find it helpful. - Only take out locks when actually performing a subscription, not before checking whether the address is already subscribed; we only really need it when we are actually changing the file, and there is a potential deadlock which could occur while waiting for unsubscription to complete as part of a switch if the lock is taken earlier. - Also moved code which sends unsubscription confirmations so that it only runs once, after unsubsciption has been completed, not every time the address is removed; this is only really important when 'both' is a realistic subscription option, but it could avoid other double-sends as well. --- ChangeLog | 1 + README.listtexts | 4 +- include/mlmmj.h | 3 +- include/subscriberfuncs.h | 2 +- src/listcontrol.c | 83 ++++++++++- src/mlmmj-bounce.c | 2 +- src/mlmmj-process.c | 2 +- src/mlmmj-sub.c | 307 +++++++++++++++++++++----------------- src/mlmmj-unsub.c | 63 ++++---- src/subscriberfuncs.c | 14 +- 10 files changed, 307 insertions(+), 174 deletions(-) diff --git a/ChangeLog b/ChangeLog index 87f37d4e..d45b1fcb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + o Add ability to subscribe to both (normal and digest). o Fix access logic so subonlypost doesn't override a send access rule. o Make +unsubscribe remove the requester from all versions of the list. o Make mlmmj-unsub default to removing the requester from all versions of the diff --git a/README.listtexts b/README.listtexts index 87006456..84f872bf 100644 --- a/README.listtexts +++ b/README.listtexts @@ -79,9 +79,9 @@ is given in brackets. Those with asterisks (*) are not yet used. sent to a person requesting subscription when they need to wait for gatekeeping for permission to join -- deny-sub-disabled-digest (sub-deny-digest) +- deny-sub-disabled-{digest|both} (sub-deny-digest) - deny-sub-disabled-nomail (sub-deny-nomail) -- deny-sub-subbed-{normal|digest|nomail} (sub-subscribed) +- deny-sub-subbed-{normal|digest|nomail|both} (sub-subscribed) - deny-sub-closed * - deny-sub-expired * - deny-sub-obstruct * diff --git a/include/mlmmj.h b/include/mlmmj.h index c98ccc3f..4db3339f 100644 --- a/include/mlmmj.h +++ b/include/mlmmj.h @@ -77,10 +77,11 @@ enum subtype { SUB_NOMAIL, SUB_FILE, /* For single files (moderator, owner etc.) */ SUB_ALL, /* For listing or unsubscribing all kinds of subscribers */ + SUB_BOTH, /* For normal+digest subscription */ SUB_NONE /* For when an address is not subscribed at all */ }; -char *subtype_strs[6]; /* count matches enum above; defined in subscriberfuncs.c */ +char *subtype_strs[7]; /* count matches enum above; defined in subscriberfuncs.c */ enum subreason { SUB_REQUEST, diff --git a/include/subscriberfuncs.h b/include/subscriberfuncs.h index 2d442733..57f99cca 100644 --- a/include/subscriberfuncs.h +++ b/include/subscriberfuncs.h @@ -26,6 +26,6 @@ off_t find_subscriber(int fd, const char *address); int is_subbed_in(const char *subddirname, const char *address); -enum subtype is_subbed(const char *listdir, const char *address); +enum subtype is_subbed(const char *listdir, const char *address, int both); #endif /* SUBSCRIBERFUNC_H */ diff --git a/src/listcontrol.c b/src/listcontrol.c index d2b7e7ad..58c22c55 100644 --- a/src/listcontrol.c +++ b/src/listcontrol.c @@ -51,9 +51,11 @@ enum ctrl_e { CTRL_SUBSCRIBE_DIGEST, CTRL_SUBSCRIBE_NOMAIL, + CTRL_SUBSCRIBE_BOTH, CTRL_SUBSCRIBE, CTRL_CONFSUB_DIGEST, CTRL_CONFSUB_NOMAIL, + CTRL_CONFSUB_BOTH, CTRL_CONFSUB, CTRL_UNSUBSCRIBE_DIGEST, CTRL_UNSUBSCRIBE_NOMAIL, @@ -85,9 +87,11 @@ struct ctrl_command { static struct ctrl_command ctrl_commands[] = { { "subscribe-digest", 0 }, { "subscribe-nomail", 0 }, + { "subscribe-both", 0 }, { "subscribe", 0 }, { "confsub-digest", 1 }, { "confsub-nomail", 1 }, + { "confsub-both", 1 }, { "confsub", 1 }, { "unsubscribe-digest", 0 }, { "unsubscribe-nomail", 0 }, @@ -296,6 +300,55 @@ int listcontrol(struct email_container *fromemails, const char *listdir, exit(EXIT_FAILURE); break; + /* listname+subscribe-both@domain.tld */ + case CTRL_SUBSCRIBE_BOTH: + if (closedlist || closedlistsub) { + errno = 0; + log_error(LOG_ARGS, "A subscribe-both request was" + " sent to a closed list. Ignoring mail"); + return -1; + } + if (!strchr(fromemails->emaillist[0], '@')) { + /* Not a valid From: address */ + errno = 0; + log_error(LOG_ARGS, "A subscribe-both request was" + " sent with an invalid From: header." + " Ignoring mail"); + return -1; + } + if (statctrl(listdir, "nodigestsub")) { + errno = 0; + log_error(LOG_ARGS, "A subscribe-both request was" + " denied"); + txt = open_text(listdir, "deny", "sub", "disabled", + "both", "sub-deny-digest"); + MY_ASSERT(txt); + register_unformatted(txt, "subaddr", + fromemails->emaillist[0]); + queuefilename = prepstdreply(txt, listdir, + "$listowner$", + fromemails->emaillist[0], NULL); + MY_ASSERT(queuefilename); + close_text(txt); + send_help(listdir, queuefilename, + fromemails->emaillist[0], mlmmjsend); + return -1; + } + log_oper(listdir, OPLOGFNAME, "mlmmj-sub: request for both" + " subscription from %s", + fromemails->emaillist[0]); + execlp(mlmmjsub, mlmmjsub, + "-L", listdir, + "-a", fromemails->emaillist[0], + "-b", + "-r", "-c", + (nosubconfirm ? (char *)NULL : "-C"), + (char *)NULL); + log_error(LOG_ARGS, "execlp() of '%s' failed", + mlmmjsub); + exit(EXIT_FAILURE); + break; + /* listname+subscribe@domain.tld */ case CTRL_SUBSCRIBE: if (closedlist || closedlistsub) { @@ -382,6 +435,34 @@ int listcontrol(struct email_container *fromemails, const char *listdir, exit(EXIT_FAILURE); break; + /* listname+subconf-both-COOKIE@domain.tld */ + case CTRL_CONFSUB_BOTH: + conffilename = concatstr(3, listdir, "/subconf/", param); + myfree(param); + if((tmpfd = open(conffilename, O_RDONLY)) < 0) { + /* invalid COOKIE */ + errno = 0; + log_error(LOG_ARGS, "A subconf-both request was" + " sent with a mismatching cookie." + " Ignoring mail"); + return -1; + } + tmpstr = mygetline(tmpfd); + chomp(tmpstr); + close(tmpfd); + unlink(conffilename); + log_oper(listdir, OPLOGFNAME, "mlmmj-sub: %s confirmed" + " subscription to both", tmpstr); + execlp(mlmmjsub, mlmmjsub, + "-L", listdir, + "-a", tmpstr, + "-b", + "-R", "-c", (char *)NULL); + log_error(LOG_ARGS, "execlp() of '%s' failed", + mlmmjsub); + exit(EXIT_FAILURE); + break; + /* listname+subconf-COOKIE@domain.tld */ case CTRL_CONFSUB: conffilename = concatstr(3, listdir, "/subconf/", param); @@ -763,7 +844,7 @@ permit: } subonlyget = statctrl(listdir, "subonlyget"); if(subonlyget) { - if(is_subbed(listdir, fromemails->emaillist[0]) == + if(is_subbed(listdir, fromemails->emaillist[0], 0) == SUB_NONE) { errno = 0; log_error(LOG_ARGS, "A get request was sent" diff --git a/src/mlmmj-bounce.c b/src/mlmmj-bounce.c index 93d1bfee..9f9630a6 100644 --- a/src/mlmmj-bounce.c +++ b/src/mlmmj-bounce.c @@ -342,7 +342,7 @@ int main(int argc, char **argv) *a = '@'; /* make sure it's a subscribed address */ - if(is_subbed(listdir, address) == SUB_NONE) { + if(is_subbed(listdir, address, 0) == SUB_NONE) { log_error(LOG_ARGS, "%s is bouncing but not subscribed?", address); if(mailname) diff --git a/src/mlmmj-process.c b/src/mlmmj-process.c index 12f5bdf2..dd1ccb6e 100644 --- a/src/mlmmj-process.c +++ b/src/mlmmj-process.c @@ -984,7 +984,7 @@ int main(int argc, char **argv) myfree(donemailname); exit(EXIT_SUCCESS); } - if(is_subbed(listdir, posteraddr) == SUB_NONE) { + if(is_subbed(listdir, posteraddr, 0) == SUB_NONE) { if(statctrl(listdir, "modnonsubposts")) { moderated = 1; modreason = MODNONSUBPOSTS; diff --git a/src/mlmmj-sub.c b/src/mlmmj-sub.c index 70965f9c..3335817c 100644 --- a/src/mlmmj-sub.c +++ b/src/mlmmj-sub.c @@ -75,6 +75,9 @@ static void moderate_sub(const char *listdir, const char *listaddr, case SUB_NOMAIL: str = concatstr(4, subaddr, "\n", "SUB_NOMAIL", "\n"); break; + case SUB_BOTH: + str = concatstr(4, subaddr, "\n", "SUB_BOTH", "\n"); + break; } for (;;) { @@ -270,6 +273,11 @@ void getaddrandtype(const char *listdir, const char *modstr, goto freedone; } + if(strncmp(readtype, "SUB_BOTH", 8) == 0) { + *subtypeptr = SUB_BOTH; + goto freedone; + } + log_error(LOG_ARGS, "Type %s not valid in %s", readtype, modfilename); @@ -305,6 +313,10 @@ void confirm_sub(const char *listdir, const char *listaddr, case SUB_NOMAIL: listtext = mystrdup("sub-ok-nomail"); break; + case SUB_BOTH: + /* No legacy list text as feature didn't exist. */ + listtext = mystrdup("sub-ok"); + break; } txt = open_text(listdir, "finish", "sub", @@ -356,6 +368,10 @@ void notify_sub(const char *listdir, const char *listaddr, case SUB_NOMAIL: listtext = mystrdup("notifysub-nomail"); break; + case SUB_BOTH: + /* No legacy list text as feature didn't exist. */ + listtext = mystrdup("notifysub"); + break; } txt = open_text(listdir, "notify", "sub", @@ -441,6 +457,11 @@ void generate_subconfirm(const char *listdir, const char *listaddr, listtext = mystrdup("sub-confirm-nomail"); tmpstr = mystrdup("confsub-nomail-"); break; + case SUB_BOTH: + /* No legacy list text as feature didn't exist. */ + listtext = mystrdup("sub-confirm"); + tmpstr = mystrdup("confsub-both-"); + break; } confirmaddr = concatstr(6, listname, listdelim, tmpstr, randomstr, "@", @@ -537,19 +558,100 @@ void generate_subscribed(const char *listdir, const char *subaddr, exit(EXIT_FAILURE); } +static void subscribe_type(char *listdir, char *listaddr, char *listdelim, + char *address, char *mlmmjsend, + enum subtype typesub, enum subreason reasonsub) { + char *subfilename = NULL; + char chstr[2], *subdir; + char *subddirname = NULL, *sublockname; + int groupwritable = 0, sublock, sublockfd, lock, subfilefd; + struct stat st; + size_t len; + + switch(typesub) { + default: + case SUB_NORMAL: + subdir = "/subscribers.d/"; + break; + case SUB_DIGEST: + subdir = "/digesters.d/"; + break; + case SUB_NOMAIL: + subdir = "/nomailsubs.d/"; + break; + } + + subddirname = concatstr(2, listdir, subdir); + if (stat(subddirname, &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'; + + subfilename = concatstr(3, listdir, subdir, chstr); + + sublockname = concatstr(5, listdir, subdir, ".", chstr, ".lock"); + sublockfd = open(sublockname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if(sublockfd < 0) { + log_error(LOG_ARGS, "Error opening lock file %s", + sublockname); + myfree(sublockname); + exit(EXIT_FAILURE); + } + + sublock = myexcllock(sublockfd); + if(sublock < 0) { + log_error(LOG_ARGS, "Error locking '%s' file", + sublockname); + myfree(sublockname); + close(sublockfd); + exit(EXIT_FAILURE); + } + + subfilefd = open(subfilename, O_RDWR|O_CREAT, + S_IRUSR|S_IWUSR|groupwritable); + if(subfilefd == -1) { + log_error(LOG_ARGS, "Could not open '%s'", subfilename); + myfree(sublockname); + exit(EXIT_FAILURE); + } + + lock = myexcllock(subfilefd); + if(lock) { + log_error(LOG_ARGS, "Error locking subscriber file"); + close(subfilefd); + close(sublockfd); + myfree(sublockname); + exit(EXIT_FAILURE); + } + + lseek(subfilefd, 0L, SEEK_END); + len = strlen(address); + address[len] = '\n'; + writen(subfilefd, address, len + 1); + address[len] = 0; + close(subfilefd); + close(sublockfd); + unlink(sublockname); + myfree(sublockname); +} + int main(int argc, char **argv) { - char *listaddr, *listdelim, *listdir = NULL, *address = NULL; - char *subfilename = NULL, *mlmmjsend, *mlmmjunsub, *bindir; - char chstr[2], *subdir; - char *subddirname = NULL, *sublockname, *lowcaseaddr; - char *modstr = NULL; - int subconfirm = 0, confirmsub = 0, opt, subfilefd, lock, notifysub; - int changeuid = 1, status, digest = 0, nomail = 0, i = 0, submod = 0; - int groupwritable = 0, sublock, sublockfd, nogensubscribed = 0; - int force = 0, quiet = 0; + char *listaddr, *listdelim, *listdir = NULL; + char *mlmmjsend, *mlmmjunsub, *bindir; + char *address = NULL, *lowcaseaddr, *modstr = NULL; + const char *flag = NULL; + int opt, subconfirm = 0, confirmsub = 0, notifysub; + int changeuid = 1, status, digest = 0, nomail = 0, both = 0; + int nogensubscribed = 0; + int force = 0, quiet = 0, i = 0; enum subtype subbed; - size_t len; struct stat st; pid_t pid, childpid = 0; uid_t uid; @@ -565,11 +667,14 @@ int main(int argc, char **argv) mlmmjunsub = concatstr(2, bindir, "/mlmmj-unsub"); myfree(bindir); - while ((opt = getopt(argc, argv, "hcCdfm:nsVUL:a:qrR")) != -1) { + while ((opt = getopt(argc, argv, "hbcCdfm:nsVUL:a:qrR")) != -1) { switch(opt) { case 'a': address = optarg; break; + case 'b': + both = 1; + break; case 'c': confirmsub = 1; break; @@ -627,19 +732,8 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } - if(modstr) { - getaddrandtype(listdir, modstr, &address, &typesub); - reasonsub = SUB_PERMIT; - } - - if(strchr(address, '@') == NULL) { - log_error(LOG_ARGS, "No '@' sign in '%s', not subscribing", - address); - exit(EXIT_SUCCESS); - } - - if(digest && nomail) { - fprintf(stderr, "Specify at most one of -d and -n\n"); + if(both + digest + nomail > 1) { + fprintf(stderr, "Specify at most one of -b, -d and -n\n"); fprintf(stderr, "%s -h for help\n", argv[0]); exit(EXIT_FAILURE); } @@ -648,6 +742,8 @@ int main(int argc, char **argv) typesub = SUB_DIGEST; if(nomail) typesub = SUB_NOMAIL; + if(both) + typesub = SUB_BOTH; if(reasonsub == SUB_CONFIRM && subconfirm) { fprintf(stderr, "Cannot specify both -C and -R\n"); @@ -655,6 +751,17 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + if(modstr) { + getaddrandtype(listdir, modstr, &address, &typesub); + reasonsub = SUB_PERMIT; + } + + if(strchr(address, '@') == NULL) { + log_error(LOG_ARGS, "No '@' sign in '%s', not subscribing", + address); + exit(EXIT_SUCCESS); + } + /* Make the address lowercase */ lowcaseaddr = mystrdup(address); i = 0; @@ -671,28 +778,6 @@ int main(int argc, char **argv) exit(EXIT_SUCCESS); /* XXX is this success? */ } - switch(typesub) { - default: - case SUB_NORMAL: - subdir = "/subscribers.d/"; - break; - case SUB_DIGEST: - subdir = "/digesters.d/"; - break; - case SUB_NOMAIL: - subdir = "/nomailsubs.d/"; - break; - } - - subddirname = concatstr(2, listdir, subdir); - if (stat(subddirname, &st) == 0) { - if(st.st_mode & S_IWGRP) { - groupwritable = S_IRGRP|S_IWGRP; - umask(S_IWOTH); - setgid(st.st_gid); - } - } - if(changeuid) { uid = getuid(); if(!uid && stat(listdir, &st) == 0) { @@ -706,74 +791,37 @@ int main(int argc, char **argv) } } - chstr[0] = address[0]; - chstr[1] = '\0'; - - subfilename = concatstr(3, listdir, subdir, chstr); - - sublockname = concatstr(5, listdir, subdir, ".", chstr, ".lock"); - sublockfd = open(sublockname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); - if(sublockfd < 0) { - log_error(LOG_ARGS, "Error opening lock file %s", - sublockname); - myfree(sublockname); - exit(EXIT_FAILURE); - } - - sublock = myexcllock(sublockfd); - if(sublock < 0) { - log_error(LOG_ARGS, "Error locking '%s' file", - sublockname); - myfree(sublockname); - close(sublockfd); - exit(EXIT_FAILURE); - } - - subfilefd = open(subfilename, O_RDWR|O_CREAT, - S_IRUSR|S_IWUSR|groupwritable); - if(subfilefd == -1) { - log_error(LOG_ARGS, "Could not open '%s'", subfilename); - myfree(sublockname); - exit(EXIT_FAILURE); - } + subbed = is_subbed(listdir, address, 1); - lock = myexcllock(subfilefd); - if(lock) { - log_error(LOG_ARGS, "Error locking subscriber file"); - close(subfilefd); - close(sublockfd); - myfree(sublockname); - exit(EXIT_FAILURE); - } - subbed = is_subbed(listdir, address); - listdelim = getlistdelim(listdir); - if(subbed == typesub) { - close(subfilefd); - myfree(subfilename); - close(sublockfd); - unlink(sublockname); - myfree(sublockname); - if(!nogensubscribed) generate_subscribed(listdir, address, mlmmjsend, typesub); - return EXIT_SUCCESS; } else if(subbed != SUB_NONE) { reasonsub = SUB_SWITCH; - childpid = fork(); - if(childpid < 0) + /* 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)) { + childpid = fork(); + if(childpid < 0) { log_error(LOG_ARGS, "Could not fork; " "not unsubscribed from current version"); - if (childpid == 0) { - execlp(mlmmjunsub, mlmmjunsub, - "-L", listdir, "-q", - "-a", address, - (char *)NULL); - log_error(LOG_ARGS, "execlp() of '%s' failed", - mlmmjunsub); - exit(EXIT_FAILURE); + } + if (childpid == 0) { + if (subbed == SUB_BOTH) { + if (typesub == SUB_NORMAL) flag = "-d"; + if (typesub == SUB_DIGEST) flag = "-N"; + } + execlp(mlmmjunsub, mlmmjunsub, + "-L", listdir, "-q", + "-a", address, flag, + (char *)NULL); + log_error(LOG_ARGS, "execlp() of '%s' failed", + mlmmjunsub); + exit(EXIT_FAILURE); + } } } @@ -783,38 +831,31 @@ int main(int argc, char **argv) while(pid == -1 && errno == EINTR); } - if(subbed == SUB_NONE && subconfirm) { - close(subfilefd); - close(sublockfd); - unlink(sublockname); - myfree(sublockname); - generate_subconfirm(listdir, listaddr, listdelim, - address, mlmmjsend, typesub, reasonsub); - } else { - if(modstr == NULL) - submod = subbed == SUB_NONE && !force && - statctrl(listdir, "submod"); - if(submod) { - close(subfilefd); - close(sublockfd); - unlink(sublockname); - myfree(sublockname); - moderate_sub(listdir, listaddr, listdelim, + listdelim = getlistdelim(listdir); + + if(subbed == SUB_NONE && subconfirm) + generate_subconfirm(listdir, listaddr, listdelim, + address, mlmmjsend, typesub, reasonsub); + + if(modstr == NULL && subbed == SUB_NONE && !force && + statctrl(listdir, "submod")) { + moderate_sub(listdir, listaddr, listdelim, address, mlmmjsend, typesub, reasonsub); - } - lseek(subfilefd, 0L, SEEK_END); - len = strlen(address); - address[len] = '\n'; - writen(subfilefd, address, len + 1); - address[len] = 0; - close(subfilefd); - close(sublockfd); - unlink(sublockname); } - close(sublockfd); - unlink(sublockname); - myfree(sublockname); + if (typesub == SUB_BOTH) { + if (subbed != SUB_NORMAL) { + subscribe_type(listdir, listaddr, listdelim, address, + mlmmjsend, SUB_NORMAL, reasonsub); + } + if (subbed != SUB_DIGEST) { + subscribe_type(listdir, listaddr, listdelim, address, + mlmmjsend, SUB_DIGEST, reasonsub); + } + } else if (!(subbed == SUB_BOTH && typesub != SUB_NOMAIL)) { + subscribe_type(listdir, listaddr, listdelim, address, + mlmmjsend, typesub, reasonsub); + } if(confirmsub) { childpid = fork(); diff --git a/src/mlmmj-unsub.c b/src/mlmmj-unsub.c index a32f5c59..0fa42a11 100644 --- a/src/mlmmj-unsub.c +++ b/src/mlmmj-unsub.c @@ -353,19 +353,18 @@ static void generate_notsubscribed(const char *listdir, const char *subaddr, } static void unsubscribe_type(char *listdir, char *listaddr, char *listdelim, - char *address, char *mlmmjsend, int confirmunsub, + char *address, char *mlmmjsend, enum subtype typesub, enum subreason reasonsub) { char *subdir, *subddirname, *sublockname; char *subreadname = NULL, *subwritename; int subread, subwrite, rlock, wlock; int sublock, sublockfd; int groupwritable = 0; - int unsubres, status; + int unsubres; struct stat st; DIR *subddir; struct dirent *dp; off_t suboff; - pid_t pid, childpid; switch(typesub) { default: @@ -514,34 +513,11 @@ static void unsubscribe_type(char *listdir, char *listaddr, char *listdelim, unlink(sublockname); myfree(sublockname); - if(confirmunsub) { - childpid = fork(); - - if(childpid < 0) { - log_error(LOG_ARGS, "Could not fork"); - confirm_unsub(listdir, listaddr, listdelim, - address, mlmmjsend, - typesub, reasonsub); - } - - if(childpid > 0) { - do /* Parent waits for the child */ - pid = waitpid(childpid, &status, 0); - while(pid == -1 && errno == EINTR); - } - - /* child confirms subscription */ - if(childpid == 0) - confirm_unsub(listdir, listaddr, listdelim, - address, mlmmjsend, - typesub, reasonsub); - } } closedir(subddir); } - int main(int argc, char **argv) { int opt; @@ -549,6 +525,7 @@ int main(int argc, char **argv) int confirmunsub = 0, unsubconfirm = 0, notifysub = 0; int changeuid = 1, quiet = 0; int nogennotsubscribed = 0, i = 0; + int status; char *listaddr, *listdelim, *listdir = NULL, *address = NULL; char *mlmmjsend, *bindir, *subdir, *subddirname; char *lowcaseaddr; @@ -556,6 +533,7 @@ int main(int argc, char **argv) enum subreason reasonsub = SUB_ADMIN; uid_t uid; struct stat st; + pid_t pid, childpid; CHECKFULLPATH(argv[0]); @@ -678,7 +656,7 @@ int main(int argc, char **argv) } if (typesub == SUB_ALL) { - subbed = is_subbed(listdir, address) != SUB_NONE; + subbed = is_subbed(listdir, address, 0) != SUB_NONE; } else { switch(typesub) { default: @@ -716,14 +694,37 @@ int main(int argc, char **argv) if (typesub == SUB_ALL) { unsubscribe_type(listdir, listaddr, listdelim, address, - mlmmjsend, confirmunsub, SUB_NORMAL, reasonsub); + mlmmjsend, SUB_NORMAL, reasonsub); unsubscribe_type(listdir, listaddr, listdelim, address, - mlmmjsend, confirmunsub, SUB_DIGEST, reasonsub); + mlmmjsend, SUB_DIGEST, reasonsub); unsubscribe_type(listdir, listaddr, listdelim, address, - mlmmjsend, confirmunsub, SUB_NOMAIL, reasonsub); + mlmmjsend, SUB_NOMAIL, reasonsub); } else { unsubscribe_type(listdir, listaddr, listdelim, address, - mlmmjsend, confirmunsub, typesub, reasonsub); + mlmmjsend, typesub, reasonsub); + } + + if(confirmunsub) { + childpid = fork(); + + if(childpid < 0) { + log_error(LOG_ARGS, "Could not fork"); + confirm_unsub(listdir, listaddr, listdelim, + address, mlmmjsend, + typesub, reasonsub); + } + + if(childpid > 0) { + do /* Parent waits for the child */ + pid = waitpid(childpid, &status, 0); + while(pid == -1 && errno == EINTR); + } + + /* child confirms subscription */ + if(childpid == 0) + confirm_unsub(listdir, listaddr, listdelim, + address, mlmmjsend, + typesub, reasonsub); } notifysub = !quiet && statctrl(listdir, "notifysub"); diff --git a/src/subscriberfuncs.c b/src/subscriberfuncs.c index 8e9841d4..88766fec 100644 --- a/src/subscriberfuncs.c +++ b/src/subscriberfuncs.c @@ -46,6 +46,7 @@ char *subtype_strs[] = { "nomail", "file", "all", + "both", "none" }; @@ -151,20 +152,27 @@ int is_subbed_in(const char *subddirname, const char *address) return retval; } -enum subtype is_subbed(const char *listdir, const char *address) +enum subtype is_subbed(const char *listdir, const char *address, int both) { int retval; char *subddirname; + enum subtype typesub = SUB_NONE; subddirname = concatstr(2, listdir, "/subscribers.d/"); retval = is_subbed_in(subddirname, address); myfree(subddirname); - if (retval) return SUB_NORMAL; + if (retval) { + if (!both) return SUB_NORMAL; + typesub = SUB_NORMAL; + } subddirname = concatstr(2, listdir, "/digesters.d/"); retval = is_subbed_in(subddirname, address); myfree(subddirname); - if (retval) return SUB_DIGEST; + if (retval) { + if (typesub == SUB_NORMAL) return SUB_BOTH; + return SUB_DIGEST; + } subddirname = concatstr(2, listdir, "/nomailsubs.d/"); retval = is_subbed_in(subddirname, address); -- 2.47.3