1 /* $NetBSD: util.c,v 1.17 2001/02/05 01:56:51 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.17 2001/02/05 01:56:51 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 "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 err(1, NULL); 210 211 key.data = (char *)pw->pw_name; 212 key.size = strlen(pw->pw_name); 213 214 switch ((*db->get)(db, &key, &data, 0)) { 215 case 0: 216 memmove(&pn, data.data, sizeof pn); 217 return (pn); 218 default: 219 case -1: 220 err(1, "db get"); 221 /* NOTREACHED */ 222 case 1: 223 ++entries; 224 pn = palloc(); 225 userinfo(pn, pw); 226 pn->whead = NULL; 227 228 data.size = sizeof(PERSON *); 229 data.data = &pn; 230 if ((*db->put)(db, &key, &data, 0)) 231 err(1, "db put"); 232 return (pn); 233 } 234 } 235 236 PERSON * 237 find_person(name) 238 char *name; 239 { 240 int cnt; 241 DBT data, key; 242 PERSON *p; 243 char buf[UT_NAMESIZE + 1]; 244 245 if (!db) 246 return(NULL); 247 248 /* Name may be only UT_NAMESIZE long and not NUL terminated. */ 249 for (cnt = 0; cnt < UT_NAMESIZE && *name; ++name, ++cnt) 250 buf[cnt] = *name; 251 buf[cnt] = '\0'; 252 key.data = buf; 253 key.size = cnt; 254 255 if ((*db->get)(db, &key, &data, 0)) 256 return (NULL); 257 memmove(&p, data.data, sizeof p); 258 return (p); 259 } 260 261 PERSON * 262 palloc() 263 { 264 PERSON *p; 265 266 if ((p = malloc((u_int) sizeof(PERSON))) == NULL) 267 err(1, NULL); 268 return(p); 269 } 270 271 static WHERE * 272 walloc(pn) 273 PERSON *pn; 274 { 275 WHERE *w; 276 277 if ((w = malloc((u_int) sizeof(WHERE))) == NULL) 278 err(1, NULL); 279 if (pn->whead == NULL) 280 pn->whead = pn->wtail = w; 281 else { 282 pn->wtail->next = w; 283 pn->wtail = w; 284 } 285 w->next = NULL; 286 return(w); 287 } 288 289 char * 290 prphone(num) 291 char *num; 292 { 293 char *p; 294 int len; 295 static char pbuf[15]; 296 297 /* don't touch anything if the user has their own formatting */ 298 for (p = num; *p; ++p) 299 if (!isdigit((unsigned char)*p)) 300 return(num); 301 len = p - num; 302 p = pbuf; 303 switch(len) { 304 case 11: /* +0-123-456-7890 */ 305 *p++ = '+'; 306 *p++ = *num++; 307 *p++ = '-'; 308 /* FALLTHROUGH */ 309 case 10: /* 012-345-6789 */ 310 *p++ = *num++; 311 *p++ = *num++; 312 *p++ = *num++; 313 *p++ = '-'; 314 /* FALLTHROUGH */ 315 case 7: /* 012-3456 */ 316 *p++ = *num++; 317 *p++ = *num++; 318 *p++ = *num++; 319 break; 320 case 5: /* x0-1234 */ 321 case 4: /* x1234 */ 322 *p++ = 'x'; 323 *p++ = *num++; 324 break; 325 default: 326 return(num); 327 } 328 if (len != 4) { 329 *p++ = '-'; 330 *p++ = *num++; 331 } 332 *p++ = *num++; 333 *p++ = *num++; 334 *p++ = *num++; 335 *p = '\0'; 336 return(pbuf); 337 } 338 339 static void 340 find_idle_and_ttywrite(w) 341 WHERE *w; 342 { 343 struct stat sb; 344 345 (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_DEV, w->tty); 346 if (stat(tbuf, &sb) < 0) { 347 warn("%s", tbuf); 348 return; 349 } 350 w->idletime = now < sb.st_atime ? 0 : now - sb.st_atime; 351 352 #define TALKABLE 0220 /* tty is writable if 220 mode */ 353 w->writable = ((sb.st_mode & TALKABLE) == TALKABLE); 354 } 355 356 static void 357 userinfo(pn, pw) 358 PERSON *pn; 359 struct passwd *pw; 360 { 361 char *p; 362 char *bp, name[1024]; 363 struct stat sb; 364 365 pn->realname = pn->office = pn->officephone = pn->homephone = NULL; 366 367 pn->uid = pw->pw_uid; 368 pn->name = strdup(pw->pw_name); 369 pn->dir = strdup(pw->pw_dir); 370 pn->shell = strdup(pw->pw_shell); 371 372 (void)strncpy(bp = tbuf, pw->pw_gecos, sizeof(tbuf)); 373 tbuf[sizeof(tbuf) - 1] = '\0'; 374 375 /* ampersands get replaced by the login name */ 376 if (!(p = strsep(&bp, ","))) 377 return; 378 expandusername(p, pw->pw_name, name, sizeof(name)); 379 pn->realname = strdup(name); 380 pn->office = ((p = strsep(&bp, ",")) && *p) ? 381 strdup(p) : NULL; 382 pn->officephone = ((p = strsep(&bp, ",")) && *p) ? 383 strdup(p) : NULL; 384 pn->homephone = ((p = strsep(&bp, ",")) && *p) ? 385 strdup(p) : NULL; 386 (void)snprintf(tbuf, sizeof(tbuf), "%s/%s", _PATH_MAILSPOOL, 387 pw->pw_name); 388 pn->mailrecv = -1; /* -1 == not_valid */ 389 if (stat(tbuf, &sb) < 0) { 390 if (errno != ENOENT) { 391 (void)fprintf(stderr, 392 "finger: %s: %s\n", tbuf, strerror(errno)); 393 return; 394 } 395 } else if (sb.st_size != 0) { 396 pn->mailrecv = sb.st_mtime; 397 pn->mailread = sb.st_atime; 398 } 399 } 400