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