#include "sys_defs.h"
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
#include <vstream.h>
#include <vstring.h>
#include <make_dirs.h>
#include <set_eugid.h>
#include <get_hostname.h>
#include <sane_fsops.h>
#include <warn_stat.h>
#include <mail_copy.h>
#include <bounce.h>
#include <defer.h>
#include <sent.h>
#include <mail_params.h>
#include <dsn_util.h>
#include <mbox_open.h>
#include "local.h"
int deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
{
const char *myname = "deliver_maildir";
char *newdir;
char *tmpdir;
char *curdir;
char *tmpfile;
char *newfile;
DSN_BUF *why = state.msg_attr.why;
VSTRING *buf;
VSTREAM *dst;
int mail_copy_status;
int deliver_status;
int copy_flags;
struct stat st;
struct timeval starttime;
GETTIMEOFDAY(&starttime);
state.level++;
if (msg_verbose)
MSG_LOG_STATE(myname, state);
if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
dsb_simple(why, "2.0.0", "delivers to maildir");
return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
}
if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
if (var_frozen_delivered == 0)
state.msg_attr.delivered = state.msg_attr.rcpt.address;
mail_copy_status = MAIL_COPY_STAT_WRITE;
buf = vstring_alloc(100);
copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_ORIG_RCPT;
if (local_deliver_hdr_mask & DELIVER_HDR_FILE)
copy_flags |= MAIL_COPY_DELIVERED;
newdir = concatenate(path, "new/", (char *) 0);
tmpdir = concatenate(path, "tmp/", (char *) 0);
curdir = concatenate(path, "cur/", (char *) 0);
set_eugid(usr_attr.uid, usr_attr.gid);
vstring_sprintf(buf, "%lu.P%d.%s",
(unsigned long) starttime.tv_sec, var_pid, get_hostname());
tmpfile = concatenate(tmpdir, STR(buf), (char *) 0);
newfile = 0;
if ((dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0
&& (errno != ENOENT
|| make_dirs(tmpdir, 0700) < 0
|| (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
dsb_simple(why, mbox_dsn(errno, "5.2.0"),
"create maildir file %s: %m", tmpfile);
} else if (fstat(vstream_fileno(dst), &st) < 0) {
msg_fatal("fstat %s: %m", tmpfile);
} else {
vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
(unsigned long) starttime.tv_sec,
(unsigned long) st.st_dev,
(unsigned long) st.st_ino,
(unsigned long) starttime.tv_usec,
get_hostname());
newfile = concatenate(newdir, STR(buf), (char *) 0);
if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
dst, copy_flags, "\n",
why)) == 0) {
if (sane_link(tmpfile, newfile) < 0
&& (errno != ENOENT
|| (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
|| sane_link(tmpfile, newfile) < 0)) {
dsb_simple(why, mbox_dsn(errno, "5.2.0"),
"create maildir file %s: %m", newfile);
mail_copy_status = MAIL_COPY_STAT_WRITE;
}
}
if (unlink(tmpfile) < 0)
msg_warn("remove %s: %m", tmpfile);
}
set_eugid(var_owner_uid, var_owner_gid);
if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
deliver_status = DEL_STAT_DEFER;
} else if (mail_copy_status != 0) {
if (errno == EACCES) {
msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
(long) usr_attr.uid, (long) usr_attr.gid,
STR(why->reason));
msg_warn("perhaps you need to create the maildirs in advance");
}
vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
deliver_status =
(STR(why->status)[0] == '4' ?
defer_append : bounce_append)
(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
} else {
dsb_simple(why, "2.0.0", "delivered to maildir");
deliver_status = sent(BOUNCE_FLAGS(state.request),
SENT_ATTR(state.msg_attr));
}
vstring_free(buf);
myfree(newdir);
myfree(tmpdir);
myfree(curdir);
myfree(tmpfile);
if (newfile)
myfree(newfile);
return (deliver_status);
}