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