1/*++
2/* NAME
3/*	file 3
4/* SUMMARY
5/*	mail delivery to arbitrary file
6/* SYNOPSIS
7/*	#include "local.h"
8/*
9/*	int	deliver_file(state, usr_attr, path)
10/*	LOCAL_STATE state;
11/*	USER_ATTR usr_attr;
12/*	char	*path;
13/* DESCRIPTION
14/*	deliver_file() appends a message to a file, UNIX mailbox format,
15/*	or qmail maildir format,
16/*	with duplicate suppression. It will deliver only to non-executable
17/*	regular files.
18/*
19/*	Arguments:
20/* .IP state
21/*	The attributes that specify the message, recipient and more.
22/*	Attributes describing alias, include or forward expansion.
23/*	A table with the results from expanding aliases or lists.
24/* .IP usr_attr
25/*	Attributes describing user rights and environment information.
26/* .IP path
27/*	The file to deliver to. If the name ends in '/', delivery is done
28/*	in qmail maildir format, otherwise delivery is done in UNIX mailbox
29/*	format.
30/* DIAGNOSTICS
31/*	deliver_file() returns non-zero when delivery should be tried again.
32/* SEE ALSO
33/*	defer(3)
34/*	bounce(3)
35/* LICENSE
36/* .ad
37/* .fi
38/*	The Secure Mailer license must be distributed with this software.
39/* AUTHOR(S)
40/*	Wietse Venema
41/*	IBM T.J. Watson Research
42/*	P.O. Box 704
43/*	Yorktown Heights, NY 10598, USA
44/*--*/
45
46/* System library. */
47
48#include <sys_defs.h>
49#include <sys/stat.h>
50#include <unistd.h>
51#include <fcntl.h>
52#include <errno.h>
53#include <string.h>
54
55/* Utility library. */
56
57#include <msg.h>
58#include <htable.h>
59#include <vstring.h>
60#include <vstream.h>
61#include <deliver_flock.h>
62#include <set_eugid.h>
63
64/* Global library. */
65
66#include <mail_copy.h>
67#include <bounce.h>
68#include <defer.h>
69#include <sent.h>
70#include <been_here.h>
71#include <mail_params.h>
72#include <mbox_conf.h>
73#include <mbox_open.h>
74#include <dsn_util.h>
75
76/* Application-specific. */
77
78#include "local.h"
79
80/* deliver_file - deliver to file */
81
82int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
83{
84    const char *myname = "deliver_file";
85    struct stat st;
86    MBOX   *mp;
87    DSN_BUF *why = state.msg_attr.why;
88    int     mail_copy_status = MAIL_COPY_STAT_WRITE;
89    int     deliver_status;
90    int     copy_flags;
91
92    /*
93     * Make verbose logging easier to understand.
94     */
95    state.level++;
96    if (msg_verbose)
97	MSG_LOG_STATE(myname, state);
98
99    /*
100     * DUPLICATE ELIMINATION
101     *
102     * Skip this file if it was already delivered to as this user.
103     */
104    if (been_here(state.dup_filter, "file %ld %s", (long) usr_attr.uid, path))
105	return (0);
106
107    /*
108     * DELIVERY POLICY
109     *
110     * Do we allow delivery to files?
111     */
112    if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0) {
113	dsb_simple(why, "5.7.1", "mail to file is restricted");
114	/* Account for possible owner- sender address override. */
115	return (bounce_workaround(state));
116    }
117
118    /*
119     * Don't deliver trace-only requests.
120     */
121    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
122	dsb_simple(why, "2.0.0", "delivers to file: %s", path);
123	return (sent(BOUNCE_FLAGS(state.request),
124		     SENT_ATTR(state.msg_attr)));
125    }
126
127    /*
128     * DELIVERY RIGHTS
129     *
130     * Use a default uid/gid when none are given.
131     */
132    if (usr_attr.uid == 0 && (usr_attr.uid = var_default_uid) == 0)
133	msg_panic("privileged default user id");
134    if (usr_attr.gid == 0 && (usr_attr.gid = var_default_gid) == 0)
135	msg_panic("privileged default group id");
136
137    /*
138     * If the name ends in /, use maildir-style delivery instead.
139     */
140    if (path[strlen(path) - 1] == '/')
141	return (deliver_maildir(state, usr_attr, path));
142
143    /*
144     * Deliver. From here on, no early returns or we have a memory leak.
145     */
146    if (msg_verbose)
147	msg_info("deliver_file (%ld,%ld): %s",
148		 (long) usr_attr.uid, (long) usr_attr.gid, path);
149    if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
150	msg_fatal("seek queue file %s: %m", state.msg_attr.queue_id);
151
152    /*
153     * As the specified user, open or create the file, lock it, and append
154     * the message.
155     */
156    copy_flags = MAIL_COPY_MBOX;
157    if ((local_deliver_hdr_mask & DELIVER_HDR_FILE) == 0)
158	copy_flags &= ~MAIL_COPY_DELIVERED;
159
160    set_eugid(usr_attr.uid, usr_attr.gid);
161    mp = mbox_open(path, O_APPEND | O_CREAT | O_WRONLY,
162		   S_IRUSR | S_IWUSR, &st, -1, -1,
163		   local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL,
164		   "5.2.0", why);
165    if (mp != 0) {
166	if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
167	    vstream_fclose(mp->fp);
168	    dsb_simple(why, "5.7.1", "file is executable");
169	} else {
170	    mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
171					 S_ISREG(st.st_mode) ? copy_flags :
172					 (copy_flags & ~MAIL_COPY_TOFILE),
173					 "\n", why);
174	}
175	mbox_release(mp);
176    }
177    set_eugid(var_owner_uid, var_owner_gid);
178
179    /*
180     * As the mail system, bounce, defer delivery, or report success.
181     */
182    if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
183	deliver_status = DEL_STAT_DEFER;
184    } else if (mail_copy_status != 0) {
185	vstring_sprintf_prepend(why->reason,
186				"cannot append message to file %s: ", path);
187	if (STR(why->status)[0] == '4')
188	    deliver_status =
189		defer_append(BOUNCE_FLAGS(state.request),
190			     BOUNCE_ATTR(state.msg_attr));
191	else
192	    /* Account for possible owner- sender address override. */
193	    deliver_status = bounce_workaround(state);
194    } else {
195	dsb_simple(why, "2.0.0", "delivered to file: %s", path);
196	deliver_status = sent(BOUNCE_FLAGS(state.request),
197			      SENT_ATTR(state.msg_attr));
198    }
199    return (deliver_status);
200}
201