Logo Search packages:      
Sourcecode: mailavenger version File versions  Download package

enqmsg.C

/* $Id$ */

/*
 *
 * 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;
  }
  const 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 ());
    if (fchdir (olddir) && chdir ("/"))
      warn ("chdir: %m\n");
    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) && chdir ("/"))
    warn ("chdir: %m\n");
  close (olddir);
  if (rmdir (enqmsg_file::spooldir))
    warn ("%s: %m\n", enqmsg_file::spooldir.cstr ());
  enqmsg_file::spooldir = NULL;
}


Generated by  Doxygen 1.6.0   Back to index