7 /* mail delivery to arbitrary file
11 /* int deliver_file(state, usr_attr, path)
13 /* USER_ATTR usr_attr;
16 /* deliver_file() appends a message to a file, UNIX mailbox format,
17 /* or qmail maildir format,
18 /* with duplicate suppression. It will deliver only to non-executable
23 /* The attributes that specify the message, recipient and more.
24 /* Attributes describing alias, include or forward expansion.
25 /* A table with the results from expanding aliases or lists.
27 /* Attributes describing user rights and environment information.
29 /* The file to deliver to. If the name ends in '/', delivery is done
30 /* in qmail maildir format, otherwise delivery is done in UNIX mailbox
33 /* deliver_file() returns non-zero when delivery should be tried again.
40 /* The Secure Mailer license must be distributed with this software.
43 /* IBM T.J. Watson Research
45 /* Yorktown Heights, NY 10598, USA
57 /* Utility library. */
63 #include <deliver_flock.h>
64 #include <set_eugid.h>
68 #include <mail_copy.h>
72 #include <been_here.h>
73 #include <mail_params.h>
74 #include <mbox_conf.h>
75 #include <mbox_open.h>
78 /* Application-specific. */
82 /* deliver_file - deliver to file */
84 int deliver_file(LOCAL_STATE state
, USER_ATTR usr_attr
, char *path
)
86 const char *myname
= "deliver_file";
89 DSN_BUF
*why
= state
.msg_attr
.why
;
90 int mail_copy_status
= MAIL_COPY_STAT_WRITE
;
95 * Make verbose logging easier to understand.
99 MSG_LOG_STATE(myname
, state
);
102 * DUPLICATE ELIMINATION
104 * Skip this file if it was already delivered to as this user.
106 if (been_here(state
.dup_filter
, "file %ld %s", (long) usr_attr
.uid
, path
))
112 * Do we allow delivery to files?
114 if ((local_file_deliver_mask
& state
.msg_attr
.exp_type
) == 0) {
115 dsb_simple(why
, "5.7.1", "mail to file is restricted");
116 return (bounce_append(BOUNCE_FLAGS(state
.request
),
117 BOUNCE_ATTR(state
.msg_attr
)));
121 * Don't deliver trace-only requests.
123 if (DEL_REQ_TRACE_ONLY(state
.request
->flags
)) {
124 dsb_simple(why
, "2.0.0", "delivers to file: %s", path
);
125 return (sent(BOUNCE_FLAGS(state
.request
),
126 SENT_ATTR(state
.msg_attr
)));
132 * Use a default uid/gid when none are given.
134 if (usr_attr
.uid
== 0 && (usr_attr
.uid
= var_default_uid
) == 0)
135 msg_panic("privileged default user id");
136 if (usr_attr
.gid
== 0 && (usr_attr
.gid
= var_default_gid
) == 0)
137 msg_panic("privileged default group id");
140 * If the name ends in /, use maildir-style delivery instead.
142 if (path
[strlen(path
) - 1] == '/')
143 return (deliver_maildir(state
, usr_attr
, path
));
146 * Deliver. From here on, no early returns or we have a memory leak.
149 msg_info("deliver_file (%ld,%ld): %s",
150 (long) usr_attr
.uid
, (long) usr_attr
.gid
, path
);
151 if (vstream_fseek(state
.msg_attr
.fp
, state
.msg_attr
.offset
, SEEK_SET
) < 0)
152 msg_fatal("seek queue file %s: %m", state
.msg_attr
.queue_id
);
155 * As the specified user, open or create the file, lock it, and append
158 copy_flags
= MAIL_COPY_MBOX
;
159 if ((local_deliver_hdr_mask
& DELIVER_HDR_FILE
) == 0)
160 copy_flags
&= ~MAIL_COPY_DELIVERED
;
162 set_eugid(usr_attr
.uid
, usr_attr
.gid
);
163 mp
= mbox_open(path
, O_APPEND
| O_CREAT
| O_WRONLY
,
164 S_IRUSR
| S_IWUSR
, &st
, -1, -1,
165 local_mbox_lock_mask
| MBOX_DOT_LOCK_MAY_FAIL
,
168 if (S_ISREG(st
.st_mode
) && st
.st_mode
& (S_IXUSR
| S_IXGRP
| S_IXOTH
)) {
169 vstream_fclose(mp
->fp
);
170 dsb_simple(why
, "5.7.1", "file is executable");
172 mail_copy_status
= mail_copy(COPY_ATTR(state
.msg_attr
), mp
->fp
,
173 S_ISREG(st
.st_mode
) ? copy_flags
:
174 (copy_flags
& ~MAIL_COPY_TOFILE
),
179 set_eugid(var_owner_uid
, var_owner_gid
);
182 * As the mail system, bounce, defer delivery, or report success.
184 if (mail_copy_status
& MAIL_COPY_STAT_CORRUPT
) {
185 deliver_status
= DEL_STAT_DEFER
;
186 } else if (mail_copy_status
!= 0) {
187 vstring_sprintf_prepend(why
->reason
,
188 "cannot append message to file %s: ", path
);
190 (STR(why
->status
)[0] == '4' ?
191 defer_append
: bounce_append
)
192 (BOUNCE_FLAGS(state
.request
),
193 BOUNCE_ATTR(state
.msg_attr
));
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
));
199 return (deliver_status
);