]> git.ipfire.org Git - thirdparty/mlmmj.git/commitdiff
mlmmj-sub: move all subcription code into a reusable function
authorBaptiste Daroussin <bapt@FreeBSD.org>
Thu, 6 Jul 2023 11:07:37 +0000 (13:07 +0200)
committerBaptiste Daroussin <bapt@FreeBSD.org>
Thu, 6 Jul 2023 11:07:37 +0000 (13:07 +0200)
This codes allows to remove a execl code from listcontrol,
allow to deduplicate plenty of code and remove the need for mlmmmj-process
to know about mlmmj-sub command path

include/listcontrol.h
include/subscriberfuncs.h
src/listcontrol.c
src/mlmmj-process.c
src/mlmmj-sub.c
src/subscriberfuncs.c
tests/mlmmj.c

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