ossp-pkg/petidomo/mailer.c
/*
$Source: /v/ossp/cvs/ossp-pkg/petidomo/mailer.c,v $
$Revision: 1.7 $
Copyright (C) 2000 by CyberSolutions GmbH, Germany.
This file is part of Petidomo.
Petidomo is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
Petidomo is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <limits.h>
#include <sys/wait.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include "libtext/text.h"
#include "petidomo.h"
#ifndef ARG_NUM_MAX
# define ARG_NUM_MAX 4096
#endif
#ifndef ARG_MAX
# define ARG_MAX 4096
#endif
static char *
my_strcpy(char * dst, const char * src)
{
while((*dst++ = *src++) != '\0')
;
return dst-1;
}
FILE *
OpenMailer(const char * envelope, const char * recipients[])
{
assert(1==0);
return NULL;
}
FILE *
vOpenMailer(const char * envelope, ...)
{
const struct PD_Config * MasterConfig;
va_list ap;
FILE * fh;
char * cmdline;
char * p;
const char * q;
const char * options;
unsigned int cmdline_len;
MasterConfig = getMasterConfig();
/* Determine the length of the required buffer. */
cmdline_len = strlen(MasterConfig->mta);
cmdline_len += strlen(MasterConfig->mta_options);
cmdline_len += strlen(envelope);
va_start(ap, envelope);
while ((q = va_arg(ap, const char *)) != NULL) {
cmdline_len += strlen(q) + 1;
}
va_end(ap);
cmdline = xmalloc(cmdline_len+8); /* we don't take any risks :) */
/* Copy the mta's path and name into the buffer. */
p = my_strcpy(cmdline, MasterConfig->mta);
*p++ = ' ';
/* Copy the mta's options into the array, while replacing '%s'
with the envelope. */
for (options = MasterConfig->mta_options; *options != '\0'; )
{
if (options[0] == '%' && options[1] == 's')
{
p = my_strcpy(p, envelope);
*p++ = ' ';
options += 2;
break;
}
else
{
*p++ = *options++;
}
}
*p++ = ' ';
/* Append the list of recipients. */
va_start(ap, envelope);
while ((q = va_arg(ap, const char *)) != NULL)
{
p = my_strcpy(p, q);
*p++ = ' ';
}
p[-1] = '\0';
va_end(ap);
fh = popen(cmdline, "w");
if (fh == NULL)
syslog(LOG_ERR, "Failed opening pipe to \"%s\": %s", cmdline, strerror(errno));
free(cmdline);
return fh;
}
int
CloseMailer(FILE * fh)
{
return pclose(fh);
}
static int
my_strlen(const char * p)
{
unsigned int i;
for (i = 0; *p && !isspace((int)*p); p++)
i++;
return i;
}
#define MYPIPE_READ fildes[0]
#define MYPIPE_WRITE fildes[1]
int
ListMail(const char * envelope, const char * listname, const struct Mail * MailStruct)
{
const struct PD_Config * MasterConfig = getMasterConfig();
const struct List_Config * ListConfig = getListConfig(listname);
char ** arguments;
unsigned int arguments_num = 256;
char buffer[256];
char * listfile;
char * nextAddress;
char * currAddress;
char * p;
unsigned int counter;
unsigned int len;
unsigned int address_byte;
unsigned int max_address_byte;
int fildes[2];
pid_t child_pid;
int child_status;
/* Initialize internal stuff. */
arguments = xmalloc((arguments_num+1) * sizeof(char *));
max_address_byte = ARG_MAX - strlen(envelope) - strlen(MasterConfig->mta) -
strlen(MasterConfig->mta_options) - 8;
/* Load the list of recipients. */
listfile = loadfile(ListConfig->address_file);
if (listfile == NULL)
return 1;
/* Now go into delivery loop until we're finished. */
for(counter = 0, currAddress = listfile; *currAddress != '\0'; counter = 0)
{
/* Set up the call to the MTA, including options. */
arguments[counter++] = MasterConfig->mta;
sprintf(buffer, MasterConfig->mta_options, envelope);
for (p = buffer, arguments[counter++] = buffer; *p != '\0'; p++)
{
if (isspace((int)*p))
{
*p++ = '\0';
while(*p != '\0' && isspace((int)*p))
p++;
arguments[counter++] = p;
}
}
if (strlen(arguments[counter-1]) == 0)
counter--;
/* Append as many recipients as fit. */
for (address_byte = 0; *currAddress != '\0' ; currAddress = nextAddress)
{
nextAddress = text_find_next_line(currAddress);
len = my_strlen(currAddress);
if (address_byte + len > max_address_byte)
break;
if (counter > ARG_NUM_MAX)
break;
currAddress[len] = '\0';
address_byte += len;
arguments[counter++] = currAddress;
if (counter+8 >= arguments_num)
{
arguments_num += 256;
arguments = realloc(arguments, (arguments_num+1) * sizeof(char *));
if (arguments == NULL)
return -1;
}
}
/* Deliver the mail. */
arguments[counter++] = NULL;
if (pipe(fildes) == -1)
{
syslog(LOG_ERR, "Couldn't open a pipe to my child process: %s", strerror(errno));
return -1;
}
child_pid = fork();
switch(child_pid)
{
case 0:
/* Child */
close(MYPIPE_WRITE);
if (dup2(MYPIPE_READ, STDIN_FILENO) == -1)
{
syslog(LOG_ERR, "Child process couldn't read from pipe: %s", strerror(errno));
return -1;
}
close(MYPIPE_READ);
execv(MasterConfig->mta, arguments);
syslog(LOG_ERR, "Couldn't exec(\"%s\"): %s", MasterConfig->mta, strerror(errno));
return -1;
case -1:
/* Error */
syslog(LOG_ERR, "Couldn't fork: %s", strerror(errno));
return -1;
default:
/* everything is fine */
close(MYPIPE_READ);
}
write(MYPIPE_WRITE, MailStruct->Header, strlen(MailStruct->Header));
write(MYPIPE_WRITE, "\n", 1);
write(MYPIPE_WRITE, MailStruct->Body, strlen(MailStruct->Body));
if (MailStruct->ListSignature != NULL)
write(MYPIPE_WRITE, MailStruct->ListSignature, strlen(MailStruct->ListSignature));
close(MYPIPE_WRITE);
waitpid(child_pid, &child_status, 0);
if (!(WIFEXITED(child_status) && WEXITSTATUS(child_status) == 0))
{
syslog(LOG_ERR, "The executed mail agent return error %d, aborting.",
WEXITSTATUS(child_status));
return -1;
}
}
return 0;
}