# HG changeset patch # User Ben Schmidt # Date 1327491297 -39600 # Node ID 2ea56ea4bd341871b4bc7b3539ce8060d48c6b83 # Parent c6fe438f3e601d230b3b4f10822a71443401b9f9 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. diff -r c6fe438f3e60 -r 2ea56ea4bd34 ChangeLog --- a/ChangeLog Tue Jan 24 04:08:24 2012 +1100 +++ b/ChangeLog Wed Jan 25 22:34:57 2012 +1100 @@ -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 -r c6fe438f3e60 -r 2ea56ea4bd34 README.listtexts --- a/README.listtexts Tue Jan 24 04:08:24 2012 +1100 +++ b/README.listtexts Wed Jan 25 22:34:57 2012 +1100 @@ -79,9 +79,9 @@ 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 -r c6fe438f3e60 -r 2ea56ea4bd34 include/mlmmj.h --- a/include/mlmmj.h Tue Jan 24 04:08:24 2012 +1100 +++ b/include/mlmmj.h Wed Jan 25 22:34:57 2012 +1100 @@ -77,10 +77,11 @@ 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 -r c6fe438f3e60 -r 2ea56ea4bd34 include/subscriberfuncs.h --- a/include/subscriberfuncs.h Tue Jan 24 04:08:24 2012 +1100 +++ b/include/subscriberfuncs.h Wed Jan 25 22:34:57 2012 +1100 @@ -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 -r c6fe438f3e60 -r 2ea56ea4bd34 src/listcontrol.c --- a/src/listcontrol.c Tue Jan 24 04:08:24 2012 +1100 +++ b/src/listcontrol.c Wed Jan 25 22:34:57 2012 +1100 @@ -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 @@ 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 @@ 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 @@ 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 @@ } 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 -r c6fe438f3e60 -r 2ea56ea4bd34 src/mlmmj-bounce.c --- a/src/mlmmj-bounce.c Tue Jan 24 04:08:24 2012 +1100 +++ b/src/mlmmj-bounce.c Wed Jan 25 22:34:57 2012 +1100 @@ -342,7 +342,7 @@ *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 -r c6fe438f3e60 -r 2ea56ea4bd34 src/mlmmj-process.c --- a/src/mlmmj-process.c Tue Jan 24 04:08:24 2012 +1100 +++ b/src/mlmmj-process.c Wed Jan 25 22:34:57 2012 +1100 @@ -984,7 +984,7 @@ 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 -r c6fe438f3e60 -r 2ea56ea4bd34 src/mlmmj-sub.c --- a/src/mlmmj-sub.c Tue Jan 24 04:08:24 2012 +1100 +++ b/src/mlmmj-sub.c Wed Jan 25 22:34:57 2012 +1100 @@ -75,6 +75,9 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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,6 +732,25 @@ exit(EXIT_FAILURE); } + 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); + } + + if(digest) + 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"); + fprintf(stderr, "%s -h for help\n", argv[0]); + exit(EXIT_FAILURE); + } + if(modstr) { getaddrandtype(listdir, modstr, &address, &typesub); reasonsub = SUB_PERMIT; @@ -638,23 +762,6 @@ exit(EXIT_SUCCESS); } - if(digest && nomail) { - fprintf(stderr, "Specify at most one of -d and -n\n"); - fprintf(stderr, "%s -h for help\n", argv[0]); - exit(EXIT_FAILURE); - } - - if(digest) - typesub = SUB_DIGEST; - if(nomail) - typesub = SUB_NOMAIL; - - if(reasonsub == SUB_CONFIRM && subconfirm) { - fprintf(stderr, "Cannot specify both -C and -R\n"); - fprintf(stderr, "%s -h for help\n", argv[0]); - exit(EXIT_FAILURE); - } - /* Make the address lowercase */ lowcaseaddr = mystrdup(address); i = 0; @@ -671,28 +778,6 @@ 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,76 +791,39 @@ } } - 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); - } - subbed = is_subbed(listdir, address); - listdelim = getlistdelim(listdir); + subbed = is_subbed(listdir, address, 1); 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; + /* 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) + if(childpid < 0) { log_error(LOG_ARGS, "Could not fork; " "not unsubscribed from current version"); + } 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, + "-a", address, flag, (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjunsub); exit(EXIT_FAILURE); } } + } if(childpid > 0) { do /* Parent waits for the child */ @@ -783,38 +831,31 @@ while(pid == -1 && errno == EINTR); } - if(subbed == SUB_NONE && subconfirm) { - close(subfilefd); - close(sublockfd); - unlink(sublockname); - myfree(sublockname); + listdelim = getlistdelim(listdir); + + if(subbed == SUB_NONE && subconfirm) 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); + + 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); + + if (typesub == SUB_BOTH) { + if (subbed != SUB_NORMAL) { + subscribe_type(listdir, listaddr, listdelim, address, + mlmmjsend, SUB_NORMAL, reasonsub); } - - close(sublockfd); - unlink(sublockname); - myfree(sublockname); + 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 -r c6fe438f3e60 -r 2ea56ea4bd34 src/mlmmj-unsub.c --- a/src/mlmmj-unsub.c Tue Jan 24 04:08:24 2012 +1100 +++ b/src/mlmmj-unsub.c Wed Jan 25 22:34:57 2012 +1100 @@ -353,19 +353,18 @@ } 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 @@ 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 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 @@ enum subreason reasonsub = SUB_ADMIN; uid_t uid; struct stat st; + pid_t pid, childpid; CHECKFULLPATH(argv[0]); @@ -678,7 +656,7 @@ } 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 @@ 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 -r c6fe438f3e60 -r 2ea56ea4bd34 src/subscriberfuncs.c --- a/src/subscriberfuncs.c Tue Jan 24 04:08:24 2012 +1100 +++ b/src/subscriberfuncs.c Wed Jan 25 22:34:57 2012 +1100 @@ -46,6 +46,7 @@ "nomail", "file", "all", + "both", "none" }; @@ -151,20 +152,27 @@ 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);