/* $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; }