summaryrefslogtreecommitdiffstats
path: root/login-utils/chfn.c
diff options
context:
space:
mode:
authorKarel Zak2006-12-07 00:25:32 +0100
committerKarel Zak2006-12-07 00:25:32 +0100
commit6dbe3af945a63f025561abb83275cee9ff06c57b (patch)
tree19e59eac8ac465b5bc409b5adf815b582c92f633 /login-utils/chfn.c
downloadkernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.tar.gz
kernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.tar.xz
kernel-qcow2-util-linux-6dbe3af945a63f025561abb83275cee9ff06c57b.zip
Imported from util-linux-2.2 tarball.
Diffstat (limited to 'login-utils/chfn.c')
-rw-r--r--login-utils/chfn.c414
1 files changed, 414 insertions, 0 deletions
diff --git a/login-utils/chfn.c b/login-utils/chfn.c
new file mode 100644
index 000000000..2effa85d2
--- /dev/null
+++ b/login-utils/chfn.c
@@ -0,0 +1,414 @@
+/*
+ * chfn.c -- change your finger information
+ * (c) 1994 by salvatore valente <svalente@athena.mit.edu>
+ *
+ * this program is free software. you can redistribute it and
+ * modify it under the terms of the gnu general public license.
+ * there is no warranty.
+ *
+ * faith
+ * 1.1.1.1
+ * 1995/02/22 19:09:24
+ *
+ */
+
+#define _POSIX_SOURCE 1
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+
+#undef P
+#if __STDC__
+#define P(foo) foo
+#else
+#define P(foo) ()
+#endif
+
+typedef unsigned char boolean;
+#define false 0
+#define true 1
+
+static char *version_string = "chfn 0.9 beta";
+static char *whoami;
+
+static char buf[1024];
+
+struct finfo {
+ struct passwd *pw;
+ char *username;
+ char *full_name;
+ char *office;
+ char *office_phone;
+ char *home_phone;
+ char *other;
+};
+
+static boolean parse_argv P((int argc, char *argv[], struct finfo *pinfo));
+static void usage P((FILE *fp));
+static void parse_passwd P((struct passwd *pw, struct finfo *pinfo));
+static void ask_info P((struct finfo *oldfp, struct finfo *newfp));
+static char *prompt P((char *question, char *def_val));
+static int check_gecos_string P((char *msg, char *gecos));
+static boolean set_changed_data P((struct finfo *oldfp, struct finfo *newfp));
+static int save_new_data P((struct finfo *pinfo));
+static void *xmalloc P((int bytes));
+extern int strcasecmp P((char *, char *));
+extern int setpwnam P((struct passwd *pwd));
+#define memzero(ptr, size) memset((char *) ptr, 0, size)
+
+int main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cp;
+ uid_t uid;
+ struct finfo oldf, newf;
+ boolean interactive;
+ int status;
+ extern int errno;
+
+ /* whoami is the program name for error messages */
+ whoami = argv[0];
+ if (! whoami) whoami = "chfn";
+ for (cp = whoami; *cp; cp++)
+ if (*cp == '/') whoami = cp + 1;
+
+ umask (022);
+
+ /*
+ * "oldf" contains the users original finger information.
+ * "newf" contains the changed finger information, and contains NULL
+ * in fields that haven't been changed.
+ * in the end, "newf" is folded into "oldf".
+ * the reason the new finger information is not put _immediately_ into
+ * "oldf" is that on the command line, new finger information can
+ * be specified before we know what user the information is being
+ * specified for.
+ */
+ uid = getuid ();
+ memzero (&oldf, sizeof (oldf));
+ memzero (&newf, sizeof (newf));
+
+ interactive = parse_argv (argc, argv, &newf);
+ if (! newf.username) {
+ parse_passwd (getpwuid (uid), &oldf);
+ if (! oldf.username) {
+ fprintf (stderr, "%s: you (user %d) don't exist.\n", whoami, uid);
+ return (-1); }
+ }
+ else {
+ parse_passwd (getpwnam (newf.username), &oldf);
+ if (! oldf.username) {
+ cp = newf.username;
+ fprintf (stderr, "%s: user \"%s\" does not exist.\n", whoami, cp);
+ return (-1); }
+ }
+
+ /* reality check */
+ if (uid != 0 && uid != oldf.pw->pw_uid) {
+ errno = EACCES;
+ perror (whoami);
+ return (-1);
+ }
+
+ if (interactive) ask_info (&oldf, &newf);
+
+ if (! set_changed_data (&oldf, &newf)) {
+ printf ("Finger information not changed.\n");
+ return 0;
+ }
+ status = save_new_data (&oldf);
+ return status;
+}
+
+/*
+ * parse_argv () --
+ * parse the command line arguments.
+ * returns true if no information beyond the username was given.
+ */
+static boolean parse_argv (argc, argv, pinfo)
+ int argc;
+ char *argv[];
+ struct finfo *pinfo;
+{
+ int index, c, status;
+ boolean info_given;
+
+ static struct option long_options[] = {
+ { "full-name", required_argument, 0, 'f' },
+ { "office", required_argument, 0, 'o' },
+ { "office-phone", required_argument, 0, 'p' },
+ { "home-phone", required_argument, 0, 'h' },
+ { "help", no_argument, 0, 'u' },
+ { "version", no_argument, 0, 'v' },
+ { NULL, no_argument, 0, '0' },
+ };
+
+ optind = 0;
+ info_given = false;
+ while (true) {
+ c = getopt_long (argc, argv, "f:r:p:h:o:uv", long_options, &index);
+ if (c == EOF) break;
+ /* version? output version and exit. */
+ if (c == 'v') {
+ printf ("%s\n", version_string);
+ exit (0);
+ }
+ if (c == 'u') {
+ usage (stdout);
+ exit (0);
+ }
+ /* all other options must have an argument. */
+ if (! optarg) {
+ usage (stderr);
+ exit (-1);
+ }
+ /* ok, we were given an argument */
+ info_given = true;
+ status = 0;
+ strcpy (buf, whoami); strcat (buf, ": ");
+
+ /* now store the argument */
+ switch (c) {
+ case 'f':
+ pinfo->full_name = optarg;
+ strcat (buf, "full name");
+ status = check_gecos_string (buf, optarg);
+ break;
+ case 'o':
+ pinfo->office = optarg;
+ strcat (buf, "office");
+ status = check_gecos_string (buf, optarg);
+ break;
+ case 'p':
+ pinfo->office_phone = optarg;
+ strcat (buf, "office phone");
+ status = check_gecos_string (buf, optarg);
+ break;
+ case 'h':
+ pinfo->home_phone = optarg;
+ strcat (buf, "home phone");
+ status = check_gecos_string (buf, optarg);
+ break;
+ default:
+ usage (stderr);
+ status = (-1);
+ }
+ if (status < 0) exit (status);
+ }
+ /* done parsing arguments. check for a username. */
+ if (optind < argc) {
+ if (optind + 1 < argc) {
+ usage (stderr);
+ exit (-1);
+ }
+ pinfo->username = argv[optind];
+ }
+ return (! info_given);
+}
+
+/*
+ * usage () --
+ * print out a usage message.
+ */
+static void usage (fp)
+ FILE *fp;
+{
+ fprintf (fp, "Usage: %s [ -f full-name ] [ -o office ] ", whoami);
+ fprintf (fp, "[ -p office-phone ]\n [ -h home-phone ] ");
+ fprintf (fp, "[ --help ] [ --version ]\n");
+}
+
+/*
+ * parse_passwd () --
+ * take a struct password and fill in the fields of the
+ * struct finfo.
+ */
+static void parse_passwd (pw, pinfo)
+ struct passwd *pw;
+ struct finfo *pinfo;
+{
+ char *cp;
+
+ if (pw) {
+ pinfo->pw = pw;
+ pinfo->username = pw->pw_name;
+ /* use pw_gecos */
+ cp = pw->pw_gecos;
+ pinfo->full_name = cp;
+ cp = strchr (cp, ',');
+ if (cp) { *cp = 0, cp++; } else return;
+ pinfo->office = cp;
+ cp = strchr (cp, ',');
+ if (cp) { *cp = 0, cp++; } else return;
+ pinfo->office_phone = cp;
+ cp = strchr (cp, ',');
+ if (cp) { *cp = 0, cp++; } else return;
+ pinfo->home_phone = cp;
+ /* extra fields contain site-specific information, and
+ * can not be changed by this version of chfn. */
+ cp = strchr (cp, ',');
+ if (cp) { *cp = 0, cp++; } else return;
+ pinfo->other = cp;
+ }
+}
+
+/*
+ * ask_info () --
+ * prompt the user for the finger information and store it.
+ */
+static void ask_info (oldfp, newfp)
+ struct finfo *oldfp;
+ struct finfo *newfp;
+{
+ printf ("Changing finger information for %s.\n", oldfp->username);
+ newfp->full_name = prompt ("Name", oldfp->full_name);
+ newfp->office = prompt ("Office", oldfp->office);
+ newfp->office_phone = prompt ("Office Phone", oldfp->office_phone);
+ newfp->home_phone = prompt ("Home Phone", oldfp->home_phone);
+ printf ("\n");
+}
+
+/*
+ * prompt () --
+ * ask the user for a given field and check that the string is legal.
+ */
+static char *prompt (question, def_val)
+ char *question;
+ char *def_val;
+{
+ static char *blank = "none";
+ int len;
+ char *ans, *cp;
+
+ while (true) {
+ if (! def_val) def_val = "";
+ printf("%s [%s]: ", question, def_val);
+ *buf = 0;
+ if (fgets (buf, sizeof (buf), stdin) == NULL) {
+ printf ("\nAborted.\n");
+ exit (-1);
+ }
+ /* remove the newline at the end of buf. */
+ ans = buf;
+ while (isspace (*ans)) ans++;
+ len = strlen (ans);
+ while (len > 0 && isspace (ans[len-1])) len--;
+ if (len <= 0) return NULL;
+ ans[len] = 0;
+ if (! strcasecmp (ans, blank)) return "";
+ if (check_gecos_string (NULL, ans) >= 0) break;
+ }
+ cp = (char *) xmalloc (len + 1);
+ strcpy (cp, ans);
+ return cp;
+}
+
+/*
+ * check_gecos_string () --
+ * check that the given gecos string is legal. if it's not legal,
+ * output "msg" followed by a description of the problem, and
+ * return (-1).
+ */
+static int check_gecos_string (msg, gecos)
+ char *msg;
+ char *gecos;
+{
+ int i, c;
+
+ for (i = 0; i < strlen (gecos); i++) {
+ c = gecos[i];
+ if (c == ',' || c == ':' || c == '=' || c == '"' || c == '\n') {
+ if (msg) printf ("%s: ", msg);
+ printf ("'%c' is not allowed.\n", c);
+ return (-1);
+ }
+ if (iscntrl (c)) {
+ if (msg) printf ("%s: ", msg);
+ printf ("Control characters are not allowed.\n");
+ return (-1);
+ }
+ }
+ return (0);
+}
+
+/*
+ * set_changed_data () --
+ * incorporate the new data into the old finger info.
+ */
+static boolean set_changed_data (oldfp, newfp)
+ struct finfo *oldfp;
+ struct finfo *newfp;
+{
+ boolean changed = false;
+
+ if (newfp->full_name) {
+ oldfp->full_name = newfp->full_name; changed = true; }
+ if (newfp->office) {
+ oldfp->office = newfp->office; changed = true; }
+ if (newfp->office_phone) {
+ oldfp->office_phone = newfp->office_phone; changed = true; }
+ if (newfp->home_phone) {
+ oldfp->home_phone = newfp->home_phone; changed = true; }
+
+ return changed;
+}
+
+/*
+ * save_new_data () --
+ * save the given finger info in /etc/passwd.
+ * return zero on success.
+ */
+static int save_new_data (pinfo)
+ struct finfo *pinfo;
+{
+ char *gecos;
+ int len;
+
+ /* null fields will confuse printf(). */
+ if (! pinfo->full_name) pinfo->full_name = "";
+ if (! pinfo->office) pinfo->office = "";
+ if (! pinfo->office_phone) pinfo->office_phone = "";
+ if (! pinfo->home_phone) pinfo->home_phone = "";
+ if (! pinfo->other) pinfo->other = "";
+
+ /* create the new gecos string */
+ len = (strlen (pinfo->full_name) + strlen (pinfo->office) +
+ strlen (pinfo->office_phone) + strlen (pinfo->home_phone) +
+ strlen (pinfo->other) + 4);
+ gecos = (char *) xmalloc (len + 1);
+ sprintf (gecos, "%s,%s,%s,%s,%s", pinfo->full_name, pinfo->office,
+ pinfo->office_phone, pinfo->home_phone, pinfo->other);
+
+ /* write the new struct passwd to the passwd file. */
+ pinfo->pw->pw_gecos = gecos;
+ if (setpwnam (pinfo->pw) < 0) {
+ perror ("setpwnam");
+ return (-1);
+ }
+ printf ("Finger information changed.\n");
+ return 0;
+}
+
+/*
+ * xmalloc () -- malloc that never fails.
+ */
+static void *xmalloc (bytes)
+ int bytes;
+{
+ void *vp;
+
+ vp = malloc (bytes);
+ if (! vp && bytes > 0) {
+ perror ("malloc failed");
+ exit (-1);
+ }
+ return vp;
+}