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