xref: /openbsd-src/usr.bin/finger/util.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: util.c,v 1.22 2005/08/23 13:43:53 espie 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 const char rcsid[] = "$OpenBSD: util.c,v 1.22 2005/08/23 13:43:53 espie 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 struct storage {
65 	struct storage *next;
66 	char a[1];
67 };
68 
69 void
70 free_storage(struct storage *st)
71 {
72 	struct storage *nx;
73 
74 	while (st != NULL) {
75 		nx = st->next;
76 		free(st);
77 		st = nx;
78 	}
79 }
80 
81 void
82 find_idle_and_ttywrite(WHERE *w)
83 {
84 	struct stat sb;
85 
86 	(void)snprintf(tbuf, sizeof(tbuf), "%s%s", _PATH_DEV, w->tty);
87 	if (stat(tbuf, &sb) < 0) {
88 		/* Don't bitch about it, just handle it... */
89 		w->idletime = 0;
90 		w->writable = 0;
91 
92 		return;
93 	}
94 	w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime;
95 
96 #define	TALKABLE	0220		/* tty is writable if 220 mode */
97 	w->writable = ((sb.st_mode & TALKABLE) == TALKABLE);
98 }
99 
100 char *
101 estrdup(char *s)
102 {
103 	char *p = strdup(s);
104 	if (!p)
105 		err(1, "strdup");
106 	return (p);
107 }
108 
109 void
110 userinfo(PERSON *pn, struct passwd *pw)
111 {
112 	char *p;
113 	char *bp, name[1024];
114 	struct stat sb;
115 
116 	pn->realname = pn->office = pn->officephone = pn->homephone = NULL;
117 
118 	pn->uid = pw->pw_uid;
119 	pn->name = estrdup(pw->pw_name);
120 	pn->dir = estrdup(pw->pw_dir);
121 	pn->shell = estrdup(pw->pw_shell);
122 
123 	(void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf));
124 
125 	/* ampersands get replaced by the login name */
126 	if (!(p = strsep(&bp, ",")))
127 		return;
128 	expandusername(p, pw->pw_name, name, sizeof(name));
129 	pn->realname = estrdup(name);
130 	pn->office = ((p = strsep(&bp, ",")) && *p) ?
131 	    estrdup(p) : NULL;
132 	pn->officephone = ((p = strsep(&bp, ",")) && *p) ?
133 	    estrdup(p) : NULL;
134 	pn->homephone = ((p = strsep(&bp, ",")) && *p) ?
135 	    estrdup(p) : NULL;
136 	(void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILSPOOL,
137 	    pw->pw_name);
138 	pn->mailrecv = -1;		/* -1 == not_valid */
139 	if (stat(tbuf, &sb) < 0) {
140 		if (errno != ENOENT) {
141 			warn("%s", tbuf);
142 			return;
143 		}
144 	} else if (sb.st_size != 0) {
145 		pn->mailrecv = sb.st_mtime;
146 		pn->mailread = sb.st_atime;
147 	}
148 }
149 
150 int
151 match(struct passwd *pw, char *user)
152 {
153 	char *p, *t;
154 	char name[1024];
155 
156 	(void)strncpy(p = tbuf, pw->pw_gecos, sizeof(tbuf));
157 
158 	/* ampersands get replaced by the login name */
159 	if (!(p = strtok(p, ",")))
160 		return (0);
161 	expandusername(p, pw->pw_name, name, sizeof(name));
162 	for (t = name; (p = strtok(t, "\t ")) != NULL; t = NULL)
163 		if (!strcasecmp(p, user))
164 			return (1);
165 	return (0);
166 }
167 
168 /* inspired by usr.sbin/sendmail/util.c::buildfname */
169 void
170 expandusername(char *gecos, char *login, char *buf, int buflen)
171 {
172 	char *p, *bp;
173 
174 	/* why do we skip asterisks!?!? */
175 	if (*gecos == '*')
176 		gecos++;
177 	bp = buf;
178 
179 	/* copy gecos, interpolating & to be full name */
180 	for (p = gecos; *p != '\0'; p++) {
181 		if (bp >= &buf[buflen - 1]) {
182 			/* buffer overflow - just use login name */
183 			strlcpy(buf, login, buflen);
184 			buf[buflen - 1] = '\0';
185 			return;
186 		}
187 		if (*p == '&') {
188 			/* interpolate full name */
189 			strlcpy(bp, login, buflen - (bp - buf));
190 			*bp = toupper(*bp);
191 			bp += strlen(bp);
192 		}
193 		else
194 			*bp++ = *p;
195 	}
196 	*bp = '\0';
197 }
198 
199 void
200 enter_lastlog(PERSON *pn)
201 {
202 	WHERE *w;
203 	static int opened, fd;
204 	struct lastlog ll;
205 	char doit = 0;
206 
207 	/* some systems may not maintain lastlog, don't report errors. */
208 	if (!opened) {
209 		fd = open(_PATH_LASTLOG, O_RDONLY);
210 		opened = 1;
211 	}
212 	if (fd == -1 ||
213 	    lseek(fd, (off_t)(pn->uid * sizeof(ll)), SEEK_SET) !=
214 	    (long)(pn->uid * sizeof(ll)) ||
215 	    read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) {
216 			/* as if never logged in */
217 			ll.ll_line[0] = ll.ll_host[0] = '\0';
218 			ll.ll_time = 0;
219 		}
220 	if ((w = pn->whead) == NULL)
221 		doit = 1;
222 	else if (ll.ll_time != 0) {
223 		/* if last login is earlier than some current login */
224 		for (; !doit && w != NULL; w = w->next)
225 			if (w->info == LOGGEDIN && w->loginat < ll.ll_time)
226 				doit = 1;
227 		/*
228 		 * and if it's not any of the current logins
229 		 * can't use time comparison because there may be a small
230 		 * discrepency since login calls time() twice
231 		 */
232 		for (w = pn->whead; doit && w != NULL; w = w->next)
233 			if (w->info == LOGGEDIN &&
234 			    strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0)
235 				doit = 0;
236 	}
237 	if (doit) {
238 		w = walloc(pn);
239 		w->info = LASTLOG;
240 		bcopy(ll.ll_line, w->tty, UT_LINESIZE);
241 		w->tty[UT_LINESIZE] = 0;
242 		bcopy(ll.ll_host, w->host, UT_HOSTSIZE);
243 		w->host[UT_HOSTSIZE] = 0;
244 		w->loginat = ll.ll_time;
245 	}
246 }
247 
248 void
249 enter_where(struct utmp *ut, PERSON *pn)
250 {
251 	WHERE *w = walloc(pn);
252 
253 	w->info = LOGGEDIN;
254 	bcopy(ut->ut_line, w->tty, UT_LINESIZE);
255 	w->tty[UT_LINESIZE] = 0;
256 	bcopy(ut->ut_host, w->host, UT_HOSTSIZE);
257 	w->host[UT_HOSTSIZE] = 0;
258 	w->loginat = (time_t)ut->ut_time;
259 	find_idle_and_ttywrite(w);
260 }
261 
262 PERSON *
263 enter_person(struct passwd *pw)
264 {
265 	PERSON *pn, **pp;
266 
267 	for (pp = htab + hash(pw->pw_name);
268 	    *pp != NULL && strcmp((*pp)->name, pw->pw_name) != 0;
269 	    pp = &(*pp)->hlink)
270 		;
271 	if ((pn = *pp) == NULL) {
272 		pn = palloc();
273 		entries++;
274 		if (phead == NULL)
275 			phead = ptail = pn;
276 		else {
277 			ptail->next = pn;
278 			ptail = pn;
279 		}
280 		pn->next = NULL;
281 		pn->hlink = NULL;
282 		*pp = pn;
283 		userinfo(pn, pw);
284 		pn->whead = NULL;
285 	}
286 	return (pn);
287 }
288 
289 PERSON *
290 find_person(char *name)
291 {
292 	PERSON *pn;
293 
294 	/* name may be only UT_NAMESIZE long and not terminated */
295 	for (pn = htab[hash(name)];
296 	    pn != NULL && strncmp(pn->name, name, UT_NAMESIZE) != 0;
297 	    pn = pn->hlink)
298 		;
299 	return (pn);
300 }
301 
302 int
303 hash(char *name)
304 {
305 	int h, i;
306 
307 	h = 0;
308 	/* name may be only UT_NAMESIZE long and not terminated */
309 	for (i = UT_NAMESIZE; --i >= 0 && *name;)
310 		h = ((h << 2 | h >> (HBITS - 2)) ^ *name++) & HMASK;
311 	return (h);
312 }
313 
314 PERSON *
315 palloc(void)
316 {
317 	PERSON *p;
318 
319 	if ((p = (PERSON *)malloc((u_int) sizeof(PERSON))) == NULL)
320 		err(1, "malloc");
321 	return (p);
322 }
323 
324 WHERE *
325 walloc(PERSON *pn)
326 {
327 	WHERE *w;
328 
329 	if ((w = (WHERE *)malloc((u_int) sizeof(WHERE))) == NULL)
330 		err(1, "malloc");
331 	if (pn->whead == NULL)
332 		pn->whead = pn->wtail = w;
333 	else {
334 		pn->wtail->next = w;
335 		pn->wtail = w;
336 	}
337 	w->next = NULL;
338 	return (w);
339 }
340 
341 char *
342 prphone(char *num)
343 {
344 	char *p;
345 	int len;
346 	static char pbuf[15];
347 
348 	/* don't touch anything if the user has their own formatting */
349 	for (p = num; *p; ++p)
350 		if (!isdigit(*p))
351 			return (num);
352 	len = p - num;
353 	p = pbuf;
354 	switch (len) {
355 	case 11:			/* +0-123-456-7890 */
356 		*p++ = '+';
357 		*p++ = *num++;
358 		*p++ = '-';
359 		/* FALLTHROUGH */
360 	case 10:			/* 012-345-6789 */
361 		*p++ = *num++;
362 		*p++ = *num++;
363 		*p++ = *num++;
364 		*p++ = '-';
365 		/* FALLTHROUGH */
366 	case 7:				/* 012-3456 */
367 		*p++ = *num++;
368 		*p++ = *num++;
369 		*p++ = *num++;
370 		break;
371 	case 5:				/* x0-1234 */
372 	case 4:				/* x1234 */
373 		*p++ = 'x';
374 		*p++ = *num++;
375 		break;
376 	default:
377 		return (num);
378 	}
379 	if (len != 4) {
380 		*p++ = '-';
381 		*p++ = *num++;
382 	}
383 	*p++ = *num++;
384 	*p++ = *num++;
385 	*p++ = *num++;
386 	*p = '\0';
387 	return (pbuf);
388 }
389 
390 /* Like strvis(), but use malloc() to get the space and return a pointer
391  * to the beginning of the converted string, not the end.
392  *
393  * The caller is responsible for free()'ing the returned string.
394  */
395 char *
396 vs(struct storage **exist, char *src)
397 {
398 	char *dst;
399 	struct storage *n;
400 
401 	if ((n = malloc(sizeof(struct storage) + 4 * strlen(src))) == NULL)
402 		err(1, "malloc failed");
403 	n->next = *exist;
404 	*exist = n;
405 
406 	dst = n->a;
407 
408 	strvis(dst, src, VIS_SAFE|VIS_NOSLASH);
409 	return (dst);
410 }
411