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