]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/chfn.c
Imported from util-linux-2.2 tarball.
[thirdparty/util-linux.git] / login-utils / chfn.c
CommitLineData
6dbe3af9
KZ
1/*
2 * chfn.c -- change your finger information
3 * (c) 1994 by salvatore valente <svalente@athena.mit.edu>
4 *
5 * this program is free software. you can redistribute it and
6 * modify it under the terms of the gnu general public license.
7 * there is no warranty.
8 *
9 * faith
10 * 1.1.1.1
11 * 1995/02/22 19:09:24
12 *
13 */
14
15#define _POSIX_SOURCE 1
16
17#include <sys/types.h>
18#include <stdio.h>
19#include <string.h>
20#include <stdlib.h>
21#include <unistd.h>
22#include <pwd.h>
23#include <errno.h>
24#include <ctype.h>
25#include <getopt.h>
26
27#undef P
28#if __STDC__
29#define P(foo) foo
30#else
31#define P(foo) ()
32#endif
33
34typedef unsigned char boolean;
35#define false 0
36#define true 1
37
38static char *version_string = "chfn 0.9 beta";
39static char *whoami;
40
41static char buf[1024];
42
43struct finfo {
44 struct passwd *pw;
45 char *username;
46 char *full_name;
47 char *office;
48 char *office_phone;
49 char *home_phone;
50 char *other;
51};
52
53static boolean parse_argv P((int argc, char *argv[], struct finfo *pinfo));
54static void usage P((FILE *fp));
55static void parse_passwd P((struct passwd *pw, struct finfo *pinfo));
56static void ask_info P((struct finfo *oldfp, struct finfo *newfp));
57static char *prompt P((char *question, char *def_val));
58static int check_gecos_string P((char *msg, char *gecos));
59static boolean set_changed_data P((struct finfo *oldfp, struct finfo *newfp));
60static int save_new_data P((struct finfo *pinfo));
61static void *xmalloc P((int bytes));
62extern int strcasecmp P((char *, char *));
63extern int setpwnam P((struct passwd *pwd));
64#define memzero(ptr, size) memset((char *) ptr, 0, size)
65
66int main (argc, argv)
67 int argc;
68 char *argv[];
69{
70 char *cp;
71 uid_t uid;
72 struct finfo oldf, newf;
73 boolean interactive;
74 int status;
75 extern int errno;
76
77 /* whoami is the program name for error messages */
78 whoami = argv[0];
79 if (! whoami) whoami = "chfn";
80 for (cp = whoami; *cp; cp++)
81 if (*cp == '/') whoami = cp + 1;
82
83 umask (022);
84
85 /*
86 * "oldf" contains the users original finger information.
87 * "newf" contains the changed finger information, and contains NULL
88 * in fields that haven't been changed.
89 * in the end, "newf" is folded into "oldf".
90 * the reason the new finger information is not put _immediately_ into
91 * "oldf" is that on the command line, new finger information can
92 * be specified before we know what user the information is being
93 * specified for.
94 */
95 uid = getuid ();
96 memzero (&oldf, sizeof (oldf));
97 memzero (&newf, sizeof (newf));
98
99 interactive = parse_argv (argc, argv, &newf);
100 if (! newf.username) {
101 parse_passwd (getpwuid (uid), &oldf);
102 if (! oldf.username) {
103 fprintf (stderr, "%s: you (user %d) don't exist.\n", whoami, uid);
104 return (-1); }
105 }
106 else {
107 parse_passwd (getpwnam (newf.username), &oldf);
108 if (! oldf.username) {
109 cp = newf.username;
110 fprintf (stderr, "%s: user \"%s\" does not exist.\n", whoami, cp);
111 return (-1); }
112 }
113
114 /* reality check */
115 if (uid != 0 && uid != oldf.pw->pw_uid) {
116 errno = EACCES;
117 perror (whoami);
118 return (-1);
119 }
120
121 if (interactive) ask_info (&oldf, &newf);
122
123 if (! set_changed_data (&oldf, &newf)) {
124 printf ("Finger information not changed.\n");
125 return 0;
126 }
127 status = save_new_data (&oldf);
128 return status;
129}
130
131/*
132 * parse_argv () --
133 * parse the command line arguments.
134 * returns true if no information beyond the username was given.
135 */
136static boolean parse_argv (argc, argv, pinfo)
137 int argc;
138 char *argv[];
139 struct finfo *pinfo;
140{
141 int index, c, status;
142 boolean info_given;
143
144 static struct option long_options[] = {
145 { "full-name", required_argument, 0, 'f' },
146 { "office", required_argument, 0, 'o' },
147 { "office-phone", required_argument, 0, 'p' },
148 { "home-phone", required_argument, 0, 'h' },
149 { "help", no_argument, 0, 'u' },
150 { "version", no_argument, 0, 'v' },
151 { NULL, no_argument, 0, '0' },
152 };
153
154 optind = 0;
155 info_given = false;
156 while (true) {
157 c = getopt_long (argc, argv, "f:r:p:h:o:uv", long_options, &index);
158 if (c == EOF) break;
159 /* version? output version and exit. */
160 if (c == 'v') {
161 printf ("%s\n", version_string);
162 exit (0);
163 }
164 if (c == 'u') {
165 usage (stdout);
166 exit (0);
167 }
168 /* all other options must have an argument. */
169 if (! optarg) {
170 usage (stderr);
171 exit (-1);
172 }
173 /* ok, we were given an argument */
174 info_given = true;
175 status = 0;
176 strcpy (buf, whoami); strcat (buf, ": ");
177
178 /* now store the argument */
179 switch (c) {
180 case 'f':
181 pinfo->full_name = optarg;
182 strcat (buf, "full name");
183 status = check_gecos_string (buf, optarg);
184 break;
185 case 'o':
186 pinfo->office = optarg;
187 strcat (buf, "office");
188 status = check_gecos_string (buf, optarg);
189 break;
190 case 'p':
191 pinfo->office_phone = optarg;
192 strcat (buf, "office phone");
193 status = check_gecos_string (buf, optarg);
194 break;
195 case 'h':
196 pinfo->home_phone = optarg;
197 strcat (buf, "home phone");
198 status = check_gecos_string (buf, optarg);
199 break;
200 default:
201 usage (stderr);
202 status = (-1);
203 }
204 if (status < 0) exit (status);
205 }
206 /* done parsing arguments. check for a username. */
207 if (optind < argc) {
208 if (optind + 1 < argc) {
209 usage (stderr);
210 exit (-1);
211 }
212 pinfo->username = argv[optind];
213 }
214 return (! info_given);
215}
216
217/*
218 * usage () --
219 * print out a usage message.
220 */
221static void usage (fp)
222 FILE *fp;
223{
224 fprintf (fp, "Usage: %s [ -f full-name ] [ -o office ] ", whoami);
225 fprintf (fp, "[ -p office-phone ]\n [ -h home-phone ] ");
226 fprintf (fp, "[ --help ] [ --version ]\n");
227}
228
229/*
230 * parse_passwd () --
231 * take a struct password and fill in the fields of the
232 * struct finfo.
233 */
234static void parse_passwd (pw, pinfo)
235 struct passwd *pw;
236 struct finfo *pinfo;
237{
238 char *cp;
239
240 if (pw) {
241 pinfo->pw = pw;
242 pinfo->username = pw->pw_name;
243 /* use pw_gecos */
244 cp = pw->pw_gecos;
245 pinfo->full_name = cp;
246 cp = strchr (cp, ',');
247 if (cp) { *cp = 0, cp++; } else return;
248 pinfo->office = cp;
249 cp = strchr (cp, ',');
250 if (cp) { *cp = 0, cp++; } else return;
251 pinfo->office_phone = cp;
252 cp = strchr (cp, ',');
253 if (cp) { *cp = 0, cp++; } else return;
254 pinfo->home_phone = cp;
255 /* extra fields contain site-specific information, and
256 * can not be changed by this version of chfn. */
257 cp = strchr (cp, ',');
258 if (cp) { *cp = 0, cp++; } else return;
259 pinfo->other = cp;
260 }
261}
262
263/*
264 * ask_info () --
265 * prompt the user for the finger information and store it.
266 */
267static void ask_info (oldfp, newfp)
268 struct finfo *oldfp;
269 struct finfo *newfp;
270{
271 printf ("Changing finger information for %s.\n", oldfp->username);
272 newfp->full_name = prompt ("Name", oldfp->full_name);
273 newfp->office = prompt ("Office", oldfp->office);
274 newfp->office_phone = prompt ("Office Phone", oldfp->office_phone);
275 newfp->home_phone = prompt ("Home Phone", oldfp->home_phone);
276 printf ("\n");
277}
278
279/*
280 * prompt () --
281 * ask the user for a given field and check that the string is legal.
282 */
283static char *prompt (question, def_val)
284 char *question;
285 char *def_val;
286{
287 static char *blank = "none";
288 int len;
289 char *ans, *cp;
290
291 while (true) {
292 if (! def_val) def_val = "";
293 printf("%s [%s]: ", question, def_val);
294 *buf = 0;
295 if (fgets (buf, sizeof (buf), stdin) == NULL) {
296 printf ("\nAborted.\n");
297 exit (-1);
298 }
299 /* remove the newline at the end of buf. */
300 ans = buf;
301 while (isspace (*ans)) ans++;
302 len = strlen (ans);
303 while (len > 0 && isspace (ans[len-1])) len--;
304 if (len <= 0) return NULL;
305 ans[len] = 0;
306 if (! strcasecmp (ans, blank)) return "";
307 if (check_gecos_string (NULL, ans) >= 0) break;
308 }
309 cp = (char *) xmalloc (len + 1);
310 strcpy (cp, ans);
311 return cp;
312}
313
314/*
315 * check_gecos_string () --
316 * check that the given gecos string is legal. if it's not legal,
317 * output "msg" followed by a description of the problem, and
318 * return (-1).
319 */
320static int check_gecos_string (msg, gecos)
321 char *msg;
322 char *gecos;
323{
324 int i, c;
325
326 for (i = 0; i < strlen (gecos); i++) {
327 c = gecos[i];
328 if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
329 if (msg) printf ("%s: ", msg);
330 printf ("'%c' is not allowed.\n", c);
331 return (-1);
332 }
333 if (iscntrl (c)) {
334 if (msg) printf ("%s: ", msg);
335 printf ("Control characters are not allowed.\n");
336 return (-1);
337 }
338 }
339 return (0);
340}
341
342/*
343 * set_changed_data () --
344 * incorporate the new data into the old finger info.
345 */
346static boolean set_changed_data (oldfp, newfp)
347 struct finfo *oldfp;
348 struct finfo *newfp;
349{
350 boolean changed = false;
351
352 if (newfp->full_name) {
353 oldfp->full_name = newfp->full_name; changed = true; }
354 if (newfp->office) {
355 oldfp->office = newfp->office; changed = true; }
356 if (newfp->office_phone) {
357 oldfp->office_phone = newfp->office_phone; changed = true; }
358 if (newfp->home_phone) {
359 oldfp->home_phone = newfp->home_phone; changed = true; }
360
361 return changed;
362}
363
364/*
365 * save_new_data () --
366 * save the given finger info in /etc/passwd.
367 * return zero on success.
368 */
369static int save_new_data (pinfo)
370 struct finfo *pinfo;
371{
372 char *gecos;
373 int len;
374
375 /* null fields will confuse printf(). */
376 if (! pinfo->full_name) pinfo->full_name = "";
377 if (! pinfo->office) pinfo->office = "";
378 if (! pinfo->office_phone) pinfo->office_phone = "";
379 if (! pinfo->home_phone) pinfo->home_phone = "";
380 if (! pinfo->other) pinfo->other = "";
381
382 /* create the new gecos string */
383 len = (strlen (pinfo->full_name) + strlen (pinfo->office) +
384 strlen (pinfo->office_phone) + strlen (pinfo->home_phone) +
385 strlen (pinfo->other) + 4);
386 gecos = (char *) xmalloc (len + 1);
387 sprintf (gecos, "%s,%s,%s,%s,%s", pinfo->full_name, pinfo->office,
388 pinfo->office_phone, pinfo->home_phone, pinfo->other);
389
390 /* write the new struct passwd to the passwd file. */
391 pinfo->pw->pw_gecos = gecos;
392 if (setpwnam (pinfo->pw) < 0) {
393 perror ("setpwnam");
394 return (-1);
395 }
396 printf ("Finger information changed.\n");
397 return 0;
398}
399
400/*
401 * xmalloc () -- malloc that never fails.
402 */
403static void *xmalloc (bytes)
404 int bytes;
405{
406 void *vp;
407
408 vp = malloc (bytes);
409 if (! vp && bytes > 0) {
410 perror ("malloc failed");
411 exit (-1);
412 }
413 return vp;
414}