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