1 /* $OpenBSD: util.c,v 1.21 2005/03/15 12:28:48 niallo 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.21 2005/03/15 12:28:48 niallo 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
find_idle_and_ttywrite(WHERE * w)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 *
estrdup(char * s)84 estrdup(char *s)
85 {
86 char *p = strdup(s);
87 if (!p)
88 err(1, "strdup");
89 return (p);
90 }
91
92 void
userinfo(PERSON * pn,struct passwd * pw)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
match(struct passwd * pw,char * user)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
expandusername(char * gecos,char * login,char * buf,int buflen)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 strlcpy(buf, login, buflen);
167 buf[buflen - 1] = '\0';
168 return;
169 }
170 if (*p == '&') {
171 /* interpolate full name */
172 strlcpy(bp, login, buflen - (bp - buf));
173 *bp = toupper(*bp);
174 bp += strlen(bp);
175 }
176 else
177 *bp++ = *p;
178 }
179 *bp = '\0';
180 }
181
182 void
enter_lastlog(PERSON * pn)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 memmove(w->tty, ll.ll_line, UT_LINESIZE);
224 w->tty[UT_LINESIZE] = 0;
225 memmove(w->host, ll.ll_host, UT_HOSTSIZE);
226 w->host[UT_HOSTSIZE] = 0;
227 w->loginat = ll.ll_time;
228 }
229 }
230
231 void
enter_where(struct utmp * ut,PERSON * pn)232 enter_where(struct utmp *ut, PERSON *pn)
233 {
234 WHERE *w = walloc(pn);
235
236 w->info = LOGGEDIN;
237 memmove(w->tty, ut->ut_line, UT_LINESIZE);
238 w->tty[UT_LINESIZE] = 0;
239 memmove(w->host, ut->ut_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 *
enter_person(struct passwd * pw)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 *
find_person(char * name)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
hash(char * name)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 *
palloc(void)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 *
walloc(PERSON * pn)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 *
prphone(char * num)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 *
vs(char * src)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