ossp-pkg/petidomo/filter.c
/*
$Source: /v/ossp/cvs/ossp-pkg/petidomo/filter.c,v $
$Revision: 1.5 $
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 <sys/wait.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "petidomo.h"
#define READ 0
#define WRITE 1
#define IO_BLOCKSIZE (4*1024)
enum {
STATE_HEADER,
STATE_SEPERATOR,
STATE_BODY,
STATE_FINISHED
};
int
MailFilter(struct Mail * MailStruct, const char * filter)
{
int child_in[2];
int child_out[2];
pid_t child_pid;
int rc, i;
int write_state;
int byte_read, byte_written;
char * newmail = NULL;
int newmail_size = 0;
char * p = NULL;
assert(MailStruct != NULL);
assert(filter != NULL);
/* Sanity checks. */
if (MailStruct == NULL || filter == NULL)
return 0;
/* Init pipes. */
if (pipe(child_in) == -1) {
syslog(LOG_ERR, "Couldn't open a writing-pipe to my child process: %s", strerror(errno));
return -1;
}
if (pipe(child_out) == -1) {
syslog(LOG_ERR, "Couldn't open a read-pipe from my child process: %s", strerror(errno));
return -1;
}
/* Fork. */
child_pid = fork();
switch(child_pid) {
case 0:
/* Child */
close(child_in[WRITE]);
close(child_out[READ]);
if (dup2(child_in[READ], STDIN_FILENO) == -1) {
syslog(LOG_ERR, "Child process couldn't read from pipe: %s", strerror(errno));
return -1;
}
if (dup2(child_out[WRITE], STDOUT_FILENO) == -1) {
syslog(LOG_ERR, "Child process couldn't read from pipe: %s", strerror(errno));
return -1;
}
close(child_in[READ]);
close(child_out[WRITE]);
execl("/bin/sh", "sh", "-c", filter, NULL);
return -1;
case -1:
/* Error */
close(child_in[READ]);
close(child_in[WRITE]);
close(child_out[READ]);
close(child_out[WRITE]);
syslog(LOG_ERR, "Couldn't fork: %s", strerror(errno));
return -1;
default:
/* everything is fine */
close(child_in[READ]);
close(child_out[WRITE]);
}
/* Switch the pipes into non-blocking mode. */
rc = fcntl(child_in[WRITE], F_GETFL, 0);
if (rc == -1) {
syslog(LOG_ERR, "Couldn't get flags from write-pipe descriptor: %s", strerror(errno));
goto error_exit;
}
rc |= O_NONBLOCK;
rc = fcntl(child_in[WRITE], F_SETFL, rc);
if (rc == -1) {
syslog(LOG_ERR, "Couldn't set flags for write-pipe descriptor: %s", strerror(errno));
goto error_exit;
}
rc = fcntl(child_out[READ], F_GETFL, 0);
if (rc == -1) {
syslog(LOG_ERR, "Couldn't get flags from write-pipe descriptor: %s", strerror(errno));
goto error_exit;
}
rc |= O_NONBLOCK;
rc = fcntl(child_out[READ], F_SETFL, rc);
if (rc == -1) {
syslog(LOG_ERR, "Couldn't set flags for write-pipe descriptor: %s", strerror(errno));
goto error_exit;
}
/* Now write the mail into the pipe and read the result from the
child. This has to happen parallely or we risk that the child
hangs with a blocking i/o. */
write_state = STATE_HEADER;
byte_read = 0;
byte_written = 0;
for (;;) {
/* Write to the pipe. */
switch (write_state) {
case STATE_HEADER:
p = MailStruct->Header;
break;
case STATE_SEPERATOR:
p = "\n";
break;
case STATE_BODY:
p = MailStruct->Body;
break;
case STATE_FINISHED:
p = NULL;
break;
}
if (p != NULL) {
rc = write(child_in[WRITE], p + byte_written,
(strlen(p + byte_written) > IO_BLOCKSIZE) ?
IO_BLOCKSIZE : strlen(p + byte_written));
if (rc >= 0) {
byte_written += rc;
if (p[byte_written] == '\0') {
write_state++; /* new state */
byte_written = 0;
if (write_state == STATE_FINISHED)
close(child_in[WRITE]);
}
}
else if (errno != EAGAIN) {
syslog(LOG_ERR, "Writing to the filter process failed: %s", strerror(errno));
goto error_exit;
}
}
/* Read from the pipe. */
if ((newmail_size - byte_read) <= (IO_BLOCKSIZE)) {
newmail_size += 10*1024;
newmail = realloc(newmail, newmail_size);
if (newmail == NULL) {
syslog(LOG_ERR, "Failed to allocate %d byte of memory: %s", newmail_size, strerror(errno));
goto error_exit;
}
}
rc = read(child_out[READ], newmail + byte_read, IO_BLOCKSIZE);
if (rc > 0)
byte_read += rc;
else if (rc == 0) {
close(child_out[READ]);
break; /* we are finished */
}
else if (errno != EAGAIN) {
syslog(LOG_ERR, "Reading from filter process failed: %s", strerror(errno));
goto error_exit;
}
}
newmail[byte_read] = '\0';
/* Parse mail and put it into the structure. */
for (i = 0; newmail[i] != '\0'; i++) {
if (i > 0 && newmail[i-1] == '\n' && newmail[i] == '\n') {
free(MailStruct->Header);
MailStruct->Header = newmail;
newmail[i] = '\0';
MailStruct->Body = newmail+i+1;
break;
}
}
/* Get returncode. */
waitpid(child_pid, &rc, 0);
if (!WIFEXITED(rc))
return -1;
return WEXITSTATUS(rc);
error_exit:
close(child_in[WRITE]);
close(child_out[READ]);
kill(child_pid, SIGTERM);
return -1;
}