/* $Id: enqmsg.C,v 1.7 2006/02/13 02:53:53 dm Exp $ */
/*
*
* Copyright (C) 2004 David Mazieres (dm@uun.org)
*
* This program 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.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
*/
#include "asmtpd.h"
#include <dirent.h>
str enqmsg_file::spooldir;
vec<const char *> enqmsg_file::mini_env;
str
enqmsg_file::get_spooldir ()
{
static uid_t uid;
static time_t lastcheck;
struct stat sb;
if (!spooldir)
uid = getuid ();
else if (timenow <= lastcheck + 10)
return spooldir;
else if (!lstat (spooldir, &sb) && S_ISDIR (sb.st_mode)
&& sb.st_uid == uid) {
lastcheck = timenow;
return spooldir;
}
char *p = getenv ("TMPDIR");
if (!p)
p = "/var/tmp";
mstr m (strlen (p) + progname.len () + 12);
#if HAVE_MKDTEMP
strcpy (m, p);
strcat (m, "/");
strcat (m, progname);
strcat (m, "XXXXXXXXXX");
errno = 0;
if (!mkdtemp (m.cstr ()))
fatal ("mkdtemp %s failed\n", m.cstr ());
#else /* !HAVE_MKDTEMP */
do {
strcpy (m, p);
strcat (m, "/");
strcat (m, progname);
strcat (m, "XXXXXXXXXX");
errno = 0;
if (!mktemp (m.cstr ()))
fatal ("mktemp %s failed\n", m.cstr ());
errno = 0;
} while (mkdir (m.cstr (), 0700) && errno == EEXIST);
if (errno)
fatal ("could not create spool directory in %s: %m\n", p);
#endif /* !HAVE_MKDTEMP */
m.setlen (strlen (m.cstr ()));
spooldir = m;
lastcheck = timenow;
return spooldir;
}
inline void
preserve (vec<const char *> *e, const char *var)
{
if (const char *p = getenv (var)) {
str s (strbuf ("%s=%s", var, p));
e->push_back (xstrdup (s.cstr ()));
}
}
void
enqmsg_file::init_mini_env ()
{
while (!mini_env.empty ())
xfree ((void *) mini_env.pop_front ());
preserve (&mini_env, "PATH");
preserve (&mini_env, "PWD");
preserve (&mini_env, "TZ");
preserve (&mini_env, "TMPDIR");
preserve (&mini_env, "DMALLOC_OPTIONS");
preserve (&mini_env, "STKTRACE");
mini_env.push_back (xstrdup ("USER=unknown-user"));
mini_env.push_back (NULL);
}
enqmsg_file::enqmsg_file ()
: fd (-1), efd (-1), error (false), eof (false), rps (NULL)
{
}
enqmsg_file::~enqmsg_file ()
{
if (fd >= 0)
close (fd);
if (path)
unlink (path);
if (rps)
runprog_cancel (rps);
}
bool
enqmsg_file::init (str f, const vec<str> &to, str rcvd_line)
{
assert (fd < 0 && !error && !eof && !path);
str tmplate (get_spooldir () << "/msg.XXXXXXXXXX");
mstr tpath (tmplate.len ());
memcpy (tpath.cstr (), tmplate, tmplate.len () + 1);
umask (077);
fd = mkstemp (tpath);
if (fd < 0)
return false;
path = tpath;
close_on_exec (fd);
errno = 0;
if (write (fd, rcvd_line, rcvd_line.len ())
!= implicit_cast<ssize_t> (rcvd_line.len ())) {
if (errno)
warn ("%s: %m\n", path.cstr ());
close (fd);
error = true;
fd = -1;
return false;
}
#if 0
unlink (path);
path = NULL;
#endif
from = f;
top = &to;
return true;
}
void
enqmsg_file::writev (suio *uiop, cbs cb)
{
assert (!eof);
errno = 0;
uiop->output (fd);
if (!uiop->resid ()) {
(*cb) (NULL);
return;
}
eof = true;
if (errno)
(*cb) (strbuf ("451 %m\r\n"));
else
(*cb) (strbuf ("451 local disk write error\r\n"));
}
int
enqmsg_file::getfd ()
{
eof = true;
if (fd >= 0 && lseek (fd, 0, SEEK_SET) != -1)
return fd;
return -1;
}
void
enqmsg_file::commit (cbs cb)
{
assert (fd >= 0 && !error && efd < 0 && !rps);
eof = true;
if (lseek (fd, 0, SEEK_SET) == -1) {
(*cb) (strbuf ("451 %m\r\n"));
return;
}
if (!opt->sendmailfromline) {
char buf[8192];
int n = read (fd, buf, sizeof (buf));
if (n >= 5 && !strncmp (buf, "From ", 5)) {
for (;;) {
char *p = static_cast<char *> (memchr (buf, '\n', n));
if (p) {
if (lseek (fd, p - buf + 1 - n, SEEK_CUR) == -1) {
(*cb) (strbuf ("451 %m\r\n"));
return;
}
break;
}
if ((n = read (fd, buf, sizeof (buf))) < 0) {
(*cb) (strbuf ("451 %m\r\n"));
return;
}
}
}
else if (lseek (fd, 0, SEEK_SET) == -1) {
(*cb) (strbuf ("451 %m\r\n"));
return;
}
}
vec<const char *> av;
for (const str *sp = opt->sendmail.base (); sp < opt->sendmail.lim (); sp++)
av.push_back (*sp);
av.push_back ("-f");
if (from.len ())
av.push_back (from);
else
av.push_back (opt->emptysender);
av.push_back ("--");
for (const str *sp = top->base (); sp < top->lim (); sp++)
av.push_back (*sp);
av.push_back (NULL);
if (mini_env.empty ())
init_mini_env ();
rps = runprog (av[0], av.base (), fd, true,
wrap (this, &enqmsg_file::smcb, av[0], cb),
(opt->sendmailpriv ? NULL
: wrap (become_user, opt->av_user, false)),
mini_env.base ());
}
void
enqmsg_file::smcb (str sm, cbs cb, ref<progout> po)
{
rps = NULL;
if (!po->status) {
(*cb) (NULL);
return;
}
const char *smbn = strrchr (sm, '/');
if (smbn)
smbn++;
else
smbn = sm;
for (u_int i = 0; i < 5 && i < po->output.size (); i++)
warn << smbn << ": " << po->output[i] << "\n";
warn << sm << ": exited with " << exitstr (po->status) << "\n";
if (str resp = po->response (451))
(*cb) (resp);
else
(*cb) (strbuf () << "451 " << smbn << " failed\r\n");
}
EXITFN (cleanup);
static void
cleanup ()
{
if (!enqmsg_file::spooldir)
return;
int olddir = open (".", O_RDONLY);
if (chdir (enqmsg_file::spooldir)) {
warn ("%s: %m\n", enqmsg_file::spooldir.cstr ());
return;
}
DIR *dir = opendir (".");
if (!dir) {
warn ("%s: %m\n", enqmsg_file::spooldir.cstr ());
fchdir (olddir);
close (olddir);
return;
}
while (struct dirent *de = readdir (dir))
if (!strncmp (de->d_name, "msg", 3))
unlink (de->d_name);
closedir (dir);
if (fchdir (olddir) < 0)
chdir ("/");
close (olddir);
if (rmdir (enqmsg_file::spooldir))
warn ("%s: %m\n", enqmsg_file::spooldir.cstr ());
enqmsg_file::spooldir = NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1