# HG changeset patch # User Ben Schmidt # Date 1327334520 -39600 # Node ID 0f8242ffa2d02cdcd932067e28ee824b7b7abc7a # Parent 605ce8682e9bc6fc7f8b5eb463df629bc4df03fc Changes to how subscription and unsubscribe work. - Make +unsubscribe remove the requester from all versions of the list. - Make mlmmj-unsub default to removing the requester from all versions of the list. - Make mlmmj-sub and +subscribe[-digest|-nomail] switch existing subscriptions. - Add a switch to bypass notifying the owner on subscribe/unsubscribe. - Make type available in finish list texts. diff -r 605ce8682e9b -r 0f8242ffa2d0 ChangeLog --- a/ChangeLog Tue Jan 24 14:20:58 2012 +1100 +++ b/ChangeLog Tue Jan 24 03:02:00 2012 +1100 @@ -1,3 +1,9 @@ + 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 + list. + o Make mlmmj-sub and +subscribe[-digest|-nomail] switch existing + subscriptions. + o Add a switch to bypass notifying the owner on subscribe/unsubscribe. o Introduce \ to indicate line-break positions to enable sensible wrapping of Chinese and similar text. o Allow lines to be longer than the wrapping width if there are no spaces, diff -r 605ce8682e9b -r 0f8242ffa2d0 README.listtexts --- a/README.listtexts Tue Jan 24 14:20:58 2012 +1100 +++ b/README.listtexts Tue Jan 24 03:02:00 2012 +1100 @@ -81,11 +81,11 @@ - deny-sub-disabled-digest (sub-deny-digest) - deny-sub-disabled-nomail (sub-deny-nomail) -- deny-sub-subbed (sub-subscribed) +- deny-sub-subbed-{normal|digest|nomail} (sub-subscribed) - deny-sub-closed * - deny-sub-expired * - deny-sub-obstruct * -- deny-unsub-unsubbed (unsub-notsubscribed) +- deny-unsub-unsubbed-{normal|digest|nomail|all} (unsub-notsubscribed) - deny-post-subonlypost (subonlypost) - deny-post-access (access) - deny-post-maxmailsize (maxmailsize) @@ -106,9 +106,9 @@ if the rejection fails; but deny-post-reject will go to the person requesting the post if the rejection succeeds, causing the post to fail) -- finish-sub-{request|confirm|admin|permit}-normal (sub-ok) -- finish-sub-{request|confirm|admin|permit}-digest (sub-ok-digest) -- finish-sub-{request|confirm|admin|permit}-nomail (sub-ok-nomail) +- finish-sub-{request|confirm|admin|permit|switch}-normal (sub-ok) +- finish-sub-{request|confirm|admin|permit|switch}-digest (sub-ok-digest) +- finish-sub-{request|confirm|admin|permit|switch}-nomail (sub-ok-nomail) - finish-unsub-{request|confirm|admin}-normal (unsub-ok) - finish-unsub-{request|confirm|admin}-digest (unsub-ok-digest) - finish-unsub-{request|confirm|admin}-nomail (unsub-ok-nomail) diff -r 605ce8682e9b -r 0f8242ffa2d0 include/mlmmj.h --- a/include/mlmmj.h Tue Jan 24 14:20:58 2012 +1100 +++ b/include/mlmmj.h Tue Jan 24 03:02:00 2012 +1100 @@ -76,20 +76,22 @@ SUB_DIGEST, SUB_NOMAIL, SUB_FILE, /* For single files (moderator, owner etc.) */ - SUB_ALL /* For listing all kinds of subscribers */ + SUB_ALL, /* For listing or unsubscribing all kinds of subscribers */ + SUB_NONE /* For when an address is not subscribed at all */ }; -char *subtype_strs[5]; /* count matches enum above; defined in mlmmj-sub.c */ +char *subtype_strs[6]; /* count matches enum above; defined in mlmmj-sub.c */ enum subreason { SUB_REQUEST, SUB_CONFIRM, SUB_PERMIT, SUB_ADMIN, - SUB_BOUNCING + SUB_BOUNCING, + SUB_SWITCH }; -char * subreason_strs[5]; /* count matches enum above; defined in mlmmj-sub.c */ +char * subreason_strs[6]; /* count matches enum above; defined in mlmmj-sub.c */ void print_version(const char *prg); diff -r 605ce8682e9b -r 0f8242ffa2d0 include/subscriberfuncs.h --- a/include/subscriberfuncs.h Tue Jan 24 14:20:58 2012 +1100 +++ b/include/subscriberfuncs.h Tue Jan 24 03:02:00 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); -int is_subbed(const char *listdir, const char *address); +enum subtype is_subbed(const char *listdir, const char *address); #endif /* SUBSCRIBERFUNC_H */ diff -r 605ce8682e9b -r 0f8242ffa2d0 man/mlmmj-sub.1 --- a/man/mlmmj-sub.1 Tue Jan 24 14:20:58 2012 +1100 +++ b/man/mlmmj-sub.1 Tue Jan 24 03:02:00 2012 +1100 @@ -4,13 +4,13 @@ .SH SYNOPSIS .B mlmmj-sub \fI\-L /path/to/list\fR [\fI\-a john@doe.org\fR | \fI\-m str\fR] -[\fI\-c\fR | \fI\-C\fR] [\fI\-d\fR | \fI\-n\fR] [\fI\-f\fR] [\fI\-h\fR] \fR[\fI\-r\fR | \fI\-R\fR] [\fI\-s\fR] [\fI\-U\fR] [\fI\-V\fR] +[\fI\-c\fR] [\fI\-C\fR] [\fI\-d\fR | \fI\-n\fR] [\fI\-f\fR] [\fI\-h\fR] [\fI\-q\fR] \fR[\fI\-r\fR | \fI\-R\fR] [\fI\-s\fR] [\fI\-U\fR] [\fI\-V\fR] .HP \fB\-a\fR: Email address to subscribe .HP -\fB\-c\fR: Send welcome mail +\fB\-c\fR: Send welcome mail (unless requesting confirmation) .HP -\fB\-C\fR: Request mail confirmation +\fB\-C\fR: Request mail confirmation (unless switching versions) .HP \fB\-d\fR: Subscribe to digest version of the list .HP @@ -24,6 +24,8 @@ .HP \fB\-n\fR: Subscribe to nomail version of the list .HP +\fB\-q\fR: Be quiet (don't notify owner about the subscription) +.HP \fB\-r\fR: Behave as if request arrived via email (internal use) .HP \fB\-R\fR: Behave as if confirmation arrived via email (internal use) @@ -38,23 +40,35 @@ write the email address in a file with the name of the beginning letter of the email address getting subscribed in the /subscribers.d/ directory. -Unless the \fB\-U\fR switch is used it will switch its user id to the user id -owning the list directory. This is done to make sure that new files created are -having correct permissions. +The digest version of the list is a list version where people receive postings +to the list periodically (e.g. once a day) or when a large number of posts have +accumulated. Digest subscribers are in the /digesters.d/ directory. The nomail version of the list is a list version where people are subscribed like usual, but they won't receive any postings to the list. This is useful for people who read the mailinglist through a news gateway, but want to be able to -post to the list. +post to the list. Nomail subscribers are in the /nomailsubs.d/ +directory. -Normally a mail is sent to the subscriber if the address is already subscribed -to the list. If the \fB\-s\fR switch is used such a mail will not be sent. +Unless the \fB\-U\fR switch is used it will switch its user id to the user id +owning the list directory. This is done to make sure that new files created are +having correct permissions. + +If the given address is already subscribed to the list, but to a different +version, the subscription is switched to that version, and confirmation and +moderation are bypassed. If the address is already subscribed to the version +requested, a mail is sent to the subscriber, unless the \fB\-s\fR switch is +used. Subscription may be moderated (if /control/submod exists) unless the -\fB\-f\fR switch is given. +\fB\-f\fR switch is given. When a subscription is permitted by a gatekeeper, +welcome messages are sent to the subscriber as usual, regardless of options +given now. -To ensure a silent subscription, use \fB\-f\fR, but neither \fB\-c\fR -nor \fB\-C\fR. +To ensure subscription is silent from the point of view of the subscriber, use +\fB\-f\fR, but neither \fB\-c\fR nor \fB\-C\fR. To inhibit notification of the +owner, use \fB\-q\fR. Use of \fB\-s\fR is recommended to ensure you don't spam +already-subscribed addresses by accident. .SH "SEE ALSO" mlmmj-unsub(1), setuid(2) .SH AUTHORS diff -r 605ce8682e9b -r 0f8242ffa2d0 man/mlmmj-unsub.1 --- a/man/mlmmj-unsub.1 Tue Jan 24 14:20:58 2012 +1100 +++ b/man/mlmmj-unsub.1 Tue Jan 24 03:02:00 2012 +1100 @@ -4,7 +4,7 @@ .SH SYNOPSIS .B mlmmj-unsub \fI\-L /path/to/list \-a john@doe.org\fR [\fI\-b\fR] [\fI\-c\fR | \fI\-C\fR] -[\fI\-d\fR | \fI\-n\fR] [\fI\-h\fR] [\fI\-r\fR | \fI\-R\fR] [\fI\-s\fR] [\fI\-U\fR] [\fI\-V\fR] +[\fI\-d\fR | \fI\-n\fR | \fI\-N\fR] [\fI\-h\fR] [\fI\-q\fR] [\fI\-r\fR | \fI\-R\fR] [\fI\-s\fR] [\fI\-U\fR] [\fI\-V\fR] .HP \fB\-a\fR: Email address to unsubscribe .HP @@ -22,6 +22,10 @@ .HP \fB\-n\fR: Unsubscribe from the nomail version of the list .HP +\fB\-N\fR: Unsubscribe from the normal version of the list +.HP +\fB\-q\fR: Be quiet (don't notify owner about the unsubscribe) +.HP \fB\-r\fR: Behave as if request arrived via email (internal use) .HP \fB\-R\fR: Behave as if confirmation arrived via email (internal use) @@ -34,7 +38,9 @@ .SH DESCRIPTION This utility is used to unsubscribe people from the specified mailinglist. It will remove the specified email address from every file in the -/subscribers.d/ directory. +/subscribers.d/, /digesters.d/ and /nomailsubs.d/ +directories (or if the \-d, \-n or \-N switch is given, only the one relevant +directory). Unless the \fB\-U\fR switch is used it will switch its user id to the user id owning the list directory. This is done to make sure that new files created are @@ -44,8 +50,11 @@ subscribed to the list. If the \fB\-s\fR switch is used such a mail will not be sent. -When neither \fB\-c\fR nor \fB\-C\fR are specified, unsubscription silently -happens. +When neither \fB\-c\fR nor \fB\-C\fR is specified, unsubscription happens +silently from the point of view of the subscriber. When \fB\-q\fR is specified, +unsubscription happens silently from the point of view of the list owner. Use +of \fB\-s\fR is recommended to ensure you don't spam unsubscribed addresses by +accident. .SH "SEE ALSO" mlmmj-sub(1) .SH AUTHORS diff -r 605ce8682e9b -r 0f8242ffa2d0 src/listcontrol.c --- a/src/listcontrol.c Tue Jan 24 14:20:58 2012 +1100 +++ b/src/listcontrol.c Tue Jan 24 03:02:00 2012 +1100 @@ -118,7 +118,6 @@ char *omitfilename; char *omit = NULL; char *c, *archivefilename, *sendfilename; - const char *subswitch; struct stat stbuf; int closedlist, nosubconfirm, tmpfd, noget, i, closedlistsub, subonlyget = 0; @@ -136,10 +135,6 @@ closedlistsub = statctrl(listdir, "closedlistsub"); nosubconfirm = statctrl(listdir, "nosubconfirm"); - if(nosubconfirm) - subswitch = "-c"; - else - subswitch = "-C"; #if 0 log_error(LOG_ARGS, "controlstr = [%s]\n", controlstr); @@ -244,7 +239,9 @@ "-L", listdir, "-a", fromemails->emaillist[0], "-d", - "-r", subswitch, (char *)NULL); + "-r", "-c", + (nosubconfirm ? (char *)NULL : "-C"), + (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); @@ -291,7 +288,9 @@ "-L", listdir, "-a", fromemails->emaillist[0], "-n", - "-r", subswitch, (char *)NULL); + "-r", "-c", + (nosubconfirm ? (char *)NULL : "-C"), + (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); @@ -319,7 +318,9 @@ execlp(mlmmjsub, mlmmjsub, "-L", listdir, "-a", fromemails->emaillist[0], - "-r", subswitch, (char *)NULL); + "-r", "-c", + (nosubconfirm ? (char *)NULL : "-C"), + (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub); exit(EXIT_FAILURE); @@ -409,64 +410,10 @@ exit(EXIT_FAILURE); break; - /* listname+unsubscribe-digest@domain.tld */ + /* DEPRECATED: listname+unsubscribe-digest@domain.tld */ case CTRL_UNSUBSCRIBE_DIGEST: - if (closedlist) { - errno = 0; - log_error(LOG_ARGS, "An unsubscribe-digest 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, "An unsubscribe-digest request was" - " sent with an invalid From: header." - " Ignoring mail"); - return -1; - } - log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s requests" - " unsubscribe from digest", - fromemails->emaillist[0]); - execlp(mlmmjunsub, mlmmjunsub, - "-L", listdir, - "-a", fromemails->emaillist[0], - "-d", - "-r", subswitch, (char *)NULL); - log_error(LOG_ARGS, "execlp() of '%s' failed", - mlmmjunsub); - exit(EXIT_FAILURE); - break; - - /* listname+unsubscribe-nomail@domain.tld */ + /* DEPRECATED: listname+unsubscribe-nomail@domain.tld */ case CTRL_UNSUBSCRIBE_NOMAIL: - if (closedlist) { - errno = 0; - log_error(LOG_ARGS, "An unsubscribe-nomail 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, "An unsubscribe-nomail request was" - " sent with an invalid From: header." - " Ignoring mail"); - return -1; - } - log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s requests" - " unsubscribe from nomail", - fromemails->emaillist[0]); - execlp(mlmmjunsub, mlmmjunsub, - "-L", listdir, - "-a", fromemails->emaillist[0], - "-n", - "-r", subswitch, (char *)NULL); - log_error(LOG_ARGS, "execlp() of '%s' failed", - mlmmjunsub); - exit(EXIT_FAILURE); - break; - /* listname+unsubscribe@domain.tld */ case CTRL_UNSUBSCRIBE: if (closedlist) { @@ -484,12 +431,13 @@ return -1; } log_oper(listdir, OPLOGFNAME, "mlmmj-unsub: %s requests" - " unsubscribe from regular list", + " unsubscribe", fromemails->emaillist[0]); execlp(mlmmjunsub, mlmmjunsub, "-L", listdir, "-a", fromemails->emaillist[0], - "-r", subswitch, (char *)NULL); + "-r", (nosubconfirm ? "-c" : "-C"), + (char *)NULL); log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjunsub); exit(EXIT_FAILURE); @@ -815,7 +763,8 @@ } subonlyget = statctrl(listdir, "subonlyget"); if(subonlyget) { - if(is_subbed(listdir, fromemails->emaillist[0]) != 0) { + if(is_subbed(listdir, fromemails->emaillist[0]) == + SUB_NONE) { errno = 0; log_error(LOG_ARGS, "A get request was sent" " from a non-subscribed address to a" diff -r 605ce8682e9b -r 0f8242ffa2d0 src/mlmmj-bounce.c --- a/src/mlmmj-bounce.c Tue Jan 24 14:20:58 2012 +1100 +++ b/src/mlmmj-bounce.c Tue Jan 24 03:02:00 2012 +1100 @@ -342,7 +342,7 @@ *a = '@'; /* make sure it's a subscribed address */ - if(is_subbed(listdir, address)) { + if(is_subbed(listdir, address) == SUB_NONE) { log_error(LOG_ARGS, "%s is bouncing but not subscribed?", address); if(mailname) diff -r 605ce8682e9b -r 0f8242ffa2d0 src/mlmmj-process.c --- a/src/mlmmj-process.c Tue Jan 24 14:20:58 2012 +1100 +++ b/src/mlmmj-process.c Tue Jan 24 03:02:00 2012 +1100 @@ -912,7 +912,7 @@ myfree(donemailname); exit(EXIT_SUCCESS); } - if(is_subbed(listdir, posteraddr) != 0) { + if(is_subbed(listdir, posteraddr) == SUB_NONE) { modnonsubposts = statctrl(listdir, "modnonsubposts"); if(modnonsubposts) { diff -r 605ce8682e9b -r 0f8242ffa2d0 src/mlmmj-sub.c --- a/src/mlmmj-sub.c Tue Jan 24 14:20:58 2012 +1100 +++ b/src/mlmmj-sub.c Tue Jan 24 03:02:00 2012 +1100 @@ -55,7 +55,8 @@ "digest", "nomail", "file", - "all" + "all", + "none" }; char * subreason_strs[] = { @@ -63,7 +64,8 @@ "confirm", "permit", "admin", - "bouncing" + "bouncing", + "switch" }; static void moderate_sub(const char *listdir, const char *listaddr, @@ -494,28 +496,28 @@ static void print_help(const char *prg) { printf("Usage: %s -L /path/to/list {-a john@doe.org | -m str}\n" - " [-c | -C] [-f] [-h] [-L] [-d | -n] [-r | -R] [-s] [-U] [-V]\n" + " [-c] [-C] [-f] [-h] [-L] [-d | -n] [-q] [-r | -R] [-s] [-U] [-V]\n" " -a: Email address to subscribe \n" - " -c: Send welcome mail\n" - " -C: Request mail confirmation\n" + " -c: Send welcome mail (unless requesting confirmation)\n" + " -C: Request mail confirmation (unless switching versions)\n" " -d: Subscribe to digest of list\n" " -f: Force subscription (do not moderate)\n" " -h: This help\n" " -L: Full path to list directory\n" " -m: moderation string\n" " -n: Subscribe to no mail version of list\n", prg); - printf(" -r: Behave as if request arrived via email (internal use)\n" + printf(" -q: Be quiet (don't notify owner about the subscription)\n" + " -r: Behave as if request arrived via email (internal use)\n" " -R: Behave as if confirmation arrived via email (internal use)\n" " -s: Don't send a mail to subscriber if already subscribed\n" " -U: Don't switch to the user id of the listdir owner\n" " -V: Print version\n" - "When no options are specified, subscription may be " - "moderated;\nto ensure a silent subscription, use -f\n"); + "To ensure a silent subscription, use -f -q -s\n"); exit(EXIT_SUCCESS); } void generate_subscribed(const char *listdir, const char *subaddr, - const char *mlmmjsend) + const char *mlmmjsend, enum subtype typesub) { text *txt; char *queuefilename, *fromaddr, *listname, *listfqdn, *listaddr; @@ -529,7 +531,8 @@ myfree(listdelim); txt = open_text(listdir, - "deny", "sub", "subbed", NULL, "sub-subscribed"); + "deny", "sub", "subbed", subtype_strs[typesub], + "sub-subscribed"); MY_ASSERT(txt); register_unformatted(txt, "subaddr", subaddr); queuefilename = prepstdreply(txt, listdir, @@ -555,16 +558,18 @@ int main(int argc, char **argv) { char *listaddr, *listdelim, *listdir = NULL, *address = NULL; - char *subfilename = NULL, *mlmmjsend, *bindir, chstr[2], *subdir; + 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, subbed; - int force = 0; + int groupwritable = 0, sublock, sublockfd, nogensubscribed = 0; + int force = 0, quiet = 0; + enum subtype subbed; size_t len; struct stat st; - pid_t pid, childpid; + pid_t pid, childpid = 0; uid_t uid; enum subtype typesub = SUB_NORMAL; enum subreason reasonsub = SUB_ADMIN; @@ -575,9 +580,10 @@ bindir = mydirname(argv[0]); mlmmjsend = concatstr(2, bindir, "/mlmmj-send"); + mlmmjunsub = concatstr(2, bindir, "/mlmmj-unsub"); myfree(bindir); - while ((opt = getopt(argc, argv, "hcCdfm:nsVUL:a:rR")) != -1) { + while ((opt = getopt(argc, argv, "hcCdfm:nsVUL:a:qrR")) != -1) { switch(opt) { case 'a': address = optarg; @@ -606,6 +612,9 @@ case 'n': nomail = 1; break; + case 'q': + quiet = 1; + break; case 'r': reasonsub = SUB_REQUEST; break; @@ -648,7 +657,7 @@ } if(digest && nomail) { - fprintf(stderr, "Specify either -d or -n, not both\n"); + fprintf(stderr, "Specify at most one of -d and -n\n"); fprintf(stderr, "%s -h for help\n", argv[0]); exit(EXIT_FAILURE); } @@ -658,12 +667,6 @@ if(nomail) typesub = SUB_NOMAIL; - if(confirmsub && subconfirm) { - fprintf(stderr, "Cannot specify both -C and -c\n"); - fprintf(stderr, "%s -h for help\n", argv[0]); - exit(EXIT_FAILURE); - } - if(reasonsub == SUB_CONFIRM && subconfirm) { fprintf(stderr, "Cannot specify both -C and -R\n"); fprintf(stderr, "%s -h for help\n", argv[0]); @@ -760,11 +763,45 @@ myfree(sublockname); exit(EXIT_FAILURE); } - subbed = is_subbed_in(subddirname, address); + subbed = is_subbed(listdir, address); listdelim = getlistdelim(listdir); - if(subbed) { - if(subconfirm) { + 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) + 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) { + do /* Parent waits for the child */ + pid = waitpid(childpid, &status, 0); + while(pid == -1 && errno == EINTR); + } + + if(subbed == SUB_NONE && subconfirm) { close(subfilefd); close(sublockfd); unlink(sublockname); @@ -773,7 +810,8 @@ address, mlmmjsend, typesub, reasonsub); } else { if(modstr == NULL) - submod = !force && statctrl(listdir, "submod"); + submod = subbed == SUB_NONE && !force && + statctrl(listdir, "submod"); if(submod) { close(subfilefd); close(sublockfd); @@ -791,18 +829,6 @@ close(sublockfd); unlink(sublockname); } - } else { - close(subfilefd); - myfree(subfilename); - close(sublockfd); - unlink(sublockname); - myfree(sublockname); - - if(!nogensubscribed) - generate_subscribed(listdir, address, mlmmjsend); - - return EXIT_SUCCESS; - } close(sublockfd); unlink(sublockname); @@ -829,7 +855,8 @@ mlmmjsend, typesub, reasonsub); } - notifysub = statctrl(listdir, "notifysub"); + notifysub = !quiet && reasonsub != SUB_SWITCH && + statctrl(listdir, "notifysub"); /* Notify list owner about subscription */ if (notifysub) diff -r 605ce8682e9b -r 0f8242ffa2d0 src/mlmmj-unsub.c --- a/src/mlmmj-unsub.c Tue Jan 24 14:20:58 2012 +1100 +++ b/src/mlmmj-unsub.c Tue Jan 24 03:02:00 2012 +1100 @@ -293,7 +293,7 @@ static void print_help(const char *prg) { printf("Usage: %s -L /path/to/list -a john@doe.org\n" - " [-b] [-c | -C] [-h] [-L] [-d | -n] [-r | -R] [-s] [-V]\n" + " [-b] [-c | -C] [-h] [-L] [-d | -n | -N] [-q] [-r | -R] [-s] [-V]\n" " -a: Email address to unsubscribe \n" " -b: Behave as if unsubscription is due to bouncing (internal use)\n" " -c: Send goodbye mail\n" @@ -301,19 +301,20 @@ " -d: Unsubscribe from digest of list\n" " -h: This help\n" " -L: Full path to list directory\n" - " -n: Unsubscribe from no mail version of list\n", prg); - printf(" -r: Behave as if request arrived via email (internal use)\n" + " -n: Unsubscribe from no mail version of list\n" + " -N: Unsubscribe from normal version of list\n", prg); + printf(" -q: Be quiet (don't notify owner about the subscription)\n" + " -r: Behave as if request arrived via email (internal use)\n" " -R: Behave as if confirmation arrived via email (internal use)\n" " -s: Don't send a mail to the address if not subscribed\n" " -U: Don't switch to the user id of the listdir owner\n" " -V: Print version\n" - "When no options are specified, unsubscription silently " - "happens\n"); + "To ensure a silent unsubscription, use -q -s\n"); exit(EXIT_SUCCESS); } -void generate_notsubscribed(const char *listdir, const char *subaddr, - const char *mlmmjsend) +static void generate_notsubscribed(const char *listdir, const char *subaddr, + const char *mlmmjsend, enum subtype typesub) { text *txt; char *queuefilename, *fromaddr, *listname, *listfqdn, *listaddr; @@ -327,7 +328,7 @@ myfree(listdelim); txt = open_text(listdir, - "deny", "unsub", "unsubbed", NULL, + "deny", "unsub", "unsubbed", subtype_strs[typesub], "unsub-notsubscribed"); MY_ASSERT(txt); register_unformatted(txt, "subaddr", subaddr); @@ -351,135 +352,20 @@ exit(EXIT_FAILURE); } - -int main(int argc, char **argv) -{ - int subread, subwrite, rlock, wlock, opt, unsubres, status, nomail = 0; - int confirmunsub = 0, unsubconfirm = 0, notifysub = 0, digest = 0; - int changeuid = 1, groupwritable = 0, sublock, sublockfd; - int nogennotsubscribed = 0, i = 0; - char *listaddr, *listdelim, *listdir = NULL, *address = NULL; - char *subreadname = NULL, *subwritename, *mlmmjsend, *bindir, *subdir; - char *subddirname, *sublockname, *lowcaseaddr; - off_t suboff; +static void unsubscribe_type(char *listdir, char *listaddr, char *listdelim, + char *address, char *mlmmjsend, int confirmunsub, + 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; + struct stat st; DIR *subddir; struct dirent *dp; + off_t suboff; pid_t pid, childpid; - enum subtype typesub = SUB_NORMAL; - enum subreason reasonsub = SUB_ADMIN; - uid_t uid; - struct stat st; - - CHECKFULLPATH(argv[0]); - - log_set_name(argv[0]); - - bindir = mydirname(argv[0]); - mlmmjsend = concatstr(2, bindir, "/mlmmj-send"); - myfree(bindir); - - while ((opt = getopt(argc, argv, "hcCdnVUL:a:sbrR")) != -1) { - switch(opt) { - case 'L': - listdir = optarg; - break; - case 'n': - nomail = 1; - break; - case 'a': - address = optarg; - break; - case 'b': - reasonsub = SUB_BOUNCING; - break; - case 'c': - confirmunsub = 1; - break; - case 'C': - unsubconfirm = 1; - break; - case 'd': - digest = 1; - break; - case 'h': - print_help(argv[0]); - break; - case 'r': - reasonsub = SUB_REQUEST; - break; - case 'R': - reasonsub = SUB_CONFIRM; - break; - case 's': - nogennotsubscribed = 1; - break; - case 'U': - changeuid = 0; - break; - case 'V': - print_version(argv[0]); - exit(0); - } - } - if(listdir == 0 || address == 0) { - fprintf(stderr, "You have to specify -L and -a\n"); - fprintf(stderr, "%s -h for help\n", argv[0]); - exit(EXIT_FAILURE); - } - - if(digest && nomail) { - fprintf(stderr, "Specify either -d or -n, not both\n"); - fprintf(stderr, "%s -h for help\n", argv[0]); - exit(EXIT_FAILURE); - } - - if(digest) - typesub = SUB_DIGEST; - if(nomail) - typesub = SUB_NOMAIL; - - if(confirmunsub && unsubconfirm) { - fprintf(stderr, "Cannot specify both -C and -c\n"); - fprintf(stderr, "%s -h for help\n", argv[0]); - exit(EXIT_FAILURE); - } - - if(reasonsub == SUB_CONFIRM && unsubconfirm) { - fprintf(stderr, "Cannot specify both -C and -R\n"); - fprintf(stderr, "%s -h for help\n", argv[0]); - exit(EXIT_FAILURE); - } - - if(reasonsub == SUB_BOUNCING && unsubconfirm) { - fprintf(stderr, "Cannot specify both -C and -b\n"); - fprintf(stderr, "%s -h for help\n", argv[0]); - exit(EXIT_FAILURE); - } - - /* Make the address lowercase */ - lowcaseaddr = mystrdup(address); - i = 0; - while(lowcaseaddr[i]) { - lowcaseaddr[i] = tolower(lowcaseaddr[i]); - i++; - } - address = lowcaseaddr; - - /* get the list address */ - listaddr = getlistaddr(listdir); - - if(changeuid) { - uid = getuid(); - if(!uid && stat(listdir, &st) == 0) { - printf("Changing to uid %d, owner of %s.\n", - (int)st.st_uid, listdir); - if(setuid(st.st_uid) < 0) { - perror("setuid"); - fprintf(stderr, "Continuing as uid %d\n", - (int)uid); - } - } - } switch(typesub) { default: @@ -502,29 +388,10 @@ } } - if(is_subbed_in(subddirname, address)) { - /* Address is not subscribed */ - myfree(subddirname); - myfree(listaddr); - - if(!nogennotsubscribed) { - generate_notsubscribed(listdir, address, mlmmjsend); - } - - exit(EXIT_SUCCESS); - } - - listdelim = getlistdelim(listdir); - if(unsubconfirm) - generate_unsubconfirm(listdir, listaddr, listdelim, address, - mlmmjsend, typesub, reasonsub); - if((subddir = opendir(subddirname)) == NULL) { log_error(LOG_ARGS, "Could not opendir(%s)", subddirname); myfree(subddirname); - myfree(listaddr); - myfree(listdelim); exit(EXIT_FAILURE); } @@ -672,8 +539,194 @@ } closedir(subddir); +} - notifysub = statctrl(listdir, "notifysub"); + +int main(int argc, char **argv) +{ + int opt; + int normal = 0, digest = 0, nomail = 0, subbed; + int confirmunsub = 0, unsubconfirm = 0, notifysub = 0; + int changeuid = 1, quiet = 0; + int nogennotsubscribed = 0, i = 0; + char *listaddr, *listdelim, *listdir = NULL, *address = NULL; + char *mlmmjsend, *bindir, *subdir, *subddirname; + char *lowcaseaddr; + enum subtype typesub = SUB_ALL; + enum subreason reasonsub = SUB_ADMIN; + uid_t uid; + struct stat st; + + CHECKFULLPATH(argv[0]); + + log_set_name(argv[0]); + + bindir = mydirname(argv[0]); + mlmmjsend = concatstr(2, bindir, "/mlmmj-send"); + myfree(bindir); + + while ((opt = getopt(argc, argv, "hcCdenNVUL:a:sbqrR")) != -1) { + switch(opt) { + case 'L': + listdir = optarg; + break; + case 'a': + address = optarg; + break; + case 'b': + reasonsub = SUB_BOUNCING; + break; + case 'c': + confirmunsub = 1; + break; + case 'C': + unsubconfirm = 1; + break; + case 'd': + digest = 1; + break; + case 'h': + print_help(argv[0]); + break; + case 'n': + nomail = 1; + break; + case 'N': + normal = 1; + break; + case 'q': + quiet = 1; + break; + case 'r': + reasonsub = SUB_REQUEST; + break; + case 'R': + reasonsub = SUB_CONFIRM; + break; + case 's': + nogennotsubscribed = 1; + break; + case 'U': + changeuid = 0; + break; + case 'V': + print_version(argv[0]); + exit(0); + } + } + if(listdir == 0 || address == 0) { + fprintf(stderr, "You have to specify -L and -a\n"); + fprintf(stderr, "%s -h for help\n", argv[0]); + exit(EXIT_FAILURE); + } + + if(digest + nomail + normal > 1) { + fprintf(stderr, "Specify at most one of -d, -n 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(normal) + typesub = SUB_NORMAL; + + if(confirmunsub && unsubconfirm) { + fprintf(stderr, "Cannot specify both -C and -c\n"); + fprintf(stderr, "%s -h for help\n", argv[0]); + exit(EXIT_FAILURE); + } + + if(reasonsub == SUB_CONFIRM && unsubconfirm) { + fprintf(stderr, "Cannot specify both -C and -R\n"); + fprintf(stderr, "%s -h for help\n", argv[0]); + exit(EXIT_FAILURE); + } + + if(reasonsub == SUB_BOUNCING && unsubconfirm) { + fprintf(stderr, "Cannot specify both -C and -b\n"); + fprintf(stderr, "%s -h for help\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* Make the address lowercase */ + lowcaseaddr = mystrdup(address); + i = 0; + while(lowcaseaddr[i]) { + lowcaseaddr[i] = tolower(lowcaseaddr[i]); + i++; + } + address = lowcaseaddr; + + /* get the list address */ + listaddr = getlistaddr(listdir); + listdelim = getlistdelim(listdir); + + if(changeuid) { + uid = getuid(); + if(!uid && stat(listdir, &st) == 0) { + printf("Changing to uid %d, owner of %s.\n", + (int)st.st_uid, listdir); + if(setuid(st.st_uid) < 0) { + perror("setuid"); + fprintf(stderr, "Continuing as uid %d\n", + (int)uid); + } + } + } + + if (typesub == SUB_ALL) { + subbed = is_subbed(listdir, address) != SUB_NONE; + } else { + 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); + subbed = is_subbed_in(subddirname, address); + myfree(subddirname); + } + + if(!subbed) { + /* Address is not subscribed */ + myfree(listaddr); + myfree(listdelim); + + if(!nogennotsubscribed) { + generate_notsubscribed(listdir, address, mlmmjsend, + typesub); + } + + exit(EXIT_SUCCESS); + } + + if(unsubconfirm) + generate_unsubconfirm(listdir, listaddr, listdelim, address, + mlmmjsend, typesub, reasonsub); + + if (typesub == SUB_ALL) { + unsubscribe_type(listdir, listaddr, listdelim, address, + mlmmjsend, confirmunsub, SUB_NORMAL, reasonsub); + unsubscribe_type(listdir, listaddr, listdelim, address, + mlmmjsend, confirmunsub, SUB_DIGEST, reasonsub); + unsubscribe_type(listdir, listaddr, listdelim, address, + mlmmjsend, confirmunsub, SUB_NOMAIL, reasonsub); + } else { + unsubscribe_type(listdir, listaddr, listdelim, address, + mlmmjsend, confirmunsub, typesub, reasonsub); + } + + notifysub = !quiet && statctrl(listdir, "notifysub"); /* Notify list owner about subscription */ if (notifysub) diff -r 605ce8682e9b -r 0f8242ffa2d0 src/subscriberfuncs.c --- a/src/subscriberfuncs.c Tue Jan 24 14:20:58 2012 +1100 +++ b/src/subscriberfuncs.c Tue Jan 24 03:02:00 2012 +1100 @@ -93,7 +93,7 @@ int is_subbed_in(const char *subddirname, const char *address) { - int retval = 1, subread; + int retval = 0, subread; char *subreadname; off_t suboff; DIR *subddir; @@ -124,7 +124,7 @@ if(suboff == -1) { continue; } else { - retval = 0; + retval = 1; break; } } @@ -133,7 +133,7 @@ return retval; } -int is_subbed(const char *listdir, const char *address) +enum subtype is_subbed(const char *listdir, const char *address) { int retval; char *subddirname; @@ -141,20 +141,17 @@ subddirname = concatstr(2, listdir, "/subscribers.d/"); retval = is_subbed_in(subddirname, address); myfree(subddirname); - if (retval == 0) - return 0; + if (retval) return SUB_NORMAL; subddirname = concatstr(2, listdir, "/digesters.d/"); retval = is_subbed_in(subddirname, address); myfree(subddirname); - if (retval == 0) - return 0; + if (retval) return SUB_DIGEST; subddirname = concatstr(2, listdir, "/nomailsubs.d/"); retval = is_subbed_in(subddirname, address); myfree(subddirname); - if (retval == 0) - return 0; + if (retval) return SUB_NOMAIL; - return 1; + return SUB_NONE; }