1: #ifndef lint 2: static char *sccsid = "@(#)chfn.c 4.3 (Berkeley) 5/18/83"; 3: #endif lint 4: 5: /* 6: * changefinger - change finger entries 7: */ 8: #include <whoami.h> 9: #include <stdio.h> 10: #include <signal.h> 11: #include <pwd.h> 12: #include <ctype.h> 13: #include <sys/types.h> 14: 15: struct default_values { 16: char *name; 17: char *office_num; 18: char *office_phone; 19: char *home_phone; 20: }; 21: 22: char passwd[] = "/etc/passwd"; 23: char temp[] = "/etc/ptmp"; 24: struct passwd *pwd; 25: struct passwd *getpwent(), *getpwnam(), *getpwuid(); 26: int endpwent(); 27: char *crypt(); 28: char *getpass(); 29: char buf[BUFSIZ]; 30: 31: main(argc, argv) 32: int argc; 33: char *argv[]; 34: { 35: int user_uid; 36: unsigned num_bytes; 37: int fi, fo; 38: char replacement[4*BUFSIZ]; 39: FILE *tf; 40: 41: if (argc > 2) { 42: printf("Usage: changefinger [user]\n"); 43: exit(1); 44: } 45: /* 46: * Error check to make sure the user (foolishly) typed their own name. 47: */ 48: user_uid = getuid(); 49: if ((argc == 2) && (user_uid != 0)) { 50: pwd = getpwnam(argv[1]); 51: if (pwd == NULL) { 52: printf("%s%s%s%s%s%s%s%s", 53: "There is no account for ", argv[1], 54: " on this machine.\n", 55: "You probably mispelled your login name;\n", 56: "only root is allowed to change another", 57: " person's finger entry.\n", 58: "Note: you do not need to type your login", 59: " name as an argument.\n"); 60: exit(1); 61: } 62: if (pwd->pw_uid != user_uid) { 63: printf("%s%s", 64: "You are not allowed to change another", 65: " person's finger entry.\n"); 66: exit(1); 67: } 68: } 69: /* 70: * If root is changing a finger entry, then find the uid that 71: * corresponds to the user's login name. 72: */ 73: if ((argc == 2) && (user_uid == 0)) { 74: pwd = getpwnam(argv[1]); 75: if (pwd == NULL) { 76: printf("There is no account for %s on this machine\n", 77: pwd->pw_name); 78: exit(1); 79: } 80: user_uid = pwd->pw_uid; 81: } 82: if (argc == 1) { 83: pwd = getpwuid(user_uid); 84: if (pwd == NULL) { 85: fprintf(stderr, "No passwd file entry!?\n"); 86: exit(1); 87: } 88: } 89: /* 90: * Collect name, room number, school phone, and home phone. 91: */ 92: get_info(pwd->pw_gecos, replacement); 93: 94: /* 95: * Update the entry in the password file. 96: */ 97: while (access(temp, 0) >= 0) { 98: printf("Password file busy -- waiting for it to be free.\n"); 99: sleep(10); 100: } 101: (void) signal(SIGHUP, SIG_IGN); 102: (void) signal(SIGINT, SIG_IGN); 103: (void) signal(SIGQUIT, SIG_IGN); 104: #ifdef MENLO_JCL 105: (void) signal(SIGTSTP, SIG_IGN); 106: #endif 107: /* 108: * Race condition -- the locking mechinism is not my idea (ns) 109: */ 110: if (access(temp, 0) >= 0) { 111: printf("It's not your day! Password file is busy again.\n"); 112: printf("Try again later.\n"); 113: exit(1); 114: } 115: if ((tf=fopen(temp,"w")) == NULL) { 116: printf("Cannot create temporary file\n"); 117: exit(1); 118: } 119: /* 120: * There is another race condition here: if the passwd file 121: * has changed since the error checking at the beginning of the program, 122: * then user_uid may not be in the file. Of course, the uid might have 123: * been changed, but this is not supposed to happen. 124: */ 125: if (getpwuid(user_uid) == NULL) { 126: printf("%s%d%s\n", "Passwd file has changed. Uid ", user_uid, 127: " is no longer in the file!?"); 128: goto out; 129: } 130: /* 131: * copy passwd to temp, replacing matching line 132: * with new finger entry (gecos field). 133: */ 134: while ((pwd=getpwent()) != NULL) { 135: if (pwd->pw_uid == user_uid) { 136: pwd->pw_gecos = replacement; 137: } 138: fprintf(tf,"%s:%s:%d:%d:%s:%s:%s\n", 139: pwd->pw_name, 140: pwd->pw_passwd, 141: pwd->pw_uid, 142: pwd->pw_gid, 143: pwd->pw_gecos, 144: pwd->pw_dir, 145: pwd->pw_shell); 146: } 147: (void) endpwent(); 148: (void) fclose(tf); 149: /* 150: * Copy temp back to password file. 151: */ 152: if((fi=open(temp,0)) < 0) { 153: printf("Temp file disappeared!\n"); 154: goto out; 155: } 156: if((fo=creat(passwd, 0644)) < 0) { 157: printf("Cannot recreat passwd file.\n"); 158: goto out; 159: } 160: while((num_bytes=read(fi,buf,sizeof(buf))) > 0) 161: (void) write(fo,buf,num_bytes); 162: out: 163: (void) unlink(temp); 164: } 165: 166: /* 167: * Get name, room number, school phone, and home phone. 168: */ 169: get_info(gecos_field, answer) 170: char *gecos_field; 171: char *answer; 172: { 173: char *strcpy(), *strcat(); 174: char in_str[BUFSIZ]; 175: struct default_values *defaults, *get_defaults(); 176: 177: answer[0] = '\0'; 178: defaults = get_defaults(gecos_field); 179: printf("Default values are printed inside of of '[]'.\n"); 180: printf("To accept the default, type <return>.\n"); 181: printf("To have a blank entry, type the word 'none'.\n"); 182: /* 183: * Get name. 184: */ 185: do { 186: printf("\nName [%s]: ", defaults->name); 187: (void) fgets(in_str, BUFSIZ, stdin); 188: if (special_case(in_str, defaults->name)) 189: break; 190: } while (ill_input(in_str)); 191: (void) strcpy(answer, in_str); 192: /* 193: * Get room number. 194: */ 195: do { 196: printf("Room number (Exs: 597E or 197C) [%s]: ", 197: defaults->office_num); 198: (void) fgets(in_str, BUFSIZ, stdin); 199: if (special_case(in_str, defaults->office_num)) 200: break; 201: } while (ill_input(in_str) || ill_building(in_str)); 202: (void) strcat(strcat(answer, ","), in_str); 203: /* 204: * Get office phone number. 205: * Remove hyphens and 642, x2, or 2 prefixes if present. 206: */ 207: do { 208: printf("Office Phone (Ex: 1632) [%s]: ", 209: defaults->office_phone); 210: (void) fgets(in_str, BUFSIZ, stdin); 211: if (special_case(in_str, defaults->office_phone)) 212: break; 213: remove_hyphens(in_str); 214: if ((strlen(in_str) == 8) && (strncmp(in_str, "642", 3) == 0)) 215: (void) strcpy(in_str, in_str+3); 216: if ((strlen(in_str) == 7) && (strncmp(in_str, "x2", 2) == 0)) 217: (void) strcpy(in_str, in_str+2); 218: if ((strlen(in_str) == 6) && (in_str[0] == '2')) 219: (void) strcpy(in_str, in_str+1); 220: } while (ill_input(in_str) || not_all_digits(in_str) 221: || wrong_length(in_str, 4)); 222: (void) strcat(strcat(answer, ","), in_str); 223: /* 224: * Get home phone number. 225: * Remove hyphens if present. 226: */ 227: do { 228: printf("Home Phone (Ex: 9875432) [%s]: ", defaults->home_phone); 229: (void) fgets(in_str, BUFSIZ, stdin); 230: if (special_case(in_str, defaults->home_phone)) 231: break; 232: remove_hyphens(in_str); 233: } while (ill_input(in_str) || not_all_digits(in_str)); 234: (void) strcat(strcat(answer, ","), in_str); 235: } 236: 237: /* 238: * Prints an error message if a ':' or a newline is found in the string. 239: * A message is also printed if the input string is too long. 240: * The password file uses :'s as seperators, and are not allowed in the "gcos" 241: * field. Newlines serve as delimiters between users in the password file, 242: * and so, those too, are checked for. (I don't think that it is possible to 243: * type them in, but better safe than sorry) 244: * 245: * Returns '1' if a colon or newline is found or the input line is too long. 246: */ 247: ill_input(input_str) 248: char *input_str; 249: { 250: char *index(); 251: char *ptr; 252: int error_flag = 0; 253: int length = strlen(input_str); 254: 255: if (index(input_str, ':')) { 256: printf("':' is not allowed.\n"); 257: error_flag = 1; 258: } 259: if (input_str[length-1] != '\n') { 260: /* the newline and the '\0' eat up two characters */ 261: printf("Maximum number of characters allowed is %d\n", 262: BUFSIZ-2); 263: /* flush the rest of the input line */ 264: while (getchar() != '\n') 265: /* void */; 266: error_flag = 1; 267: } 268: /* 269: * Delete newline by shortening string by 1. 270: */ 271: input_str[length-1] = '\0'; 272: /* 273: * Don't allow control characters, etc in input string. 274: */ 275: for (ptr=input_str; *ptr != '\0'; ptr++) { 276: if ((int) *ptr < 040) { 277: printf("Control characters are not allowed.\n"); 278: error_flag = 1; 279: break; 280: } 281: } 282: return(error_flag); 283: } 284: 285: /* 286: * Removes '-'s from the input string. 287: */ 288: remove_hyphens(str) 289: char *str; 290: { 291: char *hyphen, *index(), *strcpy(); 292: 293: while ((hyphen=index(str, '-')) != NULL) { 294: (void) strcpy(hyphen, hyphen+1); 295: } 296: } 297: 298: /* 299: * Checks to see if 'str' contains only digits (0-9). If not, then 300: * an error message is printed and '1' is returned. 301: */ 302: not_all_digits(str) 303: char *str; 304: { 305: char *ptr; 306: 307: for (ptr=str; *ptr != '\0'; ++ptr) { 308: if (!isdigit(*ptr)) { 309: printf("Phone numbers can only contain digits.\n"); 310: return(1); 311: } 312: } 313: return(0); 314: } 315: 316: /* 317: * Returns 1 when the length of the input string is not zero or equal to n. 318: * Prints an error message in this case. 319: */ 320: wrong_length(str, n) 321: char *str; 322: int n; 323: { 324: 325: if ((strlen(str) != 0) && (strlen(str) != n)) { 326: printf("The phone number should be %d digits long.\n", n); 327: return(1); 328: } 329: return(0); 330: } 331: 332: /* 333: * Make sure that building is 'E' or 'C'. 334: * Error correction is done if building is 'e', 'c', "evans", or "cory". 335: * Correction changes "str". 336: * The finger program determines the building by looking at the last 337: * character. Currently, finger only allows that character to be 'E' or 'C'. 338: * 339: * Returns 1 if incorrect room format. 340: * 341: * Note: this function assumes that the newline has been removed from str. 342: */ 343: ill_building(str) 344: char *str; 345: { 346: int length = strlen(str); 347: char *last_ch, *ptr; 348: 349: /* 350: * Zero length strings are acceptable input. 351: */ 352: if (length == 0) 353: return(0); 354: /* 355: * Delete "vans" and "ory". 356: */ 357: if (strncmp(str+length-4, "vans", 4) == 0) { 358: length -= 4; 359: str[length] = '\0'; 360: } 361: if (strncmp(str+length-3, "ory", 3) == 0) { 362: length -= 3; 363: str[length] = '\0'; 364: } 365: last_ch = str+length-1; 366: /* 367: * Now change e to E or c to C. 368: */ 369: if (*last_ch == 'e') 370: *last_ch = 'E'; 371: if (*last_ch == 'c') 372: *last_ch = 'C'; 373: /* 374: * Delete any spaces before the E or C. 375: */ 376: for (ptr=last_ch-1; ptr>str; ptr--) { 377: if (*ptr != ' ') 378: break; 379: } 380: (void) strcpy(ptr+1, last_ch); 381: /* 382: * Make sure building is evans or cory. 383: */ 384: if ((*last_ch != 'E') && (*last_ch != 'C')) { 385: printf("%s%s%s", 386: "The finger program requires that your", 387: " office be in Cory or Evans.\n", 388: "Enter this as (for example) 597E or 197C.\n"); 389: return(1); 390: } 391: return(0); 392: } 393: 394: /* get_defaults picks apart "str" and returns a structure points. 395: * "str" contains up to 4 fields separated by commas. 396: * Any field that is missing is set to blank. 397: */ 398: struct default_values 399: *get_defaults(str) 400: char *str; 401: { 402: struct default_values *answer; 403: char *malloc(), *index(); 404: 405: answer = (struct default_values *) 406: malloc((unsigned)sizeof(struct default_values)); 407: if (answer == (struct default_values *) NULL) { 408: fprintf(stderr, 409: "\nUnable to allocate storage in get_defaults!\n"); 410: exit(1); 411: } 412: /* 413: * Values if no corresponding string in "str". 414: */ 415: answer->name = str; 416: answer->office_num = ""; 417: answer->office_phone = ""; 418: answer->home_phone = ""; 419: str = index(answer->name, ','); 420: if (str == 0) 421: return(answer); 422: *str = '\0'; 423: answer->office_num = str + 1; 424: str = index(answer->office_num, ','); 425: if (str == 0) 426: return(answer); 427: *str = '\0'; 428: answer->office_phone = str + 1; 429: str = index(answer->office_phone, ','); 430: if (str == 0) 431: return(answer); 432: *str = '\0'; 433: answer->home_phone = str + 1; 434: return(answer); 435: } 436: 437: /* 438: * special_case returns true when either the default is accepted 439: * (str = '\n'), or when 'none' is typed. 'none' is accepted in 440: * either upper or lower case (or any combination). 'str' is modified 441: * in these two cases. 442: */ 443: int special_case(str,default_str) 444: char *str; 445: char *default_str; 446: { 447: static char word[] = "none\n"; 448: char *ptr, *wordptr; 449: 450: /* 451: * If the default is accepted, then change the old string do the 452: * default string. 453: */ 454: if (*str == '\n') { 455: (void) strcpy(str, default_str); 456: return(1); 457: } 458: /* 459: * Check to see if str is 'none'. (It is questionable if case 460: * insensitivity is worth the hair). 461: */ 462: wordptr = word-1; 463: for (ptr=str; *ptr != '\0'; ++ptr) { 464: ++wordptr; 465: if (*wordptr == '\0') /* then words are different sizes */ 466: return(0); 467: if (*ptr == *wordptr) 468: continue; 469: if (isupper(*ptr) && (tolower(*ptr) == *wordptr)) 470: continue; 471: /* 472: * At this point we have a mismatch, so we return 473: */ 474: return(0); 475: } 476: /* 477: * Make sure that words are the same length. 478: */ 479: if (*(wordptr+1) != '\0') 480: return(0); 481: /* 482: * Change 'str' to be the null string 483: */ 484: *str = '\0'; 485: return(1); 486: }