util.c revision 72109
1169689Skan/*
2169689Skan * Copyright (c) 1989, 1993
3171825Skan *	The Regents of the University of California.  All rights reserved.
4169689Skan *
5169689Skan * This code is derived from software contributed to Berkeley by
6169689Skan * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
7169689Skan *
8169689Skan * Redistribution and use in source and binary forms, with or without
9169689Skan * modification, are permitted provided that the following conditions
10169689Skan * are met:
11169689Skan * 1. Redistributions of source code must retain the above copyright
12169689Skan *    notice, this list of conditions and the following disclaimer.
13169689Skan * 2. Redistributions in binary form must reproduce the above copyright
14169689Skan *    notice, this list of conditions and the following disclaimer in the
15169689Skan *    documentation and/or other materials provided with the distribution.
16169689Skan * 3. All advertising materials mentioning features or use of this software
17169689Skan *    must display the following acknowledgement:
18169689Skan *	This product includes software developed by the University of
19169689Skan *	California, Berkeley and its contributors.
20169689Skan * 4. Neither the name of the University nor the names of its contributors
21169689Skan *    may be used to endorse or promote products derived from this software
22169689Skan *    without specific prior written permission.
23169689Skan *
24169689Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25169689Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26169689Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27169689Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28169689Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29169689Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32169689Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33169689Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34169689Skan * SUCH DAMAGE.
35169689Skan */
36169689Skan
37169689Skan#ifndef lint
38169689Skan#if 0
39169689Skanstatic char sccsid[] = "@(#)util.c	8.3 (Berkeley) 4/28/95";
40169689Skan#endif
41169689Skanstatic const char rcsid[] =
42169689Skan  "$FreeBSD: head/usr.bin/finger/util.c 72109 2001-02-06 20:13:48Z charnier $";
43169689Skan#endif /* not lint */
44169689Skan
45169689Skan#include <sys/param.h>
46169689Skan#include <sys/stat.h>
47169689Skan#include <ctype.h>
48169689Skan#include <db.h>
49169689Skan#include <err.h>
50169689Skan#include <errno.h>
51169689Skan#include <fcntl.h>
52169689Skan#include <paths.h>
53169689Skan#include <pwd.h>
54169689Skan#include <stdio.h>
55169689Skan#include <stdlib.h>
56169689Skan#include <string.h>
57169689Skan#include <unistd.h>
58169689Skan#include <utmp.h>
59169689Skan#include "finger.h"
60169689Skan
61169689Skanstatic void	 find_idle_and_ttywrite __P((WHERE *));
62169689Skanstatic void	 userinfo __P((PERSON *, struct passwd *));
63169689Skanstatic WHERE	*walloc __P((PERSON *));
64169689Skan
65169689Skanint
66169689Skanmatch(pw, user)
67169689Skan	struct passwd *pw;
68169689Skan	char *user;
69169689Skan{
70169689Skan	register char *p, *t;
71169689Skan	char name[1024];
72169689Skan
73169689Skan	if (!strcasecmp(pw->pw_name, user))
74169689Skan		return(1);
75169689Skan
76169689Skan	/*
77169689Skan	 * XXX
78169689Skan	 * Why do we skip asterisks!?!?
79169689Skan	 */
80169689Skan	(void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf));
81169689Skan	tbuf[sizeof(tbuf) - 1] = '\0';
82169689Skan	if (*p == '*')
83169689Skan		++p;
84169689Skan
85169689Skan	/* Ampersands get replaced by the login name. */
86169689Skan	if ((p = strtok(p, ",")) == NULL)
87169689Skan		return(0);
88169689Skan
89169689Skan	for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) {
90169689Skan		if (*t == '&') {
91169689Skan			(void)strncpy(t, pw->pw_name,
92169689Skan			    sizeof(name) - (t - name));
93169689Skan			name[sizeof(name) - 1] = '\0';
94169689Skan			while (t < &name[sizeof(name) - 1] && *++t)
95169689Skan				continue;
96169689Skan		} else {
97169689Skan			++t;
98169689Skan		}
99169689Skan	}
100169689Skan	*t = '\0';
101169689Skan	for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL)
102169689Skan		if (!strcasecmp(p, user))
103169689Skan			return(1);
104169689Skan	return(0);
105169689Skan}
106169689Skan
107169689Skanvoid
108169689Skanenter_lastlog(pn)
109169689Skan	register PERSON *pn;
110169689Skan{
111169689Skan	register WHERE *w;
112169689Skan	static int opened, fd;
113169689Skan	struct lastlog ll;
114169689Skan	char doit = 0;
115169689Skan
116169689Skan	/* some systems may not maintain lastlog, don't report errors. */
117169689Skan	if (!opened) {
118169689Skan		fd = open(_PATH_LASTLOG, O_RDONLY, 0);
119169689Skan		opened = 1;
120169689Skan	}
121169689Skan	if (fd == -1 ||
122169689Skan	    lseek(fd, (long)pn->uid * sizeof(ll), SEEK_SET) !=
123169689Skan	    (long)pn->uid * sizeof(ll) ||
124169689Skan	    read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) {
125169689Skan			/* as if never logged in */
126169689Skan			ll.ll_line[0] = ll.ll_host[0] = '\0';
127169689Skan			ll.ll_time = 0;
128169689Skan		}
129169689Skan	if ((w = pn->whead) == NULL)
130169689Skan		doit = 1;
131169689Skan	else if (ll.ll_time != 0) {
132169689Skan		/* if last login is earlier than some current login */
133169689Skan		for (; !doit && w != NULL; w = w->next)
134169689Skan			if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
135169689Skan				doit = 1;
136169689Skan		/*
137169689Skan		 * and if it's not any of the current logins
138169689Skan		 * can't use time comparison because there may be a small
139169689Skan		 * discrepancy since login calls time() twice
140169689Skan		 */
141169689Skan		for (w = pn->whead; doit && w != NULL; w = w->next)
142169689Skan			if (w->info == LOGGEDIN &&
143169689Skan			    strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
144169689Skan				doit = 0;
145169689Skan	}
146169689Skan	if (doit) {
147169689Skan		w = walloc(pn);
148169689Skan		w->info = LASTLOG;
149169689Skan		bcopy(ll.ll_line, w->tty, UT_LINESIZE);
150169689Skan		w->tty[UT_LINESIZE] = 0;
151169689Skan		bcopy(ll.ll_host, w->host, UT_HOSTSIZE);
152169689Skan		w->host[UT_HOSTSIZE] = 0;
153169689Skan		w->loginat = ll.ll_time;
154169689Skan	}
155169689Skan}
156169689Skan
157169689Skanvoid
158169689Skanenter_where(ut, pn)
159169689Skan	struct utmp *ut;
160169689Skan	PERSON *pn;
161169689Skan{
162169689Skan	register WHERE *w;
163169689Skan
164169689Skan	w = walloc(pn);
165169689Skan	w->info = LOGGEDIN;
166169689Skan	bcopy(ut->ut_line, w->tty, UT_LINESIZE);
167169689Skan	w->tty[UT_LINESIZE] = 0;
168169689Skan	bcopy(ut->ut_host, w->host, UT_HOSTSIZE);
169169689Skan	w->host[UT_HOSTSIZE] = 0;
170169689Skan	w->loginat = (time_t)ut->ut_time;
171169689Skan	find_idle_and_ttywrite(w);
172169689Skan}
173169689Skan
174169689SkanPERSON *
175169689Skanenter_person(pw)
176169689Skan	register struct passwd *pw;
177169689Skan{
178169689Skan	DBT data, key;
179169689Skan	PERSON *pn;
180169689Skan
181169689Skan	if (db == NULL &&
182169689Skan	    (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL)
183169689Skan		err(1, NULL);
184169689Skan
185169689Skan	key.data = pw->pw_name;
186169689Skan	key.size = strlen(pw->pw_name);
187169689Skan
188169689Skan	switch ((*db->get)(db, &key, &data, 0)) {
189169689Skan	case 0:
190169689Skan		memmove(&pn, data.data, sizeof pn);
191169689Skan		return (pn);
192169689Skan	default:
193169689Skan	case -1:
194169689Skan		err(1, "db get");
195169689Skan		/* NOTREACHED */
196169689Skan	case 1:
197169689Skan		++entries;
198169689Skan		pn = palloc();
199169689Skan		userinfo(pn, pw);
200169689Skan		pn->whead = NULL;
201169689Skan
202169689Skan		data.size = sizeof(PERSON *);
203169689Skan		data.data = &pn;
204169689Skan		if ((*db->put)(db, &key, &data, 0))
205169689Skan			err(1, "db put");
206169689Skan		return (pn);
207169689Skan	}
208169689Skan}
209169689Skan
210169689SkanPERSON *
211169689Skanfind_person(name)
212169689Skan	char *name;
213169689Skan{
214169689Skan	struct passwd *pw;
215169689Skan
216169689Skan	register int cnt;
217169689Skan	DBT data, key;
218169689Skan	PERSON *p;
219169689Skan	char buf[UT_NAMESIZE + 1];
220169689Skan
221169689Skan	if (!db)
222169689Skan		return(NULL);
223169689Skan
224169689Skan	if ((pw = getpwnam(name)) && hide(pw))
225169689Skan		return(NULL);
226169689Skan
227169689Skan	/* Name may be only UT_NAMESIZE long and not NUL terminated. */
228169689Skan	for (cnt = 0; cnt < UT_NAMESIZE && *name; ++name, ++cnt)
229169689Skan		buf[cnt] = *name;
230169689Skan	buf[cnt] = '\0';
231169689Skan	key.data = buf;
232169689Skan	key.size = cnt;
233169689Skan
234169689Skan	if ((*db->get)(db, &key, &data, 0))
235169689Skan		return (NULL);
236169689Skan	memmove(&p, data.data, sizeof p);
237169689Skan	return (p);
238169689Skan}
239169689Skan
240169689SkanPERSON *
241169689Skanpalloc()
242169689Skan{
243169689Skan	PERSON *p;
244169689Skan
245169689Skan	if ((p = malloc((u_int) sizeof(PERSON))) == NULL)
246169689Skan		err(1, NULL);
247169689Skan	return(p);
248169689Skan}
249169689Skan
250169689Skanstatic WHERE *
251169689Skanwalloc(pn)
252169689Skan	register PERSON *pn;
253169689Skan{
254169689Skan	register WHERE *w;
255169689Skan
256169689Skan	if ((w = malloc((u_int) sizeof(WHERE))) == NULL)
257169689Skan		err(1, NULL);
258169689Skan	if (pn->whead == NULL)
259169689Skan		pn->whead = pn->wtail = w;
260169689Skan	else {
261169689Skan		pn->wtail->next = w;
262169689Skan		pn->wtail = w;
263169689Skan	}
264169689Skan	w->next = NULL;
265169689Skan	return(w);
266169689Skan}
267169689Skan
268169689Skanchar *
269169689Skanprphone(num)
270169689Skan	char *num;
271169689Skan{
272169689Skan	register char *p;
273169689Skan	int len;
274169689Skan	static char pbuf[20];
275169689Skan
276169689Skan	/* don't touch anything if the user has their own formatting */
277169689Skan	for (p = num; *p; ++p)
278169689Skan		if (!isdigit(*p))
279169689Skan			return(num);
280169689Skan	len = p - num;
281169689Skan	p = pbuf;
282169689Skan	switch(len) {
283169689Skan	case 11:			/* +0-123-456-7890 */
284169689Skan		*p++ = '+';
285169689Skan		*p++ = *num++;
286169689Skan		*p++ = '-';
287169689Skan		/* FALLTHROUGH */
288169689Skan	case 10:			/* 012-345-6789 */
289169689Skan		*p++ = *num++;
290169689Skan		*p++ = *num++;
291169689Skan		*p++ = *num++;
292169689Skan		*p++ = '-';
293169689Skan		/* FALLTHROUGH */
294169689Skan	case 7:				/* 012-3456 */
295169689Skan		*p++ = *num++;
296169689Skan		*p++ = *num++;
297169689Skan		*p++ = *num++;
298169689Skan		break;
299169689Skan	case 5:				/* x0-1234 */
300169689Skan	case 4:				/* x1234 */
301169689Skan		*p++ = 'x';
302169689Skan		*p++ = *num++;
303169689Skan		break;
304169689Skan	default:
305169689Skan		return(num);
306169689Skan	}
307169689Skan	if (len != 4) {
308169689Skan	    *p++ = '-';
309169689Skan	    *p++ = *num++;
310169689Skan	}
311169689Skan	*p++ = *num++;
312169689Skan	*p++ = *num++;
313169689Skan	*p++ = *num++;
314169689Skan	*p = '\0';
315169689Skan	return(pbuf);
316169689Skan}
317169689Skan
318169689Skanstatic void
319169689Skanfind_idle_and_ttywrite(w)
320169689Skan	register WHERE *w;
321169689Skan{
322169689Skan	extern time_t now;
323169689Skan	struct stat sb;
324169689Skan	time_t touched;
325169689Skan
326169689Skan	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty);
327169689Skan	if (stat(tbuf, &sb) < 0) {
328169689Skan		warn("%s", tbuf);
329169689Skan		return;
330169689Skan	}
331169689Skan	touched = sb.st_atime;
332169689Skan	if (touched < w->loginat) {
333169689Skan		/* tty untouched since before login */
334169689Skan		touched = w->loginat;
335169689Skan	}
336169689Skan	w->idletime = now < touched ? 0 : now - touched;
337169689Skan
338169689Skan#define	TALKABLE	0220		/* tty is writable if 220 mode */
339169689Skan	w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
340169689Skan}
341169689Skan
342169689Skanstatic void
343169689Skanuserinfo(pn, pw)
344169689Skan	register PERSON *pn;
345169689Skan	register struct passwd *pw;
346169689Skan{
347169689Skan	register char *p, *t;
348169689Skan	char *bp, name[1024];
349169689Skan	struct stat sb;
350169689Skan
351169689Skan	pn->realname = pn->office = pn->officephone = pn->homephone = NULL;
352169689Skan
353169689Skan	pn->uid = pw->pw_uid;
354169689Skan	if ((pn->name = strdup(pw->pw_name)) == NULL)
355169689Skan		err(1, "strdup failed");
356169689Skan	if ((pn->dir = strdup(pw->pw_dir)) == NULL)
357169689Skan		err(1, "strdup failed");
358169689Skan	if ((pn->shell = strdup(pw->pw_shell)) == NULL)
359169689Skan		err(1, "strdup failed");
360169689Skan
361169689Skan	/* why do we skip asterisks!?!? */
362169689Skan	(void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf));
363169689Skan	tbuf[sizeof(tbuf) - 1] = '\0';
364169689Skan	if (*bp == '*')
365169689Skan		++bp;
366169689Skan
367169689Skan	/* ampersands get replaced by the login name */
368169689Skan	if (!(p = strsep(&bp, ",")))
369169689Skan		return;
370169689Skan	for (t = name; t < &name[sizeof(name) - 1] && (*t = *p) != '\0'; ++p) {
371169689Skan		if (*t == '&') {
372169689Skan			(void)strncpy(t, pw->pw_name,
373169689Skan			    sizeof(name) - (t - name));
374169689Skan			name[sizeof(name) - 1] = '\0';
375169689Skan			if (islower(*t))
376169689Skan				*t = toupper(*t);
377169689Skan			while (t < &name[sizeof(name) - 1] && *++t)
378169689Skan				continue;
379169689Skan		} else {
380169689Skan			++t;
381169689Skan		}
382169689Skan	}
383169689Skan	*t = '\0';
384169689Skan	if ((pn->realname = strdup(name)) == NULL)
385169689Skan		err(1, "strdup failed");
386169689Skan	pn->office = ((p = strsep(&bp, ",")) && *p) ?
387169689Skan	    strdup(p) : NULL;
388169689Skan	pn->officephone = ((p = strsep(&bp, ",")) && *p) ?
389169689Skan	    strdup(p) : NULL;
390169689Skan	pn->homephone = ((p = strsep(&bp, ",")) && *p) ?
391169689Skan	    strdup(p) : NULL;
392169689Skan	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pw->pw_name);
393169689Skan	pn->mailrecv = -1;		/* -1 == not_valid */
394169689Skan	if (stat(tbuf, &sb) < 0) {
395169689Skan		if (errno != ENOENT) {
396169689Skan			warn("%s", tbuf);
397169689Skan			return;
398169689Skan		}
399169689Skan	} else if (sb.st_size != 0) {
400169689Skan		pn->mailrecv = sb.st_mtime;
401169689Skan		pn->mailread = sb.st_atime;
402169689Skan	}
403169689Skan}
404169689Skan
405169689Skan/*
406169689Skan * Is this user hiding from finger?
407169689Skan * If ~<user>/.nofinger exists, return 1 (hide), else return 0 (nohide).
408169689Skan */
409169689Skan
410169689Skanint
411169689Skanhide(pw)
412169689Skan	struct passwd *pw;
413169689Skan{
414169689Skan	char buf[MAXPATHLEN+1];
415169689Skan
416169689Skan	if (!pw->pw_dir)
417169689Skan		return 0;
418169689Skan
419169689Skan	snprintf(buf, sizeof(buf), "%s/.nofinger", pw->pw_dir);
420169689Skan	buf[sizeof(buf) - 1] = '\0';
421169689Skan
422169689Skan	if (access(buf, F_OK) == 0)
423169689Skan		return 1;
424169689Skan
425169689Skan	return 0;
426169689Skan}
427169689Skan