mail.local.c revision 1.20 1 /* $NetBSD: mail.local.c,v 1.20 2002/05/26 00:02:08 wiz Exp $ */
2
3 /*-
4 * Copyright (c) 1990, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #if 0
41 static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 6/21/95";
42 #else
43 __RCSID("$NetBSD: mail.local.c,v 1.20 2002/05/26 00:02:08 wiz Exp $");
44 #endif
45 #endif /* not lint */
46
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/socket.h>
50
51 #include <netinet/in.h>
52
53 #include <errno.h>
54 #include <fcntl.h>
55 #include <pwd.h>
56 #include <netdb.h>
57 #include <stdarg.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <syslog.h>
62 #include <time.h>
63 #include <unistd.h>
64
65 #include "pathnames.h"
66
67 #define FATAL 1
68 #define NOTFATAL 0
69
70 int deliver __P((int, char *, int));
71 void err __P((int, const char *, ...))
72 __attribute__((__format__(__printf__, 2, 3)));
73 void notifybiff __P((char *));
74 int store __P((const char *));
75 void usage __P((void));
76 int main __P((int, char **));
77
78 int
79 main(argc, argv)
80 int argc;
81 char **argv;
82 {
83 struct passwd *pw;
84 int ch, fd, eval, lockfile = 0;
85 uid_t uid;
86 const char *from;
87
88 /* use a reasonable umask */
89 (void) umask(0077);
90
91 openlog("mail.local", LOG_PERROR, LOG_MAIL);
92
93 from = NULL;
94 while ((ch = getopt(argc, argv, "ldf:r:")) != -1)
95 switch (ch) {
96 case 'd': /* backward compatible */
97 break;
98 case 'f':
99 case 'r': /* backward compatible */
100 if (from)
101 err(FATAL, "multiple -f options");
102 from = optarg;
103 break;
104 case 'l':
105 lockfile++;
106 break;
107 case '?':
108 default:
109 usage();
110 }
111 argc -= optind;
112 argv += optind;
113
114 if (!*argv)
115 usage();
116
117 /*
118 * If from not specified, use the name from getlogin() if the
119 * uid matches, otherwise, use the name from the password file
120 * corresponding to the uid.
121 */
122 uid = getuid();
123 if (!from && (!(from = getlogin()) ||
124 !(pw = getpwnam(from)) || pw->pw_uid != uid))
125 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
126
127 fd = store(from);
128 for (eval = 0; *argv; ++argv)
129 eval |= deliver(fd, *argv, lockfile);
130 exit (eval);
131 }
132
133 int
134 store(from)
135 const char *from;
136 {
137 FILE *fp = NULL; /* XXX gcc */
138 time_t tval;
139 int fd, eline;
140 char *tn, line[2048];
141
142 tn = strdup(_PATH_LOCTMP);
143 if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+")))
144 err(FATAL, "unable to open temporary file");
145 (void)unlink(tn);
146 free(tn);
147
148 (void)time(&tval);
149 (void)fprintf(fp, "From %s %s", from, ctime(&tval));
150
151 line[0] = '\0';
152 for (eline = 1; fgets(line, sizeof(line), stdin);) {
153 if (line[0] == '\n')
154 eline = 1;
155 else {
156 if (eline && line[0] == 'F' && !memcmp(line, "From ", 5))
157 (void)putc('>', fp);
158 eline = 0;
159 }
160 (void)fprintf(fp, "%s", line);
161 if (ferror(fp))
162 break;
163 }
164
165 /* If message not newline terminated, need an extra. */
166 if (!index(line, '\n'))
167 (void)putc('\n', fp);
168 /* Output a newline; note, empty messages are allowed. */
169 (void)putc('\n', fp);
170
171 (void)fflush(fp);
172 if (ferror(fp))
173 err(FATAL, "temporary file write error");
174 return(fd);
175 }
176
177 int
178 deliver(fd, name, lockfile)
179 int fd;
180 char *name;
181 int lockfile;
182 {
183 struct stat sb;
184 struct passwd *pw;
185 int created, mbfd, nr, nw, off, rval=0, lfd=-1;
186 char biffmsg[100], buf[8*1024], path[MAXPATHLEN], lpath[MAXPATHLEN];
187 off_t curoff;
188
189 /*
190 * Disallow delivery to unknown names -- special mailboxes can be
191 * handled in the sendmail aliases file.
192 */
193 if (!(pw = getpwnam(name))) {
194 err(NOTFATAL, "unknown name: %s", name);
195 return(1);
196 }
197
198 (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
199
200 if (lockfile) {
201 (void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
202 _PATH_MAILDIR, name);
203
204 if((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL,
205 S_IRUSR|S_IWUSR)) < 0) {
206 err(NOTFATAL, "%s: %s", lpath, strerror(errno));
207 return(1);
208 }
209 }
210
211 if (!(created = lstat(path, &sb)) &&
212 (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) {
213 err(NOTFATAL, "%s: linked file", path);
214 return(1);
215 }
216 if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
217 S_IRUSR|S_IWUSR)) < 0) {
218 if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK,
219 S_IRUSR|S_IWUSR)) < 0) {
220 err(NOTFATAL, "%s: %s", path, strerror(errno));
221 return(1);
222 }
223 }
224
225 curoff = lseek(mbfd, 0, SEEK_END);
226 (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name,
227 (long long)curoff);
228 if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
229 err(FATAL, "temporary file: %s", strerror(errno));
230 rval = 1;
231 goto bad;
232 }
233
234 while ((nr = read(fd, buf, sizeof(buf))) > 0)
235 for (off = 0; off < nr; off += nw)
236 if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
237 err(NOTFATAL, "%s: %s", path, strerror(errno));
238 goto trunc;
239 }
240 if (nr < 0) {
241 err(FATAL, "temporary file: %s", strerror(errno));
242 trunc: (void)ftruncate(mbfd, curoff);
243 rval = 1;
244 }
245
246 /*
247 * Set the owner and group. Historically, binmail repeated this at
248 * each mail delivery. We no longer do this, assuming that if the
249 * ownership or permissions were changed there was a reason for doing
250 * so.
251 */
252 bad:
253 if (lockfile) {
254 if (lfd >= 0) {
255 unlink(lpath);
256 close(lfd);
257 }
258 }
259 if (created)
260 (void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
261
262 (void)fsync(mbfd); /* Don't wait for update. */
263 (void)close(mbfd); /* Implicit unlock. */
264
265 if (!rval)
266 notifybiff(biffmsg);
267 return(rval);
268 }
269
270 void
271 notifybiff(msg)
272 char *msg;
273 {
274 static struct sockaddr_in addr;
275 static int f = -1;
276 struct hostent *hp;
277 struct servent *sp;
278 int len;
279
280 if (!addr.sin_family) {
281 /* Be silent if biff service not available. */
282 if (!(sp = getservbyname("biff", "udp")))
283 return;
284 if (!(hp = gethostbyname("localhost"))) {
285 err(NOTFATAL, "localhost: %s", strerror(errno));
286 return;
287 }
288 addr.sin_len = sizeof(struct sockaddr_in);
289 addr.sin_family = hp->h_addrtype;
290 addr.sin_port = sp->s_port;
291 memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
292 }
293 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
294 err(NOTFATAL, "socket: %s", strerror(errno));
295 return;
296 }
297 len = strlen(msg) + 1;
298 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
299 != len)
300 err(NOTFATAL, "sendto biff: %s", strerror(errno));
301 }
302
303 void
304 usage()
305 {
306 err(FATAL, "usage: mail.local [-l] [-f from] user ...");
307 }
308
309 void
310 err(int isfatal, const char *fmt, ...)
311 {
312 va_list ap;
313
314 va_start(ap, fmt);
315 vsyslog(LOG_ERR, fmt, ap);
316 va_end(ap);
317 if (isfatal)
318 exit(1);
319 }
320