#include <strings.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <signal.h>
#include "mlmmj-send.h"
#include "mlmmj.h"
#include "init_sockfd.h"
#include "strgen.h"
#include "log_error.h"
+#include "mygetline.h"
+static int conncount = 0; /* Connection count */
static void print_help(const char *prg)
{
exit(EXIT_SUCCESS);
}
-static char *bounce_from_adr(char *recipient, char *listadr, char *mailfilename)
+char *bounce_from_adr(const char *recipient, const char *listadr,
+ const char *mailfilename)
{
- char *bounce_adr;
- char *indexstr, *listdomain, *a;
+ char *bounceaddr, *myrecipient, *mylistadr;
+ char *indexstr, *listdomain, *a, *mymailfilename;
size_t len;
- indexstr = strrchr(mailfilename, '/');
+ mymailfilename = strdup(mailfilename);
+ if (!mymailfilename) {
+ return NULL;
+ }
+
+ indexstr = strrchr(mymailfilename, '/');
if (indexstr) {
indexstr++; /* skip the slash */
} else {
- indexstr = mailfilename;
+ indexstr = mymailfilename;
}
- recipient = strdup(recipient);
- if (!recipient) {
+ myrecipient = strdup(recipient);
+ if (!myrecipient) {
+ free(mymailfilename);
return NULL;
}
- a = strchr(recipient, '@');
+ a = strchr(myrecipient, '@');
if (a) *a = '=';
- listadr = strdup(listadr);
- if (!listadr) {
- free(recipient);
+ mylistadr = strdup(listadr);
+ if (!mylistadr) {
+ free(mymailfilename);
+ free(myrecipient);
return NULL;
}
- listdomain = strchr(listadr, '@');
+ listdomain = strchr(mylistadr, '@');
if (!listdomain) {
- free(recipient);
- free(listadr);
+ free(mymailfilename);
+ free(myrecipient);
+ free(mylistadr);
+ return NULL;
}
*listdomain++ = '\0';
/* 12 = RECIPDELIM + "bounces-" + "-" + "@" + NUL */
- len = strlen(listadr) + strlen(recipient) + strlen(indexstr)
+ len = strlen(mylistadr) + strlen(myrecipient) + strlen(indexstr)
+ strlen(listdomain) + 12;
- bounce_adr = malloc(len);
- if (!bounce_adr) {
- free(recipient);
- free(listadr);
+ bounceaddr = malloc(len);
+ if (!bounceaddr) {
+ free(myrecipient);
+ free(mylistadr);
return NULL;
}
- snprintf(bounce_adr, len, "%s%cbounces-%s-%s@%s", listadr, RECIPDELIM,
- recipient, indexstr, listdomain);
+ snprintf(bounceaddr, len, "%s%cbounces-%s-%s@%s", mylistadr, RECIPDELIM,
+ myrecipient, indexstr, listdomain);
- free(recipient);
- free(listadr);
- return bounce_adr;
+ free(myrecipient);
+ free(mylistadr);
+ free(mymailfilename);
+
+ return bounceaddr;
}
-int send_mail(int sockfd, const char *from, const char *to, const char *replyto,
- FILE *mailfile)
+int send_mail(int sockfd, const char *from, const char *to,
+ const char *replyto, FILE *mailfile)
{
int retval;
/* FIXME: Queue etc.*/
return retval;
}
- if((checkwait_smtpreply(sockfd, MLMMJ_DATA)) != 0) {
+ if((retval = checkwait_smtpreply(sockfd, MLMMJ_DATA)) != 0) {
log_error(LOG_ARGS, "Mailserver not ready for DATA\n");
write_rset(sockfd);
/* FIXME: Queue etc.*/
return retval;
}
- if((checkwait_smtpreply(sockfd, MLMMJ_DOT)) != 0) {
- log_error(LOG_ARGS, "Mailserver did not acknowledge end of mail\n"
+ if((retval = checkwait_smtpreply(sockfd, MLMMJ_DOT)) != 0) {
+ log_error(LOG_ARGS, "Mailserver did not ack end of mail.\n"
"<CR><LF>.<CR><LF> was written, to no"
"avail\n");
write_rset(sockfd);
return 0;
}
+int initsmtp(int *sockfd, const char *relayhost)
+{
+ int retval = 0;
+
+ init_sockfd(sockfd, relayhost);
+
+ if((retval = checkwait_smtpreply(*sockfd, MLMMJ_CONNECT)) != 0) {
+ log_error(LOG_ARGS, "No proper greeting to our connect\n"
+ "We continue and hope for the best\n");
+ /* FIXME: Queue etc. */
+ }
+ write_helo(*sockfd, relayhost);
+ if((checkwait_smtpreply(*sockfd, MLMMJ_HELO)) != 0) {
+ log_error(LOG_ARGS, "Error with HELO\n"
+ "We continue and hope for the best\n");
+ /* FIXME: quit and tell admin to configure correctly */
+ }
+
+ return retval;
+}
+
+int endsmtp(int *sockfd)
+{
+ int retval = 0;
+
+ write_quit(*sockfd);
+
+ if((retval = checkwait_smtpreply(*sockfd, MLMMJ_QUIT)) != 0) {
+ log_error(LOG_ARGS, "Mailserver would not let us QUIT\n"
+ "We close the socket anyway though\n");
+ }
+
+ close(*sockfd);
+
+ return retval;
+}
+
+int send_mail_many(int sockfd, const char *from, const char *replyto,
+ FILE *mailfile, FILE *subfile, const char *listaddr,
+ const char *archivefilename)
+{
+ char *bounceaddr, *addr;
+
+ while((addr = myfgetline(subfile))) {
+ chomp(addr);
+ if(from)
+ send_mail(sockfd, from, addr, replyto, mailfile);
+ else {
+ bounceaddr = bounce_from_adr(addr, listaddr,
+ archivefilename);
+ send_mail(sockfd, bounceaddr, addr, replyto, mailfile);
+ free(bounceaddr);
+ }
+ free(addr);
+ }
+ return 0;
+}
+
+void sig_child(int sig)
+{
+ pid_t pid;
+ int stat;
+
+ while((pid = waitpid(-1, &stat, WNOHANG) > 0))
+ conncount--;
+}
+
int main(int argc, char **argv)
{
size_t len = 0;
- int sockfd = 0, opt, mindex, retval = 0;
+ int sockfd = 0, opt, mindex;
FILE *subfile = NULL, *mailfile = NULL;
- char *listadr, buf[READ_BUFSIZE];
- char *mailfilename = NULL, *subfilename = NULL, *listdir = NULL;
- char *replyto = NULL, *bounce_adr = NULL, *to_addr = NULL;
- char *bufres, *relayhost = NULL, *archivefilename = NULL;
- char *listctrl = NULL;
- int deletewhensent = 1;
+ char *listaddr, *mailfilename = NULL, *subfilename = NULL;
+ char *replyto = NULL, *bounceaddr = NULL, *to_addr = NULL;
+ char *relayhost = NULL, *archivefilename = NULL;
+ char *listctrl = NULL, *subddirname = NULL, *listdir = NULL;
+ int deletewhensent = 1, *newsockfd;
+ DIR *subddir;
+ struct dirent *dp;
+ pid_t childpid;
+ struct sigaction sigact;
log_set_name(argv[0]);
deletewhensent = 0;
break;
case 'F':
- bounce_adr = optarg;
+ bounceaddr = optarg;
break;
case 'h':
print_help(argv[0]);
/* get the list address */
- if(listctrl[0] == '1' && (bounce_adr == NULL || to_addr == NULL)) {
+ if(listctrl[0] == '1' && (bounceaddr == NULL || to_addr == NULL)) {
fprintf(stderr, "With -l 1 you need -F and -T\n");
exit(EXIT_FAILURE);
}
- if((listctrl[0] == '2' && listdir == NULL)) {
- fprintf(stderr, "With -l 2 you need -L\n");
+ if((listctrl[0] == '2' && (listdir == NULL || bounceaddr == NULL))) {
+ fprintf(stderr, "With -l 2 you need -L and -F\n");
exit(EXIT_FAILURE);
}
if(listctrl[0] != '1' && listctrl[0] != '2')
- listadr = getlistaddr(listdir);
+ listaddr = getlistaddr(listdir);
/* initialize file with mail to send */
exit(EXIT_FAILURE);
}
- if(relayhost)
- init_sockfd(&sockfd, relayhost);
- else
- init_sockfd(&sockfd, RELAYHOST);
-
switch(listctrl[0]) {
case '1': /* A single mail is to be sent, do nothing */
break;
exit(EXIT_SUCCESS);
}
break;
- default: /* normal list mail */
- subfilename = concatstr(2, listdir, "/subscribers");
- if((subfile = fopen(subfilename, "r")) == NULL) {
- log_error(LOG_ARGS, "Could not open '%s':",
- subfilename);
- free(subfilename);
- exit(EXIT_FAILURE);
- }
+ default: /* normal list mail -- now handled when forking */
break;
}
mindex);
}
- if((retval = checkwait_smtpreply(sockfd, MLMMJ_CONNECT)) != 0) {
- log_error(LOG_ARGS, "No proper greeting to our connect\n"
- "We continue and hope for the best\n");
- /* FIXME: Queue etc. */
- }
- write_helo(sockfd, "localhost");
- if((checkwait_smtpreply(sockfd, MLMMJ_HELO)) != 0) {
- log_error(LOG_ARGS, "Error with HELO\n"
- "We continue and hope for the best\n");
- /* FIXME: quit and tell admin to configure correctly */
- }
+ if(!relayhost)
+ relayhost = strdup(RELAYHOST);
- /* FIXME: use myfgetline instead! */
switch(listctrl[0]) {
case '1': /* A single mail is to be sent */
- send_mail(sockfd, bounce_adr, to_addr, replyto, mailfile);
+ initsmtp(&sockfd, relayhost);
+ send_mail(sockfd, bounceaddr, to_addr, replyto, mailfile);
+ endsmtp(&sockfd);
break;
case '2': /* Moderators */
- while((bufres = fgets(buf, READ_BUFSIZE, subfile))) {
- chomp(buf);
- send_mail(sockfd, bounce_adr, buf, 0, mailfile);
- }
+ initsmtp(&sockfd, relayhost);
+ send_mail_many(sockfd, bounceaddr, NULL, mailfile, subfile,
+ NULL, NULL);
+ endsmtp(&sockfd);
break;
default: /* normal list mail */
- while((bufres = fgets(buf, READ_BUFSIZE, subfile))) {
- chomp(buf);
- bounce_adr = bounce_from_adr(buf, listadr,
- archivefilename);
- send_mail(sockfd, bounce_adr, buf, 0, mailfile);
- free(bounce_adr);
+ subddirname = concatstr(2, listdir, "/subscribers.d/");
+ if((subddir = opendir(subddirname)) == NULL) {
+ log_error(LOG_ARGS, "Could not opendir(%s)",
+ subddirname);
+ free(subddirname);
}
- break;
- }
+ free(subddirname);
+
+ sigact.sa_handler = sig_child;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &sigact, 0);
+
+ 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);
+ if((subfile = fopen(subfilename, "r")) == NULL) {
+ log_error(LOG_ARGS, "Could not open '%s'",
+ subfilename);
+ free(subfilename);
+ continue;
+ }
+ fprintf(stderr, "found subfile '%s'\n", subfilename);
+ free(subfilename);
- write_quit(sockfd);
- if((checkwait_smtpreply(sockfd, MLMMJ_QUIT)) != 0) {
- log_error(LOG_ARGS, "Mailserver would not let us QUIT\n"
- "We close the socket anyway though\n");
+ while((conncount > MAX_CONNECTIONS))
+ usleep(100);
+
+ childpid = fork();
+ if(childpid < 0)
+ log_error(LOG_ARGS, "Could not fork.");
+ /* TODO: we have to keep track of unsent
+ * files */
+
+ conncount++;
+
+ if(childpid == 0) {
+ newsockfd = malloc(sizeof(int));
+ initsmtp(newsockfd, relayhost);
+ send_mail_many(*newsockfd, NULL, NULL,
+ mailfile, subfile, listaddr,
+ archivefilename);
+ endsmtp(newsockfd);
+ free(newsockfd);
+ exit(EXIT_SUCCESS);
+ } else {
+ log_error(LOG_ARGS, "%d/%d connections open",
+ conncount, MAX_CONNECTIONS);
+ }
+ }
+ closedir(subddir);
+ break;
}
-
+
if(listctrl[0] != '1' && listctrl[0] != '2') {
/* The mail now goes to the archive */
rename(mailfilename, archivefilename);
fclose(subfile);
free(archivefilename);
- free(subfilename);
} else if(deletewhensent)
unlink(mailfilename);
- close(sockfd);
fclose(mailfile);
return EXIT_SUCCESS;
}