1/*	$NetBSD: finger.c,v 1.31 2021/10/30 09:12:09 nia 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 * Luke Mewburn <lukem@NetBSD.org> added the following on 961121:
37 *    - mail status ("No Mail", "Mail read:...", or "New Mail ...,
38 *	Unread since ...".)
39 *    - 4 digit phone extensions (3210 is printed as x3210.)
40 *    - host/office toggling in short format with -h & -o.
41 *    - short day names (`Tue' printed instead of `Jun 21' if the
42 *	login time is < 6 days.
43 */
44
45#include <sys/cdefs.h>
46#ifndef lint
47__COPYRIGHT("@(#) Copyright (c) 1989, 1993\
48 The Regents of the University of California.  All rights reserved.");
49#endif /* not lint */
50
51#ifndef lint
52#if 0
53static char sccsid[] = "@(#)finger.c	8.5 (Berkeley) 5/4/95";
54#else
55__RCSID("$NetBSD: finger.c,v 1.31 2021/10/30 09:12:09 nia Exp $");
56#endif
57#endif /* not lint */
58
59/*
60 * Finger prints out information about users.  It is not portable since
61 * certain fields (e.g. the full user name, office, and phone numbers) are
62 * extracted from the gecos field of the passwd file which other UNIXes
63 * may not have or may use for other things.
64 *
65 * There are currently two output formats; the short format is one line
66 * per user and displays login name, tty, login time, real name, idle time,
67 * and either remote host information (default) or office location/phone
68 * number, depending on if -h or -o is used respectively.
69 * The long format gives the same information (in a more legible format) as
70 * well as home directory, shell, mail info, and .plan/.project files.
71 */
72
73#include <sys/param.h>
74
75#include <db.h>
76#include <err.h>
77#include <errno.h>
78#include <fcntl.h>
79#include <pwd.h>
80#include <stdio.h>
81#include <stdlib.h>
82#include <string.h>
83#include <time.h>
84#include <unistd.h>
85
86#include <locale.h>
87#include <langinfo.h>
88
89#include "utmpentry.h"
90
91#include "finger.h"
92#include "extern.h"
93
94DB *db;
95time_t now;
96int entries, gflag, lflag, mflag, oflag, sflag, eightflag, pplan;
97char tbuf[1024];
98struct utmpentry *ehead;
99
100static void loginlist(void);
101static void userlist(int, char **);
102
103int
104main(int argc, char **argv)
105{
106	int ch;
107
108	/* Allow user's locale settings to affect character output. */
109	setlocale(LC_CTYPE, "");
110
111	/*
112	 * Reset back to the C locale, unless we are using a known
113	 * single-byte 8-bit locale.
114	 */
115	if (strncmp(nl_langinfo(CODESET), "ISO8859-", 8))
116		setlocale(LC_CTYPE, "C");
117
118	oflag = 1;		/* default to old "office" behavior */
119
120	while ((ch = getopt(argc, argv, "lmpshog8")) != -1)
121		switch(ch) {
122		case 'l':
123			lflag = 1;		/* long format */
124			break;
125		case 'm':
126			mflag = 1;		/* force exact match of names */
127			break;
128		case 'p':
129			pplan = 1;		/* don't show .plan/.project */
130			break;
131		case 's':
132			sflag = 1;		/* short format */
133			break;
134		case 'h':
135			oflag = 0;		/* remote host info */
136			break;
137		case 'o':
138			oflag = 1;		/* office info */
139			break;
140		case 'g':
141			gflag = 1;		/* no gecos info, besides name */
142			break;
143		case '8':
144			eightflag = 1;		/* 8-bit pass-through */
145			break;
146		case '?':
147		default:
148			(void)fprintf(stderr,
149			    "usage: finger [-lmpshog8] [login ...]\n");
150			exit(1);
151		}
152	argc -= optind;
153	argv += optind;
154
155	(void)time(&now);
156	setpassent(1);
157	entries = getutentries(NULL, &ehead);
158	if (argc == 0) {
159		/*
160		 * Assign explicit "small" format if no names given and -l
161		 * not selected.  Force the -s BEFORE we get names so proper
162		 * screening will be done.
163		 */
164		if (!lflag)
165			sflag = 1;	/* if -l not explicit, force -s */
166		loginlist();
167		if (entries == 0)
168			(void)printf("No one logged on.\n");
169	} else {
170		userlist(argc, argv);
171		/*
172		 * Assign explicit "large" format if names given and -s not
173		 * explicitly stated.  Force the -l AFTER we get names so any
174		 * remote finger attempts specified won't be mishandled.
175		 */
176		if (!sflag)
177			lflag = 1;	/* if -s not explicit, force -l */
178	}
179	if (entries) {
180		if (lflag)
181			lflag_print();
182		else
183			sflag_print();
184	}
185	return (0);
186}
187
188static void
189loginlist(void)
190{
191	PERSON *pn;
192	DBT data, key;
193	struct passwd *pw;
194	int r, seqflag;
195	struct utmpentry *ep;
196
197	for (ep = ehead; ep; ep = ep->next) {
198		if ((pn = find_person(ep->name)) == NULL) {
199			if ((pw = getpwnam(ep->name)) == NULL)
200				continue;
201			pn = enter_person(pw);
202		}
203		enter_where(ep, pn);
204	}
205	if (db && lflag)
206		for (seqflag = R_FIRST;; seqflag = R_NEXT) {
207			PERSON *tmp;
208
209			r = (*db->seq)(db, &key, &data, seqflag);
210			if (r == -1)
211				err(1, "db seq");
212			if (r == 1)
213				break;
214			memmove(&tmp, data.data, sizeof tmp);
215			enter_lastlog(tmp);
216		}
217}
218
219static void
220userlist(int argc, char **argv)
221{
222	PERSON *pn;
223	DBT data, key;
224	struct passwd *pw;
225	int r, seqflag, *used, *ip;
226	char **ap, **nargv, **np, **p;
227	struct utmpentry *ep;
228
229	nargv = NULL;
230	if (reallocarr(&nargv, argc + 1, sizeof(char *)) != 0)
231		err(1, NULL);
232	if ((used = calloc(argc, sizeof(int))) == NULL)
233		err(1, NULL);
234
235	/* Pull out all network requests. */
236	for (ap = p = argv, np = nargv; *p; ++p)
237		if (strchr(*p, '@'))
238			*np++ = *p;
239		else
240			*ap++ = *p;
241
242	*np++ = NULL;
243	*ap++ = NULL;
244
245	if (!*argv)
246		goto net;
247
248	/*
249	 * Traverse the list of possible login names and check the login name
250	 * and real name against the name specified by the user.
251	 */
252	if (mflag) {
253		for (p = argv; *p; ++p)
254			if ((pw = getpwnam(*p)) != NULL)
255				enter_person(pw);
256			else
257				warnx("%s: no such user", *p);
258	} else {
259		while ((pw = getpwent()) != NULL)
260			for (p = argv, ip = used; *p; ++p, ++ip)
261				if (match(pw, *p)) {
262					enter_person(pw);
263					*ip = 1;
264				}
265		for (p = argv, ip = used; *p; ++p, ++ip)
266			if (!*ip)
267				warnx("%s: no such user", *p);
268	}
269
270	/* Handle network requests. */
271net:
272	for (p = nargv; *p;)
273		netfinger(*p++);
274
275	if (entries == 0)
276		goto done;
277
278	/*
279	 * Scan thru the list of users currently logged in, saving
280	 * appropriate data whenever a match occurs.
281	 */
282	for (ep = ehead; ep; ep = ep->next) {
283		if ((pn = find_person(ep->name)) == NULL)
284			continue;
285		enter_where(ep, pn);
286	}
287	if (db != NULL)
288		for (seqflag = R_FIRST;; seqflag = R_NEXT) {
289			PERSON *tmp;
290
291			r = (*db->seq)(db, &key, &data, seqflag);
292			if (r == -1)
293				err(1, "db seq");
294			if (r == 1)
295				break;
296			memmove(&tmp, data.data, sizeof tmp);
297			enter_lastlog(tmp);
298		}
299done:
300	free(nargv);
301	free(used);
302}
303