Description: fix FTBFS bug
  Prior to this patch MLMMJ 1.5.0 compiles successfully on all Debian
  architectures but then fails the atf-sh post-compile tests intermittently.
Author: Erwan MAS <erwan@mas.nom.fr>, Baptiste Daroussin <bapt@FreeBSD.org>
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1103308
Bug-Debian: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1104274
Applied-Upstream: yes, the upstream bugfix commits between 1.5.0 and 1.5.2
Last-Updated: 2025-05-31

--- a/src/gethdrline.c
+++ b/src/gethdrline.c
@@ -36,7 +36,7 @@
 char *gethdrline(FILE *f, char **unfolded)
 {
 	char *line = NULL, *retstr = NULL, *oldretstr = NULL, *oldunfolded = NULL;
-	char ch;
+	int ch;
 	size_t linecap = 0;
 
 	if (getline(&line, &linecap, f) <= 0) {
--- a/tests/mlmmj.c
+++ b/tests/mlmmj.c
@@ -257,7 +257,7 @@
 		int s = fakesmtp(mypipe[1]);
 		int c;
 		struct sockaddr_in cl;
-		socklen_t clsize = 0;
+		socklen_t clsize = sizeof(struct sockaddr_in);
 		/*
 		 * Now we can accept incoming connections one
 		 * at a time using accept(2).
@@ -956,6 +956,7 @@
 		    "250 2.1.0 Ok\n", /* MAIL FROM */
 		    "250 2.1.0 Ok\n", /* RCPT TO */
 		    "354 Send message content; end with <CRLF>.<CRLF>\n", /* DATA */
+		    NULL, /* BEFORE DOT */
 		    "250 2.1.0 Ok\n", /* DATA */
 		    "221 2.0.0 Bye\n", /* QUIT */
 		    "250 2.0.0 Ok\n", /* RSET */
@@ -963,7 +964,9 @@
 		dprintf(smtppipe[0], "220 me fake smtp\n");
 		for (uint8_t i = 0; i < NELEM(replies); i++) {
 			atf_utils_readline(smtppipe[0]);
-			dprintf(smtppipe[0], "%s", replies[i]);
+			if ( replies[i] != NULL ) {
+				dprintf(smtppipe[0], "%s", replies[i]);
+			}
 		}
 		exit (0);
 	}
@@ -1005,7 +1008,7 @@
 		int s = fakesmtp(smtppipe[1]);
 		int c;
 		struct sockaddr_in cl;
-		socklen_t clsize = 0;
+		socklen_t clsize = sizeof(struct sockaddr_in);
 		c = accept(s, (struct sockaddr *) &cl, &clsize);
 		if (c == -1)
 			err(5, "accept()");
@@ -1045,7 +1048,7 @@
 		int s = fakesmtp(smtppipe[1]);
 		int c;
 		struct sockaddr_in cl;
-		socklen_t clsize = 0;
+		socklen_t clsize = sizeof(struct sockaddr_in);
 		c = accept(s, (struct sockaddr *) &cl, &clsize);
 		if (c == -1)
 			err(5, "accept()");
@@ -1070,7 +1073,7 @@
 		int s = fakesmtp(smtppipe[1]);
 		int c;
 		struct sockaddr_in cl;
-		socklen_t clsize = 0;
+		socklen_t clsize = sizeof(struct sockaddr_in);
 		c = accept(s, (struct sockaddr *) &cl, &clsize);
 		if (c == -1)
 			err(5, "accept()");
@@ -1097,7 +1100,7 @@
 		int s = fakesmtp(smtppipe[1]);
 		int c;
 		struct sockaddr_in cl;
-		socklen_t clsize = 0;
+		socklen_t clsize = sizeof(struct sockaddr_in);
 		c = accept(s, (struct sockaddr *) &cl, &clsize);
 		if (c == -1)
 			err(5, "accept()");
@@ -1888,20 +1891,26 @@
 	finish_subs_list(s);
 }
 
+static void
+setup_listtext(const atf_tc_t *tc)
+{
+	char *dir = NULL;
+	rmdir("list/text");
+	xasprintf(&dir, "%s/../listtexts/en",
+	    atf_tc_get_config_var(tc, "srcdir"));
+
+	symlink(dir, "list/text");
+}
+
 ATF_TC_BODY(notify_sub, tc)
 {
-	char *dir;
 	const char *path;
 	init_ml(true);
 	struct ml ml;
 	ml_init(&ml);
 	ml.dir = "list";
 	ml_open(&ml, false);
-	rmdir("list/text");
-	xasprintf(&dir, "%s/listtexts/en",
-	    atf_tc_get_config_var(tc, "top_srcdir"));
-
-	symlink(dir, "list/text");
+	setup_listtext(tc);
 	atf_utils_create_file("list/control/smtpport", "25678");
 	atf_utils_create_file("list/control/smtphelo", "heloname");
 	int smtppipe[2];
@@ -2100,7 +2109,7 @@
 		int s = fakesmtp(smtppipe[1]);
 		int c;
 		struct sockaddr_in cl;
-		socklen_t clsize = 0;
+		socklen_t clsize = sizeof(struct sockaddr_in);
 		c = accept(s, (struct sockaddr *) &cl, &clsize);
 		if (c == -1)
 			err(5, "accept()");
@@ -2225,18 +2234,13 @@
 
 ATF_TC_BODY(generate_subscription, tc)
 {
-	char *dir;
 	const char *path;
 	init_ml(true);
 	struct ml ml;
 	ml_init(&ml);
 	ml.dir = "list";
 	ml_open(&ml, false);
-	rmdir("list/text");
-	xasprintf(&dir, "%s/listtexts/en",
-	    atf_tc_get_config_var(tc, "top_srcdir"));
-
-	symlink(dir, "list/text");
+	setup_listtext(tc);
 	atf_utils_create_file("list/control/smtpport", "25678");
 	atf_utils_create_file("list/control/smtphelo", "heloname");
 	int smtppipe[2];
@@ -2273,18 +2277,13 @@
 
 ATF_TC_BODY(generate_subconfirm, tc)
 {
-	char *dir;
 	const char *path;
 	init_ml(true);
 	struct ml ml;
 	ml_init(&ml);
 	ml.dir = "list";
 	ml_open(&ml, false);
-	rmdir("list/text");
-	xasprintf(&dir, "%s/listtexts/en",
-	    atf_tc_get_config_var(tc, "top_srcdir"));
-
-	symlink(dir, "list/text");
+	setup_listtext(tc);
 	atf_utils_create_file("list/control/smtpport", "25678");
 	atf_utils_create_file("list/control/smtphelo", "heloname");
 	int smtppipe[2];
@@ -2429,18 +2428,13 @@
 
 ATF_TC_BODY(send_confirmation_mail, tc)
 {
-	char *dir;
 	const char *path;
 	init_ml(true);
 	struct ml ml;
 	ml_init(&ml);
 	ml.dir = "list";
 	ml_open(&ml, false);
-	rmdir("list/text");
-	xasprintf(&dir, "%s/listtexts/en",
-	    atf_tc_get_config_var(tc, "top_srcdir"));
-
-	symlink(dir, "list/text");
+	setup_listtext(tc);
 	atf_utils_create_file("list/control/smtpport", "25678");
 	atf_utils_create_file("list/control/smtphelo", "heloname");
 	int smtppipe[2];
@@ -2562,17 +2556,12 @@
 
 ATF_TC_BODY(send_help, tc)
 {
-	char *dir;
 	init_ml(true);
 	struct ml ml;
 	ml_init(&ml);
 	ml.dir = "list";
 	ml_open(&ml, false);
-	rmdir("list/text");
-	xasprintf(&dir, "%s/listtexts/en",
-	    atf_tc_get_config_var(tc, "top_srcdir"));
-
-	symlink(dir, "list/text");
+	setup_listtext(tc);
 	atf_utils_create_file("list/control/smtpport", "25678");
 	atf_utils_create_file("list/control/smtphelo", "heloname");
 	atf_utils_create_file("mail", "headers\n\nbody\n");
@@ -2703,6 +2692,8 @@
 	pid_t p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2713,6 +2704,8 @@
 	p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2723,6 +2716,8 @@
 	p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype = 0;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2736,6 +2731,8 @@
 	p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype = 0;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2747,6 +2744,8 @@
 	p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype = 0;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2761,6 +2760,8 @@
 	p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype = 0;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2775,6 +2776,8 @@
 	p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype = 0;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2788,6 +2791,8 @@
 	p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype = 0;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2800,6 +2805,8 @@
 	p = atf_utils_fork();
 	if (p == 0) {
 		struct ml ml;
+		ml_init(&ml);
+		ml.dir = "list";
 		enum subtype subtype = 0;
 		char *addr;
 		ml.fd = open(".", O_DIRECTORY);
@@ -2811,17 +2818,12 @@
 
 ATF_TC_BODY(send_probe, tc)
 {
-	char *dir;
 	init_ml(true);
 	struct ml ml;
 	ml_init(&ml);
 	ml.dir = "list";
 	ml_open(&ml, false);
-	rmdir("list/text");
-	xasprintf(&dir, "%s/listtexts/en",
-	    atf_tc_get_config_var(tc, "top_srcdir"));
-
-	symlink(dir, "list/text");
+	setup_listtext(tc);
 	atf_utils_create_file("list/control/smtpport", "25678");
 	atf_utils_create_file("list/control/smtphelo", "heloname");
 	int smtppipe[2];
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 # Process this file with autoconf to produce a configure script.
 AC_PREREQ([2.69])
-AC_INIT([mlmmj],[1.5.0],[bapt@nours.eu])
+AC_INIT([mlmmj],[1.5.2],[bapt@nours.eu])
 
 VERSION=$PACKAGE_VERSION
 AC_SUBST(VERSION)
--- a/tests/mlmmj-process.in
+++ b/tests/mlmmj-process.in
@@ -77,6 +77,8 @@
 Bcc: list@test
 EOF
 	echo "list@test" > list/control/listaddress
+	# ensure we don't try to really send email to a real mailserver
+	echo "25678" > list/control/smtpport
 	rmdir list/text
 	ln -s ${top_srcdir}/listtexts/en list/text
 	atf_check -s exit:0 $mlmmjprocess -L list -m mailfile
--- a/src/mlmmj-maintd.c
+++ b/src/mlmmj-maintd.c
@@ -50,7 +50,7 @@
 #define log(...) dprintf(logfd, __VA_ARGS__);
 #define opendirat(_dirfd, _fd, _dirent, _path) \
 	do { \
-		_fd = openat(_dirfd, _path, O_DIRECTORY|O_CLOEXEC); \
+		_fd = openat(_dirfd, _path, O_DIRECTORY|O_CLOEXEC|O_RDONLY); \
 		if (_fd == -1 || (_dirent = fdopendir(_fd)) == NULL) { \
 			log(" - Could not open '%s': %s\n", _path, \
 			    strerror(errno)); \
@@ -203,7 +203,7 @@
 			   mail currently being sent */
 			t = time(NULL);
 			if(fstatat(qfd, dp->d_name, &st, 0) == 0) {
-				if(t - st.st_mtime > (time_t)36000) {
+				if(t -  (time_t)36000 > st.st_mtime) {
 					unlinkat(qfd, dp->d_name, 0);
 					/* avoid leaving orphans */
 					unlinkat(qfd, fromname, 0);
@@ -221,7 +221,7 @@
 		/* before we try again, check and see if it's old */
 		bouncelife = ctrltimet(ml->ctrlfd, "bouncelife", BOUNCELIFE);
 		t = time(NULL);
-		if(t - st.st_mtime > bouncelife) {
+		if(t - bouncelife > st.st_mtime ) {
 			if (unlinkat(qfd, dp->d_name, 0) == -1) {
 				log(" - Could not remove queue/%s: %s\n",
 				    dp->d_name, strerror(errno));
@@ -291,7 +291,7 @@
 
 		/* Remove old empty directories */
 		t = time(NULL);
-		if(t - st.st_mtime > (time_t)3600 &&
+		if(t - (time_t)3600 > st.st_mtime &&
 		    unlinkat(fd, dp->d_name, AT_REMOVEDIR) == 0)
 			continue;
 
@@ -792,7 +792,7 @@
 			goto mainsleep;
 		}
 
-		int dfd = open(dirlists, O_DIRECTORY);
+		int dfd = open(dirlists, O_DIRECTORY|O_RDONLY);
 		if (dfd == -1 || (dirp = fdopendir(dfd)) == NULL) {
 			log_err("Could not open the directory containing "
 			    "mailing lists (%s): %s", dirlists,
--- a/tests/mlmmj-maintd.in
+++ b/tests/mlmmj-maintd.in
@@ -737,7 +737,7 @@
 	rm list/requeue/1
 	mkdir list/requeue/1
 	mkdir list/requeue/2
-	touch -t 000101010101 list/requeue/2
+	touch -t 197001010101 list/requeue/2
 	mkdir list/requeue/3
 	cat >> list/requeue/1/subscribers <<EOF
 user1@test1
@@ -917,13 +917,13 @@
 	atf_check -s exit:0 test -f list/queue/file
 	atf_check -s exit:1 test -f list/queue/plop.hey
 
-	touch -t 000101010101 list/queue/file
+	touch -t 197001010101 list/queue/file
 	# no .mailfrom, no.reciptto no.reply-to but recent
 	atf_check $mlmmjmaintd -L list -F
 	atf_check -s exit:1 test -f mail-1.txt
 	atf_check -s exit:1 test -f list/queue/file
 
-	touch -t 000101010101 list/queue/file
+	touch -t 197001010101 list/queue/file
 	echo "bla" > list/queue/file.mailfrom
 	# no.reciptto no.reply-to old recent
 	atf_check $mlmmjmaintd -L list -F
@@ -931,7 +931,7 @@
 	atf_check -s exit:1 test -f list/queue/file
 	atf_check -s exit:1 test -f list/queue/file.mailfrom
 
-	touch -t 000101010101 list/queue/file
+	touch -t 197001010101 list/queue/file
 	echo "bla" > list/queue/file.mailfrom
 	touch list/queue/file.reply-to
 	# no.reciptto no.reply-to old recent
@@ -940,7 +940,7 @@
 	atf_check -s exit:1 test -f list/queue/file.mailfrom
 	atf_check -s exit:1 test -f list/queue/file.reply-to
 
-	touch -t 000101010101 list/queue/file
+	touch -t 197001010101 list/queue/file
 	echo "bla" > list/queue/file.mailfrom
 	echo "meh" > list/queue/file.reciptto
 	touch list/queue/file.reply-to
--- a/tests/mlmmj-bounce.in
+++ b/tests/mlmmj-bounce.in
@@ -89,6 +89,7 @@
 
 basics_6_body()
 {
+	test $(id -u) = 0 && atf_skip "Can only be run as non-root"
 	mlmmjbounce=$(command -v mlmmj-bounce)
 	init_ml ml
 	echo test@mlmmjtest > ml/control/listaddress
--- a/tests/mlmmj_tests.c
+++ b/tests/mlmmj_tests.c
@@ -115,7 +115,7 @@
 		int c;
 		char *r;
 		struct sockaddr_in cl;
-		socklen_t clsize = 0;
+		socklen_t clsize = sizeof(struct sockaddr_in);
 		c = accept(s, (struct sockaddr *) &cl, &clsize);
 		if (c == -1)
 			exit(5);
--- a/tests/fakesmtpd.c
+++ b/tests/fakesmtpd.c
@@ -52,7 +52,7 @@
 {
 	int s, c;
 	FILE *f;
-	socklen_t clsize = 0;
+	socklen_t clsize = sizeof(struct sockaddr_in);
 	struct sockaddr_in me = { 0 }, cl;
 	char *line = NULL;
 	size_t linecap = 0;
@@ -60,17 +60,18 @@
 
 	s = socket(AF_INET, SOCK_STREAM, 0);
 	if (s < 0)
-		exit(1);
-	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &(int) { 1 }, sizeof(int)) != 0)
-		exit(2);
+		err(EXIT_FAILURE, "socket");
+	int opt = 1;
+	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) != 0)
+		err(EXIT_FAILURE, "setsockopt");
 	me.sin_family = AF_INET;
 	me.sin_addr.s_addr = inet_addr("127.0.0.1");
 	/* specific interface */
 	me.sin_port = htons(25678);
 	if (bind(s, (struct sockaddr *) &me, sizeof(me)) == -1)
-		exit(EXIT_FAILURE);
+		err(EXIT_FAILURE, "bin");
 	if (listen(s, 1) == -1)
-		exit(EXIT_FAILURE);
+		err(EXIT_FAILURE, "listen");
 
 	if (daemon(1, 0) != 0)
 		err(EXIT_FAILURE, "daemon");
@@ -82,7 +83,7 @@
 		FILE *fp, *fp2;
 		c = accept(s, (struct sockaddr *) &cl, &clsize);
 		if (c == -1)
-			exit(EXIT_FAILURE);
+			err(EXIT_FAILURE, "accept");
 
 		mailnum++;
 		asprintf(&mail, "mail-%d.txt", mailnum);
--- a/src/mlmmj-list.c
+++ b/src/mlmmj-list.c
@@ -28,6 +28,7 @@
 #include <dirent.h>
 #include <string.h>
 #include <err.h>
+#include <sys/stat.h>
 
 #include "mlmmj.h"
 #include "chomp.h"
@@ -54,11 +55,18 @@
 	FILE *f;
 	char *line = NULL;
 	size_t linecap = 0;
+	struct stat fst;
 
 	f = fdopen(fd, "r");
 	if (f == NULL)
 		return (-1);
 
+	if (fstat(fd, &fst) == 0) {
+		if (S_ISDIR(fst.st_mode)) {
+			return(-1);
+		}
+	}
+
 	while (getline(&line, &linecap, f) > 0) {
 		chomp(line);
 		if (count)
--- a/src/mlmmj-send.c
+++ b/src/mlmmj-send.c
@@ -485,7 +485,7 @@
 		} else {
 			subddirname = "digesters.d";
 		}
-		subdirfd = openat(ml.fd, subddirname, O_DIRECTORY|O_CLOEXEC);
+		subdirfd = openat(ml.fd, subddirname, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
 		if (subdirfd == -1 || (subddir = fdopendir(subdirfd)) == NULL) {
 			log_error(LOG_ARGS, "Could not opendir(%s/%s)", ml.dir,
 					    subddirname);
--- a/src/mlmmj.c
+++ b/src/mlmmj.c
@@ -272,7 +272,7 @@
 		*subdir = dir;
 	if (dir == NULL)
 		return (-1);
-	return (openat(listfd, dir, O_DIRECTORY|O_CLOEXEC));
+	return (openat(listfd, dir, O_DIRECTORY|O_CLOEXEC|O_RDONLY));
 }
 
 bool
--- a/src/send_list.c
+++ b/src/send_list.c
@@ -68,7 +68,7 @@
 	struct subs_list_state *s = (struct subs_list_state *)state;
 	if (s == NULL) return;
 	if (s->dirp != NULL) closedir(s->dirp);
-	s->dfd = openat(s->listfd, s->dirname, O_DIRECTORY|O_CLOEXEC);
+	s->dfd = openat(s->listfd, s->dirname, O_DIRECTORY|O_CLOEXEC|O_RDONLY);
 	if (s->dfd == -1 || (s->dirp = fdopendir(s->dfd)) == NULL)
 		log_error(LOG_ARGS, "Could not opendir(%s);\n", s->dirname);
 	s->used = true;
--- a/src/subscriberfuncs.c
+++ b/src/subscriberfuncs.c
@@ -136,19 +136,19 @@
 	enum subtype typesub = SUB_NONE;
 	int fd;
 
-	fd = openat(listfd, "subscribers.d", O_DIRECTORY);
+	fd = openat(listfd, "subscribers.d", O_DIRECTORY|O_RDONLY);
 	if (fd != -1 && is_subbed_in(fd, "subscribers.d", address)) {
 		if (!both) return SUB_NORMAL;
 		typesub = SUB_NORMAL;
 	}
 
-	fd = openat(listfd, "digesters.d", O_DIRECTORY);
+	fd = openat(listfd, "digesters.d", O_DIRECTORY|O_RDONLY);
 	if (fd != -1 && is_subbed_in(fd, "digesters.d", address)) {
 		if (typesub == SUB_NORMAL) return SUB_BOTH;
 		return SUB_DIGEST;
 	}
 
-	fd = openat(listfd, "nomailsubs.d", O_DIRECTORY);
+	fd = openat(listfd, "nomailsubs.d", O_DIRECTORY|O_RDONLY);
 	if (fd != -1 && is_subbed_in(fd, "nomailsubs.d", address))
 		return SUB_NOMAIL;
 
