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

mailbox.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"

int garbage;

static int
calc_frompos (int frompos, const char *buf, size_t size)
{
  int endpos;
  int i;

  if (size == 0)
    return frompos;

  switch (buf[size - 1]) {
  case '>':
    for (i = size - 2;; i--) {
      if (i < 0)
      return frompos == 0 ? 0 : -1;
      if (buf[i] == '\n')
      return 0;
      if (buf[i] != '>')
      return -1;
    }
  case 'F':
    endpos = 1;
    break;
  case 'r':
    endpos = 2;
    break;
  case 'o':
    endpos = 3;
    break;
  case 'm':
    endpos = 4;
    break;
  case ' ':
    endpos = 5;
    break;
  case '\n':
    return 0;
  default:
    return -1;
  }

  if (size <= (unsigned) endpos) {
    if (frompos < 0 || frompos + endpos != 5)
      return -1;
    if (strncmp ("From " + 5 - size, buf, size))
      return -1;
    return frompos + size;
  }

  if (strncmp ("From ", buf + size - endpos, endpos))
    return -1;
  i = size - endpos;
  while (i-- > 0) {
    if (buf[i] == '\n')
      return endpos;
    if (buf[i] != '>')
      return -1;
  }
  return endpos;
}

static int
read_esc_from (int rfd, off_t *offset, char *buf, size_t size,
             int *frompos, int *eofp)
{
  int shift = *frompos > 0 ? *frompos : 0;
  int n;

  assert (size > 5);
  if (shift > 0)
    strncpy (buf, "From ", size);
  if (offset)
    n = pread (rfd, buf + shift, size - shift, *offset);
  else
    n = read (rfd, buf + shift, size - shift);
  if (n <= 0) {
    *eofp = 1;
    return n < 0 ? -1 : n + shift;
  }
  *eofp = 0;
  if (offset)
    *offset += n;
  n += shift;
  *frompos = calc_frompos (*frompos, buf, n);
  if (*frompos > 0)
    n -= *frompos;
  assert (n >= 0);
  return n;
}

static int
write_esc_from (int dfd, const char *buf, size_t size, int frompos)
{
  const char *s = buf;
  const char *e = buf + size;
  const char *p;

  p = s;
  if (frompos != 0)
    goto nextln;

  for (;;) {
    if (p > s && write (dfd, s, p - s) != p - s)
      return -1;
    if (p == e)
      return 0;
    s = p;

    while (p < e && *p == '>')
      p++;
    if (p + 5 <= e && !strncmp (p, "From ", 5)) {
      if (write (dfd, ">", 1) != 1)
      return -1;
      p += 5;
    }

  nextln:
    if ((p = memchr (p, '\n', e - p)))
      p++;
    else
      p = e;
  }
}

static int
copy_msg (int dfd, int mfd, int uf, int use_readp, int esc_from)
{
  int n = 0;
  char buf[8192];
  int frompos = 0;
  int eof = 0;
  int eol = 1;
  off_t pos = 0;

  if (uf) {
    n = strlen (msg_ufline);
    if (write (dfd, msg_ufline, n) != n || write (dfd, "\n", 1) != 1)
      return -1;
  }

  n = strlen (msg_rpline);
  if (write (dfd, msg_rpline, n) != n || write (dfd, "\n", 1) != 1)
    return -1;

  while (!eof) {
    n = read_esc_from (mfd, use_readp ? &pos : 0,
                   buf, sizeof (buf), &frompos, &eof);
    if (n > 0) {
      eol = buf[n - 1] == '\n';
      if (esc_from)
      n = write_esc_from (dfd, buf, n, frompos);
      else
      n = write (dfd, buf, n);
      if (n < 0)
      eof = 1;
    }
  }
  if (frompos > 0 && n >= 0) {
    n = write (dfd, "From ", frompos);
    eol = 0;
  }
  if (n >= 0 && !eol)
    n = write (dfd, "\n", 1);
  if (n >= 0 && esc_from)
    n = write (dfd, "\n", 1);
  if (n >= 0)
    return fsync (dfd);
  return -1;
}

int
deliver_mbox (const char *path, int mfd, int use_readp)
{
  int dfd;
  off_t msgstart = -1;
  int n;
  int dump = !strcmp (path, "-");

  if (dump) {
    dfd = 1;
  }
  else {
    if ((n = dotlock (&dfd, &deleteme, path, NULL)))
      return n;

    if (lseek (dfd, 0, SEEK_END) == -1
      || (msgstart = lseek (dfd, 0, SEEK_CUR)) == -1) {
      close (dfd);
      unlink (deleteme);
      free (deleteme);
      deleteme = NULL;
      return EX_OSERR;
    }

    truncfd = dfd;
    truncpos = msgstart;
  }

  n = copy_msg (dfd, mfd, 1, use_readp, !dump);

  if (!dump) {
    if (n < 0)
      garbage = ftruncate (dfd, msgstart);
    truncfd = -1;

    unlink (deleteme);
    free (deleteme);
    deleteme = NULL;

    close (dfd);
  }
  return n < 0 ? EX_OSERR : 0;
}

#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 256
#endif /* !MAXHOSTNAMELEN */

static char *
myhostname (void)
{
  char namebuf[MAXHOSTNAMELEN+1];
  static char res[4 * sizeof (namebuf)];
  char *p, *q;

  namebuf[sizeof (namebuf) - 1] = '\0';
  if (gethostname (namebuf, sizeof (namebuf) - 1) < 0) {
    perror ("gethostname");
    exit (EX_OSERR);
  }

  for (p = namebuf, q = res; *p && q < res + sizeof (res) - 5; p++) {
    switch (*p) {
    case '/':
    case ':':
    case '\\':
      q += sprintf (q, "\\%03o", *p);
      break;
    default:
      *q++ = *p;
      break;
    }
  }
  *q = '\0';

  return res;
}

static int
mksubmaildir (char *buf, size_t blen, const char *path, const char *sub)
{
  struct stat sb;
  if (sub)
    snprintf (buf, blen, "%s/%s", path, sub);
  else
    snprintf (buf, blen, "%s", path);
  if (!mkdir (buf, 0777)
      || (errno == EEXIST && !stat (buf, &sb) && S_ISDIR (sb.st_mode)))
    return 0;
  return -1;
}

int
deliver_maildir (const char *path, int mfd, int use_readp)
{
  static int deliv_ctr;
  const size_t blen = strlen (path) + 300;
  char *buf = xmalloc (blen);
  char *buf2;
  char *host;
  int pid;
  struct stat sb;
  struct timeval tv;
  int dfd;

  snprintf (buf, blen, "%s/cur", path);
  if (stat (buf, &sb)
      && (errno != ENOENT
        || mksubmaildir (buf, blen, path, NULL)
        || mksubmaildir (buf, blen, path, "tmp")
        || mksubmaildir (buf, blen, path, "new")
        || mksubmaildir (buf, blen, path, "cur"))) {
    perror (buf);
    free (buf);
    return tmperr (errno) ? EX_OSERR : EX_CANTCREAT;
  }

  gettimeofday (&tv, NULL);
  host = myhostname ();
  pid = getpid ();
  snprintf (buf, blen, "%s/tmp/%lu.M%uP%dQ%d.%s",
          path, (u_long) tv.tv_sec, (unsigned) tv.tv_usec, pid,
          ++deliv_ctr, host);

  deleteme = buf;
  dfd = open (buf, O_WRONLY|O_CREAT|O_EXCL, 0666);
  if (dfd < 0 && errno == EEXIST) {
    unlink (buf);
    dfd = open (buf, O_WRONLY|O_CREAT|O_EXCL, 0666);
  }
  if (dfd < 0) {
    perror (buf);
    free (buf);
    return tmperr (errno) ? EX_OSERR : EX_CANTCREAT;
  }

  if (copy_msg (dfd, mfd, 0, use_readp, 0)) {
    close (dfd);
    unlink (buf);
    return EX_OSERR;
  }

  close (dfd);
  buf2 = xmalloc (strlen (buf) + 1);
  sprintf (buf2, "%s/new/%lu.M%uP%dQ%d.%s",
         path, (u_long) tv.tv_sec, (unsigned) tv.tv_usec, pid,
         deliv_ctr, host);
  if (rename (buf, buf2)) {
    perror (buf2);
    unlink (buf);
    free (buf);
    free (buf2);
    deleteme = NULL;
    return tmperr (errno) ? EX_OSERR : EX_CANTCREAT;
  }

  deleteme = NULL;
  free (buf);

  *strrchr (buf2, '/') = '\0';
  if ((dfd = open (buf2, O_RDONLY)) >= 0) {
    fsync (dfd);
    close (dfd);
  }
  utimes (buf2, NULL);

  free (buf2);
  return 0;
}


Generated by  Doxygen 1.6.0   Back to index