mail.local.c revision 1.12 1 /* $NetBSD: mail.local.c,v 1.12 1997/10/07 13:13:41 mrg 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.12 1997/10/07 13:13:41 mrg 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 <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <syslog.h>
61 #include <time.h>
62 #include <unistd.h>
63
64 #include "pathnames.h"
65
66 #define FATAL 1
67 #define NOTFATAL 0
68
69 int deliver __P((int, char *, int));
70 void err __P((int, const char *, ...));
71 void notifybiff __P((char *));
72 int store __P((char *));
73 void usage __P((void));
74 int main __P((int, char **));
75
76 int
77 main(argc, argv)
78 int argc;
79 char **argv;
80 {
81 struct passwd *pw;
82 int ch, fd, eval, lockfile = 0;
83 uid_t uid;
84 char *from;
85
86 /* use a reasonable umask */
87 (void) umask(0077);
88
89 openlog("mail.local", LOG_PERROR, LOG_MAIL);
90
91 from = NULL;
92 while ((ch = getopt(argc, argv, "ldf:r:")) != EOF)
93 switch(ch) {
94 case 'd': /* backward compatible */
95 break;
96 case 'f':
97 case 'r': /* backward compatible */
98 if (from)
99 err(FATAL, "multiple -f options");
100 from = optarg;
101 break;
102 case 'l':
103 lockfile++;
104 break;
105 case '?':
106 default:
107 usage();
108 }
109 argc -= optind;
110 argv += optind;
111
112 if (!*argv)
113 usage();
114
115 /*
116 * If from not specified, use the name from getlogin() if the
117 * uid matches, otherwise, use the name from the password file
118 * corresponding to the uid.
119 */
120 uid = getuid();
121 if (!from && (!(from = getlogin()) ||
122 !(pw = getpwnam(from)) || pw->pw_uid != uid))
123 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
124
125 fd = store(from);
126 for (eval = 0; *argv; ++argv)
127 eval |= deliver(fd, *argv, lockfile);
128 exit (eval);
129 }
130
131 int
132 store(from)
133 char *from;
134 {
135 FILE *fp = NULL; /* XXX gcc */
136 time_t tval;
137 int fd, eline;
138 char *tn, line[2048];
139
140 tn = strdup(_PATH_LOCTMP);
141 if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+")))
142 err(FATAL, "unable to open temporary file");
143 (void)unlink(tn);
144 free(tn);
145
146 (void)time(&tval);
147 (void)fprintf(fp, "From %s %s", from, ctime(&tval));
148
149 line[0] = '\0';
150 for (eline = 1; fgets(line, sizeof(line), stdin);) {
151 if (line[0] == '\n')
152 eline = 1;
153 else {
154 if (eline && line[0] == 'F' && !bcmp(line, "From ", 5))
155 (void)putc('>', fp);
156 eline = 0;
157 }
158 (void)fprintf(fp, "%s", line);
159 if (ferror(fp))
160 break;
161 }
162
163 /* If message not newline terminated, need an extra. */
164 if (!index(line, '\n'))
165 (void)putc('\n', fp);
166 /* Output a newline; note, empty messages are allowed. */
167 (void)putc('\n', fp);
168
169 (void)fflush(fp);
170 if (ferror(fp))
171 err(FATAL, "temporary file write error");
172 return(fd);
173 }
174
175 int
176 deliver(fd, name, lockfile)
177 int fd;
178 char *name;
179 int lockfile;
180 {
181 struct stat sb;
182 struct passwd *pw;
183 int created, mbfd, nr, nw, off, rval=0, lfd=-1;
184 char biffmsg[100], buf[8*1024], path[MAXPATHLEN], lpath[MAXPATHLEN];
185 off_t curoff;
186
187 /*
188 * Disallow delivery to unknown names -- special mailboxes can be
189 * handled in the sendmail aliases file.
190 */
191 if (!(pw = getpwnam(name))) {
192 err(NOTFATAL, "unknown name: %s", name);
193 return(1);
194 }
195
196 (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
197
198 if (lockfile) {
199 (void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
200 _PATH_MAILDIR, name);
201
202 if((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL,
203 S_IRUSR|S_IWUSR)) < 0) {
204 err(NOTFATAL, "%s: %s", lpath, strerror(errno));
205 return(1);
206 }
207 }
208
209 if (!(created = lstat(path, &sb)) &&
210 (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) {
211 err(NOTFATAL, "%s: linked file", path);
212 return(1);
213 }
214 if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
215 S_IRUSR|S_IWUSR)) < 0) {
216 if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK,
217 S_IRUSR|S_IWUSR)) < 0) {
218 err(NOTFATAL, "%s: %s", path, strerror(errno));
219 return(1);
220 }
221 }
222
223 curoff = lseek(mbfd, 0, SEEK_END);
224 (void)snprintf(biffmsg, sizeof biffmsg, "%s@%qd\n", name,
225 (long long)curoff);
226 if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
227 err(FATAL, "temporary file: %s", strerror(errno));
228 rval = 1;
229 goto bad;
230 }
231
232 while ((nr = read(fd, buf, sizeof(buf))) > 0)
233 for (off = 0; off < nr; off += nw)
234 if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
235 err(NOTFATAL, "%s: %s", path, strerror(errno));
236 goto trunc;
237 }
238 if (nr < 0) {
239 err(FATAL, "temporary file: %s", strerror(errno));
240 trunc: (void)ftruncate(mbfd, curoff);
241 rval = 1;
242 }
243
244 /*
245 * Set the owner and group. Historically, binmail repeated this at
246 * each mail delivery. We no longer do this, assuming that if the
247 * ownership or permissions were changed there was a reason for doing
248 * so.
249 */
250 bad:
251 if (lockfile) {
252 if (lfd >= 0) {
253 unlink(lpath);
254 close(lfd);
255 }
256 }
257 if (created)
258 (void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
259
260 (void)fsync(mbfd); /* Don't wait for update. */
261 (void)close(mbfd); /* Implicit unlock. */
262
263 if (!rval)
264 notifybiff(biffmsg);
265 return(rval);
266 }
267
268 void
269 notifybiff(msg)
270 char *msg;
271 {
272 static struct sockaddr_in addr;
273 static int f = -1;
274 struct hostent *hp;
275 struct servent *sp;
276 int len;
277
278 if (!addr.sin_family) {
279 /* Be silent if biff service not available. */
280 if (!(sp = getservbyname("biff", "udp")))
281 return;
282 if (!(hp = gethostbyname("localhost"))) {
283 err(NOTFATAL, "localhost: %s", strerror(errno));
284 return;
285 }
286 addr.sin_len = sizeof(struct sockaddr_in);
287 addr.sin_family = hp->h_addrtype;
288 addr.sin_port = sp->s_port;
289 bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
290 }
291 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
292 err(NOTFATAL, "socket: %s", strerror(errno));
293 return;
294 }
295 len = strlen(msg) + 1;
296 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
297 != len)
298 err(NOTFATAL, "sendto biff: %s", strerror(errno));
299 }
300
301 void
302 usage()
303 {
304 err(FATAL, "usage: mail.local [-f from] user ...");
305 }
306
307 #if __STDC__
308 #include <stdarg.h>
309 #else
310 #include <varargs.h>
311 #endif
312
313 void
314 #if __STDC__
315 err(int isfatal, const char *fmt, ...)
316 #else
317 err(isfatal, fmt)
318 int isfatal;
319 char *fmt;
320 va_dcl
321 #endif
322 {
323 va_list ap;
324 #if __STDC__
325 va_start(ap, fmt);
326 #else
327 va_start(ap);
328 #endif
329 vsyslog(LOG_ERR, fmt, ap);
330 va_end(ap);
331 if (isfatal)
332 exit(1);
333 }
334