/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2002 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * user.c: support for the scadm useradd, userdel, usershow, userpassword,
 * userperm options (administration of service processor users)
 */

#include <libintl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <time.h>  /* required by librsc.h */

#include "librsc.h"
#include "adm.h"


static void ADM_Get_Password(char  *password);
static void ADM_Destroy_Password(char  *password);
static void max_username();
static void malformed_username();
static void wrong_response();
static void no_user();
static void no_info();
static void userperm_usage();
static void show_header();
static void cleanup();


/* Globals so that exit routine can clean up echo */
static int		echoOff = 0;
static struct termios	oldOpts;

typedef union {
	char	DataBuffer[DP_MAX_MSGLEN];
	void	*DataBuffer_p;
} data_buffer_t;


void
ADM_Process_useradd(int argc, char *argv[])
{
	static data_buffer_t	dataBuffer;
	rscp_msg_t		Message;
	struct timespec		Timeout;
	dp_user_adm_t		*admMessage;
	dp_user_adm_r_t		*admResponse;
	char			*userName;


	if (argc != 3) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("USAGE: scadm useradd <username>"));
		exit(-1);
	}

	ADM_Start();

	if (strlen(argv[2]) > DP_USER_NAME_SIZE) {
		max_username();
		exit(-1);
	}

	admMessage = (dp_user_adm_t *)&dataBuffer;
	userName   = (char *)(&((char *)admMessage)[sizeof (dp_user_adm_t)]);
	admMessage->command = DP_USER_CMD_ADD;
	(void) strcpy(userName, argv[2]);

	Message.type = DP_USER_ADM;
	Message.len  = sizeof (dp_user_adm_t) + strlen(userName) + 1;
	Message.data = admMessage;
	ADM_Send(&Message);

	Timeout.tv_nsec = 0;
	Timeout.tv_sec  = ADM_SEPROM_TIMEOUT;
	ADM_Recv(&Message, &Timeout, DP_USER_ADM_R, sizeof (dp_user_adm_r_t));

	admResponse = (dp_user_adm_r_t *)Message.data;
	if (admResponse->command != DP_USER_CMD_ADD) {
		wrong_response();
		exit(-1);
	}

	if (admResponse->status == DP_ERR_USER_FULL) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("scadm: all user slots are full"));
		exit(-1);
	} else if (admResponse->status == DP_ERR_USER_THERE) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("scadm: user already exists"));
		exit(-1);
	} else if (admResponse->status == DP_ERR_USER_WARNING) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("scadm: username did not start with letter\n"
		    "        or did not contain lower case letter\n"));
		exit(-1);
	} else if (admResponse->status == DP_ERR_USER_BAD) {
		malformed_username();
		exit(-1);
	} else if (admResponse->status != 0) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("scadm: couldn't add user"));
		exit(-1);
	}

	ADM_Free(&Message);
}


void
ADM_Process_userdel(int argc, char *argv[])
{
	static data_buffer_t	dataBuffer;
	rscp_msg_t		Message;
	struct timespec		Timeout;
	dp_user_adm_t		*admMessage;
	dp_user_adm_r_t		*admResponse;
	char			*userName;


	if (argc != 3) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("USAGE: scadm userdel <username>"));
		exit(-1);
	}

	ADM_Start();

	if (strlen(argv[2]) > DP_USER_NAME_SIZE) {
		max_username();
		exit(-1);
	}

	admMessage = (dp_user_adm_t *)&dataBuffer;
	userName   = (char *)(&((char *)admMessage)[sizeof (dp_user_adm_t)]);
	admMessage->command = DP_USER_CMD_DEL;
	(void) strcpy(userName, argv[2]);

	Message.type = DP_USER_ADM;
	Message.len  = sizeof (dp_user_adm_t) + strlen(userName) + 1;
	Message.data = admMessage;
	ADM_Send(&Message);

	Timeout.tv_nsec = 0;
	Timeout.tv_sec  = ADM_SEPROM_TIMEOUT;
	ADM_Recv(&Message, &Timeout, DP_USER_ADM_R, sizeof (dp_user_adm_r_t));

	admResponse = (dp_user_adm_r_t *)Message.data;
	if (admResponse->command != DP_USER_CMD_DEL) {
		wrong_response();
		exit(-1);
	}

	if (admResponse->status == DP_ERR_USER_NONE) {
		no_user();
		exit(-1);
	} else if (admResponse->status == DP_ERR_USER_BAD) {
		malformed_username();
		exit(-1);
	} else if (admResponse->status != 0) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("scadm: couldn't delete user"));
		exit(-1);
	}

	ADM_Free(&Message);
}


void
ADM_Process_usershow(int argc, char *argv[])
{
	static data_buffer_t	dataBuffer;
	rscp_msg_t		Message;
	struct timespec		Timeout;
	dp_user_adm_t		*admMessage;
	dp_user_adm_r_t		*admResponse;
	char			*userName;
	char			*permissions;
	char			*passwd;
	int			index;



	if ((argc != 2) && (argc != 3)) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("USAGE: scadm usershow [username]"));
		exit(-1);
	}

	ADM_Start();

	if (argc == 3) {
		admMessage = (dp_user_adm_t *)&dataBuffer;
		admMessage->command = DP_USER_CMD_SHOW;
		Message.type = DP_USER_ADM;
		Message.data = admMessage;

		if (strlen(argv[2]) > DP_USER_NAME_SIZE) {
			max_username();
			exit(-1);
		}
		userName = (char *)(&((char *)admMessage)[
		    sizeof (dp_user_adm_t)]);
		(void) strcpy(userName, argv[2]);
		admMessage->parm = DP_USER_SHOW_USERNAME;
		Message.len = sizeof (dp_user_adm_t) + strlen(userName) + 1;
		ADM_Send(&Message);

		Timeout.tv_nsec = 0;
		Timeout.tv_sec  = ADM_SEPROM_TIMEOUT;
		ADM_Recv(&Message, &Timeout,
		    DP_USER_ADM_R, sizeof (dp_user_adm_r_t));

		admResponse = (dp_user_adm_r_t *)Message.data;
		if (admResponse->command != DP_USER_CMD_SHOW) {
			wrong_response();
			exit(-1);
		}

		if (admResponse->status == DP_ERR_USER_NONE) {
			no_user();
			exit(-1);
		} else if (admResponse->status == DP_ERR_USER_BAD) {
			malformed_username();
			exit(-1);
		} else if (admResponse->status != 0) {
			no_info();
			exit(-1);
		}

		userName = &(((char *)admResponse)[
		    sizeof (dp_user_adm_r_t)]);
		permissions = &userName[strlen(userName)+1];
		passwd = &permissions[strlen(permissions)+1];
		show_header();
		(void) printf(" %-16s    %-15s    ", userName, permissions);
		if (strncmp(passwd, "Assigned", 12) == 0) {
			(void) printf("%s\n\n", gettext("Assigned"));
		} else if (strncmp(passwd, "None", 12) == 0) {
			(void) printf("%s\n\n", gettext("None"));
		} else {
			(void) printf("%-12s\n\n", passwd);
		}
		ADM_Free(&Message);
	} else {
		show_header();
		for (index = 1; index <= DP_USER_MAX; index++) {
			admMessage = (dp_user_adm_t *)&dataBuffer;
			admMessage->command = DP_USER_CMD_SHOW;
			admMessage->parm    = index;

			Message.type = DP_USER_ADM;
			Message.data = admMessage;
			Message.len  = sizeof (dp_user_adm_t);
			ADM_Send(&Message);

			Timeout.tv_nsec = 0;
			Timeout.tv_sec  = ADM_SEPROM_TIMEOUT;
			ADM_Recv(&Message, &Timeout,
			    DP_USER_ADM_R, sizeof (dp_user_adm_r_t));

			admResponse = (dp_user_adm_r_t *)Message.data;
			if (admResponse->command != DP_USER_CMD_SHOW) {
				wrong_response();
				exit(-1);
			}

			if (admResponse->status == DP_ERR_USER_NONE) {
				ADM_Free(&Message);
				continue;
			} else if (admResponse->status == DP_ERR_USER_BAD) {
				malformed_username();
				exit(-1);
			} else if (admResponse->status != 0) {
				no_info();
				exit(-1);
			}

			userName = &(((char *)admResponse)[
			    sizeof (dp_user_adm_r_t)]);
			permissions = &userName[strlen(userName)+1];
			passwd = &permissions[strlen(permissions)+1];
			(void) printf(" %-16s    %-15s    ",
			    userName, permissions);
			if (strncmp(passwd, "Assigned", 12) == 0) {
				(void) printf("%s\n", gettext("Assigned"));
			} else if (strncmp(passwd, "None", 12) == 0) {
				(void) printf("%s\n", gettext("None"));
			} else {
				(void) printf("%-12s\n", passwd);
			}

			ADM_Free(&Message);
		}
		(void) printf("\n");
	}
}


void
ADM_Process_userpassword(int argc, char *argv[])
{
	static data_buffer_t	dataBuffer;
	rscp_msg_t		Message;
	struct timespec		Timeout;
	dp_user_adm_t		*admMessage;
	dp_user_adm_r_t		*admResponse;
	char			*userName;
	char			*password;
	int			passTry;


	/* Try to set password up to 3 times on Malformed password */
	passTry = 3;

	if (argc != 3) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("USAGE: scadm userpassword <username>"));
		exit(-1);
	}

	ADM_Start();

	if (strlen(argv[2]) > DP_USER_NAME_SIZE) {
		max_username();
		exit(-1);
	}

	admMessage = (dp_user_adm_t *)&dataBuffer;
	admMessage->command = DP_USER_CMD_PASSWORD;
	userName = (&((char *)admMessage)[sizeof (dp_user_adm_t)]);
	(void) strcpy(userName, argv[2]);
	password = (&((char *)admMessage)[sizeof (dp_user_adm_t) +
	    strlen(userName) + 1]);

	for (;;) {
		ADM_Get_Password(password);

		Message.type = DP_USER_ADM;
		Message.len  = sizeof (dp_user_adm_t) + strlen(userName) +
		    strlen(password) + 2;
		Message.data = admMessage;
		ADM_Send(&Message);

		ADM_Destroy_Password(password);
		Timeout.tv_nsec = 0;
		Timeout.tv_sec  = ADM_SEPROM_TIMEOUT;
		ADM_Recv(&Message, &Timeout,
		    DP_USER_ADM_R, sizeof (dp_user_adm_r_t));

		admResponse = (dp_user_adm_r_t *)Message.data;
		if (admResponse->command != DP_USER_CMD_PASSWORD) {
			wrong_response();
			exit(-1);
		}

		if (admResponse->status == DP_ERR_USER_NONE) {
			no_user();
			exit(-1);
		} else if (admResponse->status == DP_ERR_USER_BAD) {
			malformed_username();
			exit(-1);
		} else if (admResponse->status == DP_ERR_USER_PASSWD) {
			(void) fprintf(stderr, "\n%s\n\n",
			    gettext("scadm: malformed password\n"
			    "        A valid password is between 6 and 8 "
			    "characters,\n"
			    "        has at least two alphabetic characters, "
			    "and at\n"
			    "        least one numeric or special character. "
			    "The\n"
			    "        password must differ from the user's "
			    "login name\n"
			    "        and any reverse or circular shift of that "
			    "login\n"
			    "        name.\n"));
			passTry--;
			if (passTry > 0) {
				ADM_Free(&Message);
				continue;
			} else
				exit(-1);
		} else if (admResponse->status != 0) {
			(void) fprintf(stderr, "\n%s\n\n",
			    gettext("scadm: couldn't change password"));
			exit(-1);
		}

		/* password was changed successfully, get out of while */
		break;
	}

	ADM_Free(&Message);
}


void
ADM_Process_userperm(int argc, char *argv[])
{
	static data_buffer_t	dataBuffer;
	rscp_msg_t		Message;
	struct timespec		Timeout;
	dp_user_adm_t		*admMessage;
	dp_user_adm_r_t		*admResponse;
	char			*userName;
	int			permissions;
	int			index;


	if ((argc != 3) && (argc != 4)) {
		userperm_usage();
		exit(-1);
	}

	if (argc == 3) {
		permissions = 0;
	} else {
		if ((strlen(argv[3]) > 4) || (strlen(argv[3]) < 1)) {
			userperm_usage();
			exit(-1);
		}

		permissions = 0;
		for (index = 0; index < strlen(argv[3]); index++) {
			if ((argv[3][index] != 'c') &&
			    (argv[3][index] != 'C') &&
			    (argv[3][index] != 'u') &&
			    (argv[3][index] != 'U') &&
			    (argv[3][index] != 'a') &&
			    (argv[3][index] != 'A') &&
			    (argv[3][index] != 'r') &&
			    (argv[3][index] != 'R')) {
				userperm_usage();
				exit(-1);
			}

			if ((argv[3][index] == 'c') ||
			    (argv[3][index] == 'C')) {
				/* See if this field was entered twice */
				if ((permissions & DP_USER_PERM_C) != 0) {
					userperm_usage();
					exit(-1);
				}
				permissions = permissions | DP_USER_PERM_C;
			}

			if ((argv[3][index] == 'u') ||
			    (argv[3][index] == 'U')) {
				/* See if this field was enetered twice */
				if ((permissions & DP_USER_PERM_U) != 0) {
					userperm_usage();
					exit(-1);
				}
				permissions = permissions | DP_USER_PERM_U;
			}

			if ((argv[3][index] == 'a') ||
			    (argv[3][index] == 'A')) {
				/* See if this field was enetered twice */
				if ((permissions & DP_USER_PERM_A) != 0) {
					userperm_usage();
					exit(-1);
				}
				permissions = permissions | DP_USER_PERM_A;
			}

			if ((argv[3][index] == 'r') ||
			    (argv[3][index] == 'R')) {
				/* See if this field was enetered twice */
				if ((permissions & DP_USER_PERM_R) != 0) {
					userperm_usage();
					exit(-1);
				}
				permissions = permissions | DP_USER_PERM_R;
			}
		}
	}

	ADM_Start();

	if (strlen(argv[2]) > DP_USER_NAME_SIZE) {
		max_username();
		exit(-1);
	}

	admMessage = (dp_user_adm_t *)&dataBuffer;
	admMessage->command = DP_USER_CMD_PERM;
	admMessage->parm    = permissions;
	userName   = (char *)(&((char *)admMessage)[sizeof (dp_user_adm_t)]);
	(void) strcpy(userName, argv[2]);

	Message.type = DP_USER_ADM;
	Message.len  = sizeof (dp_user_adm_t) + strlen(userName) + 1;
	Message.data = admMessage;
	ADM_Send(&Message);

	Timeout.tv_nsec = 0;
	Timeout.tv_sec  = ADM_SEPROM_TIMEOUT;
	ADM_Recv(&Message, &Timeout, DP_USER_ADM_R, sizeof (dp_user_adm_r_t));

	admResponse = (dp_user_adm_r_t *)Message.data;
	if (admResponse->command != DP_USER_CMD_PERM) {
		wrong_response();
		exit(-1);
	}

	if (admResponse->status == DP_ERR_USER_NONE) {
		no_user();
		exit(-1);
	} else if (admResponse->status == DP_ERR_USER_BAD) {
		malformed_username();
		exit(-1);
	} else if (admResponse->status != 0) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("scadm: couldn't change permissions"));
		exit(-1);
	}

	ADM_Free(&Message);
}


static void
ADM_Get_Password(char *password)
{
	static char		pass1[64];
	static char		pass2[64];
	static struct termios	newOpts;
	int			passTry;
	int			validPass;


	validPass = 0;
	passTry   = 3;

	if (signal(SIGINT, cleanup) == SIG_ERR) {
		(void) fprintf(stderr, "\n%s\n\n",
		    gettext("scadm: cleanup() registration failed"));
		exit(-1);
	}

	echoOff = 1;
	(void) tcgetattr(0, &oldOpts);
	newOpts = oldOpts;
	newOpts.c_lflag &= ~ECHO;
	(void) tcsetattr(0, TCSANOW, &newOpts);

	while ((passTry > 0) && (validPass == 0)) {
		passTry = passTry - 1;
		(void) printf("%s", gettext("Password: "));
		(void) scanf("%s", pass1);
		(void) printf("\n");
		(void) fflush(stdin);
		(void) printf("%s", gettext("Re-enter Password: "));
		(void) scanf("%s", pass2);
		(void) printf("\n");

		/* Truncate at 8 characters  */
		pass1[8] = pass2[8] = '\0';

		if ((strcmp(pass1, pass2) != 0) && (passTry > 0)) {
			ADM_Destroy_Password(pass1);
			ADM_Destroy_Password(pass2);
			(void) fprintf(stderr, "%s\n\n",
			    gettext("Passwords didn't match, try again"));
		} else if ((strcmp(pass1, pass2) != 0) && (passTry <= 0)) {
			ADM_Destroy_Password(pass1);
			ADM_Destroy_Password(pass2);
			(void) fprintf(stderr, "\n%s\n\n",
			    gettext("scadm: ERROR, passwords didn't match"));
			(void) tcsetattr(0, TCSANOW, &oldOpts);
			exit(-1);
		} else {
			validPass = 1;
		}
	}

	(void) tcsetattr(0, TCSANOW, &oldOpts);
	echoOff = 0;
	(void) strcpy(password, pass1);
	ADM_Destroy_Password(pass1);
	ADM_Destroy_Password(pass2);
}


static void
cleanup()
{
	if (echoOff)
		(void) tcsetattr(0, TCSANOW, &oldOpts);

	exit(-1);
}


static void
ADM_Destroy_Password(char *password)
{
	int index;

	for (index = 0; index < strlen(password); index++)
		password[index] = 0x1;
}


static void
max_username()
{
	(void) fprintf(stderr,
	    gettext("\nscadm: maximum username length is %d\n\n"),
	    DP_USER_NAME_SIZE);
}


static void
malformed_username()
{
	(void) fprintf(stderr,
	    "\n%s\n\n", gettext("scadm: malformed username"));
}


static void
wrong_response()
{
	(void) fprintf(stderr, "\n%s\n\n",
	    gettext("scadm: SC returned wrong response"));
}


static void
no_user()
{
	(void) fprintf(stderr,
	    "\n%s\n\n", gettext("scadm: username does not exist"));
}


static void
no_info()
{
	(void) fprintf(stderr, "\n%s\n\n",
	    gettext("scadm: couldn't get information on user"));
}


static void
userperm_usage()
{
	(void) fprintf(stderr, "\n%s\n\n",
	    gettext("USAGE: scadm userperm <username> [cuar]"));
}


static void
show_header()
{
	int i;
	int usernLen = strlen(gettext("username"));
	int permLen = strlen(gettext("permissions"));
	int pwdLen = strlen(gettext("password"));

	(void) printf("\n");
	(void) putchar(' ');
	(void) printf("%s", gettext("username"));
	for (i = 0; i < (20 - usernLen); i++)
		(void) putchar(' ');

	(void) printf("%s", gettext("permissions"));
	for (i = 0; i < (19 - permLen); i++)
		(void) putchar(' ');

	(void) printf("%s\n", gettext("password"));

	(void) putchar(' ');
	for (i = 0; i < usernLen; i++)
		(void) putchar('-');
	for (; i < 20; i++)
		(void) putchar(' ');

	for (i = 0; i < permLen; i++)
		(void) putchar('-');
	for (; i < 19; i++)
		(void) putchar(' ');

	for (i = 0; i < pwdLen; i++)
		(void) putchar('-');
	(void) printf("\n");
}