<?php
////////////////////////////////////////////////////////////////////////
// Breeze Website Builder
// Copyright Silver Dolphin Solutions, LLC, Edward Lemmers, P.E.
// For licensing, see license.txt.
// Revised: 4/26/2010	ELL
// Added dictionary attack protection.
// Revised: 10/15/2011	ELL
// Added ability to lockout file manager for restricted users.
// Revised: 2/8/2012	ELL
// Added table prefix handling.
// Revised: 11/22/2012	ELL
// Added handling of passwords sent SHA-256 hashed and added handling of salt.
// Revised: 3/25/2013	ELL
// Security enhancements for PDO.
// Revised: 11/23/2016	ELL
// Modified reset password to store temp password in temp users table, then update users table upon logging in with temp password to new temp password.
//

	$userid = $_REQUEST['userid'];
	$password = $_REQUEST['password'];
	$user_table = $_REQUEST['user_table'];
	$AccessLevel = $_REQUEST['AccessLevel'];
	$redirect = $_REQUEST['redirect'];
	if (isset($_POST['redirect_err_to_src']) && ($_POST['redirect_err_to_src'] != ""))
		$redirect_err_to_src = 1;
	else
		$redirect_err_to_src = 0;

	// DB connection for retrieving HTML message format for message back to inquirer, etc.
	include_once("connect_finder.php");
	include("ignore_fields.php");
	
	// If alternate go back specified, then use that if any errors.  This is usefule if sending page session expires if back button is used.
	if ($_POST['alt_go_back'])
	{
		$go_back = $_POST['alt_go_back'];
		$delim = "?";
		foreach($_POST as $key => $val)
		{
			if (!in_array($key, $ignore_fields))
			{
				if ((strpos($key, "password") === false) && (isset($val) && ($val != "")))
				{
					$go_back .= $delim . $key . "=" . $val;
					$delim = "&";
				}
			}
		}
	}
	else
		$go_back = "javascript:history.go(-1);";
		
//////////////////////////////////////////////////
function CheckPassword($enc_text, $key, $password)
{
	/////////////////////////////////////////////
	//Decrypt password for comparison:
	$enc_text = stripslashes($enc_text);
	$key = strtolower(stripslashes($key));
	//Convert key to codes and pad as necessary.
	for( $i = 0; $i < (strlen($enc_text))/2; $i++)
	{
		if ($i < strlen($key))
		{
			$c = $key{$i};
			$k[$i] = ord($c);
		}
		else
			$k[$i] = 65 + $i;
	}
	
	//Convert encrypted string to array of HEX uni-character codes.
	$j = 0;
	$q = 0;
	for( $i = 0; $i < strlen($enc_text); $i++)
	{
		if ($j == 0) {
			$c = $enc_text{$i};
			$j++;
		}
		else {
			$c = $c.$enc_text{$i};
			$e[$q] = hexdec($c);
			$q++;
			$j = 0;
		}
		
	}
	
	//Decrypt encrypted string with key to get original data.
	$decrypt_text = "";
	for( $i = 0; $i < (strlen($enc_text))/2; $i++)
   	{
   		$a[$i] = $e[$i] ^ $k[$i];
		$decrypt_text = $decrypt_text. chr($a[$i]);
   	}
	////////////////////////////////////////////////////

	$decrypt_text = str_replace(BWB_PUB_SALT, '', $decrypt_text);
	$password_lookup = hash('sha256', $decrypt_text);

	if ($password_lookup == $password)
		return true;
	else
		return false;
}

function Login($user_table, $userid, $dbh, $access_level, $redirect, $show_password_reset = false)
{
	$sess_save_path = session_save_path();
	if (!isset($sess_save_path) || ($sess_save_path == ""))	{
		if (defined('SESSION_SAVE_PATH'))
			session_save_path(SESSION_SAVE_PATH);
		else
			session_save_path('/tmp');
	}
	session_start();
	$_SESSION['pub_userid'] = $userid;
	$session = md5(session_id());
	$ip = $_SERVER['REMOTE_ADDR'];
	$query = "UPDATE `" . $user_table . "` SET `Session`='$session', `IP`='$ip' WHERE `UserID` = :userid";
	try {
		$stmt = $dbh->prepare($query);
	} catch (PDOException $e) {
		echo $e->getMessage();
	}
	try {
		$stmt->execute(array(':userid' => $userid));
	} catch (PDOException $e) {
		echo $e->getMessage();
	}
	CleanAttemptLog($dbh);
	LogAttempt($userid, $dbh, 1);
	
	if (strpos($redirect, '?') > 0)
		$redirect .= "&login_success=1";
	else
		$redirect .= "?login_success=1";
	header("Location: $redirect");
	exit;
}

//////////////////////////////////////////////////
function CheckAttempts($userid, $dbh)
{
	$ip = $_SERVER['REMOTE_ADDR'];
	$current_ts = date('Y-m-d H:i:s', time());
	$query = "SELECT * FROM " . BWB_TABLE_PREFIX . "login_attempts WHERE (`IP` = '$ip') AND (TIMESTAMPDIFF(MINUTE, `AttemptTimeStamp`, '$current_ts') < 60)";
	$result = $dbh->query($query);
	return ($result->rowCount());
}

function LogAttempt($userid, $dbh, $success = 0)
{
	$ip = $_SERVER['REMOTE_ADDR'];
	$query = "INSERT INTO " . BWB_TABLE_PREFIX . "login_attempts (`UserID`, `IP`, `Success`) VALUES (:userid, '$ip', $success)";
	$stmt = $dbh->prepare($query);
	$stmt->execute(array(':userid' => $userid));
}

function CleanAttemptLog($dbh)
{
	$current_ts = date('Y-m-d H:i:s', time());
	$query = "DELETE FROM " . BWB_TABLE_PREFIX . "login_attempts WHERE (TIMESTAMPDIFF(DAY, `AttemptTimeStamp`, '$current_ts') > 7)";
	$cnt = $dbh->exec($query);
}
	
 /****************************************************************
 * check_referer() breaks up the enviromental variable		*
 * HTTP_REFERER by "/" and then checks to see if the second	*
 * member of the array (from the explode) matches any of the	*
 * domains listed in the $referers array (declaired at top)	*
 ****************************************************************/

function check_referer($referers)
{
	global $errors;
	if (count($referers)) {
		if (getenv('HTTP_REFERER')) {
			$temp = explode('/', getenv('HTTP_REFERER'));
			$found = false;
			while (list(,$stored_referer) = each($referers)) {
				if (preg_match('/^' . $stored_referer . '$/i', $temp[2]))
					$found = true;
			}
			if (!$found) {
				$errors[] = '1|You are coming from an unauthorized domain.';
				error_log('[PHPFormMail] Illegal Referer. (' . getenv('HTTP_REFERER') . ')', 0);
			}
			return $found;
		} else {
			$errors[] = '0|Sorry, but I cannot figure out who sent you here.  Your browser is not sending an HTTP_REFERER.  This could be caused by a firewall or browser that removes the HTTP_REFERER from each HTTP request you submit.';
			error_log('[PHPFormMail] HTTP_REFERER not defined. Browser: ' . getenv('HTTP_USER_AGENT') . '; Client IP: ' . getenv('REMOTE_ADDR') . '; Request Method: ' . getenv('REQUEST_METHOD') . ';', 0);
			return false;
		}
	} else {
		$errors[] = '1|There are no referers defined.  All submissions will be denied.';
		error_log('[PHPFormMail] You have no referers defined.  All submissions will be denied.', 0);
		return false;
	}
}

/////////////////////////////////////////////////////////////
function DisplayError($redirect_err_to_src, $err_msg, $err_code, $go_back)
{
	if (($redirect_err_to_src == 1) && ($go_back != "javascript:history.go(-1);"))
	{
		if (strpos($go_back, "?") === false)
			$go_back .= "?err=" . $err_code;
		else
			$go_back .= "&err=" . $err_code;
			
		header("Location: $go_back");
	}
	else
	{
		$err = $err_msg;
		$button_label = "OK";
		include("error_msg.php");
	}
	exit;
}
	
	/////////////////////////////////////////////////////////////////////////////////
	// Check referers to make sure SPAM bot is not trying to brake in.
	$server_name = $_SERVER['SERVER_NAME'];
	$server_name_www = "www." . $server_name;
	$referers = array($server_name_www, $server_name);
	if (!check_referer($referers))
	{
		$err = "You do not appear to be a valid.  If this is in error, go back and try again.<br />\n<a href='$go_back'>Click here</a> to return to the login page, or use your 'Back' button.";
		$button_label = "OK";
		include("error_msg.php");
		exit;
	}
	
	// If dictionary attack detected, just dump 'em. 
	$attempt_limit = CheckAttempts($userid, $bwb_dbh);
	if ($attempt_limit > 50)
	{
		LogAttempt($userid, $bwb_dbh);
		$err = "You have exceeded the maximum number of login attempts.  Please contact your system administrator.";
		$button_label = "OK";
		include("error_msg.php");
		exit;
	}

	if (!isset($user_table) || ($user_table == ""))
	{
		$err = "There is no user table associated with the page you are trying to access.  Please contact your web master.";
		$button_label = "OK";
		include("error_msg.php");
		exit;
	}

	$query = "SELECT * FROM `" . $user_table . "` WHERE `UserID` = :userid";
	try {
		$stmt = $bwb_dbh->prepare($query);
	} catch (PDOException $e) {
		echo $e->getMessage();
	}
	try {
		$stmt->execute(array(':userid' => $userid));
	} catch (PDOException $e) {
		echo $e->getMessage();
	}
	$num_results = $stmt->rowCount();
	$row = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT);
	
	if ($num_results < 1)
	{
		LogAttempt($userid, $bwb_dbh);
		$err_msg = "There is no user by that name.  Click <a href='$go_back'><span onclick='$go_back'><u><b>here</b></u></span></a> to re-enter your user ID and password.";
		$err_code = "bad_user";
		DisplayError($redirect_err_to_src, $err_msg, $err_code, $go_back);
	}
		
	$Active = $row['Active'];
	if ($Active != 1)
	{
		$err_msg = "The user ID <b>$userid</b> has not been activated yet. Contact the administration to followup on the activation for your account.  Click <a href='$go_back'><span onclick='$go_back'> <u><b>here</b></u></span></a> and try again once account has been activated.";
		$err_code = "not_active";
		DisplayError($redirect_err_to_src, $err_msg, $err_code, $go_back);
	}
		
	$access_level = $row["AccessLevel"];
	if (($access_level != $AccessLevel) && ($AccessLevel != "") && (!is_null($AccessLevel)))
	{
		$err_msg = "You don't have the required access level to access this page.   Click <a href='$go_back'><span onclick='$go_back'> <u><b>here</b></u></span></a> to enter a different user ID and password.";
		$err_code = "bad_access_level";
		DisplayError($redirect_err_to_src, $err_msg, $err_code, $go_back);
	}
	
	/////////////////////////////////////////////
	if (CheckPassword($row["password"], $row["UserID"], $password) === true)
	{
		Login($user_table, $userid, $bwb_dbh, $access_level, $redirect);
	}
	else
	{
		$query = "SHOW TABLES LIKE 'tmp_users'";
		$result = $bwb_dbh->query($query);
		if ($result->rowCount() > 0)
		{
			// If temp users table exist, look for record with temp password.
			$query = "SELECT * FROM tmp_users WHERE (`UserID` = :userid) AND (`created` BETWEEN TIMESTAMPADD(MINUTE, -240, NOW()) AND NOW())";
			$stmt = $bwb_dbh->prepare($query);
			$stmt->execute(array(':userid' => $userid));
			$num_results = $stmt->rowCount();
			$row = $stmt->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_NEXT);
			
			if ($num_results > 0)
			{	
				if (CheckPassword($row["password"], $row["UserID"], $password) === true)	{
					// Encrypt password for password update.
					$encrypt_password = $row["password"];
					// Successful login, delete temp password record.
					$query = "DELETE FROM tmp_users WHERE `UserID` = :userid";
					$stmt = $bwb_dbh->prepare($query);
					$stmt->execute(array(':userid' => $userid));
					// If no records left in temp user table, drop the table.
					$query = "SELECT * FROM tmp_users";
					$result = $bwb_dbh->query($query);
					if ($result->rowCount() < 1)
					{
						$query = "DROP TABLE IF EXISTS tmp_users";
						$cnt = $bwb_dbh->exec($query);
					}
					// Update password with temp password.
					$query = "UPDATE " . $user_table . " SET `password` = :encrypt_password WHERE `UserID` = :userid";
					$stmt = $bwb_dbh->prepare($query);
					$stmt->execute(array(':encrypt_password' => $encrypt_password, ':userid' => $userid));
					// Login
					Login($user_table, $userid, $bwb_dbh, $access_level, $redirect, true);
				}
			}
		}
		
		LogAttempt($userid, $bwb_dbh);
		$err_msg = "Your password has not been confirmed.  Click <a href='$go_back'><span onclick='$go_back'> <u><b>here</b></u></span></a> to re-enter your password.";
		$err_code = "bad_password";
		DisplayError($redirect_err_to_src, $err_msg, $err_code, $go_back);
	}
?>
