No empty .Rs/.Re
[netbsd-mini2440.git] / libexec / mail.local / mail.local.c
bloba848193a1d28f29848d0cb81ccbf570a55a65da9
1 /* $NetBSD: mail.local.c,v 1.24 2008/05/04 03:03:11 manu Exp $ */
3 /*-
4 * Copyright (c) 1990, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
35 The Regents of the University of California. All rights reserved.");
36 #if 0
37 static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 6/21/95";
38 #else
39 __RCSID("$NetBSD: mail.local.c,v 1.24 2008/05/04 03:03:11 manu Exp $");
40 #endif
41 #endif /* not lint */
43 #include <sys/param.h>
44 #include <sys/stat.h>
45 #include <sys/socket.h>
47 #include <netinet/in.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <pwd.h>
52 #include <netdb.h>
53 #include <stdarg.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <syslog.h>
58 #include <time.h>
59 #include <unistd.h>
60 #include <sysexits.h>
63 #include "pathnames.h"
65 int deliver __P((int, char *, int));
66 void logerr __P((int, const char *, ...))
67 __attribute__((__format__(__printf__, 2, 3)));
68 void logwarn __P((const char *, ...))
69 __attribute__((__format__(__printf__, 1, 2)));
70 void notifybiff __P((char *));
71 int store __P((const char *));
72 void usage __P((void));
73 int main __P((int, char **));
75 int
76 main(argc, argv)
77 int argc;
78 char **argv;
80 struct passwd *pw;
81 int ch, fd, eval, lockfile = 0;
82 uid_t uid;
83 const char *from;
85 /* use a reasonable umask */
86 (void) umask(0077);
88 openlog("mail.local", LOG_PERROR, LOG_MAIL);
90 from = NULL;
91 while ((ch = getopt(argc, argv, "ldf:r:")) != -1)
92 switch (ch) {
93 case 'd': /* backward compatible */
94 break;
95 case 'f':
96 case 'r': /* backward compatible */
97 if (from)
98 logerr(EX_USAGE, "multiple -f options");
99 from = optarg;
100 break;
101 case 'l':
102 lockfile++;
103 break;
104 case '?':
105 default:
106 usage();
108 argc -= optind;
109 argv += optind;
111 if (!*argv)
112 usage();
115 * If from not specified, use the name from getlogin() if the
116 * uid matches, otherwise, use the name from the password file
117 * corresponding to the uid.
119 uid = getuid();
120 if (!from && (!(from = getlogin()) ||
121 !(pw = getpwnam(from)) || pw->pw_uid != uid))
122 from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
124 fd = store(from);
125 for (eval = EX_OK; *argv; ++argv) {
126 int rval;
128 rval = deliver(fd, *argv, lockfile);
129 if (eval == EX_OK && rval != EX_OK)
130 eval = rval;
132 exit (eval);
136 store(from)
137 const char *from;
139 FILE *fp = NULL; /* XXX gcc */
140 time_t tval;
141 int fd, eline;
142 char *tn, line[2048];
144 tn = strdup(_PATH_LOCTMP);
145 if (!tn)
146 logerr(EX_OSERR, "not enough core");
147 if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+")))
148 logerr(EX_OSERR, "unable to open temporary file");
149 (void)unlink(tn);
150 free(tn);
152 (void)time(&tval);
153 (void)fprintf(fp, "From %s %s", from, ctime(&tval));
155 line[0] = '\0';
156 for (eline = 1; fgets(line, sizeof(line), stdin);) {
157 if (line[0] == '\n')
158 eline = 1;
159 else {
160 if (eline && line[0] == 'F' && !memcmp(line, "From ", 5))
161 (void)putc('>', fp);
162 eline = 0;
164 (void)fprintf(fp, "%s", line);
165 if (ferror(fp))
166 break;
169 /* If message not newline terminated, need an extra. */
170 if (!index(line, '\n'))
171 (void)putc('\n', fp);
172 /* Output a newline; note, empty messages are allowed. */
173 (void)putc('\n', fp);
175 (void)fflush(fp);
176 if (ferror(fp))
177 logerr(EX_OSERR, "temporary file write error");
178 fd = dup(fd);
179 (void)fclose(fp);
180 return(fd);
184 deliver(fd, name, lockfile)
185 int fd;
186 char *name;
187 int lockfile;
189 struct stat sb;
190 struct passwd pwres, *pw;
191 char pwbuf[1024];
192 int created, mbfd, nr, nw, off, rval=EX_OK, lfd=-1;
193 char biffmsg[100], buf[8*1024], path[MAXPATHLEN], lpath[MAXPATHLEN];
194 off_t curoff;
197 * Disallow delivery to unknown names -- special mailboxes can be
198 * handled in the sendmail aliases file.
200 if ((getpwnam_r(name, &pwres, pwbuf, sizeof(pwbuf), &pw)) != 0) {
201 logwarn("unable to find user %s: %s", name, strerror(errno));
202 return(EX_TEMPFAIL);
204 if (pw == NULL) {
205 logwarn("unknown name: %s", name);
206 return(EX_NOUSER);
209 (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
211 if (lockfile) {
212 (void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
213 _PATH_MAILDIR, name);
215 if((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL,
216 S_IRUSR|S_IWUSR)) < 0) {
217 logwarn("%s: %s", lpath, strerror(errno));
218 return(EX_OSERR);
222 if (!(created = lstat(path, &sb)) &&
223 (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) {
224 logwarn("%s: linked file", path);
225 return(EX_OSERR);
228 if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
229 S_IRUSR|S_IWUSR)) < 0) {
230 if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK,
231 S_IRUSR|S_IWUSR)) < 0) {
232 logwarn("%s: %s", path, strerror(errno));
233 return(EX_OSERR);
237 curoff = lseek(mbfd, 0, SEEK_END);
238 (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name,
239 (long long)curoff);
240 if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
241 logwarn("temporary file: %s", strerror(errno));
242 rval = EX_OSERR;
243 goto bad;
246 while ((nr = read(fd, buf, sizeof(buf))) > 0)
247 for (off = 0; off < nr; off += nw)
248 if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
249 logwarn("%s: %s", path, strerror(errno));
250 goto trunc;
252 if (nr < 0) {
253 logwarn("temporary file: %s", strerror(errno));
254 trunc: (void)ftruncate(mbfd, curoff);
255 rval = EX_OSERR;
259 * Set the owner and group. Historically, binmail repeated this at
260 * each mail delivery. We no longer do this, assuming that if the
261 * ownership or permissions were changed there was a reason for doing
262 * so.
264 bad:
265 if (lockfile) {
266 if (lfd >= 0) {
267 unlink(lpath);
268 close(lfd);
271 if (created)
272 (void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
274 (void)fsync(mbfd); /* Don't wait for update. */
275 (void)close(mbfd); /* Implicit unlock. */
277 if (rval == EX_OK)
278 notifybiff(biffmsg);
280 return rval;
283 void
284 notifybiff(msg)
285 char *msg;
287 static struct sockaddr_in addr;
288 static int f = -1;
289 struct hostent *hp;
290 struct servent *sp;
291 int len;
293 if (!addr.sin_family) {
294 /* Be silent if biff service not available. */
295 if (!(sp = getservbyname("biff", "udp")))
296 return;
297 if (!(hp = gethostbyname("localhost"))) {
298 logwarn("localhost: %s", strerror(errno));
299 return;
301 addr.sin_len = sizeof(struct sockaddr_in);
302 addr.sin_family = hp->h_addrtype;
303 addr.sin_port = sp->s_port;
304 memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
306 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
307 logwarn("socket: %s", strerror(errno));
308 return;
310 len = strlen(msg) + 1;
311 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
312 != len)
313 logwarn("sendto biff: %s", strerror(errno));
316 void
317 usage()
319 logerr(EX_USAGE, "usage: mail.local [-l] [-f from] user ...");
322 void
323 logerr(int status, const char *fmt, ...)
325 va_list ap;
327 va_start(ap, fmt);
328 vsyslog(LOG_ERR, fmt, ap);
329 va_end(ap);
331 exit(status);
332 /* NOTREACHED */
333 return;
336 void
337 logwarn(const char *fmt, ...)
339 va_list ap;
341 va_start(ap, fmt);
342 vsyslog(LOG_ERR, fmt, ap);
343 va_end(ap);
344 return;