]> git.ipfire.org Git - thirdparty/mlmmj.git/commitdiff
Changes to how subscription and unsubscribe work.
authorBen Schmidt <none@none>
Mon, 23 Jan 2012 16:02:00 +0000 (03:02 +1100)
committerBen Schmidt <none@none>
Mon, 23 Jan 2012 16:02:00 +0000 (03:02 +1100)
- 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.

12 files changed:
ChangeLog
README.listtexts
include/mlmmj.h
include/subscriberfuncs.h
man/mlmmj-sub.1
man/mlmmj-unsub.1
src/listcontrol.c
src/mlmmj-bounce.c
src/mlmmj-process.c
src/mlmmj-sub.c
src/mlmmj-unsub.c
src/subscriberfuncs.c

index ca2a6a15cd32acb32abf2f6c0cdaa900b02597f8..35aca902251f2db2ed17cb79268e871a2328b2c2 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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 \<space> 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,
index d9acd4cbe9f49dcd7bb2741f81e58dacce900566..87006456ef09a4765e034703c81021401bdf8ff4 100644 (file)
@@ -81,11 +81,11 @@ is given in brackets. Those with asterisks (*) are not yet used.
 
 - 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 @@ is given in brackets. Those with asterisks (*) are not yet used.
   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)
index 768960c043addc8087ce39b2b668641ee1a518df..6f76b5714316786b8055d7db4a4314767200183b 100644 (file)
@@ -76,20 +76,22 @@ enum subtype {
        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);
 
index e36f2746dc74bbe3eb5f6c7128cab98d9e3e1a5a..2d442733b84136a927b0fb20a29b2fce568c527b 100644 (file)
@@ -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 */
index 5c7fb7415d182beb498ec998464927d2569e92c4..69303e5b86abdc88a97bf404497df718b9d9592e 100644 (file)
@@ -4,13 +4,13 @@ mlmmj-sub \- subscribe address to a mailinglist run by mlmmj
 .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 @@ mlmmj-sub \- subscribe address to a mailinglist run by mlmmj
 .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 @@ This utility is used to subscribe people to the specified mailinglist. It will
 write the email address in a file with the name of the beginning letter of the
 email address getting subscribed in the <listdir>/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 <listdir>/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 <listdir>/nomailsubs.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.
 
-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.
+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 <listdir>/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
index 84ba8e0a11c1eeb4eb0025ff3395e774c3143f74..0275e5570aeb0840d255de90f317660a61d0ec81 100644 (file)
@@ -4,7 +4,7 @@ mlmmj-unsub \- unsubscribe address from a mailinglist run by mlmmj
 .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 @@ mlmmj-unsub \- unsubscribe address from a mailinglist run by mlmmj
 .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 @@ mlmmj-unsub \- unsubscribe address from a mailinglist run by mlmmj
 .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
-<listdir>/subscribers.d/ directory.
+<listdir>/subscribers.d/, <listdir>/digesters.d/ and <listdir>/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 @@ Normally a mail is sent to the person being unsubscribed if the address is not
 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
index 52f57283952359e04667b415f1fd9a75a8dd890b..d2b7e7add5215ccfca5e91e099dae321f91ca6ef 100644 (file)
@@ -118,7 +118,6 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
        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 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
        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 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                                "-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 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                                "-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 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                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 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                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 @@ int listcontrol(struct email_container *fromemails, const char *listdir,
                        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 @@ permit:
                }
                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"
index e16ee71a25bb7ea0a693aeb4bc4a8ea4b3b7888a..93d1bfee80b76a0ed5589e24da2a250aa82008f6 100644 (file)
@@ -342,7 +342,7 @@ int main(int argc, char **argv)
        *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)
index a8894c9bf4e4f7a8c99ac787c8ccbd6845cf76ec..f7e75c8a512281fa8cfdc0450e438d94c2b2475e 100644 (file)
@@ -912,7 +912,7 @@ int main(int argc, char **argv)
                        myfree(donemailname);
                        exit(EXIT_SUCCESS);
                }
-               if(is_subbed(listdir, posteraddr) != 0) {
+               if(is_subbed(listdir, posteraddr) == SUB_NONE) {
                        modnonsubposts = statctrl(listdir,
                                        "modnonsubposts");
                        if(modnonsubposts) {
index 46fc84296b5915720238614425cbcac15e0511a2..ae194be9fc561383e0f41d6db76ca90d1e6f558f 100644 (file)
@@ -55,7 +55,8 @@ char *subtype_strs[] = {
        "digest",
        "nomail",
        "file",
-       "all"
+       "all",
+       "none"
 };
 
 char * subreason_strs[] = {
@@ -63,7 +64,8 @@ char * subreason_strs[] = {
        "confirm",
        "permit",
        "admin",
-       "bouncing"
+       "bouncing",
+       "switch"
 };
 
 static void moderate_sub(const char *listdir, const char *listaddr,
@@ -494,28 +496,28 @@ void generate_subconfirm(const char *listdir, const char *listaddr,
 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 @@ void generate_subscribed(const char *listdir, const char *subaddr,
        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 @@ void generate_subscribed(const char *listdir, const char *subaddr,
 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 @@ int main(int argc, char **argv)
 
        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 @@ int main(int argc, char **argv)
                case 'n':
                        nomail = 1;
                        break;
+               case 'q':
+                       quiet = 1;
+                       break;
                case 'r':
                        reasonsub = SUB_REQUEST;
                        break;
@@ -648,7 +657,7 @@ int main(int argc, char **argv)
        }
 
        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 @@ int main(int argc, char **argv)
        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,38 +763,10 @@ int main(int argc, char **argv)
                myfree(sublockname);
                exit(EXIT_FAILURE);
        }
-       subbed = is_subbed_in(subddirname, address);
+       subbed = is_subbed(listdir, address);
        listdelim = getlistdelim(listdir);
        
-       if(subbed) {
-               if(subconfirm) {
-                       close(subfilefd);
-                       close(sublockfd);
-                       unlink(sublockname);
-                       myfree(sublockname);
-                       generate_subconfirm(listdir, listaddr, listdelim,
-                                           address, mlmmjsend, typesub, reasonsub);
-               } else {
-                       if(modstr == NULL)
-                               submod = !force && statctrl(listdir, "submod");
-                       if(submod) {
-                               close(subfilefd);
-                               close(sublockfd);
-                               unlink(sublockname);
-                               myfree(sublockname);
-                               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);
-               }
-       } else {
+       if(subbed == typesub) {
                close(subfilefd);
                myfree(subfilename);
                close(sublockfd);
@@ -799,9 +774,60 @@ int main(int argc, char **argv)
                myfree(sublockname);
 
                if(!nogensubscribed)
-                       generate_subscribed(listdir, address, mlmmjsend);
+                       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);
+               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,
+                               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);
@@ -829,7 +855,8 @@ int main(int argc, char **argv)
                                        mlmmjsend, typesub, reasonsub);
        }
 
-       notifysub = statctrl(listdir, "notifysub");
+       notifysub = !quiet && reasonsub != SUB_SWITCH &&
+                       statctrl(listdir, "notifysub");
 
        /* Notify list owner about subscription */
        if (notifysub)
index dd210c00d670ec42960b45343eac2fe96c2dfc7b..a32f5c5938575faac050edc12175e6bcf45b1b24 100644 (file)
@@ -293,7 +293,7 @@ ssize_t unsubscribe(int subreadfd, int subwritefd, const char *address)
 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 @@ static void print_help(const char *prg)
               " -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 @@ void generate_notsubscribed(const char *listdir, const char *subaddr,
        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 @@ void generate_notsubscribed(const char *listdir, const char *subaddr,
        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:
@@ -493,7 +379,7 @@ int main(int argc, char **argv)
                        subdir = "/nomailsubs.d/";
                        break;
        }
-               
+
        subddirname = concatstr(2, listdir, subdir);
        if (stat(subddirname, &st) == 0) {
                if(st.st_mode & S_IWGRP) {
@@ -502,29 +388,10 @@ int main(int argc, char **argv)
                }
        }
 
-       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 @@ int main(int argc, char **argv)
         }
 
        closedir(subddir);
+}
+
+
+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 = statctrl(listdir, "notifysub");
+        notifysub = !quiet && statctrl(listdir, "notifysub");
 
         /* Notify list owner about subscription */
         if (notifysub)
index c413e383fa756a842a2d68869fe0ecc7452e925c..33c77cda63c48d9afb3e5ab428101e6621055caf 100644 (file)
@@ -93,7 +93,7 @@ off_t find_subscriber(int fd, const char *address)
 
 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 @@ int is_subbed_in(const char *subddirname, const char *address)
                if(suboff == -1) {
                        continue;
                } else {
-                       retval = 0;
+                       retval = 1;
                        break;
                }
        }
@@ -133,7 +133,7 @@ int is_subbed_in(const char *subddirname, const char *address)
        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 @@ int is_subbed(const char *listdir, const char *address)
        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;
 }