util.c revision 23693
1/*
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Tony Nardo of the Johns Hopkins University/Applied Physics Lab.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static char sccsid[] = "@(#)util.c	8.3 (Berkeley) 4/28/95";
39#endif /* not lint */
40
41#include <sys/param.h>
42#include <sys/stat.h>
43#include <fcntl.h>
44#include <db.h>
45#include <err.h>
46#include <pwd.h>
47#include <utmp.h>
48#include <errno.h>
49#include <unistd.h>
50#include <stdio.h>
51#include <ctype.h>
52#include <stdlib.h>
53#include <string.h>
54#include <paths.h>
55#include <errno.h>
56#include "finger.h"
57
58static void	 find_idle_and_ttywrite __P((WHERE *));
59static void	 userinfo __P((PERSON *, struct passwd *));
60static WHERE	*walloc __P((PERSON *));
61
62int
63match(pw, user)
64	struct passwd *pw;
65	char *user;
66{
67	register char *p, *t;
68	char name[1024];
69
70	if (!strcasecmp(pw->pw_name, user))
71		return(1);
72
73	/*
74	 * XXX
75	 * Why do we skip asterisks!?!?
76	 */
77	(void)strcpy(p = tbuf, pw->pw_gecos);
78	if (*p == '*')
79		++p;
80
81	/* Ampersands get replaced by the login name. */
82	if ((p = strtok(p, ",")) == NULL)
83		return(0);
84
85	for (t = name; (*t = *p) != '\0'; ++p)
86		if (*t == '&') {
87			(void)strcpy(t, pw->pw_name);
88			while (*++t);
89		}
90		else
91			++t;
92	for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL)
93		if (!strcasecmp(p, user))
94			return(1);
95	return(0);
96}
97
98void
99enter_lastlog(pn)
100	register PERSON *pn;
101{
102	register WHERE *w;
103	static int opened, fd;
104	struct lastlog ll;
105	char doit = 0;
106
107	/* some systems may not maintain lastlog, don't report errors. */
108	if (!opened) {
109		fd = open(_PATH_LASTLOG, O_RDONLY, 0);
110		opened = 1;
111	}
112	if (fd == -1 ||
113	    lseek(fd, (long)pn->uid * sizeof(ll), SEEK_SET) !=
114	    (long)pn->uid * sizeof(ll) ||
115	    read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) {
116			/* as if never logged in */
117			ll.ll_line[0] = ll.ll_host[0] = NULL;
118			ll.ll_time = 0;
119		}
120	if ((w = pn->whead) == NULL)
121		doit = 1;
122	else if (ll.ll_time != 0) {
123		/* if last login is earlier than some current login */
124		for (; !doit && w != NULL; w = w->next)
125			if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
126				doit = 1;
127		/*
128		 * and if it's not any of the current logins
129		 * can't use time comparison because there may be a small
130		 * discrepency since login calls time() twice
131		 */
132		for (w = pn->whead; doit && w != NULL; w = w->next)
133			if (w->info == LOGGEDIN &&
134			    strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
135				doit = 0;
136	}
137	if (doit) {
138		w = walloc(pn);
139		w->info = LASTLOG;
140		bcopy(ll.ll_line, w->tty, UT_LINESIZE);
141		w->tty[UT_LINESIZE] = 0;
142		bcopy(ll.ll_host, w->host, UT_HOSTSIZE);
143		w->host[UT_HOSTSIZE] = 0;
144		w->loginat = ll.ll_time;
145	}
146}
147
148void
149enter_where(ut, pn)
150	struct utmp *ut;
151	PERSON *pn;
152{
153	register WHERE *w;
154
155	w = walloc(pn);
156	w->info = LOGGEDIN;
157	bcopy(ut->ut_line, w->tty, UT_LINESIZE);
158	w->tty[UT_LINESIZE] = 0;
159	bcopy(ut->ut_host, w->host, UT_HOSTSIZE);
160	w->host[UT_HOSTSIZE] = 0;
161	w->loginat = (time_t)ut->ut_time;
162	find_idle_and_ttywrite(w);
163}
164
165PERSON *
166enter_person(pw)
167	register struct passwd *pw;
168{
169	DBT data, key;
170	PERSON *pn;
171
172	if (db == NULL &&
173	    (db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL)) == NULL)
174		err(1, NULL);
175
176	key.data = pw->pw_name;
177	key.size = strlen(pw->pw_name);
178
179	switch ((*db->get)(db, &key, &data, 0)) {
180	case 0:
181		memmove(&pn, data.data, sizeof pn);
182		return (pn);
183	default:
184	case -1:
185		err(1, "db get");
186		/* NOTREACHED */
187	case 1:
188		++entries;
189		pn = palloc();
190		userinfo(pn, pw);
191		pn->whead = NULL;
192
193		data.size = sizeof(PERSON *);
194		data.data = &pn;
195		if ((*db->put)(db, &key, &data, 0))
196			err(1, "db put");
197		return (pn);
198	}
199}
200
201PERSON *
202find_person(name)
203	char *name;
204{
205	struct passwd *pw;
206
207	register int cnt;
208	DBT data, key;
209	PERSON *p;
210	char buf[UT_NAMESIZE + 1];
211
212	if (!db)
213		return(NULL);
214
215	if ((pw = getpwnam(name)) && hide(pw))
216		return(NULL);
217
218	/* Name may be only UT_NAMESIZE long and not NUL terminated. */
219	for (cnt = 0; cnt < UT_NAMESIZE && *name; ++name, ++cnt)
220		buf[cnt] = *name;
221	buf[cnt] = '\0';
222	key.data = buf;
223	key.size = cnt;
224
225	if ((*db->get)(db, &key, &data, 0))
226		return (NULL);
227	memmove(&p, data.data, sizeof p);
228	return (p);
229}
230
231PERSON *
232palloc()
233{
234	PERSON *p;
235
236	if ((p = malloc((u_int) sizeof(PERSON))) == NULL)
237		err(1, NULL);
238	return(p);
239}
240
241static WHERE *
242walloc(pn)
243	register PERSON *pn;
244{
245	register WHERE *w;
246
247	if ((w = malloc((u_int) sizeof(WHERE))) == NULL)
248		err(1, NULL);
249	if (pn->whead == NULL)
250		pn->whead = pn->wtail = w;
251	else {
252		pn->wtail->next = w;
253		pn->wtail = w;
254	}
255	w->next = NULL;
256	return(w);
257}
258
259char *
260prphone(num)
261	char *num;
262{
263	register char *p;
264	int len;
265	static char pbuf[15];
266
267	/* don't touch anything if the user has their own formatting */
268	for (p = num; *p; ++p)
269		if (!isdigit(*p))
270			return(num);
271	len = p - num;
272	p = pbuf;
273	switch(len) {
274	case 11:			/* +0-123-456-7890 */
275		*p++ = '+';
276		*p++ = *num++;
277		*p++ = '-';
278		/* FALLTHROUGH */
279	case 10:			/* 012-345-6789 */
280		*p++ = *num++;
281		*p++ = *num++;
282		*p++ = *num++;
283		*p++ = '-';
284		/* FALLTHROUGH */
285	case 7:				/* 012-3456 */
286		*p++ = *num++;
287		*p++ = *num++;
288		*p++ = *num++;
289		break;
290	case 5:				/* x0-1234 */
291	case 4:				/* x1234 */
292		*p++ = 'x';
293		*p++ = *num++;
294		break;
295	default:
296		return(num);
297	}
298	if (len != 4) {
299	    *p++ = '-';
300	    *p++ = *num++;
301	}
302	*p++ = *num++;
303	*p++ = *num++;
304	*p++ = *num++;
305	*p = '\0';
306	return(pbuf);
307}
308
309static void
310find_idle_and_ttywrite(w)
311	register WHERE *w;
312{
313	extern time_t now;
314	struct stat sb;
315
316	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty);
317	if (stat(tbuf, &sb) < 0) {
318		warn(tbuf);
319		return;
320	}
321	w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime;
322
323#define	TALKABLE	0220		/* tty is writable if 220 mode */
324	w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
325}
326
327static void
328userinfo(pn, pw)
329	register PERSON *pn;
330	register struct passwd *pw;
331{
332	register char *p, *t;
333	char *bp, name[1024];
334	struct stat sb;
335
336	pn->realname = pn->office = pn->officephone = pn->homephone = NULL;
337
338	pn->uid = pw->pw_uid;
339	pn->name = strdup(pw->pw_name);
340	pn->dir = strdup(pw->pw_dir);
341	pn->shell = strdup(pw->pw_shell);
342
343	/* why do we skip asterisks!?!? */
344	(void)strcpy(bp = tbuf, pw->pw_gecos);
345	if (*bp == '*')
346		++bp;
347
348	/* ampersands get replaced by the login name */
349	if (!(p = strsep(&bp, ",")))
350		return;
351	for (t = name; (*t = *p) != '\0'; ++p)
352		if (*t == '&') {
353			(void)strcpy(t, pw->pw_name);
354			if (islower(*t))
355				*t = toupper(*t);
356			while (*++t);
357		}
358		else
359			++t;
360	pn->realname = strdup(name);
361	pn->office = ((p = strsep(&bp, ",")) && *p) ?
362	    strdup(p) : NULL;
363	pn->officephone = ((p = strsep(&bp, ",")) && *p) ?
364	    strdup(p) : NULL;
365	pn->homephone = ((p = strsep(&bp, ",")) && *p) ?
366	    strdup(p) : NULL;
367	(void)sprintf(tbuf,"%s/%s", _PATH_MAILDIR, pw->pw_name);
368	pn->mailrecv = -1;		/* -1 == not_valid */
369	if (stat(tbuf, &sb) < 0) {
370		if (errno != ENOENT) {
371			(void)fprintf(stderr,
372			    "finger: %s: %s\n", tbuf, strerror(errno));
373			return;
374		}
375	} else if (sb.st_size != 0) {
376		pn->mailrecv = sb.st_mtime;
377		pn->mailread = sb.st_atime;
378	}
379}
380
381/*
382 * Is this user hiding from finger?
383 * If ~<user>/.nofinger exists, return 1 (hide), else return 0 (nohide).
384 */
385
386int
387hide(pw)
388	struct passwd *pw;
389{
390	int fd;
391	char buf[MAXPATHLEN+1];
392
393	if (!pw->pw_dir)
394		return 0;
395
396	sprintf (buf, "%s/.nofinger", pw->pw_dir);
397
398	if (access (buf, F_OK) == 0)
399		return 1;
400
401	return 0;
402}
403