Sync usage with man page.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / local / dotforward.c
blob81d8b87fb5024c5d22ac0ddca9de88257b76402e
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* dotforward 3
6 /* SUMMARY
7 /* $HOME/.forward file expansion
8 /* SYNOPSIS
9 /* #include "local.h"
11 /* int deliver_dotforward(state, usr_attr, statusp)
12 /* LOCAL_STATE state;
13 /* USER_ATTR usr_attr;
14 /* int *statusp;
15 /* DESCRIPTION
16 /* deliver_dotforward() delivers a message to the destinations
17 /* listed in a recipient's .forward file(s) as specified through
18 /* the forward_path configuration parameter. The result is
19 /* zero when no acceptable .forward file was found, or when
20 /* a recipient is listed in her own .forward file. Expansions
21 /* are scrutinized with the forward_expansion_filter parameter.
23 /* Arguments:
24 /* .IP state
25 /* Message delivery attributes (sender, recipient etc.).
26 /* Attributes describing alias, include or forward expansion.
27 /* A table with the results from expanding aliases or lists.
28 /* A table with delivered-to: addresses taken from the message.
29 /* .IP usr_attr
30 /* Attributes describing user rights and environment.
31 /* .IP statusp
32 /* Message delivery status. See below.
33 /* DIAGNOSTICS
34 /* Fatal errors: out of memory. Warnings: bad $HOME/.forward
35 /* file type, permissions or ownership. The message delivery
36 /* status is non-zero when delivery should be tried again.
37 /* SEE ALSO
38 /* include(3) include file processor.
39 /* LICENSE
40 /* .ad
41 /* .fi
42 /* The Secure Mailer license must be distributed with this software.
43 /* AUTHOR(S)
44 /* Wietse Venema
45 /* IBM T.J. Watson Research
46 /* P.O. Box 704
47 /* Yorktown Heights, NY 10598, USA
48 /*--*/
50 /* System library. */
52 #include <sys_defs.h>
53 #include <sys/stat.h>
54 #include <unistd.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #ifdef USE_PATHS_H
58 #include <paths.h>
59 #endif
60 #include <string.h>
62 /* Utility library. */
64 #include <msg.h>
65 #include <vstring.h>
66 #include <vstream.h>
67 #include <htable.h>
68 #include <open_as.h>
69 #include <lstat_as.h>
70 #include <iostuff.h>
71 #include <stringops.h>
72 #include <mymalloc.h>
73 #include <mac_expand.h>
75 /* Global library. */
77 #include <mypwd.h>
78 #include <bounce.h>
79 #include <been_here.h>
80 #include <mail_params.h>
81 #include <mail_conf.h>
82 #include <ext_prop.h>
83 #include <sent.h>
84 #include <dsn_mask.h>
85 #include <trace.h>
87 /* Application-specific. */
89 #include "local.h"
91 #define NO 0
92 #define YES 1
94 /* deliver_dotforward - expand contents of .forward file */
96 int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
98 const char *myname = "deliver_dotforward";
99 struct stat st;
100 VSTRING *path;
101 struct mypasswd *mypwd;
102 int fd;
103 VSTREAM *fp;
104 int status;
105 int forward_found = NO;
106 int lookup_status;
107 int addr_count;
108 char *saved_forward_path;
109 char *lhs;
110 char *next;
111 int expand_status;
112 int saved_notify;
115 * Make verbose logging easier to understand.
117 state.level++;
118 if (msg_verbose)
119 MSG_LOG_STATE(myname, state);
122 * Skip this module if per-user forwarding is disabled.
124 if (*var_forward_path == 0)
125 return (NO);
128 * Skip non-existing users. The mailbox delivery routine will catch the
129 * error.
131 if ((mypwd = mypwnam(state.msg_attr.user)) == 0)
132 return (NO);
135 * From here on no early returns or we have a memory leak.
139 * EXTERNAL LOOP CONTROL
141 * Set the delivered message attribute to the recipient, so that this
142 * message will list the correct forwarding address.
144 if (var_frozen_delivered == 0)
145 state.msg_attr.delivered = state.msg_attr.rcpt.address;
148 * DELIVERY RIGHTS
150 * Do not inherit rights from the .forward file owner. Instead, use the
151 * recipient's rights, and insist that the .forward file is owned by the
152 * recipient. This is a small but significant difference. Use the
153 * recipient's rights for all /file and |command deliveries, and pass on
154 * these rights to command/file destinations in included files. When
155 * these are the rights of root, the /file and |command delivery routines
156 * will use unprivileged default rights instead. Better safe than sorry.
158 SET_USER_ATTR(usr_attr, mypwd, state.level);
161 * DELIVERY POLICY
163 * Update the expansion type attribute so that we can decide if deliveries
164 * to |command and /file/name are allowed at all.
166 state.msg_attr.exp_type = EXPAND_TYPE_FWD;
169 * WHERE TO REPORT DELIVERY PROBLEMS
171 * Set the owner attribute so that 1) include files won't set the sender to
172 * be this user and 2) mail forwarded to other local users will be
173 * resubmitted as a new queue file.
175 state.msg_attr.owner = state.msg_attr.user;
178 * Search the forward_path for an existing forward file.
180 * If unmatched extensions should never be propagated, or if a forward file
181 * name includes the address extension, don't propagate the extension to
182 * the recipient addresses.
184 status = 0;
185 path = vstring_alloc(100);
186 saved_forward_path = mystrdup(var_forward_path);
187 next = saved_forward_path;
188 lookup_status = -1;
190 while ((lhs = mystrtok(&next, ", \t\r\n")) != 0) {
191 expand_status = local_expand(path, lhs, &state, &usr_attr,
192 var_fwd_exp_filter);
193 if ((expand_status & (MAC_PARSE_ERROR | MAC_PARSE_UNDEF)) == 0) {
194 lookup_status =
195 lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid);
196 if (msg_verbose)
197 msg_info("%s: path %s expand_status %d look_status %d", myname,
198 STR(path), expand_status, lookup_status);
199 if (lookup_status >= 0) {
200 if ((expand_status & LOCAL_EXP_EXTENSION_MATCHED) != 0
201 || (local_ext_prop_mask & EXT_PROP_FORWARD) == 0)
202 state.msg_attr.unmatched = 0;
203 break;
209 * Process the forward file.
211 * Assume that usernames do not have file system meta characters. Open the
212 * .forward file as the user. Ignore files that aren't regular files,
213 * files that are owned by the wrong user, or files that have world write
214 * permission enabled.
216 * DUPLICATE/LOOP ELIMINATION
218 * If this user includes (an alias of) herself in her own .forward file,
219 * deliver to the user instead.
221 if (lookup_status >= 0) {
224 * Don't expand a verify-only request.
226 if (state.request->flags & DEL_REQ_FLAG_MTA_VRFY) {
227 dsb_simple(state.msg_attr.why, "2.0.0",
228 "forward via file: %s", STR(path));
229 *statusp = sent(BOUNCE_FLAGS(state.request),
230 SENT_ATTR(state.msg_attr));
231 forward_found = YES;
232 } else if (been_here(state.dup_filter, "forward %s", STR(path)) == 0) {
233 state.msg_attr.exp_from = state.msg_attr.local;
234 if (S_ISREG(st.st_mode) == 0) {
235 msg_warn("file %s is not a regular file", STR(path));
236 } else if (st.st_uid != 0 && st.st_uid != usr_attr.uid) {
237 msg_warn("file %s has bad owner uid %ld",
238 STR(path), (long) st.st_uid);
239 } else if (st.st_mode & 002) {
240 msg_warn("file %s is world writable", STR(path));
241 } else if ((fd = open_as(STR(path), O_RDONLY, 0, usr_attr.uid, usr_attr.gid)) < 0) {
242 msg_warn("cannot open file %s: %m", STR(path));
243 } else {
246 * XXX DSN. When delivering to an alias (i.e. the envelope
247 * sender address is not replaced) any ENVID, RET, or ORCPT
248 * parameters are propagated to all forwarding addresses
249 * associated with that alias. The NOTIFY parameter is
250 * propagated to the forwarding addresses, except that any
251 * SUCCESS keyword is removed.
253 close_on_exec(fd, CLOSE_ON_EXEC);
254 addr_count = 0;
255 fp = vstream_fdopen(fd, O_RDONLY);
256 saved_notify = state.msg_attr.rcpt.dsn_notify;
257 state.msg_attr.rcpt.dsn_notify =
258 (saved_notify == DSN_NOTIFY_SUCCESS ?
259 DSN_NOTIFY_NEVER : saved_notify & ~DSN_NOTIFY_SUCCESS);
260 status = deliver_token_stream(state, usr_attr, fp, &addr_count);
261 if (vstream_fclose(fp))
262 msg_warn("close file %s: %m", STR(path));
263 if (addr_count > 0) {
264 forward_found = YES;
265 been_here(state.dup_filter, "forward-done %s", STR(path));
268 * XXX DSN. When delivering to an alias (i.e. the
269 * envelope sender address is not replaced) and the
270 * original NOTIFY parameter for the alias contained the
271 * SUCCESS keyword, an "expanded" DSN is issued for the
272 * alias.
274 if (status == 0 && (saved_notify & DSN_NOTIFY_SUCCESS)) {
275 state.msg_attr.rcpt.dsn_notify = saved_notify;
276 dsb_update(state.msg_attr.why, "2.0.0", "expanded",
277 DSB_SKIP_RMTA, DSB_SKIP_REPLY,
278 "alias expanded");
279 (void) trace_append(BOUNCE_FLAG_NONE,
280 SENT_ATTR(state.msg_attr));
284 } else if (been_here_check(state.dup_filter, "forward-done %s", STR(path)) != 0)
285 forward_found = YES; /* else we're recursive */
289 * Clean up.
291 vstring_free(path);
292 myfree(saved_forward_path);
293 mypwfree(mypwd);
295 *statusp = status;
296 return (forward_found);