#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlite3.h>
#include <ctype.h>
#include <openssl/evp.h>
#include "bbs.h"
#include "inih/ini.h"

#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
#define EVP_MD_CTX_new EVP_MD_CTX_create
#define EVP_MD_CTX_free EVP_MD_CTX_destroy
#endif

extern struct bbs_config conf;
extern struct user_record *gUser;

static void open_users_db_or_die(sqlite3 **db) {
	char buffer[PATH_MAX];
	snprintf(buffer, PATH_MAX, "%s/users.sq3", conf.bbs_path);
	if (sqlite3_open(buffer, db) != SQLITE_OK) {
		dolog("Cannot open database: %s", sqlite3_errmsg(*db));
		sqlite3_close(*db);
		exit(1);
	}
	assert(db != NULL);
	sqlite3_busy_timeout(*db, 5000);
}

char *hash_sha256(char *pass, char *salt) {
	char *buffer = NULL;
	char *shash = NULL;
	unsigned char hash[EVP_MAX_MD_SIZE];
	unsigned int length_of_hash = 0;
	int i;

	buffer = str2dup(pass, salt);

	EVP_MD_CTX *context = EVP_MD_CTX_new();

	if (context != NULL) {
		if (EVP_DigestInit_ex(context, EVP_sha256(), NULL)) {
			if (EVP_DigestUpdate(context, buffer, strlen(buffer))) {
				if (EVP_DigestFinal_ex(context, hash, &length_of_hash)) {
					stralloc shash = EMPTY_STRALLOC;
					for (i = 0; i < length_of_hash; i++)
						stralloc_cat_byte(&shash, hash[i]);
					stralloc_0(&shash);
					EVP_MD_CTX_free(context);
					free(buffer);
					return shash.s;
				}
			}
		}
		EVP_MD_CTX_free(context);
	}

	free(buffer);
	dolog("Error creating hash!");
	exit(-1);
}

void gen_salt(char **s) {
	FILE *fptr;
	int i;
	char c;
	*s = (char *)malloz(11);

	char *salt = *s;

	if (!salt) {
		dolog("Out of memory..");
		exit(-1);
	}
	fptr = fopen("/dev/urandom", "rb");
	if (!fptr) {
		dolog("Unable to open /dev/urandom!");
		exit(-1);
	}
	for (i = 0; i < 10; i++) {
		fread(&c, 1, 1, fptr);
		salt[i] = (char)((abs(c) % 93) + 33);
	}
	fclose(fptr);
	salt[10] = '\0';
}

static int secLevel(void *user, const char *section, const char *name,
                    const char *value) {
	struct sec_level_t *conf = (struct sec_level_t *)user;

	if (strcasecmp(section, "main") == 0) {
		if (strcasecmp(name, "time per day") == 0) {
			conf->timeperday = atoi(value);
		}
	}
	return 1;
}

int save_user(struct user_record *user) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = 0;

	const char *update_sql =
		"UPDATE users SET password=?, salt=?, firstname=?,"
	        "lastname=?, email=?, location=?, sec_level=?, last_on=?, "
		"time_left=?, cur_mail_conf=?, cur_mail_area=?, "
		"cur_file_dir=?, cur_file_sub=?, times_on=?, bwavepktno=?, "
		"archiver=?, protocol=?,nodemsgs=?,codepage=?,exteditor=?,"
		"bwavestyle=?,signature=?,autosig=? where loginname LIKE ?";

	open_users_db_or_die(&db);

	rc = sqlite3_prepare_v2(db, update_sql, -1, &res, 0);
	if (rc != SQLITE_OK) {
		dolog("Failed to prepare statement: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		exit(1);
	}
	sqlite3_bind_text(res, 1, user->password, -1, 0);
	sqlite3_bind_text(res, 2, user->salt, -1, 0);
	sqlite3_bind_text(res, 3, user->firstname, -1, 0);
	sqlite3_bind_text(res, 4, user->lastname, -1, 0);
	sqlite3_bind_text(res, 5, user->email, -1, 0);
	sqlite3_bind_text(res, 6, user->location, -1, 0);
	sqlite3_bind_int(res, 7, user->sec_level);
	sqlite3_bind_int(res, 8, user->laston);
	sqlite3_bind_int(res, 9, user->timeleft);
	sqlite3_bind_int(res, 10, user->cur_mail_conf);
	sqlite3_bind_int(res, 11, user->cur_mail_area);
	sqlite3_bind_int(res, 12, user->cur_file_dir);
	sqlite3_bind_int(res, 13, user->cur_file_sub);
	sqlite3_bind_int(res, 14, user->timeson);
	sqlite3_bind_int(res, 15, user->bwavepktno);
	sqlite3_bind_int(res, 16, user->defarchiver);
	sqlite3_bind_int(res, 17, user->defprotocol);
	sqlite3_bind_int(res, 18, user->nodemsgs);
	sqlite3_bind_int(res, 19, user->codepage);
	sqlite3_bind_int(res, 20, user->exteditor);
	sqlite3_bind_int(res, 21, user->bwavestyle);
	sqlite3_bind_text(res, 22, user->signature, -1, 0);
	sqlite3_bind_int(res, 23, user->autosig);
	sqlite3_bind_text(res, 24, user->loginname, -1, 0);

	rc = sqlite3_step(res);
	if (rc != SQLITE_DONE) {
		sqlite3_finalize(res);
		dolog("execution failed: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		exit(1);
	}
	sqlite3_finalize(res);
	sqlite3_close(db);
	return 1;
}

int msgbase_flag_unflag(struct user_record *user, int conference, int msgbase, int msgid) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = 0;
	char *err_msg = NULL;
	int flagunflag = 0;

	static const char *create_sql =
		"CREATE TABLE IF NOT EXISTS msg_flags (conference INTEGER, msgbase INTEGER, uid INTEGER, msg INTEGER);";

	flagunflag = msgbase_is_flagged(user, conference, msgbase, msgid);

	open_users_db_or_die(&db);

	rc = sqlite3_exec(db, create_sql, 0, 0, &err_msg);
	if (rc != SQLITE_OK) {

		dolog("SQL error: %s", err_msg);

		sqlite3_free(err_msg);
		sqlite3_close(db);

		return 0;
	}
	if (flagunflag == 1) {
		static const char *unflag_buf =
			"DELETE FROM msg_flags WHERE conference=? AND msgbase=? AND uid=? AND msg=?";
		rc = sqlite3_prepare_v2(db, unflag_buf, -1, &res, 0);
	} else {
		static const char *flag_buf =
			"INSERT INTO msg_flags (conference, msgbase, uid, msg) VALUES(?, ?, ?, ?)";
		rc = sqlite3_prepare_v2(db, flag_buf, -1, &res, 0);
	}

	sqlite3_bind_int(res, 1, conference);
	sqlite3_bind_int(res, 2, msgbase);
	sqlite3_bind_int(res, 3, user->id);
	sqlite3_bind_int(res, 4, msgid);

	rc = sqlite3_step(res);

	sqlite3_finalize(res);
	sqlite3_close(db);

	return 1;
}

int msgbase_is_flagged(struct user_record *user, int conference, int msgbase, int msgid) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = 0;

	static const char *sql_buf =
		"SELECT * FROM msg_flags WHERE conference=? AND msgbase=? AND uid=? AND msg=?";

	open_users_db_or_die(&db);

	rc = sqlite3_prepare_v2(db, sql_buf, -1, &res, 0);
	if (rc != SQLITE_OK) {
		sqlite3_close(db);
		return 0;
	}

	sqlite3_bind_int(res, 1, conference);
	sqlite3_bind_int(res, 2, msgbase);
	sqlite3_bind_int(res, 3, user->id);
	sqlite3_bind_int(res, 4, msgid);

	if (sqlite3_step(res) != SQLITE_ROW) {
		sqlite3_finalize(res);
		sqlite3_close(db);
		return 0;
	}
	sqlite3_finalize(res);
	sqlite3_close(db);
	return 1;
}

int msgbase_sub_unsub(int conference, int msgbase) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = 0;
	static const char *create_sql =
		"CREATE TABLE IF NOT EXISTS msg_subs (conference INTEGER, msgbase INTEGER, uid INTEGER);";
	char *err_msg = NULL;
	int subunsub = msgbase_is_subscribed(conference, msgbase);

	open_users_db_or_die(&db);

	rc = sqlite3_exec(db, create_sql, 0, 0, &err_msg);
	if (rc != SQLITE_OK) {
		dolog("SQL error: %s", err_msg);
		sqlite3_free(err_msg);
		sqlite3_close(db);
		return 0;
	}
	if (subunsub == 1) {
		static char *unsub_buf =
			"DELETE FROM msg_subs WHERE conference=? AND msgbase=? AND uid=?";
		rc = sqlite3_prepare_v2(db, unsub_buf, -1, &res, 0);
	} else {
		static const char *sub_buf =
			"INSERT INTO msg_subs (conference, msgbase, uid) VALUES(?, ?, ?)";
		rc = sqlite3_prepare_v2(db, sub_buf, -1, &res, 0);
	}

	sqlite3_bind_int(res, 1, conference);
	sqlite3_bind_int(res, 2, msgbase);
	sqlite3_bind_int(res, 3, gUser->id);

	rc = sqlite3_step(res);

	sqlite3_finalize(res);
	sqlite3_close(db);

	return 1;
}

int msgbase_is_subscribed(int conference, int msgbase) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = 0;

	static const char *sql_buf =
		"SELECT * FROM msg_subs WHERE conference=? AND msgbase=? AND uid=?";

	open_users_db_or_die(&db);

	rc = sqlite3_prepare_v2(db, sql_buf, -1, &res, 0);
	if (rc != SQLITE_OK) {
		sqlite3_close(db);
		return 0;
	}

	sqlite3_bind_int(res, 1, conference);
	sqlite3_bind_int(res, 2, msgbase);
	sqlite3_bind_int(res, 3, gUser->id);

	if (sqlite3_step(res) != SQLITE_ROW) {
		sqlite3_finalize(res);
		sqlite3_close(db);
		return 0;
	}
	sqlite3_finalize(res);
	sqlite3_close(db);

	return 1;
}

int inst_user(struct user_record *user) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	char *err_msg = NULL;
	int rc = 0;

	static const char *create_sql =
		"CREATE TABLE IF NOT EXISTS users ("
	                   "Id INTEGER PRIMARY KEY,"
	                   "loginname TEXT COLLATE NOCASE,"
	                   "password TEXT,"
	                   "salt TEXT,"
	                   "firstname TEXT,"
	                   "lastname TEXT,"
	                   "email TEXT,"
	                   "location TEXT,"
	                   "sec_level INTEGER,"
	                   "last_on INTEGER,"
	                   "time_left INTEGER,"
	                   "cur_mail_conf INTEGER,"
	                   "cur_mail_area INTEGER,"
	                   "cur_file_sub INTEGER,"
	                   "cur_file_dir INTEGER,"
	                   "times_on INTEGER,"
	                   "bwavepktno INTEGER,"
	                   "archiver INTEGER,"
	                   "protocol INTEGER,"
	                   "nodemsgs INTEGER,"
	                   "codepage INTEGER,"
	                   "exteditor INTEGER,"
	                   "bwavestyle INTEGER,"
	                   "signature TEXT,"
	                   "autosig INTEGER);";

	static const char *insert_sql =
		"INSERT INTO users (loginname, password, salt, firstname,"
	        "lastname, email, location, sec_level, last_on, time_left, "
		"cur_mail_conf, cur_mail_area, cur_file_dir, cur_file_sub, "
		"times_on, bwavepktno, archiver, protocol, nodemsgs, "
		"codepage, exteditor, bwavestyle, signature, autosig) "
		"VALUES(?,?, ?,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

	open_users_db_or_die(&db);

	rc = sqlite3_exec(db, create_sql, 0, 0, &err_msg);
	if (rc != SQLITE_OK) {
		dolog("SQL error: %s", err_msg);
		sqlite3_free(err_msg);
		sqlite3_close(db);
		exit(1);
	}

	rc = sqlite3_prepare_v2(db, insert_sql, -1, &res, 0);
	if (rc != SQLITE_OK) {
		dolog("Failed to execute statement: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		exit(1);
	}
	sqlite3_bind_text(res, 1, user->loginname, -1, 0);
	sqlite3_bind_text(res, 2, user->password, -1, 0);
	sqlite3_bind_text(res, 3, user->salt, -1, 0);
	sqlite3_bind_text(res, 4, user->firstname, -1, 0);
	sqlite3_bind_text(res, 5, user->lastname, -1, 0);
	sqlite3_bind_text(res, 6, user->email, -1, 0);
	sqlite3_bind_text(res, 7, user->location, -1, 0);
	sqlite3_bind_int(res, 8, user->sec_level);
	sqlite3_bind_int(res, 9, user->laston);
	sqlite3_bind_int(res, 10, user->timeleft);
	sqlite3_bind_int(res, 11, user->cur_mail_conf);
	sqlite3_bind_int(res, 12, user->cur_mail_area);
	sqlite3_bind_int(res, 13, user->cur_file_dir);
	sqlite3_bind_int(res, 14, user->cur_file_sub);
	sqlite3_bind_int(res, 15, user->timeson);
	sqlite3_bind_int(res, 16, user->bwavepktno);
	sqlite3_bind_int(res, 17, user->defarchiver);
	sqlite3_bind_int(res, 18, user->defprotocol);
	sqlite3_bind_int(res, 19, user->nodemsgs);
	sqlite3_bind_int(res, 20, user->codepage);
	sqlite3_bind_int(res, 21, user->exteditor);
	sqlite3_bind_int(res, 22, user->bwavestyle);
	sqlite3_bind_text(res, 23, user->signature, -1, 0);
	sqlite3_bind_int(res, 24, user->autosig);

	rc = sqlite3_step(res);
	if (rc != SQLITE_DONE) {
		dolog("execution failed: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		exit(1);
	}

	user->id = sqlite3_last_insert_rowid(db);

	sqlite3_finalize(res);
	sqlite3_close(db);

	return 1;
}

struct user_record *check_user_pass(char *loginname, char *password) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = NULL;
	int pass_ok = 0;
	char pathbuf[PATH_MAX];

	static const char *sql =
		"SELECT Id, loginname, password, salt, firstname,"
	        "lastname, email, location, sec_level, last_on, time_left, "
		"cur_mail_conf, cur_mail_area, cur_file_dir, cur_file_sub, "
		"times_on, bwavepktno, archiver, protocol,nodemsgs, "
		"codepage, exteditor, bwavestyle, signature, autosig "
		"FROM users WHERE loginname LIKE ?";

	open_users_db_or_die(&db);
	rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
	if (rc != SQLITE_OK) {
		dolog("Failed to execute statement: %s", sqlite3_errmsg(db));
		sqlite3_finalize(res);
		sqlite3_close(db);
		return NULL;
	}
	sqlite3_bind_text(res, 1, loginname, -1, 0);
	int step = sqlite3_step(res);
	if (step != SQLITE_ROW) {
		sqlite3_finalize(res);
		sqlite3_close(db);
		return NULL;
	}
	struct user_record *user = malloz(sizeof(struct user_record));
	user->id = sqlite3_column_int(res, 0);
	user->loginname = strdup((char *)sqlite3_column_text(res, 1));
	user->password = strdup((char *)sqlite3_column_text(res, 2));
	user->salt = strdup((char *)sqlite3_column_text(res, 3));
	user->firstname = strdup((char *)sqlite3_column_text(res, 4));
	user->lastname = strdup((char *)sqlite3_column_text(res, 5));
	user->email = strdup((char *)sqlite3_column_text(res, 6));
	user->location = strdup((char *)sqlite3_column_text(res, 7));
	user->sec_level = sqlite3_column_int(res, 8);
	user->laston = (time_t)sqlite3_column_int(res, 9);
	user->timeleft = sqlite3_column_int(res, 10);
	user->cur_mail_conf = sqlite3_column_int(res, 11);
	user->cur_mail_area = sqlite3_column_int(res, 12);
	user->cur_file_dir = sqlite3_column_int(res, 13);
	user->cur_file_sub = sqlite3_column_int(res, 14);
	user->timeson = sqlite3_column_int(res, 15);
	user->bwavepktno = sqlite3_column_int(res, 16);
	user->defarchiver = sqlite3_column_int(res, 17);
	user->defprotocol = sqlite3_column_int(res, 18);
	user->nodemsgs = sqlite3_column_int(res, 19);
	user->codepage = sqlite3_column_int(res, 20);
	user->exteditor = sqlite3_column_int(res, 21);
	user->bwavestyle = sqlite3_column_int(res, 22);
	user->signature = strdup((char *)sqlite3_column_text(res, 23));
	user->autosig = sqlite3_column_int(res, 24);
	char *pass_hash = hash_sha256(password, user->salt);

	if (strcmp(pass_hash, user->password) != 0) {
		free(user->loginname);
		free(user->firstname);
		free(user->lastname);
		free(user->email);
		free(user->location);
		free(user->salt);
		free(user->signature);
		free(user);
		free(pass_hash);
		sqlite3_finalize(res);
		sqlite3_close(db);
		return NULL;
	}
	free(pass_hash);

	sqlite3_finalize(res);
	sqlite3_close(db);

	user->sec_info = (struct sec_level_t *)malloz(sizeof(struct sec_level_t));

	snprintf(pathbuf, sizeof pathbuf, "%s/s%d.ini", conf.config_path, user->sec_level);
	if (ini_parse(pathbuf, secLevel, user->sec_info) < 0) {
		dolog("Unable to load sec Level ini (%s)!", pathbuf);
		exit(-1);
	}
	if (user->cur_mail_conf >= ptr_vector_len(&conf.mail_conferences)) {
		user->cur_mail_conf = 0;
	}
	if (user->cur_file_dir >= ptr_vector_len(&conf.file_directories)) {
		user->cur_file_dir = 0;
	}
	struct mail_conference *mc = ptr_vector_get(&conf.mail_conferences, user->cur_mail_conf);
	assert(mc != NULL);
	if (user->cur_mail_area >= ptr_vector_len(&mc->mail_areas)) {
		user->cur_mail_area = 0;
	}
	struct file_directory *dir = ptr_vector_get(&conf.file_directories, user->cur_file_dir);
	assert(dir != NULL);
	if (user->cur_file_sub >= ptr_vector_len(&dir->file_subs)) {
		user->cur_file_sub = 0;
	}

	return user;
}

void list_users(struct user_record *user) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = 0;

	static const char *sql = "SELECT loginname,location,times_on FROM users";

	open_users_db_or_die(&db);

	rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
	if (rc != SQLITE_OK) {
		dolog("Cannot prepare statement: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		exit(1);
	}
	s_printf(get_string(161));
	s_printf(get_string(162));
	s_printf(get_string(163));
	for (int i = 0; sqlite3_step(res) == SQLITE_ROW; ++i) {
		s_printf(get_string(164), sqlite3_column_text(res, 0), sqlite3_column_text(res, 1), sqlite3_column_int(res, 2));
		if (i == 20) {
			s_printf(get_string(6));
			s_getc();
			i = 0;
		}
	}
	s_printf(get_string(165));
	sqlite3_finalize(res);
	sqlite3_close(db);

	s_printf(get_string(6));
	s_getc();
}

int check_fullname(char *firstname, char *lastname) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = 0;

	static const char *sql =
		"SELECT * FROM users WHERE firstname = ? AND lastname = ?";

	open_users_db_or_die(&db);
	rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
	if (rc != SQLITE_OK) {
		dolog("Failed to prepare statement: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		return 0;
	}
	sqlite3_bind_text(res, 1, firstname, -1, 0);
	sqlite3_bind_text(res, 2, lastname, -1, 0);
	int step = sqlite3_step(res);
	sqlite3_finalize(res);
	sqlite3_close(db);

	return (step == SQLITE_ROW);
}

int check_user(char *loginname) {
	sqlite3 *db = NULL;
	sqlite3_stmt *res = NULL;
	int rc = 0;

	static const char *sql = "SELECT * FROM users WHERE loginname = ?";

	open_users_db_or_die(&db);
	rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
	if (rc != SQLITE_OK) {
		dolog("Failed to execute statement: %s", sqlite3_errmsg(db));
		sqlite3_close(db);
		return 1;
	}
	sqlite3_bind_text(res, 1, loginname, -1, 0);
	int step = sqlite3_step(res);
	sqlite3_finalize(res);
	sqlite3_close(db);

	return (step != SQLITE_ROW);
}

struct user_record *new_user() {
	char buffer[PATH_MAX];
	struct user_record *user;
	int done = 0;
	char c;
	int nameok = 0;
	int passok = 0;
	int i;
	int fullnameok = 0;

	user = (struct user_record *)malloz(sizeof(struct user_record));
	s_printf("\r\n\r\n");
	s_displayansi("newuser");

	do {
		passok = 0;
		nameok = 0;
		do {
			s_printf(get_string(166));
			s_readstring(buffer, 16);
			s_printf("\r\n");
			if (strlen(buffer) < 2) {
				s_printf(get_string(167));
				continue;
			}
			if (buffer[0] == ' ' || buffer[strlen(buffer) - 1] == ' ') {
				s_printf(get_string(240));
				continue;
			}
			for (const char *p = buffer; *p != '\0'; ++p) {
				if (!(tolower(*p) >= 97 && tolower(*p) <= 122) && *p != 32 && !(*p >= '0' && *p <= '9')) {
					s_printf(get_string(168));
					nameok = 1;
					break;
				}
			}
			if (nameok == 1) {
				nameok = 0;
				continue;
			}
			if (strcasecmp(buffer, "unknown") == 0) {
				s_printf(get_string(169));
				continue;
			}
			if (strcasecmp(buffer, "all") == 0) {
				s_printf(get_string(169));
				continue;
			}
			if (strcasecmp(buffer, "new") == 0) {
				s_printf(get_string(169));
				continue;
			}
			if (strcasecmp(buffer, "anonymous") == 0) {
				s_printf(get_string(169));
				continue;
			}
			if (strcasecmp(buffer, "ftp") == 0) {
				s_printf(get_string(169));
				continue;
			}
			user->loginname = strdup(buffer);
			nameok = check_user(user->loginname);
			if (!nameok) {
				s_printf(get_string(170));
				free(user->loginname);
				memset(buffer, 0, 256);
			}
		} while (!nameok);
		do {
			nameok = 0;
			do {
				s_printf(get_string(171));
				memset(buffer, 0, 256);
				s_readstring(buffer, 32);
				if (buffer[0] == ' ' || buffer[strlen(buffer) - 1] == ' ') {
					s_printf(get_string(241));
					continue;
				}
				if (strlen(buffer) == 0) {
					s_printf(get_string(167));
					continue;
				}
				if (strchr(buffer, ' ') != NULL) {
					s_printf(get_string(244));
					continue;
				}
				s_printf("\r\n");
				user->firstname = strdup(buffer);
				nameok = 1;
			} while (!nameok);
			nameok = 0;

			do {
				s_printf(get_string(172));
				memset(buffer, 0, 256);
				s_readstring(buffer, 32);
				if (buffer[0] == ' ' || buffer[strlen(buffer) - 1] == ' ') {
					s_printf(get_string(242));
					continue;
				}
				if (strlen(buffer) == 0) {
					s_printf(get_string(167));
					continue;
				}
				if (strchr(buffer, ' ') != NULL) {
					s_printf(get_string(244));
					continue;
				}
				s_printf("\r\n");
				nameok = 1;
				user->lastname = strdup(buffer);
			} while (!nameok);
			fullnameok = check_fullname(user->firstname, user->lastname);
			if (!fullnameok) {
				free(user->firstname);
				free(user->lastname);
				s_printf(get_string(243));
			}
		} while (!fullnameok);
		s_printf(get_string(173));
		memset(buffer, 0, 256);
		s_readstring(buffer, 64);
		s_printf("\r\n");
		user->email = strdup(buffer);

		s_printf(get_string(174));
		memset(buffer, 0, 256);
		s_readstring(buffer, 32);
		s_printf("\r\n");
		user->location = strdup(buffer);

		do {
			s_printf(get_string(175));
			memset(buffer, 0, 256);
			s_readstring(buffer, 16);
			s_printf("\r\n");
			if (strlen(buffer) >= 8) {
				passok = 1;
			} else {
				s_printf(get_string(158));
			}
		} while (!passok);
		gen_salt(&user->salt);
		user->password = hash_sha256(buffer, user->salt);

		s_printf(get_string(176));
		s_printf(get_string(177));
		s_printf(get_string(178));
		s_printf(user->loginname);
		s_printf(get_string(179));
		s_printf(user->firstname);
		s_printf(get_string(180));
		s_printf(user->lastname);
		s_printf(get_string(181));
		s_printf(user->email);
		s_printf(get_string(182));
		s_printf(user->location);
		s_printf(get_string(183));
		s_printf(get_string(184));
		c = s_getchar();
		while (tolower(c) != 'y' && tolower(c) != 'n') {
			c = s_getchar();
		}

		if (tolower(c) == 'y') {
			done = 1;
		}
	} while (!done);
	user->sec_level = conf.newuserlvl;
	user->bwavepktno = 0;
	user->sec_info = (struct sec_level_t *)malloz(sizeof(struct sec_level_t));
	snprintf(buffer, PATH_MAX, "%s/config/s%d.ini", conf.bbs_path, user->sec_level);

	if (ini_parse(buffer, secLevel, user->sec_info) < 0) {
		dolog("Unable to load sec Level ini (%s)!", buffer);
		exit(-1);
	}

	user->laston = time(NULL);
	user->timeleft = user->sec_info->timeperday;
	user->cur_file_dir = 0;
	user->cur_file_sub = 0;
	user->cur_mail_area = 0;
	user->cur_mail_conf = 0;
	user->timeson = 0;
	user->defprotocol = 1;
	user->defarchiver = 1;
	user->nodemsgs = 1;
	user->codepage = conf.codepage;
	user->exteditor = 2;
	user->bwavestyle = 0;
	user->signature = strdup("");
	user->autosig = 0;
	inst_user(user);

	return user;
}
