changeset 565:e3a9ca0e9c33

added support for digest text part and digestissue keyword
author mortenp
date Mon, 04 Sep 2006 07:41:02 +1000
parents 8de4095ec6ea
children 015056f30f84
files TUNABLES include/send_digest.h src/Makefile.am src/mlmmj-maintd.c src/send_digest.c
diffstat 5 files changed, 297 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/TUNABLES	Mon Sep 04 07:29:05 2006 +1000
+++ b/TUNABLES	Mon Sep 04 07:41:02 2006 +1000
@@ -161,3 +161,8 @@
 
    This specifies what to use as recipient delimiter for the list.
    Default is "+".
+
+ · nodigesttext			(boolean)
+
+   If this file exists, digest mails won't have a text part with a thread
+   sumary.
--- a/include/send_digest.h	Mon Sep 04 07:29:05 2006 +1000
+++ b/include/send_digest.h	Mon Sep 04 07:41:02 2006 +1000
@@ -25,6 +25,6 @@
 #define SEND_DIGEST_H
 
 int send_digest(const char *listdir, int lastindex, int index,
-		const char *addr, const char *mlmmjsend);
+		int issue, const char *addr, const char *mlmmjsend);
 
 #endif /* SEND_DIGEST_H */
--- a/src/Makefile.am	Mon Sep 04 07:29:05 2006 +1000
+++ b/src/Makefile.am	Mon Sep 04 07:41:02 2006 +1000
@@ -53,7 +53,8 @@
 mlmmj_maintd_SOURCES = mlmmj-maintd.c print-version.c log_error.c mygetline.c \
 		       strgen.c random-int.c chomp.c writen.c memory.c \
 		       ctrlvalue.c send_digest.c getlistaddr.c dumpfd2fd.c \
-		       mylocking.c log_oper.c readn.c getlistdelim.c
+		       mylocking.c log_oper.c readn.c getlistdelim.c \
+ 		       prepstdreply.c statctrl.c gethdrline.c unistr.c
 
 mlmmj_list_SOURCES = mlmmj-list.c strgen.c writen.c print-version.c memory.c \
 		     log_error.c random-int.c readn.c
--- a/src/mlmmj-maintd.c	Mon Sep 04 07:29:05 2006 +1000
+++ b/src/mlmmj-maintd.c	Mon Sep 04 07:41:02 2006 +1000
@@ -749,12 +749,12 @@
 
 int run_digests(const char *listdir, const char *mlmmjsend)
 {
-	char *lasttimestr, *lastindexstr;
+	char *lasttimestr, *lastindexstr, *lastissuestr;
 	char *digestname, *indexname;
 	char *digestintervalstr, *digestmaxmailsstr;
 	char *s1, *s2, *s3;
 	time_t digestinterval, t, lasttime;
-	long digestmaxmails, lastindex, index;
+	long digestmaxmails, lastindex, index, lastissue;
 	int fd, indexfd, lock;
 	size_t lenbuf, lenstr;
 	
@@ -792,9 +792,15 @@
 
 	s1 = mygetline(fd);
 
-	/* Syntax is lastindex:lasttime */
+	/* Syntax is lastindex:lasttime or lastindex:lasttime:lastissue */
 	if (s1 && (lasttimestr = strchr(s1, ':'))) {
 		*(lasttimestr++) = '\0';
+		if ((lastissuestr = strchr(lasttimestr, ':'))) {
+			*(lastissuestr++) = '\0';
+			lastissue = atol(lastissuestr);
+		} else {
+			lastissue = 0;
+		}
 		lasttime = atol(lasttimestr);
 		lastindexstr = s1;
 		lastindex = atol(lastindexstr);
@@ -810,6 +816,7 @@
 		/* If lastdigest is empty, we start from scratch */
 		lasttime = 0;
 		lastindex = 0;
+		lastissue = 0;
 	}
 	
 	indexname = concatstr(2, listdir, "/index");
@@ -843,15 +850,18 @@
 		if (index > lastindex+digestmaxmails)
 			index = lastindex+digestmaxmails;
 
-		send_digest(listdir, lastindex+1, index, NULL, mlmmjsend);
+		if (index > lastindex) {
+			lastissue++;
+			send_digest(listdir, lastindex+1, index, lastissue, NULL, mlmmjsend);
+		}
 
 		if (lseek(fd, 0, SEEK_SET) < 0) {
 			log_error(LOG_ARGS, "Could not seek '%s'", digestname);
 		} else {
-			/* index + ':' + time + '\n' + '\0' */
-			lenbuf = 20 + 1 + 20 + 2;
+			/* index + ':' + time + ':' + issue + '\n' + '\0' */
+			lenbuf = 20 + 1 + 20 + 1 + 20 + 2;
 			s3 = mymalloc(lenbuf);
-			lenstr = snprintf(s3, lenbuf, "%ld:%ld\n", index, (long)t);
+			lenstr = snprintf(s3, lenbuf, "%ld:%ld:%ld\n", index, (long)t, lastissue);
 			if (lenstr >= lenbuf)
 				lenstr = lenbuf - 1;
 			if (writen(fd, s3, lenstr) == -1) {
--- a/src/send_digest.c	Mon Sep 04 07:29:05 2006 +1000
+++ b/src/send_digest.c	Mon Sep 04 07:41:02 2006 +1000
@@ -1,4 +1,4 @@
-/* Copyright (C) 2004 Morten K. Poulsen <morten at afdelingp.dk>
+/* Copyright (C) 2004, 2005 Morten K. Poulsen <morten at afdelingp.dk>
  *
  * $Id$
  *
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#include <ctype.h>
 
 #include "mlmmj.h"
 #include "send_digest.h"
@@ -38,15 +39,151 @@
 #include "getlistaddr.h"
 #include "getlistdelim.h"
 #include "wrappers.h"
+#include "prepstdreply.h"
+#include "mygetline.h"
+#include "gethdrline.h"
+#include "statctrl.h"
+#include "unistr.h"
+
+
+struct mail {
+	int idx;
+	char *from;
+};
+
+struct thread {
+	char *subject;
+	int num_mails;
+	struct mail *mails;
+};
+
+
+static char *thread_list(const char *listdir, int firstindex, int lastindex)
+{
+	int i, j, archivefd, thread_idx;
+	char *ret, *line, *tmp, *subj, *from;
+	char *archivename;
+	int num_threads = 0;
+	struct thread *threads = NULL;
+	char buf[45];
+
+	for (i=firstindex; i<=lastindex; i++) {
+
+		snprintf(buf, sizeof(buf), "%d", i);
+
+		archivename = concatstr(3, listdir, "/archive/", buf);
+		archivefd = open(archivename, O_RDONLY);
+		myfree(archivename);
+
+		if (archivefd < 0)
+			continue;
+
+		subj = NULL;
+		from = NULL;
+
+		while ((line = gethdrline(archivefd))) {
+			if (strcmp(line, "\n") == 0) {
+				myfree(line);
+				break;
+			}
+			if (strncasecmp(line, "Subject: ", 9) == 0) {
+				myfree(subj);
+				subj = unistr_header_to_utf8(line + 9);
+			}
+			if (strncasecmp(line, "From: ", 6) == 0) {
+				myfree(from);
+				from = unistr_header_to_utf8(line + 6);
+			}
+			myfree(line);
+		}
+
+		if (!subj) {
+			subj = mystrdup("no subject");
+		}
+
+		if (!from) {
+			from = mystrdup("anonymous");
+		}
+
+		tmp = subj;
+		for (;;) {
+			if (isspace(*tmp)) {
+				tmp++;
+				continue;
+			}
+			if (strncasecmp(tmp, "Re:", 3) == 0) {
+				tmp += 3;
+				continue;
+			}
+			break;
+		}
+		/* tmp is now the clean subject */
+
+		thread_idx = -1;
+		for (j=0; j<num_threads; j++) {
+			if (strcmp(subj, threads[j].subject) == 0) {
+				thread_idx = j;
+				break;
+			}
+		}
+		if (thread_idx == -1) {
+			num_threads++;
+			threads = myrealloc(threads,
+					num_threads*sizeof(struct thread));
+			threads[num_threads-1].subject = mystrdup(tmp);
+			threads[num_threads-1].num_mails = 0;
+			threads[num_threads-1].mails = NULL;
+			thread_idx = num_threads-1;
+		}
+
+		threads[thread_idx].num_mails++;
+		threads[thread_idx].mails = myrealloc(threads[thread_idx].mails,
+				threads[thread_idx].num_mails*sizeof(struct mail));
+		threads[thread_idx].mails[threads[thread_idx].num_mails-1].idx = i;
+		threads[thread_idx].mails[threads[thread_idx].num_mails-1].from =
+				concatstr(5, "      ", buf, " - ", from, "\n");
+
+		myfree(subj);
+		myfree(from);
+
+		close(archivefd);
+	}
+
+	ret = mystrdup("");
+
+	for (i=0; i<num_threads; i++) {
+
+		tmp = concatstr(3, ret, threads[i].subject, "\n");
+		myfree(ret);
+		ret = tmp;
+		myfree(threads[i].subject);
+
+		for (j=0; j<threads[i].num_mails; j++) {
+			tmp = concatstr(2, ret, threads[i].mails[j].from);
+			myfree(ret);
+			ret = tmp;
+			myfree(threads[i].mails[j].from);
+		}
+		myfree(threads[i].mails);
+
+		tmp = concatstr(2, ret, "\n");
+		myfree(ret);
+		ret = tmp;
+	}
+	myfree(threads);
+
+	return ret;
+}
 
 
 int send_digest(const char *listdir, int firstindex, int lastindex,
-		const char *addr, const char *mlmmjsend)
+		int issue, const char *addr, const char *mlmmjsend)
 {
-	int i, fd, archivefd, status, hdrfd;
+	int i, fd, archivefd, status, hdrfd, txtfd;
 	char buf[45];
-	char *tmp, *queuename = NULL, *archivename, *fromstr;
+	char *tmp, *queuename = NULL, *archivename, *subject, *line = NULL;
 	char *boundary, *listaddr, *listdelim, *listname, *listfqdn;
+	char *subst_data[10];
 	pid_t childpid, pid;
 
 	if (addr) {
@@ -83,50 +220,161 @@
 	listaddr = getlistaddr(listdir);
 	listname = genlistname(listaddr);
 	listfqdn = genlistfqdn(listaddr);
-	myfree(listaddr);
+	listdelim = getlistdelim(listdir);
 	
-	if (lastindex == firstindex) {
-		snprintf(buf, sizeof(buf), " (%d)", firstindex);
-	} else {
-		snprintf(buf, sizeof(buf), " (%d-%d)", firstindex, lastindex);
+	tmp = concatstr(2, listdir, "/text/digest");
+	txtfd = open(tmp, O_RDONLY);
+	myfree(tmp);
+	if (txtfd < 0) {
+		log_error(LOG_ARGS, "Notice: Could not open std mail digest");
 	}
 
-	listdelim = getlistdelim(listdir);
-	fromstr = concatstr(6, "From: ", listname, listdelim, "help@", listfqdn,
-			    "\n");
-	myfree(listdelim);
+	subst_data[0] = "digestfirst";
+	snprintf(buf, sizeof(buf), "%d", firstindex);
+	subst_data[1] = mystrdup(buf);
+
+	subst_data[2] = "digestlast";
+	snprintf(buf, sizeof(buf), "%d", lastindex);
+	subst_data[3] = mystrdup(buf);
+
+	subst_data[4] = "digestinterval";
+	if (lastindex == firstindex) {
+		snprintf(buf, sizeof(buf), "%d", firstindex);
+	} else {
+		snprintf(buf, sizeof(buf), "%d-%d", firstindex, lastindex);
+	}
+	subst_data[5] = mystrdup(buf);
 
-	tmp = concatstr(6, "MIME-Version: 1.0"
+	subst_data[6] = "digestissue";
+	snprintf(buf, sizeof(buf), "%d", issue);
+	subst_data[7] = mystrdup(buf);
+
+	subst_data[8] = "digestthreads";
+	subst_data[9] = thread_list(listdir, firstindex, lastindex);
+
+	if ((txtfd > 0) && (line = mygetline(txtfd)) &&
+			(strncasecmp(line, "Subject: ", 9) == 0)) {
+		subject = substitute(line + 9, listaddr, listdelim,
+				5, subst_data);
+	} else {
+		subject = substitute("Digest of $listaddr$ issue $digestissue$"
+				" ($digestinterval$)\n", listaddr, listdelim,
+				5, subst_data);
+	}
+
+	tmp = concatstr(9, "From: ", listname, listdelim, "help@", listfqdn,
+			   "\nMIME-Version: 1.0"
 			    "\nContent-Type: multipart/" DIGESTMIMETYPE "; "
 			    "boundary=", boundary,
-			    "\nSubject: Digest of ", listname, buf, "\n\n");
+			   "\nSubject: ", subject);
+	/* subject includes a newline */
+
 	myfree(listfqdn);
+	myfree(subject);
 
-	if (writen(fd, fromstr, strlen(fromstr)) < -1)
+	if (writen(fd, tmp, strlen(tmp)) < 0) {
+		myfree(tmp);
 		goto errdighdrs;
+	}
+	myfree(tmp);
 
 	if(hdrfd >= 0 && dumpfd2fd(hdrfd, fd) < 0) {
-		close(hdrfd);
 		goto errdighdrs;
 	}
 
 	close(hdrfd);
+	hdrfd = -1;
 
-	if (writen(fd, tmp, strlen(tmp)) < -1) {
+	if (writen(fd, "\n", 1) < 0) {
 errdighdrs:
 		log_error(LOG_ARGS, "Could not write digest headers to '%s'",
 				queuename);
 		close(fd);
 		unlink(queuename);
 		myfree(boundary);
-		myfree(fromstr);
+		myfree(queuename);
+		myfree(listaddr);
+		myfree(listname);
+		myfree(listdelim);
+		myfree(subst_data[1]);
+		myfree(subst_data[3]);
+		myfree(subst_data[5]);
+		myfree(subst_data[7]);
+		myfree(subst_data[9]);
+		if (txtfd > 0) {
+			close(txtfd);
+			myfree(line);
+		}
+		if (hdrfd > 0) {
+			close(hdrfd);
+		}
+		return -1;
+	}
+
+	if ((txtfd > 0) && !statctrl(listdir, "nodigesttext")) {
+
+		tmp = concatstr(3, "--", boundary,
+				"\nContent-Type: text/plain; charset=UTF-8"
+				"\n\n");
+		if (writen(fd, tmp, strlen(tmp)) == -1) {
+			log_error(LOG_ARGS, "Could not write digest text/plain"
+					" part headers to '%s'", queuename);
+			close(fd);
+			unlink(queuename);
+			myfree(boundary);
 		myfree(tmp);
 		myfree(queuename);
+			myfree(listaddr);
 		myfree(listname);
+			myfree(listdelim);
+			myfree(subst_data[1]);
+			myfree(subst_data[3]);
+			myfree(subst_data[5]);
+			myfree(subst_data[7]);
+			myfree(subst_data[9]);
+			if (txtfd > 0) {
+				close(txtfd);
+				myfree(line);
+			}
 		return -1;
 	}
 	myfree(tmp);
-	myfree(fromstr);
+
+		if (line && (strncasecmp(line, "Subject: ", 9) == 0)) {
+			myfree(line);
+			line = mygetline(txtfd);
+			if (line && (strcmp(line, "\n") == 0)) {
+				/* skip empty line after Subject: */
+				line[0] = '\0';
+			}
+		}
+
+		if (line) {
+			do {
+				tmp = substitute(line, listaddr, listdelim,
+						5, subst_data);
+				myfree(line);
+				if(writen(fd, tmp, strlen(tmp)) < 0) {
+					myfree(tmp);
+					log_error(LOG_ARGS, "Could not write"
+							" std mail");
+					break;
+				}
+				myfree(tmp);
+			} while ((line = mygetline(txtfd)));
+		}
+
+		close(txtfd);
+	}
+
+	myfree(line);
+	myfree(listaddr);
+	myfree(listdelim);
+	myfree(subst_data[1]);
+	myfree(subst_data[3]);
+	myfree(subst_data[5]);
+	myfree(subst_data[7]);
+	myfree(subst_data[9]);
 
 	for (i=firstindex; i<=lastindex; i++) {
 		snprintf(buf, sizeof(buf), "%d", i);