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