--- /dev/null
- if(strncasecmp(ml.addr, address, strlen(ml.addr)) == 0)
+/* Copyright (C) 2002, 2003 Mads Martin Joergensen <mmj at mmj.dk>
+ *
+ * $Id$
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <sys/wait.h>
+#include <ctype.h>
+#include <err.h>
+
+#include "xmalloc.h"
+#include "mlmmj.h"
+#include "mlmmj-sub.h"
+#include "wrappers.h"
+#include "strgen.h"
+#include "subscriberfuncs.h"
+#include "log_error.h"
+#include "statctrl.h"
+#include "prepstdreply.h"
+#include "ctrlvalues.h"
+#include "chomp.h"
+#include "utils.h"
+#include "send_help.h"
+#include "xstring.h"
+
+static char *subtypes[7] = {
+ "SUB_NORMAL",
+ "SUB_DIGEST",
+ "SUB_NOMAIL",
+ NULL,
+ NULL,
+ "SUB_BOTH",
+ NULL,
+};
+
+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);
+}
+
+void getaddrandtype(struct ml *ml, const char *modstr,
+ char **addrptr, enum subtype *subtypeptr)
+{
+ int fd;
+ char *readaddr, *readtype, *modfilename, *line = NULL;
+ FILE *f;
+ size_t linecap = 0;
+
+ if (strncmp(modstr, "subscribe", 9) == 0)
+ modstr += 9;
+
+ xasprintf(&modfilename, "moderation/subscribe%s", modstr);
+
+ fd = openat(ml->fd, modfilename, O_RDONLY);
+ if(fd < 0) {
+ log_error(LOG_ARGS, "Could not open %s/%s", ml->dir, modfilename);
+ exit(EXIT_FAILURE);
+ }
+ f = fdopen(fd, "r");
+
+ if (getline(&line, &linecap, f) <= 0) {
+ log_error(LOG_ARGS, "Could not parse %s/%s", ml->dir, modfilename);
+ exit(EXIT_FAILURE);
+ }
+ chomp(line);
+ readaddr = xstrdup(line);
+ if (getline(&line, &linecap, f) <= 0) {
+ log_error(LOG_ARGS, "Could not parse %s/%s", ml->dir, modfilename);
+ exit(EXIT_FAILURE);
+ }
+ chomp(line);
+ readtype = xstrdup(line);
+ fclose(f);
+
+ *addrptr = readaddr;
+
+ for (size_t i = 0; i < NELEM(subtypes); i++) {
+ if (subtypes[i] == NULL)
+ continue;
+ if (strcmp(subtypes[i], readtype) == 0) {
+ *subtypeptr = i;
+ goto freedone;
+ }
+ }
+
+ log_error(LOG_ARGS, "Type %s not valid in %s/%s", readtype,
+ ml->dir, modfilename);
+
+freedone:
+ free(readtype);
+ unlinkat(ml->fd, modfilename, 0);
+ free(modfilename);
+}
+
+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] [-q] [-r | -R] [-s] [-U] [-V]\n"
+ " -a: Email address to subscribe \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(" -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"
+ "To ensure a silent subscription, use -f -q -s\n");
+ 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;
+ 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);
+ CHECKFULLPATH(argv[0]);
+
+ log_set_name(argv[0]);
+
+ bindir = mydirname(argv[0]);
+ xasprintf(&mlmmjsend, "%s/mlmmj-send", bindir);
+ free(bindir);
+
+ while ((opt = getopt(argc, argv, "hbcCdfm:nsVUL:a:qrR")) != -1) {
+ switch(opt) {
+ case 'a':
+ if (strchr(optarg, '@') == NULL)
+ errx(EXIT_FAILURE, "No '@' in the provided email address '%s'",
+ optarg);
+ address = optarg;
+ break;
+ case 'b':
+ both = true;
+ break;
+ case 'c':
+ send_welcome_mail = true;
+ break;
+ case 'C':
+ subconfirm = true;
+ break;
+ case 'd':
+ digest = true;
+ break;
+ case 'f':
+ force = true;
+ break;
+ case 'h':
+ print_help(argv[0]);
+ break;
+ case 'L':
+ ml.dir = optarg;
+ break;
+ case 'm':
+ modstr = optarg;
+ break;
+ case 'n':
+ nomail = true;
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ case 'r':
+ reasonsub = SUB_REQUEST;
+ break;
+ case 'R':
+ reasonsub = SUB_CONFIRM;
+ break;
+ case 's':
+ nogensubscribed = true;
+ break;
+ case 'U':
+ changeuid = false;
+ break;
+ case 'V':
+ print_version(argv[0]);
+ exit(0);
+ }
+ }
+
+ if(ml.dir == NULL) {
+ errx(EXIT_FAILURE, "You have to specify -L\n"
+ "%s -h for help", argv[0]);
+ }
+ if (!ml_open(&ml, false))
+ exit(EXIT_FAILURE);
+
+ if(address == NULL && modstr == NULL) {
+ errx(EXIT_FAILURE, "You have to specify -a or -m\n"
+ "%s -h for help", argv[0]);
+ }
+
+ if(both + digest + nomail > 1) {
+ errx(EXIT_FAILURE, "Specify at most one of -b, -d and -n\n"
+ "%s -h for help", argv[0]);
+ }
+
+ if(digest)
+ typesub = SUB_DIGEST;
+ if(nomail)
+ typesub = SUB_NOMAIL;
+ if(both)
+ typesub = SUB_BOTH;
+
+ if(reasonsub == SUB_CONFIRM && subconfirm) {
+ errx(EXIT_FAILURE, "Cannot specify both -C and -R\n"
+ "%s -h for help", argv[0]);
+ }
+
+ if(modstr) {
+ getaddrandtype(&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) {
+ printf("Changing to uid %d, owner of %s.\n",
+ (int)st.st_uid, ml.dir);
+ if(setuid(st.st_uid) < 0) {
+ perror("setuid");
+ fprintf(stderr, "Continuing as uid %d\n",
+ (int)uid);
+ }
+ }
+ }
+
+ 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;
+}
--- /dev/null
+/*
+ * Copyright (C) 2004, 2003, 2004 Mads Martin Joergensen <mmj at mmj.dk>
+ * Copyright (C) 2022-2023 Baptiste Daroussin <bapt@FreeBSD.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <sys/stat.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "checkwait_smtpreply.h"
+#include "mail-functions.h"
+#include "getlistdelim.h"
+#include "send_mail.h"
+#include "log_error.h"
+#include "init_sockfd.h"
+#include "mlmmj.h"
+#include "strgen.h"
+#include "tllist.h"
+#include "xmalloc.h"
+#include "ctrlvalue.h"
+#include "utils.h"
+
+int
+initsmtp(int *sockfd, const char *relayhost, unsigned short port, const char *heloname)
+{
+ int retval = 0;
+ int try_ehlo = 1;
+ char *reply = NULL;
+
+ do {
+ init_sockfd(sockfd, relayhost, port);
+
+ if(*sockfd == -1) {
+ retval = EBADF;
+ break;
+ }
+
+ if((reply = checkwait_smtpreply(*sockfd, MLMMJ_CONNECT)) != NULL) {
+ log_error(LOG_ARGS, "No proper greeting to our connect"
+ "Reply: [%s]", reply);
+ free(reply);
+ retval = MLMMJ_CONNECT;
+ /* FIXME: Queue etc. */
+ break;
+ }
+
+ if (try_ehlo) {
+ write_ehlo(*sockfd, heloname);
+ if((reply = checkwait_smtpreply(*sockfd, MLMMJ_EHLO))
+ == NULL) {
+ /* EHLO successful don't try more */
+ break;
+ }
+
+ /* RFC 1869 - 4.5. - In the case of any error response,
+ * the client SMTP should issue either the HELO or QUIT
+ * command.
+ * RFC 1869 - 4.5. - If the server SMTP recognizes the
+ * EHLO command, but the command argument is
+ * unacceptable, it will return code 501.
+ */
+ if (strncmp(reply, "501", 3) == 0) {
+ free(reply);
+ /* Commmand unacceptable; we choose to QUIT but
+ * ignore any QUIT errors; return that EHLO was
+ * the error.
+ */
+ endsmtp(sockfd);
+ retval = MLMMJ_EHLO;
+ break;
+ }
+
+ /* RFC 1869 - 4.6. - A server SMTP that conforms to RFC
+ * 821 but does not support the extensions specified
+ * here will not recognize the EHLO command and will
+ * consequently return code 500, as specified in RFC
+ * 821. The server SMTP should stay in the same state
+ * after returning this code (see section 4.1.1 of RFC
+ * 821). The client SMTP may then issue either a HELO
+ * or a QUIT command.
+ */
+
+ if (reply[0] != '5') {
+ free(reply);
+ /* Server doesn't understand EHLO, but gives a
+ * broken response. Try with new connection.
+ */
+ endsmtp(sockfd);
+ try_ehlo = 0;
+ continue;
+ }
+
+ free(reply);
+
+ /* RFC 1869 - 4.7. - Other improperly-implemented
+ * servers will not accept a HELO command after EHLO has
+ * been sent and rejected. In some cases, this problem
+ * can be worked around by sending a RSET after the
+ * failure response to EHLO, then sending the HELO.
+ */
+ write_rset(*sockfd);
+ reply = checkwait_smtpreply(*sockfd, MLMMJ_RSET);
+
+ /* RFC 1869 - 4.7. - Clients that do this should be
+ * aware that many implementations will return a failure
+ * code (e.g., 503 Bad sequence of commands) in response
+ * to the RSET. This code can be safely ignored.
+ */
+ free(reply);
+
+ /* Try HELO on the same connection
+ */
+ }
+
+ write_helo(*sockfd, heloname);
+ if((reply = checkwait_smtpreply(*sockfd, MLMMJ_HELO))
+ == NULL) {
+ /* EHLO successful don't try more */
+ break;
+ }
+ if (try_ehlo) {
+ free(reply);
+ /* We reused a connection we tried EHLO on. Maybe
+ * that's why it failed. Try with new connection.
+ */
+ endsmtp(sockfd);
+ try_ehlo = 0;
+ continue;
+ }
+
+ log_error(LOG_ARGS, "Error with HELO. Reply: "
+ "[%s]", reply);
+ free(reply);
+ /* FIXME: quit and tell admin to configure
+ * correctly */
+ retval = MLMMJ_HELO;
+ break;
+
+ } while (1);
+
+ return retval;
+}
+
+int
+endsmtp(int *sockfd)
+{
+ int retval = 0;
+ char *reply = NULL;
+
+ if(*sockfd == -1)
+ return retval;
+
+ write_quit(*sockfd);
+ reply = checkwait_smtpreply(*sockfd, MLMMJ_QUIT);
+ if(reply) {
+ log_error(LOG_ARGS, "Mailserver would not let us QUIT. "
+ "We close the socket anyway though. "
+ "Mailserver reply = [%s]", reply);
+ free(reply);
+ retval = MLMMJ_QUIT;
+ }
+
+ close(*sockfd);
+ *sockfd = -1;
+
+ return retval;
+}
+
+int do_bouncemail(int listfd, int ctrlfd, const char *from)
+{
+ /* expected format for the from: "something+anything-<number>-anything@anything" */
+ char *tofree;
+ char *myfrom = xstrdup(from);
+ char *listdelim = getlistdelim(ctrlfd);
+ char *addr, *num, *c;
+
+ tofree = myfrom;
+ if((c = strchr(myfrom, '@')) == NULL) {
+ free(myfrom);
+ free(listdelim);
+ return 0; /* Success when malformed 'from' */
+ }
+ *c = '\0';
+ num = strrchr(myfrom, '-');
+ if (num == NULL) {
+ free(tofree);
+ return (0); /* Success when malformed 'from' */
+ }
+ num++;
+ c = strstr(myfrom, listdelim);
+ if (c == NULL) {
+ free(tofree);
+ return (0); /* Success when malformed 'from' */
+ }
+ myfrom = strchr(c, '-'); /* malformed entry with delimiter after the -num */
+ if (myfrom == NULL) {
+ free(tofree);
+ return (0);
+ }
+ myfrom++;
+ if (num <= myfrom) {
+ free(tofree);
+ return (0); /* Success when malformed 'from' */
+ }
+ addr = xstrndup(myfrom, num - myfrom - 1);
+ free(listdelim);
+
+ bouncemail(listfd, num, addr);
++ free(tofree);
++ free(addr);
+ return 1;
+}
+
+int
+send_mail(int sockfd, struct mail *mail, int listfd, int ctrlfd, bool bounce)
+{
+ int retval = 0;
+ char *reply, *reply2;
+
+ if(sockfd == -1)
+ return EBADF;
+
+ if (mail->to == NULL) {
+ errno = 0;
+ log_error(LOG_ARGS, "No 'to' address, ignoring");
+ return (0);
+ }
+
+ if(strchr(mail->to, '@') == NULL) {
+ errno = 0;
+ log_error(LOG_ARGS, "No @ in address, ignoring %s",
+ mail->to);
+ return 0;
+ }
+
+ if (mail->from == NULL) {
+ errno = 0;
+ log_error(LOG_ARGS, "No from address, ignoring");
+ return 0;
+ }
+
+ if (mail->fp == NULL) {
+ errno = 0;
+ log_error(LOG_ARGS, "No mail to send, ignoring");
+ return 0;
+ }
+ rewind(mail->fp);
+
+ retval = write_mail_from(sockfd, mail->from, "");
+ if(retval) {
+ log_error(LOG_ARGS, "Could not write MAIL FROM\n");
+ return retval;
+ }
+ reply = checkwait_smtpreply(sockfd, MLMMJ_FROM);
+ if(reply) {
+ log_error(LOG_ARGS, "Error in MAIL FROM. Reply = [%s]",
+ reply);
+ free(reply);
+ write_rset(sockfd);
+ reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
+ if (reply2 != NULL) free(reply2);
+ return MLMMJ_FROM;
+ }
+ retval = write_rcpt_to(sockfd, mail->to);
+ if(retval) {
+ log_error(LOG_ARGS, "Could not write RCPT TO:\n");
+ return retval;
+ }
+
+ reply = checkwait_smtpreply(sockfd, MLMMJ_RCPTTO);
+ if(reply) {
+ write_rset(sockfd);
+ reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
+ if (reply2 != NULL) free(reply2);
+ if(bounce && ((reply[0] == '4') || (reply[0] == '5'))
+ && (reply[1] == '5')) {
+ free(reply);
+ return do_bouncemail(listfd, ctrlfd, mail->from);
+ } else {
+ log_error(LOG_ARGS, "Error in RCPT TO. Reply = [%s]",
+ reply);
+ free(reply);
+ return MLMMJ_RCPTTO;
+ }
+ }
+
+ retval = write_data(sockfd);
+ if(retval) {
+ log_error(LOG_ARGS, "Could not write DATA\b");
+ return retval;
+ }
+
+ reply = checkwait_smtpreply(sockfd, MLMMJ_DATA);
+ if(reply) {
+ log_error(LOG_ARGS, "Error with DATA. Reply = [%s]", reply);
+ free(reply);
+ write_rset(sockfd);
+ reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
+ if (reply2 != NULL) free(reply2);
+ return MLMMJ_DATA;
+ }
+
+ if(mail->replyto) {
+ retval = write_replyto(sockfd, mail->replyto);
+ if(retval) {
+ log_error(LOG_ARGS, "Could not write reply-to addr.\n");
+ return retval;
+ }
+ }
+
+ write_mailbody(sockfd, mail->fp, mail->addtohdr ? mail->to : NULL);
+
+ retval = write_dot(sockfd);
+ if(retval) {
+ log_error(LOG_ARGS, "Could not write <CR><LF>.<CR><LF>\n");
+ return retval;
+ }
+
+ reply = checkwait_smtpreply(sockfd, MLMMJ_DOT);
+ if(reply) {
+ log_error(LOG_ARGS, "Mailserver did not ack end of mail.\n"
+ "<CR><LF>.<CR><LF> was written, to no"
+ "avail. Reply = [%s]", reply);
+ free(reply);
+ write_rset(sockfd);
+ reply2 = checkwait_smtpreply(sockfd, MLMMJ_RSET);
+ if (reply2 != NULL) free(reply2);
+ return MLMMJ_DOT;
+ }
+
+ return 0;
+}
+
+int
+newsmtp(struct ml *ml)
+{
+ int sockfd;
+ char *relayhost, *smtphelo;
+ unsigned short smtpport = ctrlushort(ml->ctrlfd, "smtpport", 25);
+
+ relayhost = ctrlvalue(ml->ctrlfd, "relayhost");
+ if (relayhost == NULL)
+ relayhost = xstrdup(RELAYHOST);
+ smtphelo = ctrlvalue(ml->ctrlfd, "smtphelo");
+ if (smtphelo == NULL)
+ smtphelo = hostnamestr();
+ if (initsmtp(&sockfd, relayhost, smtpport, smtphelo) != 0)
+ return (-1);
+ return (sockfd);
+}
+
+static bool
+save_file(const char *name, const char *ext, const char *content)
+{
+ char *tmpstr;
+ int fd;
+
+ xasprintf(&tmpstr, "%s.%s", name, ext);
+ fd = open(tmpstr, O_WRONLY|O_CREAT|O_EXCL|O_SYNC, S_IRUSR|S_IWUSR);
+ if (fd == -1) {
+ free(tmpstr);
+ return (false);
+ }
+ dprintf(fd, "%s", content);
+ close(fd);
+ free(tmpstr);
+ return (true);
+}
+
+void
+save_queue(const char *queuefilename, struct mail *mail)
+{
+ if (!save_file(queuefilename, "mailfrom", mail->from))
+ return;
+ if (!save_file(queuefilename, "reciptto", mail->to))
+ return;
+ if (mail->replyto != NULL)
+ save_file(queuefilename, "reply-to", mail->replyto);
+}
+
+bool
+send_single_mail(struct mail *mail, struct ml *ml, bool bounce)
+{
+ int sockfd;
+
+ sockfd = newsmtp(ml);
+ if (sockfd == -1)
+ return (false);
+ if (send_mail(sockfd, mail, ml->fd, ml->ctrlfd, bounce)) {
+ endsmtp(&sockfd);
+ return (false);
+ }
+ endsmtp(&sockfd);
+ return (true);
+}
+
+bool
+requeuemail(int listfd, int index, strlist *addrs, const char *addr)
+{
+ int addrfd, dfd;
+ char *dirname;
+
+ if (addrs == NULL || tll_length(*addrs) == 0)
+ return (false);
+ xasprintf(&dirname, "requeue/%d", index);
+ if(mkdirat(listfd, dirname, 0750) < 0 && errno != EEXIST) {
+ log_error(LOG_ARGS, "Could not mkdir(%s) for "
+ "requeueing. Mail cannot "
+ "be requeued.", dirname);
+ free(dirname);
+ return (false);
+ }
+ dfd = openat(listfd, dirname, O_DIRECTORY);
+ addrfd = openat(dfd, "subscribers", O_WRONLY|O_CREAT|O_APPEND,
+ S_IRUSR|S_IWUSR);
+ if(addrfd < 0) {
+ log_error(LOG_ARGS, "Could not open %s/subscribers",
+ dirname);
+ free(dirname);
+ close(dfd);
+ return (false);
+ }
+ free(dirname);
+ close(dfd);
+ /* Dump the remaining addresses. We dump the remaining before
+ * we write the failing address to ensure the potential good
+ * ones will be tried first when mlmmj-maintd sends out mails
+ * that have been requeued. addrcount was so far we were */
+ tll_foreach(*addrs, it) {
+ dprintf(addrfd, "%s\n", it->item);
+ }
+ if (addr != NULL)
+ dprintf(addrfd, "%s\n", addr);
+ close(addrfd);
+
+ return (true);
+}
+
+char *
+get_bounce_from_adr(const char *recipient, struct ml *ml, int index)
+{
+ char *bounceaddr, *myrecipient;
+ char *a = NULL;
+ char *staticbounceaddr, *staticbounceaddr_localpart = NULL;
+ const char *staticbounceaddr_domain = NULL;
+
+ myrecipient = xstrdup(recipient);
+ a = strchr(myrecipient, '@');
+ if (a)
+ *a = '=';
+
+ staticbounceaddr = ctrlvalue(ml->ctrlfd, "staticbounceaddr");
+ if (staticbounceaddr) {
+ staticbounceaddr_localpart = genlistname(staticbounceaddr);
+ staticbounceaddr_domain = genlistfqdn(staticbounceaddr);
+ }
+
+ if (staticbounceaddr) {
+ xasprintf(&bounceaddr, "%s%s%s-bounces-%d-%s@%s",
+ staticbounceaddr_localpart, ml->delim, ml->name,
+ index, myrecipient, staticbounceaddr_domain);
+
+ free(staticbounceaddr);
+ free(staticbounceaddr_localpart);
+ } else {
+ xasprintf(&bounceaddr, "%s%sbounces-%d-%s@%s", ml->name,
+ ml->delim, index, myrecipient, ml->fqdn);
+ }
+
+ free(myrecipient);
+
+ return bounceaddr;
+}
+
+int
+get_index_from_filename(const char *filename)
+{
+ char *myfilename, *indexstr;
+ int ret;
+ size_t len;
+
+ myfilename = xstrdup(filename);
+ len = strlen(myfilename);
+ if (len > 9 && (strcmp(myfilename + len - 9, "/mailfile") == 0)) {
+ myfilename[len - 9] = '\0';
+ }
+
+ indexstr = strrchr(myfilename, '/');
+ if (indexstr) {
+ indexstr++; /* skip the slash */
+ } else {
+ indexstr = myfilename;
+ }
+
+ ret = strtoim(indexstr, 0, INT_MAX, NULL);
+ free(myfilename);
+
+ return ret;
+}
+