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