util.c revision 112987
1122679Sume/*
278064Sume * Copyright (c) 1989, 1993
378064Sume *	The Regents of the University of California.  All rights reserved.
478064Sume *
578064Sume * This code is derived from software contributed to Berkeley by
678064Sume * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
778064Sume *
878064Sume * Redistribution and use in source and binary forms, with or without
978064Sume * modification, are permitted provided that the following conditions
1078064Sume * are met:
1178064Sume * 1. Redistributions of source code must retain the above copyright
1278064Sume *    notice, this list of conditions and the following disclaimer.
1378064Sume * 2. Redistributions in binary form must reproduce the above copyright
1478064Sume *    notice, this list of conditions and the following disclaimer in the
1578064Sume *    documentation and/or other materials provided with the distribution.
1678064Sume * 3. All advertising materials mentioning features or use of this software
1778064Sume *    must display the following acknowledgement:
1878064Sume *	This product includes software developed by the University of
1978064Sume *	California, Berkeley and its contributors.
2078064Sume * 4. Neither the name of the University nor the names of its contributors
2178064Sume *    may be used to endorse or promote products derived from this software
2278064Sume *    without specific prior written permission.
2378064Sume *
2478064Sume * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2578064Sume * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2678064Sume * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2778064Sume * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2878064Sume * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2978064Sume * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3078064Sume * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3178064Sume * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3278064Sume * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3378064Sume * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3478064Sume * SUCH DAMAGE.
3578064Sume */
3678064Sume
3778064Sume#if 0
3878064Sume#ifndef lint
3978064Sumestatic char sccsid[] = "@(#)util.c	8.3 (Berkeley) 4/28/95";
4078064Sume#endif
4178064Sume#endif
4278064Sume
4378064Sume#include <sys/cdefs.h>
4478064Sume__FBSDID("$FreeBSD: head/usr.bin/finger/util.c 112987 2003-04-02 20:22:29Z rwatson $");
4578064Sume
4678064Sume#include <sys/param.h>
4778064Sume#include <sys/socket.h>
4878064Sume#include <sys/stat.h>
4978064Sume#include <ctype.h>
50173412Skevlo#include <db.h>
51173412Skevlo#include <err.h>
5278064Sume#include <errno.h>
53173412Skevlo#include <fcntl.h>
54173412Skevlo#include <paths.h>
5578064Sume#include <pwd.h>
5678064Sume#include <stdio.h>
5778064Sume#include <stdlib.h>
5878064Sume#include <string.h>
5978064Sume#include <unistd.h>
6078064Sume#include <utmp.h>
61122679Sume#include "finger.h"
6278064Sume#include "pathnames.h"
63122679Sume
6478064Sumestatic void	 find_idle_and_ttywrite(WHERE *);
6578064Sumestatic void	 userinfo(PERSON *, struct passwd *);
6678064Sumestatic WHERE	*walloc(PERSON *);
6778064Sume
68122679Sumeint
69122679Sumematch(struct passwd *pw, const char *user)
7078064Sume{
7178064Sume	char *p, *t;
7278064Sume	char name[1024];
7378064Sume
7478064Sume	if (!strcasecmp(pw->pw_name, user))
7578064Sume		return(1);
7678064Sume
7778064Sume	/*
7878064Sume	 * XXX
7978064Sume	 * Why do we skip asterisks!?!?
8078064Sume	 */
8178064Sume	(void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf));
8278064Sume	tbuf[sizeof(tbuf) - 1] = '\0';
8378064Sume	if (*p == '*')
8478064Sume		++p;
8578064Sume
8678064Sume	/* Ampersands get replaced by the login name. */
8778064Sume	if ((p = strtok(p, ",")) == NULL)
8878064Sume		return(0);
8978064Sume
9078064Sume	for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) {
9178064Sume		if (*t == '&') {
9278064Sume			(void)strncpy(t, pw->pw_name,
9378064Sume			    sizeof(name) - (t - name));
9478064Sume			name[sizeof(name) - 1] = '\0';
9578064Sume			while (t < &name[sizeof(name) - 1] && *++t)
9678064Sume				continue;
9778064Sume		} else {
9878064Sume			++t;
9978064Sume		}
10078064Sume	}
10178064Sume	*t = '\0';
10278064Sume	for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL)
10378064Sume		if (!strcasecmp(p, user))
10478064Sume			return(1);
10578064Sume	return(0);
10678064Sume}
10778064Sume
10878064Sumevoid
10978064Sumeenter_lastlog(PERSON *pn)
11078064Sume{
11178064Sume	WHERE *w;
11278064Sume	static int opened, fd;
11378064Sume	struct lastlog ll;
11478064Sume	char doit = 0;
11578064Sume
11678064Sume	/* some systems may not maintain lastlog, don't report errors. */
11778064Sume	if (!opened) {
11878064Sume		fd = open(_PATH_LASTLOG, O_RDONLY, 0);
11978064Sume		opened = 1;
12078064Sume	}
12178064Sume	if (fd == -1 ||
12278064Sume	    lseek(fd, (long)pn->uid * sizeof(ll), SEEK_SET) !=
12378064Sume	    (long)pn->uid * sizeof(ll) ||
124122679Sume	    read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) {
12578064Sume			/* as if never logged in */
12678064Sume			ll.ll_line[0] = ll.ll_host[0] = '\0';
12778064Sume			ll.ll_time = 0;
12878064Sume		}
12995023Ssuz	if ((w = pn->whead) == NULL)
13095023Ssuz		doit = 1;
13178064Sume	else if (ll.ll_time != 0) {
13278064Sume		/* if last login is earlier than some current login */
13378064Sume		for (; !doit && w != NULL; w = w->next)
13478064Sume			if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
13578064Sume				doit = 1;
13678064Sume		/*
137122679Sume		 * and if it's not any of the current logins
13878064Sume		 * can't use time comparison because there may be a small
13978064Sume		 * discrepancy since login calls time() twice
14078064Sume		 */
14178064Sume		for (w = pn->whead; doit && w != NULL; w = w->next)
14278064Sume			if (w->info == LOGGEDIN &&
14378064Sume			    strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
14478064Sume				doit = 0;
14578064Sume	}
14678064Sume	if (doit) {
14778064Sume		w = walloc(pn);
14878064Sume		w->info = LASTLOG;
14978064Sume		bcopy(ll.ll_line, w->tty, UT_LINESIZE);
15078064Sume		w->tty[UT_LINESIZE] = 0;
15178064Sume		bcopy(ll.ll_host, w->host, UT_HOSTSIZE);
15278064Sume		w->host[UT_HOSTSIZE] = 0;
15378064Sume		w->loginat = ll.ll_time;
15478064Sume	}
15578064Sume}
15678064Sume
15778064Sumevoid
15878064Sumeenter_where(struct utmp *ut, PERSON *pn)
15978064Sume{
16078064Sume	WHERE *w;
16178064Sume
16278064Sume	w = walloc(pn);
16378064Sume	w->info = LOGGEDIN;
16478064Sume	bcopy(ut->ut_line, w->tty, UT_LINESIZE);
16578064Sume	w->tty[UT_LINESIZE] = 0;
16678064Sume	bcopy(ut->ut_host, w->host, UT_HOSTSIZE);
16778064Sume	w->host[UT_HOSTSIZE] = 0;
16878064Sume	w->loginat = (time_t)ut->ut_time;
16978064Sume	find_idle_and_ttywrite(w);
17078064Sume}
17178064Sume
17278064SumePERSON *
17378064Sumeenter_person(struct passwd *pw)
17478064Sume{
17578064Sume	DBT data, key;
17678064Sume	PERSON *pn;
17778064Sume
17878064Sume	if (db == NULL &&
17978064Sume	    (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL)
18078064Sume		err(1, NULL);
18178064Sume
18278064Sume	key.data = pw->pw_name;
18378064Sume	key.size = strlen(pw->pw_name);
18478064Sume
18578064Sume	switch ((*db->get)(db, &key, &data, 0)) {
18678064Sume	case 0:
18778064Sume		memmove(&pn, data.data, sizeof pn);
18878064Sume		return (pn);
189122679Sume	default:
19078064Sume	case -1:
19178064Sume		err(1, "db get");
19278064Sume		/* NOTREACHED */
19378064Sume	case 1:
19478064Sume		++entries;
19578064Sume		pn = palloc();
19678064Sume		userinfo(pn, pw);
19778064Sume		pn->whead = NULL;
19878064Sume
19978064Sume		data.size = sizeof(PERSON *);
20078064Sume		data.data = &pn;
20178064Sume		if ((*db->put)(db, &key, &data, 0))
20278064Sume			err(1, "db put");
20378064Sume		return (pn);
20478064Sume	}
20578064Sume}
20678064Sume
20778064SumePERSON *
20878064Sumefind_person(const char *name)
20978064Sume{
21078064Sume	struct passwd *pw;
21178064Sume
21278064Sume	int cnt;
21378064Sume	DBT data, key;
21478064Sume	PERSON *p;
21578064Sume	char buf[UT_NAMESIZE + 1];
21678064Sume
21778064Sume	if (!db)
21878064Sume		return(NULL);
21978064Sume
22078064Sume	if ((pw = getpwnam(name)) && hide(pw))
22178064Sume		return(NULL);
22278064Sume
22378064Sume	/* Name may be only UT_NAMESIZE long and not NUL terminated. */
22478064Sume	for (cnt = 0; cnt < UT_NAMESIZE && *name; ++name, ++cnt)
22578064Sume		buf[cnt] = *name;
22678064Sume	buf[cnt] = '\0';
22778064Sume	key.data = buf;
22878064Sume	key.size = cnt;
22978064Sume
23078064Sume	if ((*db->get)(db, &key, &data, 0))
23178064Sume		return (NULL);
23278064Sume	memmove(&p, data.data, sizeof p);
23378064Sume	return (p);
23478064Sume}
23578064Sume
23678064SumePERSON *
23778064Sumepalloc(void)
23878064Sume{
23978064Sume	PERSON *p;
24078064Sume
24178064Sume	if ((p = malloc(sizeof(PERSON))) == NULL)
24278064Sume		err(1, NULL);
24378064Sume	return(p);
24478064Sume}
24578064Sume
24678064Sumestatic WHERE *
24778064Sumewalloc(PERSON *pn)
24878064Sume{
24978064Sume	WHERE *w;
25078064Sume
25178064Sume	if ((w = malloc(sizeof(WHERE))) == NULL)
25278064Sume		err(1, NULL);
25378064Sume	if (pn->whead == NULL)
25478064Sume		pn->whead = pn->wtail = w;
25578064Sume	else {
25678064Sume		pn->wtail->next = w;
25778064Sume		pn->wtail = w;
25878064Sume	}
25978064Sume	w->next = NULL;
26078064Sume	return(w);
26178064Sume}
262122679Sume
26378064Sumechar *
26478064Sumeprphone(char *num)
26578064Sume{
26678064Sume	char *p;
26778064Sume	int len;
26878064Sume	static char pbuf[20];
26978064Sume
27078064Sume	/* don't touch anything if the user has their own formatting */
27178064Sume	for (p = num; *p; ++p)
27278064Sume		if (!isdigit(*p))
27378064Sume			return(num);
27478064Sume	len = p - num;
27578064Sume	p = pbuf;
27678064Sume	switch(len) {
27778064Sume	case 11:			/* +0-123-456-7890 */
278122679Sume		*p++ = '+';
27978064Sume		*p++ = *num++;
28078064Sume		*p++ = '-';
28178064Sume		/* FALLTHROUGH */
28278064Sume	case 10:			/* 012-345-6789 */
28378064Sume		*p++ = *num++;
28478064Sume		*p++ = *num++;
28578064Sume		*p++ = *num++;
28678064Sume		*p++ = '-';
28778064Sume		/* FALLTHROUGH */
28878064Sume	case 7:				/* 012-3456 */
28978064Sume		*p++ = *num++;
29078064Sume		*p++ = *num++;
29178064Sume		*p++ = *num++;
29278064Sume		break;
29378064Sume	case 5:				/* x0-1234 */
294122679Sume	case 4:				/* x1234 */
29578064Sume		*p++ = 'x';
29678064Sume		*p++ = *num++;
29778064Sume		break;
29878064Sume	default:
29978064Sume		return(num);
30078064Sume	}
30178064Sume	if (len != 4) {
30278064Sume	    *p++ = '-';
30378064Sume	    *p++ = *num++;
30478064Sume	}
30578064Sume	*p++ = *num++;
30678064Sume	*p++ = *num++;
30778064Sume	*p++ = *num++;
30878064Sume	*p = '\0';
30978064Sume	return(pbuf);
31078064Sume}
31178064Sume
31278064Sumestatic void
31378064Sumefind_idle_and_ttywrite(WHERE *w)
31478064Sume{
31578064Sume	struct stat sb;
31678064Sume	time_t touched;
31778064Sume	int error;
31878064Sume
31978064Sume	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty);
32078064Sume
32178064Sume	error = stat(tbuf, &sb);
32278064Sume	if (error < 0 && errno == ENOENT) {
323122679Sume		/*
32478064Sume		 * The terminal listed is not actually a terminal (i.e.,
32578064Sume		 * ":0").  This is a failure, so we'll skip printing
32678064Sume		 * out the idle time, which is non-ideal but better
32778064Sume		 * than a bogus warning and idle time.
32878064Sume		 */
32978064Sume		w->idletime = -1;
33078064Sume		return;
33178064Sume	} else if (error < 0) {
33278064Sume		warn("%s", tbuf);
33378064Sume		w->idletime = -1;
33478064Sume		return;
33578064Sume	}
33678064Sume	touched = sb.st_atime;
33778064Sume	if (touched < w->loginat) {
33878064Sume		/* tty untouched since before login */
33978064Sume		touched = w->loginat;
34078064Sume	}
34178064Sume	w->idletime = now < touched ? 0 : now - touched;
34278064Sume
34378064Sume#define	TALKABLE	0220		/* tty is writable if 220 mode */
34478064Sume	w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
34578064Sume}
346
347static void
348userinfo(PERSON *pn, struct passwd *pw)
349{
350	char *p, *t;
351	char *bp, name[1024];
352	struct stat sb;
353
354	pn->realname = pn->office = pn->officephone = pn->homephone = NULL;
355
356	pn->uid = pw->pw_uid;
357	if ((pn->name = strdup(pw->pw_name)) == NULL)
358		err(1, "strdup failed");
359	if ((pn->dir = strdup(pw->pw_dir)) == NULL)
360		err(1, "strdup failed");
361	if ((pn->shell = strdup(pw->pw_shell)) == NULL)
362		err(1, "strdup failed");
363
364	/* why do we skip asterisks!?!? */
365	(void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf));
366	tbuf[sizeof(tbuf) - 1] = '\0';
367	if (*bp == '*')
368		++bp;
369
370	/* ampersands get replaced by the login name */
371	if (!(p = strsep(&bp, ",")))
372		return;
373	for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) {
374		if (*t == '&') {
375			(void)strncpy(t, pw->pw_name,
376			    sizeof(name) - (t - name));
377			name[sizeof(name) - 1] = '\0';
378			if (islower(*t))
379				*t = toupper(*t);
380			while (t < &name[sizeof(name) - 1] && *++t)
381				continue;
382		} else {
383			++t;
384		}
385	}
386	*t = '\0';
387	if ((pn->realname = strdup(name)) == NULL)
388		err(1, "strdup failed");
389	pn->office = ((p = strsep(&bp, ",")) && *p) ?
390	    strdup(p) : NULL;
391	pn->officephone = ((p = strsep(&bp, ",")) && *p) ?
392	    strdup(p) : NULL;
393	pn->homephone = ((p = strsep(&bp, ",")) && *p) ?
394	    strdup(p) : NULL;
395	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pw->pw_name);
396	pn->mailrecv = -1;		/* -1 == not_valid */
397	if (stat(tbuf, &sb) < 0) {
398		if (errno != ENOENT) {
399			warn("%s", tbuf);
400			return;
401		}
402	} else if (sb.st_size != 0) {
403		pn->mailrecv = sb.st_mtime;
404		pn->mailread = sb.st_atime;
405	}
406}
407
408/*
409 * Is this user hiding from finger?
410 * If ~<user>/.nofinger exists, return 1 (hide), else return 0 (nohide).
411 */
412
413int
414hide(struct passwd *pw)
415{
416	struct stat st;
417	char buf[MAXPATHLEN];
418
419	if (!pw->pw_dir)
420		return 0;
421
422	snprintf(buf, sizeof(buf), "%s/%s", pw->pw_dir, _PATH_NOFINGER);
423
424	if (stat(buf, &st) == 0)
425		return 1;
426
427	return 0;
428}
429