1 /* $NetBSD: util.c,v 1.18 2002/08/02 00:10:40 christos 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.18 2002/08/02 00:10:40 christos 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