/*
Copyright (C) 1999,2003 by Wolfgang Zekoll <wzk@quietsche-entchen.de>
This software 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 of the License, or
(at your option) any later version.
This program 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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include "smtp.h"
#include "acp.h"
#include "lib.h"
static struct _tmpfile {
char filename[200];
struct _tmpfile *next;
} *lasttmp = NULL;
static int count = 0;
static int pid = 0;
static char prefix[200] = "";
static int unlink_existing(char *filename)
{
struct stat sbuf;
if (stat(filename, &sbuf) == 0) {
if (unlink(filename) != 0)
return (1);
}
return (0);
}
static void cleanup(void)
{
struct stat sbuf;
struct _tmpfile *tmp;
if (pid == 0 || pid != getpid())
return;
tmp = lasttmp;
while (tmp != NULL) {
if (stat(tmp->filename, &sbuf) == 0) {
unlink(tmp->filename);
}
/*
* cleanup() is called on program termination
* so we do not need to free the memory.
*/
tmp = tmp->next;
}
return;
}
static int init_tmplist()
{
char *p;
struct _tmpfile *tmp, *next;
if (pid == 0) {
pid = getpid();
atexit(cleanup);
if ((p = getenv("TMPPREFIX")) == NULL || *p == 0)
strcpy(prefix, "smtp-");
else
copy_string(prefix, p, sizeof(prefix));
}
else if (pid != getpid()) {
/*
* we are in a child process - let's erase the parent's
* tempfile list and create our own.
*/
pid = getpid();
count = 0;
tmp = lasttmp;
while (tmp != NULL) {
next = tmp->next;
free(tmp);
tmp = next;
}
lasttmp = NULL;
if (atexit(cleanup) != 0) {
syslog(LOG_NOTICE, "-ERR: can't register cleaup()");
exit (1);
}
}
return (0);
}
char *gettmpfile(char *filename, int size)
{
int fd;
struct _tmpfile *tmp;
init_tmplist();
if ((tmp = malloc(sizeof(struct _tmpfile))) == NULL) {
syslog(LOG_NOTICE, "-ERR: memory allocation error");
exit (1);
}
snprintf (tmp->filename, sizeof(tmp->filename) - 2, "/tmp/%s%05d.%03d.tmp",
prefix, pid, count++);
tmp->next = lasttmp;
lasttmp = tmp;
unlink_existing(tmp->filename);
if ((fd = open(tmp->filename, O_CREAT | O_WRONLY | O_TRUNC, 0600)) < 0) {
syslog(LOG_NOTICE, "-ERR: can't open tmpfile: %s, error= %s", tmp->filename, strerror(errno));
exit (1);
}
close (fd);
if (filename != NULL)
copy_string(filename, tmp->filename, size);
return (tmp->filename);
}
int receivemail(smtp_t *x)
{
char *p, line[2048];
FILE *fp;
if ((fp = fopen(x->spoolfile, "w")) == NULL) {
syslog(LOG_NOTICE, "can't open spoolfile: %s, error= %s", x->spoolfile, strerror(errno));
return (1);
}
while (1) {
if (cfgets(x, line, sizeof(line), x->config->timeout) == NULL) {
syslog(LOG_NOTICE, "client terminated while sending data");
return (-1);
}
p = noctrl(line);
if (*p == '.') {
p++;
if (*p == 0)
break;
}
fprintf (fp, "%s\n", p);
}
fclose (fp);
return (0);
}
static int setvar(smtp_t *x, char *var, char *value)
{
char varname[200];
snprintf (varname, sizeof(varname) - 2, "%s%s", x->config->varname, var);
setenv(varname, value != NULL? value: "", 1);
return (0);
}
static int set_variables(smtp_t *x)
{
int c, i;
char val[200];
setvar(x, "SERVER", x->servername);
setvar(x, "CLIENT", x->ipnum);
setvar(x, "CLIENTNAME", x->client);
setvar(x, "ORIGINALSENDER", x->origsender);
setvar(x, "SENDER", x->sender);
snprintf (val, sizeof(val) - 2, "%d", x->nrcpt);
setvar(x, "NRCPT", val);
setvar(x, "RCPT", x->rcpt.list);
/*
* Export the recipient list as rc/akanga compatible list. Since
* we are in a forked process we can overwrite some data.
*/
for (i=0; (c = x->rcpt.list[i]) != 0; i++) {
if (c == ' ')
x->rcpt.list[i] = '\001';
}
setvar(x, "RCPTV", x->rcpt.list);
return (0);
}
static int set_ccpvars(smtp_t *x)
{
char val[80];
unsigned long size;
struct stat sbuf;
setvar(x, "MAIL", x->spoolfile);
size = 0;
if (stat(x->spoolfile, &sbuf) == 0)
size = sbuf.st_size;
snprintf (val, sizeof(val) - 2, "%ld", size);
setvar(x, "SIZE", val);
return (0);
}
static char *readline(int fd, char *line, int size)
{
int len;
*line = 0;
if ((len = read(fd, line, size - 2)) < 0)
len = 0;
line[len] = 0;
noctrl(line);
close(fd);
return (line);
}
static int getstatus(smtp_t *x, int rc, char *line, int size)
{
int status;
status = atoi(line);
if (status == 0)
*line = 0;
else if (status == 250 || status == 354) {
if (rc != 0)
*line = 0;
}
else if (status >= 400 && status < 600)
/* ok */ ;
else {
/*
* Everything else is an invalid response code.
*/
copy_string(line, "421 server error", size);
return (0);
}
if (*line == 0) {
if (rc == 0)
copy_string(line, "250 ok", size);
else if (rc == 1)
copy_string(line, "451 processing aborted", size);
else
copy_string(line, "554 mail rejected", size);
}
return (0);
}
int run_acp(smtp_t *x, char *line, int size)
{
int rc, pid, pfd[2], lfd[2];
/*
* Do nothing if the acp is not set.
*/
if (*x->config->acp == 0) {
copy_string(line, "250 ok", size);
return (250);
}
rc = 0;
if (pipe(pfd) != 0 || pipe(lfd)) {
syslog(LOG_NOTICE, "-ERR: can't pipe: %m");
exit (1);
}
else if ((pid = fork()) < 0) {
syslog(LOG_NOTICE, "-ERR: can't fork ccp: %m");
exit (1);
}
else if (pid == 0) {
int argc;
char *argv[32];
dup2(pfd[1], 2); /* stderr nach SMTP Client */
close(pfd[0]);
dup2(lfd[1], 1); /* stdout nach syslog */
close(lfd[0]);
close(0);
set_variables(x);
copy_string(line, x->config->acp, size);
argc = split(line, argv, ' ', 30);
argv[argc] = NULL;
execvp(argv[0], argv);
syslog(LOG_NOTICE, "-ERR: can't exec acp %s: %s", argv[0], strerror(errno));
exit (1);
}
else {
/*
* Nicht gebrauchte fd's schliessen.
*/
close(pfd[1]);
close(lfd[1]);
readline(lfd[0], line, size);
if (*line != 0)
syslog(LOG_NOTICE, "%s", line);
readline(pfd[0], line, size);
if (waitpid(pid, &rc, 0) < 0) {
syslog(LOG_NOTICE, "-ERR: error while waiting for ccp: %m");
exit (1);
}
rc = WIFEXITED(rc) != 0? WEXITSTATUS(rc): 1;
getstatus(x, rc, line, size);
}
return (0);
}
int run_ccp(smtp_t *x, char *line, int size)
{
int rc, pid, pfd[2], lfd[2];
/*
* Wenn kein ccp angegeben ist ist alles erlaubt.
*/
if (*x->config->ccp == 0) {
copy_string(line, "250 ok", size);
return (250);
}
rc = 0;
if (pipe(pfd) != 0 || pipe(lfd)) {
syslog(LOG_NOTICE, "-ERR: can't pipe: %m");
exit (1);
}
else if ((pid = fork()) < 0) {
syslog(LOG_NOTICE, "-ERR: can't fork ccp: %m");
exit (1);
}
else if (pid == 0) {
int argc;
char *argv[32];
dup2(pfd[1], 2); /* stderr nach SMTP Client */
close(pfd[0]);
dup2(lfd[1], 1); /* stdout nach syslog */
close(lfd[0]);
close(0);
set_variables(x);
set_ccpvars(x);
copy_string(line, x->config->ccp, size);
argc = split(line, argv, ' ', 30);
argv[argc] = NULL;
execvp(argv[0], argv);
syslog(LOG_NOTICE, "-ERR: can't exec ccp %s: %s", argv[0], strerror(errno));
exit (1);
}
else {
/*
* Nicht gebrauchte fd's schliessen.
*/
close(pfd[1]);
close(lfd[1]);
/*
* syslog Meldung lesen und entsprechende pipe schliessen.
*/
/* *line = 0;
* if ((len = read(lfd[0], line, size - 2)) < 0)
* len = 0;
*
* line[len] = 0;
* noctrl(line);
* close(lfd[0]);
*/
readline(lfd[0], line, size);
if (*line != 0)
syslog(LOG_NOTICE, "%s", line);
/*
* Fehlermeldung lesen, pipe schliessen.
*/
/* *line = 0;
* if ((len = read(pfd[0], line, size - 2)) < 0)
* len = 0;
*
* line[len] = 0;
* noctrl(line);
* close(pfd[0]);
*/
readline(pfd[0], line, size);
/*
* return code holen.
*/
if (waitpid(pid, &rc, 0) < 0) {
syslog(LOG_NOTICE, "-ERR: error while waiting for ccp: %m");
exit (1);
}
rc = WIFEXITED(rc) != 0? WEXITSTATUS(rc): 1;
getstatus(x, rc, line, size);
}
return (0);
}
syntax highlighted by Code2HTML, v. 0.9.1