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

mailexec.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 "local.h"
#include <ctype.h>
#include <dirent.h>
#include <signal.h>
#include <sys/wait.h>
#include "getopt_long.h"
#include "util/vector.h"

int garbage;

int opt_printfrom = 1;
int opt_printrp = 1;
int opt_newonly;
int opt_verbose;
char *progname;

int cwdfd;
int closeme = -1;
char *deleteme;

static void
cleanup (void)
{
  if (deleteme) {
    unlink (deleteme);
    deleteme = NULL;
  }
  if (closeme >= 0) {
    close (closeme);
    closeme = -1;
  }
}

int
spawnit (pid_t *pidp, char *const *av)
{
  int fds[2];
  pid_t pid;
  int execok[2];
  int err;

  if (pidp)
    *pidp = -1;

  if (pipe (fds) < 0) {
    fprintf (stderr, "%s: pipe: %s\n", progname, strerror (errno));
    return -1;
  }
  if (pipe (execok) < 0) {
    close (fds[0]);
    close (fds[1]);
    fprintf (stderr, "%s: pipe: %s\n", progname, strerror (errno));
    return -1;
  }

  pid = fork ();
  if (pid == -1) {
    fprintf (stderr, "%s: fork: %s\n", progname, strerror (errno));
    close (fds[0]);
    close (fds[1]);
    close (execok[0]);
    close (execok[1]);
    return -1;
  }

  if (!pid) {
    close (fds[1]);
    close (execok[0]);
    fcntl (execok[1], F_SETFD, 1);
    if (fds[0] != 0) {
      if (dup2 (fds[0], 0) < 0) {
      fprintf (stderr, "%s: dup2: %s\n", progname, strerror (errno));
      _exit (1);
      }
      close (fds[0]);
    }
    if (fchdir (cwdfd)) {
      fprintf (stderr, "%s: fchdir: %s\n", progname, strerror (err));
      _exit (1);
    }
    execvp (av[0], av);
    err = errno;
    garbage = write (execok[1], &err, sizeof (err));
    fprintf (stderr, "%s: %s: %s\n", progname, av[0], strerror (err));
    _exit (1);
  }

  close (fds[0]);
  close (execok[1]);

  err = 0;
  garbage = read (execok[0], &err, sizeof (err));
  close (execok[0]);
  if (err) {
    close (fds[1]);
    return -1;
  }

  if (pidp)
    *pidp = pid;
  return fds[1];
}

static int
getfrom (char **rp, char **mf, FILE *msg, char *line, time_t when)
{
  fpos_t pos;
  char buf[8192];
  char datebuf[25];
  char *date = NULL;
  char *from = NULL;
  char *ffrom;
  int size, res;

  if (fgetpos (msg, &pos))
    return -1;
  if (!line && !(line = fgets (buf, sizeof (buf), msg)))
    return -1;

  if (!strncmp (line, "From ", 5)) {
    char *f1 = line + 5, *f2;
    for (f2 = f1; *f2 && !isspace (*f2); f2++)
      ;
    if (f2 > f1) {
      *f2 = '\0';
      from = xstrdup (f1);
      while (*++f2 && isspace (*f2));
      datebuf[24] = '\0';
      date = strncpy (datebuf, f2, 24);
    }

    if (fgetpos (msg, &pos) || !(line = fgets (buf, sizeof (buf), msg))) {
      free (from);
      return -1;
    }
  }

  if (strncasecmp (line, "Return-Path:", 12)) {
    if (fsetpos (msg, &pos)) {
      free (from);
      return -1;
    }
  }
  else if (!from) {
    char *f1 = line + 12, *f2 = line + strlen (line);
    while (isspace (*f1))
      f1++;
    if (*f1++ == '<') {
      while (f2 > f1 && *--f2 != '>')
      ;
      if (f2 > f1) {
      *f2 = '\0';
      from = strcpy (xmalloc (f2 - f1 + 1), f1);
      }
    }
  }

  if (!from)
    from = xstrdup ("");
  if (!date) {
    if (!when)
      time (&when);
    date = ctime (&when);
  }

  if (*from)
    ffrom = from;
  else
    ffrom = "MAILER-DAEMON";

  res = strlen (date);
  if (res > 0 && date[res - 1] == '\n')
    date[res - 1] = '\0';
  size = strlen (ffrom) + strlen (date) + 9;
  *mf = xmalloc (size);
  res = sprintf (*mf, "From %s  %s\n", ffrom, date);
  assert (size == res + 1);

  size = sizeof ("Return-Path: <>\n") + strlen (from);
  *rp = xmalloc (size);
  res = sprintf (*rp, "Return-Path: <%s>\n", from);
  assert (size == res + 1);

  free (from);
  return 0;
}

static int
cmp_maildir_files (const void *_a, const void *_b)
{
  const char *a = *(char **) _a;
  const char *b = *(char **) _b;
  const char *aa, *bb;

  u_long na, nb;
  if (sscanf (a, "%*[^/]/%ld:", &na) != 1
      || sscanf (b, "%*[^/]/%ld:", &nb) != 1)
    return strcmp (a, b);
  if (na != nb)
    return na < nb ? -1 : 1;

  if ((aa = strchr (a, 'M')) && (bb = strchr (b, 'M'))
      && sscanf (aa + 1, "%ld", &na) && sscanf (bb + 1, "%ld", &nb)
      && na != nb)
    return na < nb ? -1 : 1;

  return strcmp (a, b);
}

void
do_maildir_file (const char *path, char *const *av)
{
  struct stat sb;
  u_long mtime = 0;
  FILE *msg;
  char *mf, *rp;
  int pfd;
  pid_t pid;
  char buf[8192];
  int n;
  int err;

  msg = fopen (path, "r");
  if (!msg) {
    fprintf (stderr, "%s: %s: %s\n", progname, path, strerror (errno));
    return;
  }

  if (sscanf (path, "%*[^/]/%ld:", &mtime) != 1
      && !fstat (fileno (msg), &sb))
    mtime = sb.st_mtime;

  if (getfrom (&rp, &mf, msg, NULL, mtime) < 0) {
    fprintf (stderr, "%s: %s: %s\n", progname, path, strerror (errno));
    fclose (msg);
    return;
  }

  pfd = spawnit (&pid, av);
  if (pfd < 0) {
    free (mf);
    free (rp);
    fclose (msg);
    exit (1);
  }

  err = 0;
  if (opt_printfrom && write (pfd, mf, strlen (mf)) < 0)
    err = 1;
  if (!err && opt_printrp && write (pfd, rp, strlen (rp)) < 0)
    err = 1;

  while (!err && (n = fread (buf, 1, sizeof (buf), msg)) > 0
       && write (pfd, buf, n) == n)
    ;
  close (pfd);

  if (waitpid (pid, &n, 0) < 0)
    fprintf (stderr, "%s: waitpid: %s\n", progname, strerror (errno));
  else if (n) {
    if (WIFEXITED (n))
      fprintf (stderr, "%s: %s: %s exited with status %d\n",
             progname, path, av[0], WEXITSTATUS (n));
    else if (WIFSIGNALED (n))
      fprintf (stderr, "%s: %s: %s exited on signal %d\n",
             progname, path, av[0], WTERMSIG (n));
    else
      fprintf (stderr, "%s: %s: %s abnormal exit status %d\n",
             progname, path, av[0], n);
  }

  free (mf);
  free (rp);
  fclose (msg);
}

int
exec_maildir (const char *maildir, char *const *av)
{
  static const char *subdirs[] = { "cur", "new", NULL };
  const char **d;
  VECTOR (char *) mv;
  int i;

  bzero (&mv, sizeof (mv));
  if (chdir (maildir)) {
    fprintf (stderr, "%s: %s: %s\n", progname, maildir, strerror (errno));
    return 1;
  }

  for (d = subdirs + opt_newonly; *d; d++) {
    struct dirent *dep;
    DIR *dp = opendir (*d);
    if (!dp) {
      fprintf (stderr, "%s: %s/%s: %s\n",
             progname, maildir, *d, strerror (errno));
      continue;
    }
    while ((dep = readdir (dp)))
      if (dep->d_name[0] != '.') {
      char *s = xmalloc (strlen (dep->d_name) + strlen (*d) + 2);
      sprintf (s, "%s/%s", *d, dep->d_name);
      VECTOR_PUSH (&mv, s);
      }
    closedir (dp);
  }

  qsort (mv.v_vec, mv.v_size, sizeof (mv.v_vec[0]), cmp_maildir_files);
  for (i = 0; i < mv.v_size; i++) {
    if (opt_verbose) {
      fprintf (stdout, "%s/%s:\n", maildir, mv.v_vec[i]);
      fflush (stdout);
    }
    do_maildir_file (mv.v_vec[i], av);
    free (mv.v_vec[i]);
  }
  VECTOR_CLEAR (&mv);

  return 0;
}

int
exec_mbox (const char *mbox, char *const *av)
{
  FILE *f = NULL, *o;
  char *mf = NULL, *rp = NULL, *line = NULL;
  struct lnbuf buf;

  bzero (&buf, sizeof (buf));

  dotlock (&closeme, &deleteme, mbox, NULL);

  f = fopen (mbox, "r");
  if (!f) {
    fprintf (stderr, "%s: %s: %s\n", progname, mbox, strerror (errno));
    cleanup ();
    return 1;
  }

  while (!getfrom (&rp, &mf, f, line, 0)) {
    int n;
    int blank = 0;
    pid_t pid;
    int msgid = 0;
    int pfd = spawnit (&pid, av);
    if (pfd < 0) {
      free (mf);
      free (rp);
      fclose (f);
      cleanup ();
      return 1;
    }
    o = fdopen (pfd, "w");
    if (!o) {
      fprintf (stderr, "%s: fdopen: %s\n", progname, strerror (errno));
      free (mf);
      free (rp);
      fclose (f);
      close (pfd);
      cleanup ();
      return 1;
    }

    if (opt_printfrom)
      fprintf (o, "%s", mf);
    if (opt_printrp)
      fprintf (o, "%s", rp);

    while ((n = readln (&buf, f, -1)) == LNBUF_OK) {
      char *p;
      if (!strncmp (buf.buf, "From ", 5) && blank) {
      line = buf.buf;
      break;
      }
      if (opt_verbose && !msgid && !strncasecmp (buf.buf, "Message-ID:", 11)) {
      char *f1 = buf.buf + 11, *f2 = buf.buf + buf.size - 1;
      msgid = 1;
      while (*f1 && *f1 != '<')
        f1++;
      while (f2 > f1 && *f2 != '>')
        f2 --;
      if (*f1 == '<' && *f2 == '>') {
        fprintf (stdout, "%.*s:\n", (int) (f2 - f1 + 1), f1);
        fflush (stdout);
      }
      }
      if (blank)
      fprintf (o, "\n");
      if (buf.buf[0] == '\n') {
      blank = 1;
      msgid = 1;
      continue;
      }
      blank = 0;
      for (p = buf.buf; *p == '>'; p++)
      ;
      if (!strncmp (p, "From ", 5))
      fprintf (o, "%s", buf.buf + 1);
      else
      fprintf (o, "%s", buf.buf);
    }
    if (n != LNBUF_OK) {
      if (n == LNBUF_EOFNL)
      fprintf (o, "%s\n", buf.buf);
      line = NULL;
    }
    fclose (o);
    if (waitpid (pid, &n, 0) < 0)
      fprintf (stderr, "%s: waitpid: %s\n", progname, strerror (errno));
    else if (n) {
      if (WIFEXITED (n))
      fprintf (stderr, "%s: %s: %s exited with status %d\n",
             progname, mbox, av[0], WEXITSTATUS (n));
      else if (WIFSIGNALED (n))
      fprintf (stderr, "%s: %s: %s exited on signal %d\n",
             progname, mbox, av[0], WTERMSIG (n));
      else
      fprintf (stderr, "%s: %s: %s abnormal exit status %d\n",
             progname, mbox, av[0], n);
    }

    free (mf);
    free (rp);
    mf = rp = NULL;
  }

  free (mf);
  free (rp);
  fclose (f);
  cleanup ();

  return 0;
}

static void killed (int) __attribute__ ((noreturn));
void
killed (int sig)
{
  cleanup ();
  exit (1);
}

static void usage (void) __attribute__ ((noreturn));
static void
usage (void)
{
  fprintf (stderr, "usage: %s [-nvFR] mailbox cmd [arg ...]\n", progname);
  exit (1);
}

int
main (int argc, char **argv)
{
  struct option o[] = {
    { "version", no_argument, NULL, 'V' },
    { NULL, 0, NULL, 0 }
  };
  int c;
  struct stat sb;
  VECTOR (char *) av;
  struct sigaction sa;

  progname = strrchr (argv[0], '/');
  if (progname)
    progname++;
  else
    progname = argv[0];

  bzero (&sa, sizeof (sa));
  sa.sa_handler = SIG_IGN;
  sigaction (SIGPIPE, &sa, NULL);
  sa.sa_handler = killed;
  sigaction (SIGINT, &sa, NULL);
  sigaction (SIGTERM, &sa, NULL);

  while ((c = getopt_long (argc, argv, "+RFnv", o, NULL)) != -1)
    switch (c) {
    case 'n':
      opt_newonly = 1;
      break;
    case 'v':
      opt_verbose = 1;
      break;
    case 'R':
      opt_printrp = 0;
      break;
    case 'F':
      opt_printfrom = 0;
      break;
    case 'V':
      version (progname, 1);
      exit (0);
      break;
    default:
      usage ();
      break;
    }

  if (optind + 2 > argc)
    usage ();
  bzero (&av, sizeof (av));
  for (c = optind + 1; c < argc; c++)
    VECTOR_PUSH (&av, argv[c]);
  VECTOR_PUSH (&av, NULL);

  if ((cwdfd = open (".", O_RDONLY)) < 0) {
    fprintf (stderr, "%s: \".\": %s\n", progname, strerror (errno));
    exit (1);
  }

  if (stat (argv[optind], &sb) < 0) {
    fprintf (stderr, "%s: %s: %s\n", progname, argv[optind], strerror (errno));
    exit (1);
  }
  if (S_ISDIR (sb.st_mode))
    exit (exec_maildir (argv[optind], av.v_vec));
  else
    exit (exec_mbox (argv[optind], av.v_vec));
}

Generated by  Doxygen 1.6.0   Back to index