Prototype declarations.
[unix-history] / usr / src / libexec / mail.local / mail.local.c
CommitLineData
4d054d75
KB
1/*-
2 * Copyright (c) 1990 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8#ifndef lint
9char copyright[] =
10"@(#) Copyright (c) 1990 The Regents of the University of California.\n\
11 All rights reserved.\n";
12#endif /* not lint */
13
205a2d85 14#ifndef lint
8775b031 15static char sccsid[] = "@(#)mail.local.c 5.1 (Berkeley) %G%";
4d054d75 16#endif /* not lint */
205a2d85 17
ff8eb9b2 18#include <sys/param.h>
4e161d3b
RC
19#include <sys/stat.h>
20#include <sys/file.h>
4d054d75
KB
21#include <sys/socket.h>
22#include <sys/syslog.h>
23#include <sys/errno.h>
24#include <netinet/in.h>
25#include <netdb.h>
1c12a606 26#include <pwd.h>
4d054d75
KB
27#include <time.h>
28#include <varargs.h>
29#include <stdio.h>
30#include <string.h>
31#include <stdlib.h>
960433c0 32#include "pathnames.h"
1c12a606 33
4d054d75
KB
34#define FATAL 1
35#define NOTFATAL 0
1c12a606
BJ
36
37main(argc, argv)
4d054d75 38 int argc;
4e161d3b 39 char **argv;
1c12a606 40{
4d054d75
KB
41 extern int optind;
42 extern char *optarg;
43 struct passwd *pw;
44 int ch, fd, eval;
45 uid_t uid;
46 char *from;
4e161d3b 47
4d054d75 48 openlog("mail.local", LOG_PERROR, LOG_MAIL);
4e161d3b 49
4d054d75
KB
50 from = NULL;
51 while ((ch = getopt(argc, argv, "df:r:")) != EOF)
52 switch(ch) {
53 case 'd': /* backward compatible */
1c12a606 54 break;
4d054d75
KB
55 case 'f':
56 case 'r': /* backward compatible */
57 if (from)
58 error(FATAL, "multiple -f options.");
59 from = optarg;
4e161d3b 60 break;
4d054d75 61 case '?':
4e161d3b 62 default:
4d054d75 63 usage();
1c12a606 64 }
4d054d75
KB
65 argc -= optind;
66 argv += optind;
1c12a606 67
4d054d75
KB
68 if (!*argv)
69 usage();
1c12a606 70
4d054d75
KB
71 /*
72 * If from not specified, use the name from getlogin() if the
73 * uid matches, otherwise, use the name from the password file
74 * corresponding to the uid.
75 */
76 uid = getuid();
8775b031
KB
77 if (!from && (!(from = getlogin()) ||
78 !(pw = getpwnam(from)) || pw->pw_uid != uid))
4d054d75
KB
79 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
80
81 fd = store(from);
82 for (eval = 0; *argv; ++argv)
83 eval |= deliver(fd, *argv);
84 exit(eval);
1c12a606
BJ
85}
86
4d054d75
KB
87store(from)
88 char *from;
1c12a606 89{
4d054d75
KB
90 FILE *fp;
91 time_t tval;
92 int fd, eline;
93 char *tn, line[2048];
1c12a606 94
4d054d75
KB
95 tn = _PATH_LOCTMP;
96 if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+")))
97 error(FATAL, "unable to open temporary file.");
98 (void)unlink(tn);
1c12a606 99
4d054d75
KB
100 (void)time(&tval);
101 (void)fprintf(fp, "From %s %s", from, ctime(&tval));
102
103 line[0] = '\0';
104 for (eline = 1; fgets(line, sizeof(line), stdin);) {
105 if (line[0] == '\n')
106 eline = 1;
107 else {
108 if (eline && line[0] == 'F' && !bcmp(line, "From ", 5))
109 (void)putc('>', fp);
110 eline = 0;
111 }
112 (void)fprintf(fp, "%s", line);
113 if (ferror(fp))
114 break;
4e161d3b 115 }
1c12a606 116
4d054d75
KB
117 /* If message not newline terminated, need an extra. */
118 if (!index(line, '\n'))
119 (void)putc('\n', fp);
120 /* Output a newline; note, empty messages are allowed. */
121 (void)putc('\n', fp);
1c12a606 122
4d054d75
KB
123 (void)fflush(fp);
124 if (ferror(fp))
125 error(FATAL, "temporary file write error.");
126 return(fd);
1c12a606
BJ
127}
128
4d054d75
KB
129deliver(fd, name)
130 int fd;
131 char *name;
1c12a606 132{
4d054d75
KB
133 struct stat sb;
134 struct passwd *pw;
135 int created, mbfd, nr, nw, off, rval;
136 char biffmsg[100], buf[8*1024], path[MAXPATHLEN];
137 off_t curoff, lseek();
1c12a606
BJ
138
139 /*
4d054d75
KB
140 * Disallow delivery to unknown names -- special mailboxes can be
141 * handled in the sendmail aliases file.
1c12a606 142 */
4d054d75
KB
143 if (!(pw = getpwnam(name))) {
144 error(NOTFATAL, "unknown name: %s.", name);
145 return(1);
146 }
1c12a606 147
4d054d75 148 (void)sprintf(path, "%s/%s", _PATH_MAILDIR, name);
1c12a606 149
4d054d75
KB
150 if (!(created = lstat(path, &sb)) &&
151 (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) {
152 error(NOTFATAL, "%s: linked file.", path);
153 return(1);
1c12a606 154 }
1c12a606 155
4d054d75
KB
156 /*
157 * There's a race here -- two processes think they both created
158 * the file. This means the file cannot be unlinked.
159 */
160 if ((mbfd =
161 open(path, O_APPEND|O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR)) < 0) {
162 error(NOTFATAL, "%s: %s.", path, strerror(errno));
163 return(1);
1c12a606 164 }
4d054d75
KB
165
166 rval = 0;
167 /* XXX: Open should allow flock'ing the file; see 4.4BSD. */
168 if (flock(mbfd, LOCK_EX)) {
169 error(NOTFATAL, "%s: %s.", path, strerror(errno));
170 rval = 1;
171 goto bad;
1c12a606 172 }
4d054d75
KB
173
174 curoff = lseek(mbfd, 0L, SEEK_END);
175 (void)sprintf(biffmsg, "%s@%ld\n", name, curoff);
176 if (lseek(fd, 0L, SEEK_SET) == (off_t)-1) {
177 error(FATAL, "temporary file: %s.", strerror(errno));
178 rval = 1;
179 goto bad;
1c12a606 180 }
4d054d75
KB
181
182 while ((nr = read(fd, buf, sizeof(buf))) > 0)
183 for (off = 0; off < nr; nr -= nw, off += nw)
184 if ((nw = write(mbfd, buf + off, nr)) < 0) {
185 error(NOTFATAL,
186 "%s: %s.", path, strerror(errno));
187 goto trunc;
188 }
189 if (nr < 0) {
190 error(FATAL, "temporary file: %s.", strerror(errno));
191trunc: (void)ftruncate(mbfd, curoff);
192 rval = 1;
1c12a606 193 }
1c12a606 194
4d054d75
KB
195 /*
196 * Set the owner and group. Historically, binmail repeated this at
197 * each mail delivery. We no longer do this, assuming that if the
198 * ownership or permissions were changed there was a reason for doing
199 * so.
200 */
201bad: if (created)
202 (void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
1c12a606 203
4d054d75
KB
204 /* Implicit unlock. */
205 (void)close(mbfd);
1c12a606 206
4d054d75
KB
207 if (!rval)
208 notifybiff(biffmsg);
209 return(rval);
210}
4e161d3b
RC
211
212notifybiff(msg)
213 char *msg;
214{
215 static struct sockaddr_in addr;
216 static int f = -1;
4d054d75
KB
217 struct hostent *hp;
218 struct servent *sp;
219 int len;
4e161d3b 220
4d054d75
KB
221 if (!addr.sin_family) {
222 /* Be silent if biff service not available. */
223 if (!(sp = getservbyname("biff", "udp")))
224 return;
225 if (!(hp = gethostbyname("localhost"))) {
226 error(NOTFATAL, "localhost: %s.", strerror(errno));
227 return;
4e161d3b 228 }
4d054d75
KB
229 addr.sin_family = hp->h_addrtype;
230 bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
231 addr.sin_port = sp->s_port;
4e161d3b 232 }
4d054d75
KB
233 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
234 error(NOTFATAL, "socket: %s.", strerror(errno));
235 return;
ff8eb9b2 236 }
4d054d75
KB
237 len = strlen(msg) + 1;
238 if (sendto(f, msg, len, 0, &addr, sizeof(addr)) != len)
239 error(NOTFATAL, "sendto biff: %s.", strerror(errno));
1c12a606
BJ
240}
241
4d054d75 242usage()
1c12a606 243{
4d054d75 244 error(FATAL, "usage: mail.local [-f from] user ...");
1c12a606 245}
fd64b7dc 246
4d054d75
KB
247/* VARARGS */
248error(va_alist)
249va_dcl
fd64b7dc 250{
4d054d75
KB
251 va_list ap;
252 int isfatal;
253 char *fmt;
254
255 va_start(ap);
256 isfatal = va_arg(ap, int);
257 fmt = va_arg(ap, char *);
258 vsyslog(LOG_ERR, fmt, ap);
259 va_end(ap);
260 if (isfatal)
261 exit(1);
4e161d3b 262}