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