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