#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "selinux_internal.h"
#include "context_internal.h"
#include <selinux/get_context_list.h>

/* context_menu - given a list of contexts, presents a menu of security contexts
 *            to the user.  Returns the number (position in the list) of
 *            the user selected context.
 */
static int context_menu(char ** list)
{
	int i;			/* array index                        */
	int choice = 0;		/* index of the user's choice         */
	char response[10];	/* string to hold the user's response */

	printf("\n\n");
	for (i = 0; list[i]; i++)
		printf("[%d] %s\n", i + 1, list[i]);

	while ((choice < 1) || (choice > i)) {
		printf("Enter number of choice: ");
		fflush(stdin);
		if (fgets(response, sizeof(response), stdin) == NULL)
			continue;
		fflush(stdin);
		choice = strtol(response, NULL, 10);
	}

	return (choice - 1);
}

/* query_user_context - given a list of context, allow the user to choose one.  The 
 *                  default is the first context in the list.  Returns 0 on
 *                  success, -1 on failure
 */
int query_user_context(char ** list, char ** usercon)
{
	char response[10];	/* The user's response                        */
	int choice;		/* The index in the list of the sid chosen by
				   the user                                   */

	if (!list[0])
		return -1;

	printf("\nYour default context is %s.\n", list[0]);
	if (list[1]) {
		printf("Do you want to choose a different one? [n]");
		fflush(stdin);
		if (fgets(response, sizeof(response), stdin) == NULL)
			return -1;
		fflush(stdin);

		if ((response[0] == 'y') || (response[0] == 'Y')) {
			choice = context_menu(list);
			*usercon = strdup(list[choice]);
			if (!(*usercon))
				return -1;
			return 0;
		}

		*usercon = strdup(list[0]);
		if (!(*usercon))
			return -1;
	} else {
		*usercon = strdup(list[0]);
		if (!(*usercon))
			return -1;
	}

	return 0;
}

/* get_field - given fieldstr - the "name" of a field, query the user 
 *             and set the new value of the field
 */
static void get_field(const char *fieldstr, char *newfield, int newfieldlen)
{
	int done = 0;		/* true if a non-empty field has been obtained */

	while (!done) {		/* Keep going until we get a value for the field */
		printf("\tEnter %s ", fieldstr);
		fflush(stdin);
		if (fgets(newfield, newfieldlen, stdin) == NULL)
			continue;
		fflush(stdin);
		if (newfield[strlen(newfield) - 1] == '\n')
			newfield[strlen(newfield) - 1] = '\0';

		if (strlen(newfield) == 0) {
			printf("You must enter a %s\n", fieldstr);
		} else {
			done = 1;
		}
	}
}

/* manual_user_enter_context - provides a way for a user to manually enter a
 *                     context in case the policy doesn't allow a list
 *                     to be obtained.
 *                     given the userid, queries the user and places the
 *                     context chosen by the user into usercon.  Returns 0
 *                     on success.
 */
int manual_user_enter_context(const char *user, char ** newcon)
{
	char response[10];	/* Used to get yes or no answers from user */
	char role[100];		/* The role requested by the user          */
	int rolelen = 100;
	char type[100];		/* The type requested by the user          */
	int typelen = 100;
	char level[100];	/* The level requested by the user         */
	int levellen = 100;
	int mls_enabled = is_selinux_mls_enabled();

	context_t new_context;	/* The new context chosen by the user     */
	char *user_context = NULL;	/* String value of the user's context     */
	int done = 0;		/* true if a valid sid has been obtained  */

	/* Initialize the context.  How this is done depends on whether
	   or not MLS is enabled                                        */
	if (mls_enabled)
		new_context = context_new("user:role:type:level");
	else
		new_context = context_new("user:role:type");

	if (!new_context)
		return -1;

	while (!done) {
		printf("Would you like to enter a security context? [y]");
		if (fgets(response, sizeof(response), stdin) == NULL
		    || (response[0] == 'n') || (response[0] == 'N')) {
			context_free(new_context);
			return -1;
		}

		/* Allow the user to enter each field of the context individually */
		if (context_user_set(new_context, user)) {
			context_free(new_context);
			return -1;
		}
		get_field("role", role, rolelen);
		if (context_role_set(new_context, role)) {
			context_free(new_context);
			return -1;
		}
		get_field("type", type, typelen);
		if (context_type_set(new_context, type)) {
			context_free(new_context);
			return -1;
		}

		if (mls_enabled) {
			get_field("level", level, levellen);
			if (context_range_set(new_context, level)) {
				context_free(new_context);
				return -1;
			}
		}

		/* Get the string value of the context and see if it is valid. */
		user_context = context_str(new_context);
		if (!user_context) {
			context_free(new_context);
			return -1;
		}
		if (!security_check_context(user_context))
			done = 1;
		else
			printf("Not a valid security context\n");
	}

	*newcon = strdup(user_context);
	context_free(new_context);
	if (!(*newcon))
		return -1;
	return 0;
}
