comsat.c revision 99632
139287Ssos/*
239643Syokota * Copyright (c) 1980, 1993
339287Ssos *	The Regents of the University of California.  All rights reserved.
439287Ssos *
539287Ssos * Redistribution and use in source and binary forms, with or without
639287Ssos * modification, are permitted provided that the following conditions
739287Ssos * are met:
839287Ssos * 1. Redistributions of source code must retain the above copyright
939643Syokota *    notice, this list of conditions and the following disclaimer.
1039643Syokota * 2. Redistributions in binary form must reproduce the above copyright
1139287Ssos *    notice, this list of conditions and the following disclaimer in the
1239287Ssos *    documentation and/or other materials provided with the distribution.
1339287Ssos * 3. All advertising materials mentioning features or use of this software
1439287Ssos *    must display the following acknowledgement:
1539643Syokota *	This product includes software developed by the University of
1639643Syokota *	California, Berkeley and its contributors.
1739643Syokota * 4. Neither the name of the University nor the names of its contributors
1839643Syokota *    may be used to endorse or promote products derived from this software
1939643Syokota *    without specific prior written permission.
2039643Syokota *
2139643Syokota * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2239643Syokota * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2339643Syokota * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2439643Syokota * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2539287Ssos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2650477Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2739287Ssos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2839287Ssos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2942504Syokota * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3066710Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3139287Ssos * SUCH DAMAGE.
3256836Speter */
3339287Ssos
3439287Ssos#ifndef lint
3539287Ssosstatic const char copyright[] =
3639287Ssos"@(#) Copyright (c) 1980, 1993\n\
3742179Syokota	The Regents of the University of California.  All rights reserved.\n";
3839287Ssos#endif /* not lint */
3948104Syokota
4048104Syokota#ifndef lint
4139287Ssos#if 0
4248104Syokotastatic char sccsid[] = "@(#)comsat.c	8.1 (Berkeley) 6/4/93";
4348104Syokota#endif
4439287Ssosstatic const char rcsid[] =
4539287Ssos  "$FreeBSD: head/libexec/comsat/comsat.c 99632 2002-07-09 02:16:49Z johan $";
4639287Ssos#endif /* not lint */
4739287Ssos
4839287Ssos#include <sys/param.h>
4939287Ssos#include <sys/socket.h>
5039287Ssos#include <sys/stat.h>
5142504Syokota#include <sys/file.h>
5242504Syokota#include <sys/wait.h>
5339287Ssos
5442504Syokota#include <netinet/in.h>
5542504Syokota
5642504Syokota#include <ctype.h>
5742504Syokota#include <err.h>
5842504Syokota#include <errno.h>
5942504Syokota#include <netdb.h>
6042504Syokota#include <paths.h>
6142611Syokota#include <pwd.h>
6242504Syokota#include <termios.h>
6342504Syokota#include <signal.h>
6439287Ssos#include <stdio.h>
6539287Ssos#include <stdlib.h>
6639287Ssos#include <string.h>
6739287Ssos#include <syslog.h>
6839287Ssos#include <unistd.h>
6939287Ssos#include <utmp.h>
7039287Ssos
7139287Ssosint	debug = 0;
7239287Ssos#define	dsyslog	if (debug) syslog
7339287Ssos
7439287Ssos#define MAXIDLE	120
7539287Ssos
7639287Ssoschar	hostname[MAXHOSTNAMELEN];
7748399Speterstruct	utmp *utmp = NULL;
7842504Syokotatime_t	lastmsgtime;
7948399Speterint	nutmp, uf;
8048104Syokota
8142504Syokotavoid jkfprintf(FILE *, char[], char[], off_t);
8239287Ssosvoid mailfor(char *);
8339287Ssosvoid notify(struct utmp *, char[], off_t, int);
8439287Ssosvoid onalrm(int);
8539287Ssosvoid reapchildren(int);
8639287Ssos
8739287Ssosint
8839287Ssosmain(int argc, char *argv[])
8939287Ssos{
9039287Ssos	struct sockaddr_in from;
9139287Ssos	int cc;
9239287Ssos	int fromlen;
9339287Ssos	char msgbuf[256];
9439287Ssos
9539287Ssos	/* verify proper invocation */
9639287Ssos	fromlen = sizeof(from);
9742504Syokota	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
9848104Syokota		err(1, "getsockname");
9942504Syokota	openlog("comsat", LOG_PID, LOG_DAEMON);
10048104Syokota	if (chdir(_PATH_MAILDIR)) {
10148104Syokota		syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR);
10248104Syokota		(void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
10348104Syokota		exit(1);
10439287Ssos	}
10548104Syokota	if ((uf = open(_PATH_UTMP, O_RDONLY, 0)) < 0) {
10644846Sjlemon		syslog(LOG_ERR, "open: %s: %m", _PATH_UTMP);
10739287Ssos		(void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
10842504Syokota		exit(1);
10942504Syokota	}
11042504Syokota	(void)time(&lastmsgtime);
11142504Syokota	(void)gethostname(hostname, sizeof(hostname));
11242504Syokota	onalrm(0);
11342504Syokota	(void)signal(SIGALRM, onalrm);
11442504Syokota	(void)signal(SIGTTOU, SIG_IGN);
11542504Syokota	(void)signal(SIGCHLD, reapchildren);
11642504Syokota	for (;;) {
11742504Syokota		cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0);
11842504Syokota		if (cc <= 0) {
11942504Syokota			if (errno != EINTR)
12042504Syokota				sleep(1);
12142504Syokota			errno = 0;
12242504Syokota			continue;
12342504Syokota		}
12442504Syokota		if (!nutmp)		/* no one has logged in yet */
12542504Syokota			continue;
12648104Syokota		sigblock(sigmask(SIGALRM));
12742504Syokota		msgbuf[cc] = '\0';
12848104Syokota		(void)time(&lastmsgtime);
12948104Syokota		mailfor(msgbuf);
13048104Syokota		sigsetmask(0L);
13148104Syokota	}
13248104Syokota}
13348104Syokota
13439287Ssosvoid
13539287Ssosreapchildren(int signo)
13639287Ssos{
13742504Syokota	while (wait3(NULL, WNOHANG, NULL) > 0);
13839287Ssos}
13939287Ssos
14039287Ssosvoid
14139287Ssosonalrm(int signo)
14239287Ssos{
14339287Ssos	static u_int utmpsize;		/* last malloced size for utmp */
14448104Syokota	static u_int utmpmtime;		/* last modification time for utmp */
14539287Ssos	struct stat statbf;
14648104Syokota
14748104Syokota	if (time(NULL) - lastmsgtime >= MAXIDLE)
14848104Syokota		exit(0);
14948104Syokota	(void)alarm((u_int)15);
15039287Ssos	(void)fstat(uf, &statbf);
15139287Ssos	if (statbf.st_mtime > utmpmtime) {
15239287Ssos		utmpmtime = statbf.st_mtime;
15339287Ssos		if (statbf.st_size > utmpsize) {
15439858Syokota			utmpsize = statbf.st_size + 10 * sizeof(struct utmp);
15539858Syokota			if ((utmp = realloc(utmp, utmpsize)) == NULL) {
15639858Syokota				syslog(LOG_ERR, "%s", strerror(errno));
15739858Syokota				exit(1);
15839287Ssos			}
15939287Ssos		}
16039287Ssos		(void)lseek(uf, (off_t)0, SEEK_SET);
16139287Ssos		nutmp = read(uf, utmp, (size_t)statbf.st_size)/sizeof(struct utmp);
16242504Syokota	}
16339287Ssos}
16439287Ssos
16545117Syokotavoid
16639287Ssosmailfor(char *name)
16742729Syokota{
16842729Syokota	struct utmp *utp = &utmp[nutmp];
16948104Syokota	char *cp;
17048104Syokota	char *file;
17142729Syokota	off_t offset;
17242729Syokota	int folder;
17348399Speter	char buf[sizeof(_PATH_MAILDIR) + sizeof(utmp[0].ut_name) + 1];
17448104Syokota	char buf2[sizeof(_PATH_MAILDIR) + sizeof(utmp[0].ut_name) + 1];
17548104Syokota
17648399Speter	if (!(cp = strchr(name, '@')))
17739287Ssos		return;
17839287Ssos	*cp = '\0';
17939287Ssos	offset = strtoll(cp + 1, NULL, 10);
18039287Ssos	if (!(cp = strchr(cp + 1, ':')))
18139287Ssos		file = name;
18239287Ssos	else
18339287Ssos		file = cp + 1;
18439287Ssos	sprintf(buf, "%s/%.*s", _PATH_MAILDIR, (int)sizeof(utmp[0].ut_name),
18539287Ssos	    name);
18639287Ssos	if (*file != '/') {
18739287Ssos		sprintf(buf2, "%s/%.*s", _PATH_MAILDIR,
18843664Syokota		    (int)sizeof(utmp[0].ut_name), file);
18950446Syokota		file = buf2;
19048399Speter	}
19148104Syokota	folder = strcmp(buf, file);
19248399Speter	while (--utp >= utmp)
19348104Syokota		if (!strncmp(utp->ut_name, name, sizeof(utmp[0].ut_name)))
19439591Syokota			notify(utp, file, offset, folder);
19539591Syokota}
19648104Syokota
19739858Syokotastatic char *cr;
19839858Syokota
19939287Ssosvoid
20039591Syokotanotify(struct utmp *utp, char file[], off_t offset, int folder)
20148104Syokota{
20248104Syokota	FILE *tp;
20339287Ssos	struct stat stb;
20448399Speter	struct termios tio;
20548104Syokota	char tty[20], name[sizeof(utmp[0].ut_name) + 1];
20648399Speter
20748104Syokota	(void)snprintf(tty, sizeof(tty), "%s%.*s",
20839287Ssos	    _PATH_DEV, (int)sizeof(utp->ut_line), utp->ut_line);
20939287Ssos	if (strchr(tty + sizeof(_PATH_DEV) - 1, '/')) {
21039287Ssos		/* A slash is an attempt to break security... */
21139287Ssos		syslog(LOG_AUTH | LOG_NOTICE, "'/' in \"%s\"", tty);
21239287Ssos		return;
21339287Ssos	}
21439287Ssos	if (stat(tty, &stb) || !(stb.st_mode & (S_IXUSR | S_IXGRP))) {
21539287Ssos		dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_name, tty);
21639287Ssos		return;
21739287Ssos	}
21839287Ssos	dsyslog(LOG_DEBUG, "notify %s on %s\n", utp->ut_name, tty);
21939287Ssos	if (fork())
22042504Syokota		return;
22142504Syokota	(void)signal(SIGALRM, SIG_DFL);
22242504Syokota	(void)alarm((u_int)30);
22342504Syokota	if ((tp = fopen(tty, "w")) == NULL) {
22442504Syokota		dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno));
22542504Syokota		_exit(1);
22642504Syokota	}
22742504Syokota	(void)tcgetattr(fileno(tp), &tio);
22842504Syokota	cr = ((tio.c_oflag & (OPOST|ONLCR)) == (OPOST|ONLCR)) ?  "\n" : "\n\r";
22942504Syokota	(void)strncpy(name, utp->ut_name, sizeof(utp->ut_name));
23042504Syokota	name[sizeof(name) - 1] = '\0';
23142504Syokota	switch (stb.st_mode & (S_IXUSR | S_IXGRP)) {
23239287Ssos	case S_IXUSR:
23339287Ssos	case (S_IXUSR | S_IXGRP):
23439287Ssos		(void)fprintf(tp,
23539287Ssos		    "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s",
23639287Ssos		    cr, name, (int)sizeof(hostname), hostname,
23744846Sjlemon		    folder ? cr : "", folder ? "to " : "", folder ? file : "",
23839287Ssos		    cr, cr);
23939287Ssos		jkfprintf(tp, name, file, offset);
24039287Ssos		break;
24139287Ssos	case S_IXGRP:
24239287Ssos		(void)fprintf(tp, "\007");
24344846Sjlemon		(void)fflush(tp);
24444866Sjlemon		(void)sleep(1);
24544846Sjlemon		(void)fprintf(tp, "\007");
24644846Sjlemon		break;
24750445Syokota	default:
24839287Ssos		break;
24939287Ssos	}
25039287Ssos	(void)fclose(tp);
25139287Ssos	_exit(0);
25239287Ssos}
25339287Ssos
25439287Ssosvoid
25539287Ssosjkfprintf(FILE *tp, char user[], char file[], off_t offset)
25639287Ssos{
25739287Ssos	unsigned char *cp, ch;
25839287Ssos	FILE *fi;
25939287Ssos	int linecnt, charcnt, inheader;
26039287Ssos	struct passwd *p;
26139287Ssos	unsigned char line[BUFSIZ];
26239287Ssos
26350445Syokota	/* Set effective uid to user in case mail drop is on nfs */
26439287Ssos	if ((p = getpwnam(user)) != NULL)
26539287Ssos		(void) setuid(p->pw_uid);
26639287Ssos
26745117Syokota	if ((fi = fopen(file, "r")) == NULL)
26845117Syokota		return;
26945117Syokota
27045117Syokota	(void)fseeko(fi, offset, SEEK_CUR);
27145117Syokota	/*
27245117Syokota	 * Print the first 7 lines or 560 characters of the new mail
27345117Syokota	 * (whichever comes first).  Skip header crap other than
27445117Syokota	 * From, Subject, To, and Date.
27545117Syokota	 */
27650445Syokota	linecnt = 7;
27745117Syokota	charcnt = 560;
27845117Syokota	inheader = 1;
27945117Syokota	while (fgets(line, sizeof(line), fi) != NULL) {
28045117Syokota		if (inheader) {
28145117Syokota			if (line[0] == '\n') {
28239287Ssos				inheader = 0;
28339287Ssos				continue;
28439287Ssos			}
28539287Ssos			if (line[0] == ' ' || line[0] == '\t' ||
28639287Ssos			    (strncmp(line, "From:", 5) &&
28739287Ssos			    strncmp(line, "Subject:", 8)))
28839287Ssos				continue;
28939287Ssos		}
29039287Ssos		if (linecnt <= 0 || charcnt <= 0) {
29150445Syokota			(void)fprintf(tp, "...more...%s", cr);
29242729Syokota			(void)fclose(fi);
29342729Syokota			return;
29439287Ssos		}
29539287Ssos		/* strip weird stuff so can't trojan horse stupid terminals */
29639287Ssos		for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) {
29742729Syokota			/* disable upper controls and enable all other
29839287Ssos			   8bit codes due to lack of locale knowledge
29939287Ssos			 */
30039287Ssos			if (((ch & 0x80) && ch < 0xA0) ||
30139287Ssos			    (!(ch & 0x80) && !isprint(ch) &&
30239287Ssos			     !isspace(ch) && ch != '\a' && ch != '\b')
30339287Ssos			   ) {
30439287Ssos				if (ch & 0x80) {
30539287Ssos					ch &= ~0x80;
30639287Ssos					(void)fputs("M-", tp);
30739287Ssos				}
30839287Ssos				if (iscntrl(ch)) {
30944846Sjlemon					ch ^= 0x40;
31044866Sjlemon					(void)fputc('^', tp);
31144846Sjlemon				}
31244846Sjlemon			}
31350445Syokota			(void)fputc(ch, tp);
31439287Ssos		}
31539287Ssos		(void)fputs(cr, tp);
31642729Syokota		--linecnt;
31739287Ssos	}
31842729Syokota	(void)fprintf(tp, "----%s\n", cr);
31942729Syokota	(void)fclose(fi);
32042729Syokota}
32139287Ssos