Commit | Line | Data |
---|---|---|
9e2ead69 | 1 | #ifndef lint |
fb8f0ff2 | 2 | static char *sccsid = "@(#)chfn.sh 4.6 (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> | |
840fc587 SL |
11 | #include <sys/time.h> |
12 | #include <sys/resource.h> | |
9f52e30a | 13 | #include <sys/file.h> |
f1ad9148 KM |
14 | #include <ctype.h> |
15 | ||
16 | struct default_values { | |
17 | char *name; | |
18 | char *office_num; | |
19 | char *office_phone; | |
20 | char *home_phone; | |
21 | }; | |
64504187 BJ |
22 | |
23 | char passwd[] = "/etc/passwd"; | |
24 | char temp[] = "/etc/ptmp"; | |
25 | struct passwd *pwd; | |
9e2ead69 | 26 | struct passwd *getpwent(), *getpwnam(), *getpwuid(); |
64504187 BJ |
27 | int endpwent(); |
28 | char *crypt(); | |
29 | char *getpass(); | |
64504187 BJ |
30 | |
31 | main(argc, argv) | |
9e2ead69 KM |
32 | int argc; |
33 | char *argv[]; | |
64504187 | 34 | { |
9e2ead69 | 35 | int user_uid; |
f1ad9148 | 36 | char replacement[4*BUFSIZ]; |
9f52e30a | 37 | int fd; |
64504187 BJ |
38 | FILE *tf; |
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 | } | |
9f52e30a SL |
106 | unlimit(RLIMIT_CPU); |
107 | unlimit(RLIMIT_FSIZE); | |
9e2ead69 | 108 | /* |
9f52e30a SL |
109 | * Copy passwd to temp, replacing matching lines |
110 | * with new gecos field. | |
9e2ead69 | 111 | */ |
9f52e30a SL |
112 | while ((pwd = getpwent()) != NULL) { |
113 | if (pwd->pw_uid == user_uid) | |
9e2ead69 | 114 | pwd->pw_gecos = replacement; |
64504187 BJ |
115 | fprintf(tf,"%s:%s:%d:%d:%s:%s:%s\n", |
116 | pwd->pw_name, | |
117 | pwd->pw_passwd, | |
118 | pwd->pw_uid, | |
119 | pwd->pw_gid, | |
120 | pwd->pw_gecos, | |
121 | pwd->pw_dir, | |
122 | pwd->pw_shell); | |
123 | } | |
9e2ead69 | 124 | (void) endpwent(); |
9f52e30a SL |
125 | if (rename(temp, passwd) < 0) { |
126 | fprintf(stderr, "chfn: "); perror("rename"); | |
127 | out: | |
128 | (void) unlink(temp); | |
129 | exit(1); | |
64504187 | 130 | } |
9f52e30a SL |
131 | (void) fclose(tf); |
132 | exit(0); | |
133 | } | |
134 | ||
135 | unlimit(lim) | |
136 | { | |
137 | struct rlimit rlim; | |
138 | ||
139 | rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; | |
140 | (void) setrlimit(lim, &rlim); | |
9e2ead69 | 141 | } |
f1ad9148 | 142 | |
9e2ead69 KM |
143 | /* |
144 | * Get name, room number, school phone, and home phone. | |
145 | */ | |
f1ad9148 KM |
146 | get_info(gecos_field, answer) |
147 | char *gecos_field; | |
9e2ead69 KM |
148 | char *answer; |
149 | { | |
150 | char *strcpy(), *strcat(); | |
f1ad9148 KM |
151 | char in_str[BUFSIZ]; |
152 | struct default_values *defaults, *get_defaults(); | |
9e2ead69 | 153 | |
f1ad9148 KM |
154 | answer[0] = '\0'; |
155 | defaults = get_defaults(gecos_field); | |
156 | printf("Default values are printed inside of of '[]'.\n"); | |
157 | printf("To accept the default, type <return>.\n"); | |
158 | printf("To have a blank entry, type the word 'none'.\n"); | |
9e2ead69 KM |
159 | /* |
160 | * Get name. | |
161 | */ | |
162 | do { | |
f1ad9148 KM |
163 | printf("\nName [%s]: ", defaults->name); |
164 | (void) fgets(in_str, BUFSIZ, stdin); | |
165 | if (special_case(in_str, defaults->name)) | |
166 | break; | |
9e2ead69 KM |
167 | } while (illegal_input(in_str)); |
168 | (void) strcpy(answer, in_str); | |
169 | /* | |
170 | * Get room number. | |
171 | */ | |
172 | do { | |
f1ad9148 KM |
173 | printf("Room number (Exs: 597E or 197C) [%s]: ", |
174 | defaults->office_num); | |
175 | (void) fgets(in_str, BUFSIZ, stdin); | |
176 | if (special_case(in_str, defaults->office_num)) | |
177 | break; | |
9e2ead69 KM |
178 | } while (illegal_input(in_str) || illegal_building(in_str)); |
179 | (void) strcat(strcat(answer, ","), in_str); | |
180 | /* | |
181 | * Get office phone number. | |
f1ad9148 | 182 | * Remove hyphens and 642, x2, or 2 prefixes if present. |
9e2ead69 KM |
183 | */ |
184 | do { | |
f1ad9148 KM |
185 | printf("Office Phone (Ex: 1632) [%s]: ", |
186 | defaults->office_phone); | |
187 | (void) fgets(in_str, BUFSIZ, stdin); | |
188 | if (special_case(in_str, defaults->office_phone)) | |
189 | break; | |
9e2ead69 KM |
190 | remove_hyphens(in_str); |
191 | if ((strlen(in_str) == 8) && (strcmpn(in_str, "642", 3) == 0)) | |
192 | (void) strcpy(in_str, in_str+3); | |
193 | if ((strlen(in_str) == 7) && (strcmpn(in_str, "x2", 2) == 0)) | |
194 | (void) strcpy(in_str, in_str+2); | |
f1ad9148 KM |
195 | if ((strlen(in_str) == 6) && (in_str[0] == '2')) |
196 | (void) strcpy(in_str, in_str+1); | |
197 | } while (illegal_input(in_str) || not_all_digits(in_str) | |
198 | || wrong_length(in_str, 4)); | |
9e2ead69 KM |
199 | (void) strcat(strcat(answer, ","), in_str); |
200 | /* | |
201 | * Get home phone number. | |
202 | * Remove hyphens if present. | |
203 | */ | |
204 | do { | |
f1ad9148 KM |
205 | printf("Home Phone (Ex: 9875432) [%s]: ", defaults->home_phone); |
206 | (void) fgets(in_str, BUFSIZ, stdin); | |
207 | if (special_case(in_str, defaults->home_phone)) | |
208 | break; | |
9e2ead69 | 209 | remove_hyphens(in_str); |
f1ad9148 | 210 | } while (illegal_input(in_str) || not_all_digits(in_str)); |
9e2ead69 KM |
211 | (void) strcat(strcat(answer, ","), in_str); |
212 | } | |
f1ad9148 | 213 | |
9e2ead69 KM |
214 | /* |
215 | * Prints an error message if a ':' or a newline is found in the string. | |
216 | * A message is also printed if the input string is too long. | |
217 | * The password file uses :'s as seperators, and are not allowed in the "gcos" | |
f1ad9148 | 218 | * field. Newlines serve as delimiters between users in the password file, |
9e2ead69 KM |
219 | * and so, those too, are checked for. (I don't think that it is possible to |
220 | * type them in, but better safe than sorry) | |
221 | * | |
222 | * Returns '1' if a colon or newline is found or the input line is too long. | |
223 | */ | |
224 | illegal_input(input_str) | |
225 | char *input_str; | |
226 | { | |
227 | char *index(); | |
228 | char *ptr; | |
229 | int error_flag = 0; | |
230 | int length = strlen(input_str); | |
231 | ||
232 | if (index(input_str, ':')) { | |
233 | printf("':' is not allowed.\n"); | |
234 | error_flag = 1; | |
235 | } | |
236 | if (input_str[length-1] != '\n') { | |
237 | /* the newline and the '\0' eat up two characters */ | |
238 | printf("Maximum number of characters allowed is %d\n", | |
f1ad9148 | 239 | BUFSIZ-2); |
9e2ead69 KM |
240 | /* flush the rest of the input line */ |
241 | while (getchar() != '\n') | |
242 | /* void */; | |
243 | error_flag = 1; | |
244 | } | |
245 | /* | |
246 | * Delete newline by shortening string by 1. | |
247 | */ | |
248 | input_str[length-1] = '\0'; | |
249 | /* | |
250 | * Don't allow control characters, etc in input string. | |
251 | */ | |
252 | for (ptr=input_str; *ptr != '\0'; ptr++) { | |
253 | if ((int) *ptr < 040) { | |
254 | printf("Control characters are not allowed.\n"); | |
255 | error_flag = 1; | |
256 | break; | |
257 | } | |
258 | } | |
259 | return(error_flag); | |
260 | } | |
261 | ||
262 | /* | |
263 | * Removes '-'s from the input string. | |
264 | */ | |
265 | remove_hyphens(str) | |
266 | char *str; | |
267 | { | |
268 | char *hyphen, *index(), *strcpy(); | |
269 | ||
270 | while ((hyphen=index(str, '-')) != NULL) { | |
271 | (void) strcpy(hyphen, hyphen+1); | |
272 | } | |
273 | } | |
64504187 | 274 | |
f1ad9148 KM |
275 | /* |
276 | * Checks to see if 'str' contains only digits (0-9). If not, then | |
277 | * an error message is printed and '1' is returned. | |
278 | */ | |
279 | not_all_digits(str) | |
280 | char *str; | |
281 | { | |
282 | char *ptr; | |
283 | ||
284 | for (ptr=str; *ptr != '\0'; ++ptr) { | |
285 | if (!isdigit(*ptr)) { | |
286 | printf("Phone numbers can only contain digits.\n"); | |
287 | return(1); | |
288 | } | |
289 | } | |
290 | return(0); | |
291 | } | |
292 | ||
9e2ead69 KM |
293 | /* |
294 | * Returns 1 when the length of the input string is not zero or equal to n. | |
295 | * Prints an error message in this case. | |
296 | */ | |
297 | wrong_length(str, n) | |
298 | char *str; | |
299 | int n; | |
300 | { | |
f1ad9148 | 301 | |
9e2ead69 KM |
302 | if ((strlen(str) != 0) && (strlen(str) != n)) { |
303 | printf("The phone number should be %d digits long.\n", n); | |
304 | return(1); | |
305 | } | |
306 | return(0); | |
307 | } | |
308 | ||
309 | /* | |
310 | * Make sure that building is 'E' or 'C'. | |
311 | * Error correction is done if building is 'e', 'c', "evans", or "cory". | |
312 | * Correction changes "str". | |
313 | * The finger program determines the building by looking at the last | |
314 | * character. Currently, finger only allows that character to be 'E' or 'C'. | |
315 | * | |
316 | * Returns 1 if incorrect room format. | |
317 | * | |
318 | * Note: this function assumes that the newline has been removed from str. | |
319 | */ | |
320 | illegal_building(str) | |
321 | char *str; | |
322 | { | |
323 | int length = strlen(str); | |
324 | char *last_ch, *ptr; | |
325 | ||
326 | /* | |
327 | * Zero length strings are acceptable input. | |
328 | */ | |
329 | if (length == 0) | |
330 | return(0); | |
331 | /* | |
332 | * Delete "vans" and "ory". | |
333 | */ | |
334 | if (strcmpn(str+length-4, "vans", 4) == 0) { | |
335 | length -= 4; | |
336 | str[length] = '\0'; | |
337 | } | |
338 | if (strcmpn(str+length-3, "ory", 3) == 0) { | |
339 | length -= 3; | |
340 | str[length] = '\0'; | |
341 | } | |
342 | last_ch = str+length-1; | |
343 | /* | |
344 | * Now change e to E or c to C. | |
345 | */ | |
346 | if (*last_ch == 'e') | |
347 | *last_ch = 'E'; | |
348 | if (*last_ch == 'c') | |
349 | *last_ch = 'C'; | |
350 | /* | |
351 | * Delete any spaces before the E or C. | |
352 | */ | |
353 | for (ptr=last_ch-1; ptr>str; ptr--) { | |
354 | if (*ptr != ' ') | |
355 | break; | |
356 | } | |
357 | (void) strcpy(ptr+1, last_ch); | |
358 | /* | |
359 | * Make sure building is evans or cory. | |
360 | */ | |
361 | if ((*last_ch != 'E') && (*last_ch != 'C')) { | |
362 | printf("%s%s%s", | |
363 | "The finger program requires that your", | |
364 | " office be in Cory or Evans.\n", | |
365 | "Enter this as (for example) 597E or 197C.\n"); | |
366 | return(1); | |
367 | } | |
368 | return(0); | |
64504187 | 369 | } |
f1ad9148 KM |
370 | |
371 | /* get_defaults picks apart "str" and returns a structure points. | |
372 | * "str" contains up to 4 fields separated by commas. | |
373 | * Any field that is missing is set to blank. | |
374 | */ | |
375 | struct default_values | |
376 | *get_defaults(str) | |
377 | char *str; | |
378 | { | |
379 | struct default_values *answer; | |
380 | char *malloc(), *index(); | |
381 | ||
382 | answer = (struct default_values *) | |
383 | malloc((unsigned)sizeof(struct default_values)); | |
384 | if (answer == (struct default_values *) NULL) { | |
385 | fprintf(stderr, | |
386 | "\nUnable to allocate storage in get_defaults!\n"); | |
387 | exit(1); | |
388 | } | |
389 | /* | |
390 | * Values if no corresponding string in "str". | |
391 | */ | |
392 | answer->name = str; | |
393 | answer->office_num = ""; | |
394 | answer->office_phone = ""; | |
395 | answer->home_phone = ""; | |
396 | str = index(answer->name, ','); | |
397 | if (str == 0) | |
398 | return(answer); | |
399 | *str = '\0'; | |
400 | answer->office_num = str + 1; | |
401 | str = index(answer->office_num, ','); | |
402 | if (str == 0) | |
403 | return(answer); | |
404 | *str = '\0'; | |
405 | answer->office_phone = str + 1; | |
406 | str = index(answer->office_phone, ','); | |
407 | if (str == 0) | |
408 | return(answer); | |
409 | *str = '\0'; | |
410 | answer->home_phone = str + 1; | |
411 | return(answer); | |
412 | } | |
413 | ||
414 | /* | |
415 | * special_case returns true when either the default is accepted | |
416 | * (str = '\n'), or when 'none' is typed. 'none' is accepted in | |
417 | * either upper or lower case (or any combination). 'str' is modified | |
418 | * in these two cases. | |
419 | */ | |
420 | int special_case(str,default_str) | |
421 | char *str; | |
422 | char *default_str; | |
423 | { | |
424 | static char word[] = "none\n"; | |
425 | char *ptr, *wordptr; | |
426 | ||
427 | /* | |
428 | * If the default is accepted, then change the old string do the | |
429 | * default string. | |
430 | */ | |
431 | if (*str == '\n') { | |
432 | (void) strcpy(str, default_str); | |
433 | return(1); | |
434 | } | |
435 | /* | |
436 | * Check to see if str is 'none'. (It is questionable if case | |
437 | * insensitivity is worth the hair). | |
438 | */ | |
439 | wordptr = word-1; | |
440 | for (ptr=str; *ptr != '\0'; ++ptr) { | |
441 | ++wordptr; | |
442 | if (*wordptr == '\0') /* then words are different sizes */ | |
443 | return(0); | |
444 | if (*ptr == *wordptr) | |
445 | continue; | |
446 | if (isupper(*ptr) && (tolower(*ptr) == *wordptr)) | |
447 | continue; | |
448 | /* | |
449 | * At this point we have a mismatch, so we return | |
450 | */ | |
451 | return(0); | |
452 | } | |
453 | /* | |
454 | * Make sure that words are the same length. | |
455 | */ | |
456 | if (*(wordptr+1) != '\0') | |
457 | return(0); | |
458 | /* | |
459 | * Change 'str' to be the null string | |
460 | */ | |
461 | *str = '\0'; | |
462 | return(1); | |
463 | } |