/* Copyright (C) 1999,2003 by Wolfgang Zekoll 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 #include #include #include #include #include #include #include #include #include #include #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); }