/* $Id: mailbox.c,v 1.9 2005/01/08 18:36:36 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 "local.h"
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)
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);
free (buf2);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1