· Regular expression access control
· Functionality to retrieve old posts
· Web-interface
+ · Digests
To use mlmmj, do the following:
If this file is present, the owner(s) will get a mail with the address of
someone sub/unsubscribing to a mailinglist.
+ · digestinterval (normal)
+
+ This file specifies how many seconds will pass before the next digest is
+ sent. Defaults to 604800 seconds, which is 7 days.
+
+ · digestmaxmails (normal)
+
+ This file specifies how many mails can accumulate before digest sending is
+ triggered. Defaults to 50 mails, meaning that if 50 mails arrive to the list
+ before digestinterval have passed, the digest is delivered.
+
· bouncelife (normal)
Here is specified for how long time in seconds an address can bounce before
int clean_nolongerbouncing(const char *listdir);
int probe_bouncers(const char *listdir, const char *mlmmjbounce);
int unsub_bouncers(const char *listdir, const char *mlmmjunsub);
+int run_digests(const char *listdir, const char *mlmmjsend);
/* I know the below is nasty, but it requires C99 to have multiple
* argument macros, and this would then be the only thing needing
#define MLMMJ_SUBSCRIBE_H
void confirm_sub(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmjsend);
+ const char *subaddr, const char *mlmmjsend, int digest);
void generate_subconfirm(const char *listdir, const char *listadr,
- const char *subaddr, const char *mlmmjsend);
+ const char *subaddr, const char *mlmmjsend,
+ int digest);
#endif /* MLMMJ_SUBSCRIBE_H */
#include <sys/types.h>
void confirm_unsub(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmj);
+ const char *subaddr, const char *mlmmj, int digest);
ssize_t unsubscribe(int subreadfd, int subwritefd, const char *address);
void generate_unsubconfirm(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmjsend);
+ const char *subaddr, const char *mlmmjsend,
+ int digest);
#endif /* MLMMJ_UNSUBSCRIBE_H */
#define RELAYHOST "127.0.0.1"
#define READ_BUFSIZE 2048
-#define RECIPDELIM '+'
+#define RECIPDELIM '+' /* XXX Warning: not changable at the moment */
#define MODREQLIFE 604800 /* How long time will moderation requests be kept?
* 604800s is 7 days */
#define DISCARDEDLIFE 604800 /* How long time will discarded mails be kept?
#define MEMORYMAILSIZE 16384 /* How big can a mail be before we don't want to
it in memory? control/memorymailsize */
+#define DIGESTINTERVAL 604800 /* How long do we collect mails for digests
+ * 604800s is 7 days */
+#define DIGESTMAXMAILS 50 /* How many mails can accumulate before we send the
+ * digest */
+#define DIGESTMIMETYPE "digest" /* Which sub-type of multipart to use when
+ * sending digest mails */
struct strlist {
int count;
--- /dev/null
+/* Copyright (C) 2004 Morten K. Poulsen <morten at afdelingp.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.
+ */
+
+#ifndef SEND_DIGEST_H
+#define SEND_DIGEST_H
+
+int send_digest(const char *listdir, int lastindex, int index,
+ const char *addr, const char *mlmmjsend);
+
+#endif /* SEND_DIGEST_H */
--- /dev/null
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+The following address has just subscribed to the digest of the
+mailinglist:
+
+*SUBADDR*
+
--- /dev/null
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+The following address has just unsubscribed from the digest of
+mailinglist:
+
+*SUBADDR*
+
--- /dev/null
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+To confirm you want the address
+
+*SUBADDR*
+
+
+added to the digest of this list, please send a reply to
+
+*CNFADDR*
+
+
+Your mailer probably automatically replies to this address, when you hit
+the reply button.
+
+This confirmation serves two purposes. It tests that mail can be sent to your
+address. Second, it makes sure someone else did not try and subscribe your
+emailaddress.
--- /dev/null
+WELCOME! You have been subscribed to the digest of the
+
+*LSTADDR*
+
+
+mailinglist.
--- /dev/null
+Hi, this is the mlmmj program managing the mailinglist
+
+*LSTADDR*
+
+
+To confirm you want the address
+
+*SUBADDR*
+
+
+removed from the digest of this list, please send a reply to
+
+*CNFADDR*
+
+
+Your mailer probably automatically replies to this address, when you hit
+the reply button.
+
+If you're not subscribed with this list, you will recieve no reply. You can
+see in the From header of a mail to the mailinglist which mail you're sub-
+scribed with.
--- /dev/null
+GOODBYE! You have been removed from the digest of the
+
+*LSTADDR*
+
+
+mailinglist.
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
+ ctrlvalue.c send_digest.c getlistaddr.c dumpfd2fd.c \
+ mylocking.c
#include "memory.h"
enum ctrl_e {
+ CTRL_SUBSCRIBE_DIGEST,
CTRL_SUBSCRIBE,
+ CTRL_CONFSUB_DIGEST,
CTRL_CONFSUB,
+ CTRL_UNSUBSCRIBE_DIGEST,
CTRL_UNSUBSCRIBE,
+ CTRL_CONFUNSUB_DIGEST,
CTRL_CONFUNSUB,
CTRL_BOUNCES,
CTRL_MODERATE,
unsigned int accepts_parameter;
};
-/* must match the enum */
+/* Must match the enum. CAREFUL when using commands that are substrings
+ * of other commands. In that case the longest one have to be listed
+ * first to match correctly. */
static struct ctrl_command ctrl_commands[] = {
- { "subscribe", 0 },
- { "confsub", 1 },
- { "unsubscribe", 0 },
- { "confunsub", 1 },
- { "bounces", 1 },
- { "moderate", 1 },
- { "help", 0 },
- { "get", 1 }
+ { "subscribe-digest", 0 },
+ { "subscribe", 0 },
+ { "confsub-digest", 1 },
+ { "confsub", 1 },
+ { "unsubscribe-digest", 0 },
+ { "unsubscribe", 0 },
+ { "confunsub-digest", 1 },
+ { "confunsub", 1 },
+ { "bounces", 1 },
+ { "moderate", 1 },
+ { "help", 0 },
+ { "get", 1 }
};
" discarding mail");
exit(EXIT_SUCCESS);
}
- myfree(controlstr);
- break;
} else if (!ctrl_commands[ctrl].accepts_parameter &&
(controlstr[cmdlen] == '\0')) {
param = NULL;
- myfree(controlstr);
- break;
} else {
- log_error(LOG_ARGS, "Received a malformed"
- " list control request");
- myfree(controlstr);
- return -1;
+ /* malformed request, ignore and clean up */
+ unlink(mailname);
+ exit(EXIT_SUCCESS);
}
+ myfree(controlstr);
+ break;
+
}
}
switch (ctrl) {
+ /* listname+subscribe-digest@domain.tld */
+ case CTRL_SUBSCRIBE_DIGEST:
+ unlink(mailname);
+ if (closedlist)
+ exit(EXIT_SUCCESS);
+ if (!strchr(fromemails->emaillist[0], '@'))
+ /* Not a valid From: address, silently ignore */
+ exit(EXIT_SUCCESS);
+ execlp(mlmmjsub, mlmmjsub,
+ "-L", listdir,
+ "-a", fromemails->emaillist[0],
+ "-d",
+ "-C", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjsub);
+ exit(EXIT_FAILURE);
+ break;
+
+ /* listname+subscribe@domain.tld */
case CTRL_SUBSCRIBE:
unlink(mailname);
- if (closedlist) exit(EXIT_SUCCESS);
- if(strchr(fromemails->emaillist[0], '@')) {
- execlp(mlmmjsub, mlmmjsub,
- "-L", listdir,
- "-a", fromemails->emaillist[0],
- "-C", NULL);
- log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsub);
- exit(EXIT_FAILURE);
- } else /* Not a valid From: address, so we silently ignore */
+ if (closedlist)
exit(EXIT_SUCCESS);
+ if (!strchr(fromemails->emaillist[0], '@'))
+ /* Not a valid From: address, silently ignore */
+ exit(EXIT_SUCCESS);
+ execlp(mlmmjsub, mlmmjsub,
+ "-L", listdir,
+ "-a", fromemails->emaillist[0],
+ "-C", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjsub);
+ exit(EXIT_FAILURE);
break;
+ /* listname+subconf-digest-COOKIE@domain.tld */
+ case CTRL_CONFSUB_DIGEST:
+ unlink(mailname);
+ if (closedlist)
+ exit(EXIT_SUCCESS);
+ conffilename = concatstr(3, listdir, "/subconf/", param);
+ myfree(param);
+ if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+ /* invalid COOKIE, silently ignore */
+ exit(EXIT_SUCCESS);
+ }
+ tmpstr = mygetline(tmpfd);
+ chomp(tmpstr);
+ close(tmpfd);
+ unlink(conffilename);
+ execlp(mlmmjsub, mlmmjsub,
+ "-L", listdir,
+ "-a", tmpstr,
+ "-d",
+ "-c", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjsub);
+ exit(EXIT_FAILURE);
+ break;
+
+ /* listname+subconf-COOKIE@domain.tld */
case CTRL_CONFSUB:
unlink(mailname);
- if (closedlist) exit(EXIT_SUCCESS);
+ if (closedlist)
+ exit(EXIT_SUCCESS);
conffilename = concatstr(3, listdir, "/subconf/", param);
myfree(param);
- if((tmpfd = open(conffilename, O_RDONLY)) >= 0) {
- tmpstr = mygetline(tmpfd);
- chomp(tmpstr);
- close(tmpfd);
- unlink(conffilename);
- execlp(mlmmjsub, mlmmjsub,
- "-L", listdir,
- "-a", tmpstr,
- "-c", NULL);
- log_error(LOG_ARGS, "execlp() of '%s' failed",
- mlmmjsub);
- exit(EXIT_FAILURE);
- } else /* Not a confirm so silently ignore */
+ if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+ /* invalid COOKIE, silently ignore */
+ exit(EXIT_SUCCESS);
+ }
+ tmpstr = mygetline(tmpfd);
+ chomp(tmpstr);
+ close(tmpfd);
+ unlink(conffilename);
+ execlp(mlmmjsub, mlmmjsub,
+ "-L", listdir,
+ "-a", tmpstr,
+ "-c", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjsub);
+ exit(EXIT_FAILURE);
+ break;
+
+ /* listname+unsubscribe-digest@domain.tld */
+ case CTRL_UNSUBSCRIBE_DIGEST:
+ unlink(mailname);
+ if (closedlist)
exit(EXIT_SUCCESS);
+ if (!strchr(fromemails->emaillist[0], '@'))
+ /* Not a valid From: address, silently ignore */
+ exit(EXIT_SUCCESS);
+ execlp(mlmmjunsub, mlmmjunsub,
+ "-L", listdir,
+ "-a", fromemails->emaillist[0],
+ "-d",
+ "-C", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjunsub);
+ exit(EXIT_FAILURE);
break;
+ /* listname+unsubscribe@domain.tld */
case CTRL_UNSUBSCRIBE:
unlink(mailname);
- if (closedlist) exit(EXIT_SUCCESS);
- if(strchr(fromemails->emaillist[0], '@')) {
- execlp(mlmmjunsub, mlmmjunsub,
- "-L", listdir,
- "-a", fromemails->emaillist[0],
- "-C", NULL);
- log_error(LOG_ARGS, "execlp() of '%s' failed",
- mlmmjunsub);
- exit(EXIT_FAILURE);
- } else /* Not a valid From: address, so we silently ignore */
+ if (closedlist)
+ exit(EXIT_SUCCESS);
+ if (!strchr(fromemails->emaillist[0], '@'))
+ /* Not a valid From: address, silently ignore */
exit(EXIT_SUCCESS);
+ execlp(mlmmjunsub, mlmmjunsub,
+ "-L", listdir,
+ "-a", fromemails->emaillist[0],
+ "-C", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjunsub);
+ exit(EXIT_FAILURE);
break;
+ /* listname+unsubconf-digest-COOKIE@domain.tld */
+ case CTRL_CONFUNSUB_DIGEST:
+ unlink(mailname);
+ if (closedlist)
+ exit(EXIT_SUCCESS);
+ conffilename = concatstr(3, listdir, "/unsubconf/", param);
+ myfree(param);
+ if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+ /* invalid COOKIE, silently ignore */
+ exit(EXIT_SUCCESS);
+ }
+ tmpstr = mygetline(tmpfd);
+ close(tmpfd);
+ chomp(tmpstr);
+ unlink(conffilename);
+ execlp(mlmmjunsub, mlmmjunsub,
+ "-L", listdir,
+ "-a", tmpstr,
+ "-d",
+ "-c", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjunsub);
+ exit(EXIT_FAILURE);
+ break;
+
+ /* listname+unsubconf-COOKIE@domain.tld */
case CTRL_CONFUNSUB:
unlink(mailname);
- if (closedlist) exit(EXIT_SUCCESS);
+ if (closedlist)
+ exit(EXIT_SUCCESS);
conffilename = concatstr(3, listdir, "/unsubconf/", param);
myfree(param);
- if((tmpfd = open(conffilename, O_RDONLY)) >= 0) {
- tmpstr = mygetline(tmpfd);
- close(tmpfd);
- chomp(tmpstr);
- unlink(conffilename);
- execlp(mlmmjunsub, mlmmjunsub,
- "-L", listdir,
- "-a", tmpstr,
- "-c", NULL);
- log_error(LOG_ARGS, "execlp() of '%s' failed",
- mlmmjunsub);
- exit(EXIT_FAILURE);
- } else /* Not a confirm so silently ignore */
+ if((tmpfd = open(conffilename, O_RDONLY)) < 0) {
+ /* invalid COOKIE, silently ignore */
exit(EXIT_SUCCESS);
+ }
+ tmpstr = mygetline(tmpfd);
+ close(tmpfd);
+ chomp(tmpstr);
+ unlink(conffilename);
+ execlp(mlmmjunsub, mlmmjunsub,
+ "-L", listdir,
+ "-a", tmpstr,
+ "-c", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjunsub);
+ exit(EXIT_FAILURE);
break;
+ /* listname+bounces-user=example.tld-INDEX@domain.tld */
case CTRL_BOUNCES:
bouncenr = strrchr(param, '-');
if (!bouncenr) { /* malformed bounce, ignore and clean up */
exit(EXIT_FAILURE);
break;
+ /* listname+moderate-COOKIE@domain.tld */
case CTRL_MODERATE:
/* TODO Add accept/reject parameter to moderate */
unlink(mailname);
if(stat(moderatefilename, &stbuf) < 0) {
myfree(moderatefilename);
exit(EXIT_SUCCESS); /* just exit, no mail to moderate */
- } else {
- execlp(mlmmjsend, mlmmjsend,
- "-L", listdir,
- "-m", moderatefilename, NULL);
- log_error(LOG_ARGS, "execlp() of %s failed", mlmmjsend);
- exit(EXIT_FAILURE);
}
+ execlp(mlmmjsend, mlmmjsend,
+ "-L", listdir,
+ "-m", moderatefilename, NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjsend);
+ exit(EXIT_FAILURE);
break;
+ /* listname+help@domain.tld */
case CTRL_HELP:
unlink(mailname);
if(strchr(fromemails->emaillist[0], '@'))
mlmmjsend);
break;
+ /* listname+get-INDEX@domain.tld */
case CTRL_GET:
- errno = 0;
unlink(mailname);
- if(!param) /* malformed get, ignore silently */
- exit(EXIT_SUCCESS);
- else { /* sanity check--is it all digits? */
- for(c = param; *c != '\0'; c++) {
- if(!isdigit((int)*c))
- exit(EXIT_SUCCESS);
- }
- archivefilename = concatstr(3, listdir, "/archive/",
- param);
- if(stat(archivefilename, &stbuf) < 0)
+ /* sanity check--is it all digits? */
+ for(c = param; *c != '\0'; c++) {
+ if(!isdigit((int)*c))
exit(EXIT_SUCCESS);
- else {
- execlp(mlmmjsend, mlmmjsend,
- "-T", fromemails->emaillist[0],
- "-L", listdir,
- "-l", "6",
- "-m", archivefilename,
- "-a", "-D", NULL);
- log_error(LOG_ARGS, "execlp() of '%s' failed",
- mlmmjsend);
- exit(EXIT_FAILURE);
- }
}
+ archivefilename = concatstr(3, listdir, "/archive/",
+ param);
+ if(stat(archivefilename, &stbuf) < 0)
+ exit(EXIT_SUCCESS);
+ execlp(mlmmjsend, mlmmjsend,
+ "-T", fromemails->emaillist[0],
+ "-L", listdir,
+ "-l", "6",
+ "-m", archivefilename,
+ "-a", "-D", NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed",
+ mlmmjsend);
+ exit(EXIT_FAILURE);
break;
}
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "log_error.h"
#include "../config.h"
#include "wrappers.h"
#include "memory.h"
#include "ctrlvalue.h"
+#include "send_digest.h"
+#include "mylocking.h"
static int maintdlogfd = -1;
return 0;
}
+int run_digests(const char *listdir, const char *mlmmjsend)
+{
+ char *lasttimestr, *lastindexstr;
+ char *digestname, *indexname;
+ char *digestintervalstr, *digestmaxmailsstr;
+ char *s1, *s2, *s3;
+ time_t digestinterval, t, lasttime;
+ long digestmaxmails, lastindex, index;
+ int fd, indexfd, lock;
+ size_t lenbuf, lenstr;
+
+ digestintervalstr = ctrlvalue(listdir, "digestinterval");
+ if (digestintervalstr) {
+ digestinterval = (time_t)atol(digestintervalstr);
+ myfree(digestintervalstr);
+ } else {
+ digestinterval = (time_t)DIGESTINTERVAL;
+ }
+
+ digestmaxmailsstr = ctrlvalue(listdir, "digestmaxmails");
+ if (digestmaxmailsstr) {
+ digestmaxmails = atol(digestmaxmailsstr);
+ myfree(digestmaxmailsstr);
+ } else {
+ digestmaxmails = DIGESTMAXMAILS;
+ }
+
+ digestname = concatstr(2, listdir, "/lastdigest");
+ fd = open(digestname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ log_error(LOG_ARGS, "Could not open '%s'", digestname);
+ myfree(digestname);
+ return 1;
+ }
+
+ lock = myexcllock(fd);
+ if(lock) {
+ log_error(LOG_ARGS, "Error locking lastdigest");
+ myfree(digestname);
+ close(fd);
+ return 1;
+ }
+
+ s1 = mygetline(fd);
+
+ /* Syntax is lastindex:lasttime */
+ if (s1 && (lasttimestr = strchr(s1, ':'))) {
+ *(lasttimestr++) = '\0';
+ lasttime = atol(lasttimestr);
+ lastindexstr = s1;
+ lastindex = atol(lastindexstr);
+ } else {
+ if (s1 && (strlen(s1) > 0)) {
+ log_error(LOG_ARGS, "'%s' contains malformed data",
+ digestname);
+ myfree(digestname);
+ myfree(s1);
+ myunlock(fd);
+ close(fd);
+ return 1;
+ }
+ /* If lastdigest is empty, we start from scratch */
+ lasttime = 0;
+ lastindex = 0;
+ }
+
+ indexname = concatstr(2, listdir, "/index");
+ indexfd = open(indexname, O_RDONLY);
+ if (indexfd < 0) {
+ log_error(LOG_ARGS, "Could not open '%s'", indexname);
+ myfree(digestname);
+ myfree(indexname);
+ myfree(s1);
+ myunlock(fd);
+ close(fd);
+ return 1;
+ }
+ s2 = mygetline(indexfd);
+ close(indexfd);
+ if (!s2) {
+ /* If we don't have an index, no mails have been sent to the
+ * list, and therefore we don't need to send a digest */
+ myfree(digestname);
+ myfree(indexname);
+ myfree(s1);
+ myunlock(fd);
+ close(fd);
+ return 1;
+ }
+
+ index = atol(s2);
+ t = time(NULL);
+
+ if ((t - lasttime >= digestinterval) ||
+ (index - lastindex >= digestmaxmails)) {
+
+ if (index > lastindex+digestmaxmails)
+ index = lastindex+digestmaxmails;
+
+ send_digest(listdir, lastindex+1, index, 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;
+ s3 = mymalloc(lenbuf);
+ lenstr = snprintf(s3, lenbuf, "%ld:%ld\n", index, t);
+ if (lenstr >= lenbuf)
+ lenstr = lenbuf - 1;
+ if (writen(fd, s3, lenstr) == -1) {
+ log_error(LOG_ARGS, "Could not write new '%s'",
+ digestname);
+ }
+ myfree(s3);
+ }
+ }
+
+ myfree(digestname);
+ myfree(indexname);
+ myfree(s1);
+ myfree(s2);
+ myunlock(fd);
+ close(fd);
+
+ return 0;
+}
+
void do_maintenance(const char *listdir, const char *mlmmjsend,
const char *mlmmjbounce, const char *mlmmjunsub)
{
mlmmjbounce, ");\n");
probe_bouncers(listdir, mlmmjbounce);
+ WRITEMAINTLOG6(5, "run_digests(", listdir, ", ", mlmmjsend,
+ ");\n");
+ run_digests(listdir, mlmmjsend);
+
close(maintdlogfd);
logstr = concatstr(3, listdir, "/", MAINTD_LOGFILE);
mkdir -p $LISTDIR
for DIR in incoming queue queue/discarded archive text subconf unsubconf \
- bounce control moderation subscribers.d requeue
+ bounce control moderation subscribers.d digesters.d requeue
do
mkdir "$LISTDIR"/"$DIR"
done
-touch "$LISTDIR"/index
+test -f "$LISTDIR"/index || touch "$LISTDIR"/index
echo -n "The Domain for the List? [] : "
read FQDN
} else
continue;
- if(from)
+ if(from) {
sendres = send_mail(sockfd, from, addr, replyto,
mailmap, mailsize, listdir, NULL,
hdrs, hdrslen, body, bodylen);
- else {
+ } else {
bounceaddr = bounce_from_adr(addr, listaddr,
archivefilename);
sendres = send_mail(sockfd, bounceaddr, addr, replyto,
" '4' means 'send to file with recipients'\n"
" '5' means 'bounceprobe'\n"
" '6' means 'single listmail to single recipient'\n"
+ " '7' means 'digest'\n"
" -L: Full path to list directory\n"
" -m: Full path to mail file\n"
" -r: Relayhost IP address (defaults to 127.0.0.1)\n"
{
size_t len = 0, hdrslen, bodylen;
int sockfd = 0, mailfd = 0, opt, mindex, subfd, tmpfd;
- int deletewhensent = 1, sendres, archive = 1;
+ int deletewhensent = 1, sendres, archive = 1, digest = 0;
char *listaddr, *mailfilename = NULL, *subfilename = NULL;
char *replyto = NULL, *bounceaddr = NULL, *to_addr = NULL;
char *relayhost = NULL, *archivefilename = NULL, *tmpstr;
exit(EXIT_FAILURE);
}
+ if((listctrl[0] == '7' && listdir == NULL)) {
+ fprintf(stderr, "With -l 7 you need -L\n");
+ exit(EXIT_FAILURE);
+ }
+
+
switch(listctrl[0]) {
case '1':
case '2':
case '4':
case '5':
case '6':
+ case '7':
archive = 0;
default:
break;
myfree(tmpstr);
}
break;
+ case '7':
+ digest = 1;
+ archivefilename = mystrdup("digest");
+ /* fall through */
default: /* normal list mail */
- subddirname = concatstr(2, listdir, "/subscribers.d/");
+ if (!digest) {
+ subddirname = concatstr(2, listdir, "/subscribers.d/");
+ } else {
+ subddirname = concatstr(2, listdir, "/digesters.d/");
+ }
if((subddir = opendir(subddirname)) == NULL) {
log_error(LOG_ARGS, "Could not opendir(%s)",
subddirname);
myfree(body);
exit(EXIT_FAILURE);
}
- myfree(subddirname);
while((dp = readdir(subddir)) != NULL) {
if(!strcmp(dp->d_name, "."))
continue;
if(!strcmp(dp->d_name, ".."))
continue;
- subfilename = concatstr(3, listdir, "/subscribers.d/",
- dp->d_name);
+ subfilename = concatstr(2, subddirname, dp->d_name);
if((subfd = open(subfilename, O_RDONLY)) < 0) {
log_error(LOG_ARGS, "Could not open '%s'",
subfilename);
close(subfd);
}
closedir(subddir);
+ myfree(subddirname);
break;
}
#include "memory.h"
void confirm_sub(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmjsend)
+ const char *subaddr, const char *mlmmjsend,
+ int digest)
{
int subtextfd, queuefd;
char *buf, *subtextfilename, *randomstr, *queuefilename = NULL;
char *fromaddr, *listname, *listfqdn, *s1;
- subtextfilename = concatstr(2, listdir, "/text/sub-ok");
+ if (!digest) {
+ subtextfilename = concatstr(2, listdir, "/text/sub-ok");
+ } else {
+ subtextfilename = concatstr(2, listdir, "/text/sub-ok-digest");
+ }
if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
- s1 = concatstr(9, "From: ", listname, "+help@", listfqdn, "\nTo: ",
- subaddr, "\nSubject: Welcome to ", listaddr, "\n\n");
+ if (!digest) {
+ s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+ "\nTo: ", subaddr, "\nSubject: Welcome to ",
+ listaddr, "\n\n");
+ } else {
+ s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+ "\nTo: ", subaddr, "\nSubject: Welcome to "
+ "digest of ", listaddr, "\n\n");
+ }
+
if(writen(queuefd, s1, strlen(s1)) < 0) {
log_error(LOG_ARGS, "Could not write welcome mail");
exit(EXIT_FAILURE);
}
void notify_sub(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmjsend)
+ const char *subaddr, const char *mlmmjsend,
+ int digest)
{
char *maildata[4] = { "*LSTADDR*", NULL, "*SUBADDR*", NULL };
char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
maildata[3] = mystrdup(subaddr);
fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
fromstr = concatstr(3, listname, "+owner@", listfqdn);
- subject = concatstr(2, "New subscription to ", listaddr);
- queuefilename = prepstdreply(listdir, "notifysub", fromstr,
- fromstr, NULL, subject, 2, maildata);
+ if (!digest) {
+ subject = concatstr(2, "New subscription to ", listaddr);
+ queuefilename = prepstdreply(listdir, "notifysub", fromstr,
+ fromstr, NULL, subject, 2,
+ maildata);
+ } else {
+ subject = concatstr(2, "New subscription to digest of ",
+ listaddr);
+ queuefilename = prepstdreply(listdir, "notifysub-digest",
+ fromstr, fromstr, NULL,
+ subject, 2, maildata);
+ }
MY_ASSERT(queuefilename)
myfree(listname);
myfree(listfqdn);
}
void generate_subconfirm(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmjsend)
+ const char *subaddr, const char *mlmmjsend,
+ int digest)
{
int subconffd, subtextfd, queuefd;
char *confirmaddr, *listname, *listfqdn, *confirmfilename = NULL;
close(subconffd);
- confirmaddr = concatstr(5, listname, "+confsub-", randomstr, "@",
- listfqdn);
-
fromaddr = concatstr(5, listname, "+bounces-confsub-", randomstr,
"@", listfqdn);
- myfree(randomstr);
+ if (!digest) {
+ subtextfilename = concatstr(2, listdir, "/text/sub-confirm");
+ confirmaddr = concatstr(5, listname, "+confsub-",
+ randomstr, "@", listfqdn);
+ } else {
+ subtextfilename = concatstr(2, listdir, "/text/sub-confirm-digest");
+ confirmaddr = concatstr(5, listname, "+confsub-digest-",
+ randomstr, "@", listfqdn);
+ }
- subtextfilename = concatstr(2, listdir, "/text/sub-confirm");
+ myfree(randomstr);
if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
exit(EXIT_FAILURE);
}
- s1 = concatstr(9, "From: ", listname, "+help@", listfqdn, "\nTo: ",
- subaddr, "\nSubject: Confirm subscribe to ", listaddr,
- "\n\n");
+ if (!digest) {
+ s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+ "\nTo: ", subaddr, "\nSubject: Confirm subscribe to ",
+ listaddr, "\n\n");
+ } else {
+ s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+ "\nTo: ", subaddr, "\nSubject: Confirm subscribe to "
+ "digest of ", listaddr, "\n\n");
+ }
+
if(writen(queuefd, s1, strlen(s1)) < 0) {
log_error(LOG_ARGS, "Could not write subconffile");
exit(EXIT_FAILURE);
" -a: Email address to subscribe \n"
" -c: Send welcome mail\n"
" -C: Request mail confirmation\n"
+ " -d: Subscribe to digest of list\n"
" -h: This help\n"
" -L: Full path to list directory\n"
" -U: Don't switch to the user id of the listdir owner\n"
char *listaddr, *listdir = NULL, *address = NULL, *subfilename = NULL;
char *mlmmjsend, *bindir, chstr[2];
int subconfirm = 0, confirmsub = 0, opt, subfilefd, lock, notifysub;
- int changeuid = 1, status;
+ int changeuid = 1, status, digest = 0;
size_t len;
off_t suboff;
struct stat st;
mlmmjsend = concatstr(2, bindir, "/mlmmj-send");
myfree(bindir);
- while ((opt = getopt(argc, argv, "hcCVL:a:")) != -1) {
+ while ((opt = getopt(argc, argv, "hcCdVL:a:")) != -1) {
switch(opt) {
case 'a':
address = optarg;
case 'C':
subconfirm = 1;
break;
+ case 'd':
+ digest = 1;
+ break;
case 'h':
print_help(argv[0]);
break;
chstr[0] = address[0];
chstr[1] = '\0';
- subfilename = concatstr(3, listdir, "/subscribers.d/", chstr);
+ if (!digest) {
+ subfilename = concatstr(3, listdir, "/subscribers.d/", chstr);
+ } else {
+ subfilename = concatstr(3, listdir, "/digesters.d/", chstr);
+ }
subfilefd = open(subfilename, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
if(subfilefd == -1) {
if(suboff == -1) {
if(subconfirm)
generate_subconfirm(listdir, listaddr, address,
- mlmmjsend);
+ mlmmjsend, digest);
else {
lseek(subfilefd, 0L, SEEK_END);
len = strlen(address);
if(childpid < 0) {
log_error(LOG_ARGS, "Could not fork");
- confirm_sub(listdir, listaddr, address, mlmmjsend);
+ confirm_sub(listdir, listaddr, address, mlmmjsend,
+ digest);
}
if(childpid > 0) {
/* child confirms subscription */
if(childpid == 0)
- confirm_sub(listdir, listaddr, address, mlmmjsend);
+ confirm_sub(listdir, listaddr, address, mlmmjsend,
+ digest);
}
notifysub = statctrl(listdir, "notifysub");
/* Notify list owner about subscription */
if (notifysub)
- notify_sub(listdir, listaddr, address, mlmmjsend);
+ notify_sub(listdir, listaddr, address, mlmmjsend, digest);
myfree(listaddr);
#include "prepstdreply.h"
void confirm_unsub(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmjsend)
+ const char *subaddr, const char *mlmmjsend, int digest)
{
int subtextfd, queuefd;
char *buf, *subtextfilename, *randomstr, *queuefilename = NULL;
char *fromaddr, *listname, *listfqdn, *s1;
- subtextfilename = concatstr(2, listdir, "/text/unsub-ok");
+ if (!digest) {
+ subtextfilename = concatstr(2, listdir, "/text/unsub-ok");
+ } else {
+ subtextfilename = concatstr(2, listdir,
+ "/text/unsub-ok-digest");
+ }
if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
- s1 = concatstr(9, "From: ", listname, "+help@", listfqdn, "\nTo: ",
- subaddr, "\nSubject: Goodbye from ", listaddr,
- "\n\n");
+ if (!digest) {
+ s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+ "\nTo: ", subaddr, "\nSubject: Goodbye from ",
+ listaddr, "\n\n");
+ } else {
+ s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+ "\nTo: ", subaddr, "\nSubject: Goodbye from "
+ "digest of ", listaddr, "\n\n");
+ }
if(writen(queuefd, s1, strlen(s1)) < 0) {
log_error(LOG_ARGS, "Could not write subconffile");
}
void notify_unsub(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmjsend)
+ const char *subaddr, const char *mlmmjsend, int digest)
{
char *maildata[4] = { "*LSTADDR*", NULL, "*SUBADDR*", NULL };
char *listfqdn, *listname, *fromaddr, *fromstr, *subject;
maildata[3] = mystrdup(subaddr);
fromaddr = concatstr(3, listname, "+bounces-help@", listfqdn);
fromstr = concatstr(3, listname, "+owner@", listfqdn);
- subject = concatstr(2, "Unsubscription from ", listaddr);
- queuefilename = prepstdreply(listdir, "notifyunsub", fromstr,
+ if (!digest) {
+ subject = concatstr(2, "Unsubscription from ", listaddr);
+ queuefilename = prepstdreply(listdir, "notifyunsub", fromstr,
fromstr, NULL, subject, 2, maildata);
+ } else {
+ subject = concatstr(2, "Unsubscription from digest of ",
+ listaddr);
+ queuefilename = prepstdreply(listdir, "notifyunsub-digest",
+ fromstr, fromstr, NULL, subject, 2,
+ maildata);
+ }
MY_ASSERT(queuefilename)
myfree(listname);
myfree(listfqdn);
void generate_unsubconfirm(const char *listdir, const char *listaddr,
- const char *subaddr, const char *mlmmjsend)
+ const char *subaddr, const char *mlmmjsend,
+ int digest)
{
char *confirmaddr, *buf, *listname, *listfqdn;
char *subtextfilename, *queuefilename = NULL, *fromaddr, *s1;
close(subconffd);
- confirmaddr = concatstr(5, listname, "+confunsub-", randomstr, "@",
- listfqdn);
fromaddr = concatstr(5, listname, "+bounces-confunsub-", randomstr,
"@", listfqdn);
- myfree(randomstr);
- subtextfilename = concatstr(2, listdir, "/text/unsub-confirm");
+ if (!digest) {
+ subtextfilename = concatstr(2, listdir, "/text/unsub-confirm");
+ confirmaddr = concatstr(5, listname, "+confunsub-", randomstr,
+ "@", listfqdn);
+ } else {
+ subtextfilename = concatstr(2, listdir,
+ "/text/unsub-confirm-digest");
+ confirmaddr = concatstr(5, listname, "+confunsub-digest-",
+ randomstr, "@", listfqdn);
+ }
+
+ myfree(randomstr);
if((subtextfd = open(subtextfilename, O_RDONLY)) < 0) {
log_error(LOG_ARGS, "Could not open '%s'", subtextfilename);
exit(EXIT_FAILURE);
}
- s1 = concatstr(9, "From: ", listname, "+help@", listfqdn, "\nTo: ",
- subaddr, "\nSubject: Confirm unsubscribe from ",
- listaddr, "\n\n");
+ if (!digest) {
+ s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+ "\nTo: ", subaddr, "\nSubject: Confirm "
+ "unsubscribe from ", listaddr, "\n\n");
+ } else {
+ s1 = concatstr(9, "From: ", listname, "+help@", listfqdn,
+ "\nTo: ", subaddr, "\nSubject: Confirm "
+ "unsubscribe from digest of ", listaddr,
+ "\n\n");
+ }
if(writen(queuefd, s1, strlen(s1)) < 0) {
log_error(LOG_ARGS, "Could not write subconffile");
" -a: Email address to unsubscribe \n"
" -c: Send goodbye mail\n"
" -C: Request mail confirmation\n"
+ " -d: Subscribe to digest of list\n"
" -h: This help\n"
" -L: Full path to list directory\n"
" -V: Print version\n"
int main(int argc, char **argv)
{
int subread, subwrite, rlock, wlock, opt, unsubres, status;
- int confirmunsub = 0, unsubconfirm = 0, notifysub = 0;
+ int confirmunsub = 0, unsubconfirm = 0, notifysub = 0, digest = 0;
char *listaddr, *listdir = NULL, *address = NULL, *subreadname = NULL;
char *subwritename, *mlmmjsend, *bindir;
char *subddirname;
mlmmjsend = concatstr(2, bindir, "/mlmmj-send");
myfree(bindir);
- while ((opt = getopt(argc, argv, "hcCVL:a:")) != -1) {
+ while ((opt = getopt(argc, argv, "hcCdVL:a:")) != -1) {
switch(opt) {
case 'L':
listdir = optarg;
case 'C':
unsubconfirm = 1;
break;
+ case 'd':
+ digest = 1;
+ break;
case 'h':
print_help(argv[0]);
break;
listaddr = getlistaddr(listdir);
if(unsubconfirm)
- generate_unsubconfirm(listdir, listaddr, address, mlmmjsend);
+ generate_unsubconfirm(listdir, listaddr, address, mlmmjsend,
+ digest);
- subddirname = concatstr(2, listdir, "/subscribers.d/");
+ if (!digest) {
+ subddirname = concatstr(2, listdir, "/subscribers.d/");
+ } else {
+ subddirname = concatstr(2, listdir, "/digesters.d/");
+ }
if((subddir = opendir(subddirname)) == NULL) {
log_error(LOG_ARGS, "Could not opendir(%s)",
subddirname);
continue;
if(!strcmp(dp->d_name, ".."))
continue;
- subreadname = concatstr(3, listdir, "/subscribers.d/",
+ if (!digest) {
+ subreadname = concatstr(3, listdir, "/subscribers.d/",
dp->d_name);
+ } else {
+ subreadname = concatstr(3, listdir, "/digesters.d/",
+ dp->d_name);
+ }
subread = open(subreadname, O_RDWR);
if(subread == -1) {
if(childpid < 0) {
log_error(LOG_ARGS, "Could not fork");
confirm_unsub(listdir, listaddr, address,
- mlmmjsend);
+ mlmmjsend, digest);
}
if(childpid > 0) {
/* child confirms subscription */
if(childpid == 0)
confirm_unsub(listdir, listaddr, address,
- mlmmjsend);
+ mlmmjsend, digest);
}
}
/* Notify list owner about subscription */
if (notifysub)
- notify_unsub(listdir, listaddr, address, mlmmjsend);
+ notify_unsub(listdir, listaddr, address, mlmmjsend, digest);
myfree(listaddr);
--- /dev/null
+/* Copyright (C) 2004 Morten K. Poulsen <morten at afdelingp.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 <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "mlmmj.h"
+#include "send_digest.h"
+#include "log_error.h"
+#include "strgen.h"
+#include "memory.h"
+#include "getlistaddr.h"
+#include "wrappers.h"
+
+
+int send_digest(const char *listdir, int firstindex, int lastindex,
+ const char *addr, const char *mlmmjsend)
+{
+ int i;
+ int fd, archivefd;
+ char buf[45];
+ char *tmp, *queuename = NULL, *archivename;
+ char *boundary, *listaddr, *listname, *listfqdn;
+ pid_t childpid, pid;
+ int status;
+
+ if (addr) {
+ errno = 0;
+ log_error(LOG_ARGS, "send_digest() does not support sending "
+ "digest mails to only one recipient yet");
+ return -1;
+ }
+
+ if (firstindex > lastindex)
+ return -1;
+
+ do {
+ tmp = random_str();
+ myfree(queuename);
+ queuename = concatstr(3, listdir, "/queue/", tmp);
+ myfree(tmp);
+ fd = open(queuename, O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
+ } while ((fd < 0) && (errno == EEXIST));
+
+ if (fd < 0) {
+ log_error(LOG_ARGS, "Could not open digest queue file '%s'",
+ queuename);
+ myfree(queuename);
+ return -1;
+ }
+
+ boundary = random_str();
+
+ listaddr = getlistaddr(listdir);
+ listname = genlistname(listaddr);
+ listfqdn = genlistfqdn(listaddr);
+ myfree(listaddr);
+
+ if (lastindex == firstindex) {
+ snprintf(buf, sizeof(buf), " (%d)", firstindex);
+ } else {
+ snprintf(buf, sizeof(buf), " (%d-%d)", firstindex, lastindex);
+ }
+ tmp = concatstr(10, "From: ", listname, "+help@", listfqdn,
+ "\nMIME-Version: 1.0"
+ "\nContent-Type: multipart/" DIGESTMIMETYPE "; "
+ "boundary=", boundary,
+ "\nSubject: Digest of ", listname, buf,
+ "\n\n");
+ myfree(listfqdn);
+
+ if (writen(fd, tmp, strlen(tmp)) == -1) {
+ log_error(LOG_ARGS, "Could not write digest headers to '%s'",
+ queuename);
+ close(fd);
+ unlink(queuename);
+ myfree(boundary);
+ myfree(tmp);
+ myfree(queuename);
+ myfree(listname);
+ return -1;
+ }
+ myfree(tmp);
+
+ 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;
+
+ tmp = concatstr(7, "--", boundary,
+ "\nContent-Type: message/rfc822"
+ "\nContent-Disposition: inline; filename=\"",
+ listname, "_", buf, ".eml\""
+ "\n\n");
+ if (writen(fd, tmp, strlen(tmp)) == -1) {
+ log_error(LOG_ARGS, "Could not write digest part "
+ "headers for archive index %d to "
+ "'%s'", i, queuename);
+ close(fd);
+ close(archivefd);
+ unlink(queuename);
+ myfree(boundary);
+ myfree(tmp);
+ myfree(queuename);
+ myfree(listname);
+ return -1;
+ }
+ myfree(tmp);
+
+ if (dumpfd2fd(archivefd, fd) < 0) {
+ log_error(LOG_ARGS, "Could not write digest part %d "
+ "to '%s'", i,
+ queuename);
+ close(fd);
+ close(archivefd);
+ unlink(queuename);
+ myfree(boundary);
+ myfree(queuename);
+ myfree(listname);
+ return -1;
+ }
+
+ close(archivefd);
+ }
+
+ tmp = concatstr(3, "--", boundary, "--\n");
+ if (writen(fd, tmp, strlen(tmp)) == -1) {
+ log_error(LOG_ARGS, "Could not write digest end to '%s'",
+ queuename);
+ close(fd);
+ unlink(queuename);
+ myfree(boundary);
+ myfree(queuename);
+ myfree(listname);
+ return -1;
+ }
+
+ close(fd);
+ myfree(boundary);
+ myfree(listname);
+
+ childpid = fork();
+
+ if(childpid < 0) {
+ log_error(LOG_ARGS, "Could not fork");
+ myfree(queuename);
+ return -1;
+ }
+
+ if(childpid > 0) {
+ do /* Parent waits for the child */
+ pid = waitpid(childpid, &status, 0);
+ while(pid == -1 && errno == EINTR);
+ } else {
+ execlp(mlmmjsend, mlmmjsend,
+ "-l", "7",
+ "-L", listdir,
+ "-m", queuename,
+ NULL);
+ log_error(LOG_ARGS, "execlp() of '%s' failed", mlmmjsend);
+ exit(EXIT_FAILURE); /* It is OK to exit, as this is a child */
+ }
+
+ unlink(queuename);
+ myfree(queuename);
+
+ return 0;
+}
return (off_t)-1;
}
-int is_subbed(const char *listdir, const char *address)
+static int is_subbed_in(const char *subddirname, const char *address)
{
int retval = 1, subread;
- char *subddirname, *subreadname;
+ char *subreadname;
off_t suboff;
DIR *subddir;
struct dirent *dp;
- subddirname = concatstr(2, listdir, "/subscribers.d/");
if((subddir = opendir(subddirname)) == NULL) {
log_error(LOG_ARGS, "Could not opendir(%s)", subddirname);
- myfree(subddirname);
exit(EXIT_FAILURE);
}
- myfree(subddirname);
-
while((dp = readdir(subddir)) != NULL) {
if(!strcmp(dp->d_name, "."))
continue;
if(!strcmp(dp->d_name, ".."))
continue;
- subreadname = concatstr(3, listdir, "/subscribers.d/",
- dp->d_name);
+ subreadname = concatstr(2, subddirname, dp->d_name);
subread = open(subreadname, O_RDONLY);
if(subread < 0) {
log_error(LOG_ARGS, "Could not open '%s'",
return retval;
}
+
+int is_subbed(const char *listdir, const char *address)
+{
+ int retval;
+ char *subddirname;
+
+ subddirname = concatstr(2, listdir, "/subscribers.d/");
+ retval = is_subbed_in(subddirname, address);
+ myfree(subddirname);
+ if (retval == 0)
+ return 0;
+
+ subddirname = concatstr(2, listdir, "/digesters.d/");
+ retval = is_subbed_in(subddirname, address);
+ myfree(subddirname);
+ if (retval == 0)
+ return 0;
+
+ return 1;
+}