<?php
require_once 'vendor/autoload.php';

$smarty = new Smarty();
$smarty->muteUndefinedOrNullWarnings();
$smarty->cache_dir = './cache';
$smarty->config_dir = './config';

$smarty->template_dir = "./templates_en";
$smarty->compile_dir ="./templates_c_en";
	
require_once("library.php");	
require_once("settings.php");
require_once 'library/2fa_lib.php';

if (!$PRODUCTIONVERSION) $smarty->assign('demo', 'true');
$php_action = $_SERVER["PHP_SELF"];
$smarty->assign('php_action', $php_action);

$task = trim(stripslashes(@$_REQUEST['task']));

if ($task != "logout") { // user is not logging out so read session data
    session_set_cookie_params([
        'lifetime' => 3600,
        'httponly' => true,
        'secure' => true,
        'samesite' => 'Strict'
    ]);
	session_start();
    // check if user is already logged in
	if (@$_SESSION['s_username'] && (@$_SESSION['login_ok'] == true) && @$_SESSION['timestamp'] && @$_SESSION['contact_type'] && @$_SESSION['contact_id'] ){
        if (!@$_SESSION['accounting_user']) {
            gdpr_check();
        } else {
            $smarty->assign('gdpr_accepted', TRUE);
        }
		$smarty->display('switchboard.tpl'); // show the main page
		die();
	}
}

// read login data
$login = trim(stripslashes(@$_REQUEST['login']));
$password = trim(stripslashes(@$_REQUEST['password']));
$email = trim(stripslashes(@$_REQUEST['email']));
$code = get2FACodeFromDigits($_REQUEST) ?? null;
if ($code === null || $code == '') $backup_code = trim(stripslashes(@$_REQUEST['backup'])) ?? null;
// Post-login redirect destination — only bverify.php links are accepted (validated before use)
$r = trim(@$_REQUEST['r']);
$smarty->assign('r', $r);

if($task == "") $task = null;
if($login == "") $login = null;
if($password == "") $password = null;
if($email == "") $email = null;

if($task == "login") { // user is logging in
    $userIsAuthenticated = FALSE;
    // check if ip is locked
    if (is_ip_locked($_SERVER["REMOTE_ADDR"], 600, 5)) {
        logger('error', 'ip '.$_SERVER['REMOTE_ADDR'].' locked / user: '.$login);
        $smarty->assign('login_locked', 'true');
        $smarty->assign('task', 'login');
        $smarty->display('login.tpl');
        die();
    }
    // username and password check
	if (!is_null($login) && !is_null($password)) {
        $login = $pdb->escapeString($login);
        unset($_SESSION['s_username_pre_2fa']);
        // login_fail_count < 6 or last_fail_timestamp over 10 minutes ago
        $tz = date_default_timezone_get();
        date_default_timezone_set('UTC');
        $query = "SELECT * FROM flusers WHERE username=LOWER('$login') AND (login_fail_count < 6 OR last_fail_timestamp < '" . date("Y-m-d H:i:s", (time() - 600)) . "')";
        date_default_timezone_set($tz);
        $db_auth_pdb = $pdb_auth->query($query);
        if ($row_db_auth = $db_auth_pdb->fetchRow()) {
            if (is_null($row_db_auth['hash'])) {
                $userisAuthenticated = FALSE;
                logger('info', 'password hash FAIL (no hash)');
                $smarty->assign('login_error', 'true');
                $smarty->assign('task', 'login');
                $smarty->display('login.tpl');
                die();
            }
            if (password_verify($password, $row_db_auth['hash'])) {
                // check if user has two factor authentication enabled
                if ($row_db_auth['secret'] == '') {
                    $userIsAuthenticated = TRUE;
                    logger('info', 'password hash OK, no 2FA');
                } else {
                    $require2FA = TRUE;
                    $_SESSION['s_username_pre_2fa'] = $login;
                    logger('info', 'password hash OK, 2FA required');
                }
            } else {
                $userisAuthenticated = FALSE;
                logger('info', 'password hash FAIL');
                increment_login_failure_by_username($login);
                increment_login_failure_by_ip($_SERVER["REMOTE_ADDR"]);
                $smarty->assign('login_error', 'true');
                $smarty->assign('task', 'login');
                $smarty->display('login.tpl');
                die();
            }
        }
    } else if (@!$_SESSION['s_username_pre_2fa']) {
        // user didn't suply username and password and we are not in two factor authentication stage
        logger('error', 'no username or password supplied');
        $error = true;
        $smarty->assign('login_error', 'true');
        $smarty->assign('task', 'login');
        $smarty->display('login.tpl');
        die();
    }

    if (@$require2FA) {
        $smarty->assign('login', $login);
        $smarty->assign('text', 'Please enter the code from your authenticator app');
        // alternative text for 2FA ask when asking for backup codes
        $smarty->assign('text_2', 'Please enter one of your unused backup codes');
        $smarty->assign('action', 'index.php');
        $smarty->display('2fa_ask.tpl');
        die();
    }

    // 2FA check
    if ( ( !is_null($code) || !is_null($backup_code) ) && @$_SESSION['s_username_pre_2fa']) {
        if ($code) {
            if (check2faSecret($_SESSION['s_username_pre_2fa'], $code)) {
                logger('info', '2FA OK for user ' . $_SESSION['s_username_pre_2fa']);
                $userIsAuthenticated = TRUE;
            }
        } else if ($backup_code) {
            $backup_codes = getUserBackupCodes($_SESSION['s_username_pre_2fa']);
            if (in_array($backup_code, $backup_codes)) {
                logger('info', 'backup code OK for user ' . $_SESSION['s_username_pre_2fa']);
                $userIsAuthenticated = TRUE;
                // remove used backup code
                $backup_codes = array_diff($backup_codes, [$backup_code]);
                updateBackupCodes($_SESSION['s_username_pre_2fa'], $backup_codes);
            }
        }
        if (!$userIsAuthenticated) {
            // 2FA failed, ask again
            increment_login_failure_by_username($_SESSION['s_username_pre_2fa']);
            increment_login_failure_by_ip($_SERVER["REMOTE_ADDR"]);
            logger('error', '2FA FAIL for user ' . $_SESSION['s_username_pre_2fa']);
            $smarty->assign('error', 'Invalid code. Please try again.');
            $smarty->assign('text', 'Please enter the code from your authenticator app');
            $smarty->assign('text_2', 'Please enter one of your unused backup codes');
            $smarty->assign('login', $login);
            $smarty->display('2fa_ask.tpl');
            die();
        } else {
            // get user data is expected in in iserIsAuthenticated block
            $query = "SELECT * FROM flusers WHERE username=LOWER('" . $_SESSION['s_username_pre_2fa'] . "')";
            $db_auth_pdb = $pdb_auth->query($query);
            $row_db_auth = $db_auth_pdb->fetchRow();

        }
    }
    if ($userIsAuthenticated) {
        $username = $row_db_auth['username'];
        $c_or_p = $row_db_auth['c_or_p'];
        $ref_ID = $row_db_auth['ref_ID'];
        if ($c_or_p == "Person") {
            $query = "SELECT emp_ID FROM EMPLOYEES WHERE emp_ID=$ref_ID";
            $db_data_res = $pdb->query($query);
            if ($db_data_row = $db_data_res->fetchRow()) {
                $query = "SELECT ref_ID FROM CONTACT_MATRIX
                WHERE contact_detail='$username' AND c_or_p='$c_or_p' AND ref_ID=$ref_ID";
                $db_data_res = $pdb->query($query);
                if ($db_data_row = $db_data_res->fetchRow()) {
                    $auth_query_ok = true;
                } else {
                    $error = true;
                    logger("info", "email not found for $username");
                    $smarty->assign('login_error', 'true');
                }
            } else {
                $error = true;
                logger("info", "login OK, references non-existent person in TTK for $username");
                $smarty->assign('login_error', 'true');
            }
        } else if ($c_or_p == "Company") {
            $query = "SELECT company_ID FROM COMPANIES WHERE company_ID=$ref_ID";
            $db_data_res = $pdb->query($query);
            if ($db_data_row = $db_data_res->fetchRow()) {
                $query = "SELECT ref_ID FROM CONTACT_MATRIX
                WHERE   contact_detail='$username' AND c_or_p='$c_or_p' AND ref_ID=$ref_ID";
                $db_data_res = $pdb->query($query);
                if ($db_data_row = $db_data_res->fetchRow()) {
                    $auth_query_ok = true;
                } else {
                    $error = true;
                    logger("info", "email not found for $username");
                    $smarty->assign('login_error', 'true');
                }
            } else {
                $error = true;
                logger("info", "name not found for $username");
                $smarty->assign('login_error', 'true');
            }
        }
        if (@$auth_query_ok) {
            $pre_username = $username;
            session_start();
            session_unset();
            $s_username = $pre_username;
            $_SESSION['s_username'] = $s_username;
            $_SESSION['login_ok'] = true;
            $_SESSION['timestamp'] = time();
            $_SESSION['contact_type'] = $c_or_p;
            $_SESSION['contact_id'] = $ref_ID;
            $_SESSION['accounting_user'] = false; // initial value

            // set last login date and clear login failures
            $query = "UPDATE flusers set last_login = now(), login_fail_count = 0, last_fail_timestamp = 0 WHERE username = \"$s_username\"";
            $tmp_set_last_login = $pdb_auth->query($query);

            if ($row_db_auth['accounting_user'] == "Yes") {
                $_SESSION['accounting_user'] = true;
                logger("info", "accounting user logged in from " . $_SERVER["REMOTE_ADDR"] . ".");
            } else {
                logger("info", " logged in from " . $_SERVER["REMOTE_ADDR"] . ".");
            }
            logger("info", "Users browser is " . $_SERVER["HTTP_USER_AGENT"]);


            // Redirect to bverify link if one was passed through the login flow,
            // otherwise send to the normal post-login destination.
            // Strictly validate r to prevent open redirects — only bverify.php?c=<hex> is allowed.
            if ($r && preg_match('/^bverify\.php\?c=[0-9a-f]+$/', $r)) {
                header("Location: https://" . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "/" . $r);
            } elseif ($_SESSION['accounting_user']) {
                header("Location: https://" . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "menu.php");
            } else {
                header("Location: https://" . $_SERVER['HTTP_HOST'] . dirname($_SERVER['PHP_SELF']) . "index.php");
            }
            exit();
        }
    }

    if (!@$auth_query_ok && !@$error) {
        logger("error", "login failed for user $login from IP ".$_SERVER["REMOTE_ADDR"]);
        $query = "SELECT login_fail_count, last_fail_timestamp FROM flusers WHERE username=LOWER('$login')";
        $res = $pdb_auth->query($query);
        if ($row = $res->fetchRow()) {
            $tz = date_default_timezone_get();
            date_default_timezone_set('UTC');

            if ($row["login_fail_count"] >= 4) $locked = TRUE;

            // increment both by username and IP address counters
            increment_login_failure_by_username($login);
            increment_login_failure_by_ip($_SERVER["REMOTE_ADDR"]);
            date_default_timezone_set($tz);

        } else {
            /*
             * if user not found, log IP for failed login attempts
             */
            logger("error", "no such user $login / logging IP for failure: " . $_SERVER["REMOTE_ADDR"]);
            increment_login_failure_by_ip($_SERVER["REMOTE_ADDR"]);
        }
        $error = true;
        if (@$locked) {
            logger("error", "account is now locked for 10 minutes.");
            $smarty->assign('login_locked', 'true');
        } else {
            $smarty->assign('login_error', 'true');
        }
    }
}

if ($task == "logout") { // user is logging out... just close the session
	logger("info", "session closed.");
	check_session();
	end_session();
}

// uusi käyttäjä
	if($task == "newuser" && !is_null($email)) {
	// tarkastetaan osoite  $ownDomains settings.php
		if (is_array($ownDomains)){
            foreach ($ownDomains as $no_mail) {
                $no_mail_match = "/^.*".$no_mail."$/i";
                if(preg_match($no_mail_match, $email)) {
                    $error = true;
                    $smarty->assign('registered', 'true');
                    logger("error", "Flex user tried to register with internal company email ($email)");
                }
            }
        }
		if(!@$error){
			if (is_valid_email($email)) {
                $createUserOK = TRUE;
				$email = $pdb->escapeString($email);
				// haetaan emailin tiedot
				$query = "SELECT * FROM CONTACT_MATRIX WHERE LOWER(contact_detail)=LOWER('$email')";
				$db_data_pdb = $pdb->query($query);
				if ($db_data_pdb->numrows() > 1) {
                    $error = true;
                    $createUserOK = FALSE;
                    $smarty->assign('registered', 'true');
                    logger("error", "Flex user tried to register with email ($email) we have multiple records of");
                } else if ($db_data_pdb->numrows() == 0) {
                    $error = true;
                    $createUserOK = FALSE;
                    $smarty->assign('registered', 'true');
                    logger("error", "Flex user tried to register with email ($email) we have no record of");
				} else {

					while($row_db_data = $db_data_pdb->fetchRow()) {
						$contact_ID = $row_db_data['contact_ID'];
						$c_or_p = $row_db_data['c_or_p'];
						$ref_ID = $row_db_data['ref_ID'];
						$email = $row_db_data['contact_detail'];

						// check that users company is not internal company
						if ($c_or_p == 'Company') {
							if (in_array($ref_ID, $internal_comp_IDs)) {
								$error = true;
                                $createUserOK = FALSE;
                                logger("error", "Flex user tried to register with internal company email ($email)");
							}
						} else {
							$query = "SELECT company_ID FROM EMPLOYEES WHERE emp_ID='$ref_ID'";
							$res = $pdb->query($query);
							if ($row = $res->fetchRow()) {
								if (in_array($row["company_ID"], $internal_comp_IDs)) {
									$error = true;
                                    $createUserOK = FALSE;
                                    logger("error", "Flex user tried to register with internal company email ($email)");
								}
							}
						}

						// tarkistetaan onko yritys alihankkija
						if($c_or_p == 'Company') {
							$query = "SELECT * FROM COMPANIES WHERE company_ID=$ref_ID";
							$db_data2_pdb = $pdb->query($query);
							while($row_db_data2 = $db_data2_pdb->fetchRow()) {
								$vendor = $row_db_data2['vendor'];
							}
							if(!$vendor == 'Yes') {
								$error = true;
                                $createUserOK = FALSE;
						        logger("info", "company is not an vendor");
							}
						}
						// check if user alrready has an account with another address
						$query = "SELECT * FROM flusers WHERE c_or_p='" . $c_or_p . "' AND ref_ID=" . $ref_ID;
						$db_auth_pdb = $pdb_auth->query($query);
						while($row_db_auth = $db_auth_pdb->fetchRow()) {
							$username = $row_db_auth['username'];
            				if ($email != $username){
  								$error = true;
                                $createUserOK = FALSE;
                                logger("error", "Flex user tried to register for a vendor that already has flex account");
                                // TODO NOTIFY USER's EMAIL THAT SOME ONE TRIED TO REGISTER WITH THEIR EMAIL
            				}
						}
                        // check if email is already in use in existing account
						$query = "SELECT * FROM flusers WHERE username='$email' ";
						$db_auth_pdb = $pdb_auth->query($query);
						while($row_db_auth = $db_auth_pdb->fetchRow()) {
                            $username = $row_db_auth['username'];
                            $error = true;
                            $createUserOK = FALSE;
                            logger("error", "Flex user tried to register with email that is already in use");
                            // TODO NOTIFY USER's EMAIL THAT SOME ONE TRIED TO REGISTER WITH THEIR EMAIL
						}
						if ($createUserOK) {
							$username = $email;
							$password = generate_password(10);
							$hash = password_hash($password, PASSWORD_BCRYPT, ["cost" => 12]);
                            $query = "INSERT INTO flusers (username, hash, c_or_p, ref_ID) VALUES ('".strtolower($username)."', '$hash', '$c_or_p', $ref_ID)";
							$db_auth_pdb = $pdb_auth->query($query);
							$message = "$email_title\r\n\r\nYour new password is: $password\r\n\r\nLogin in to our Extranet at :\r\nhttps://" . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']."\r\n\r\nWe recommend that you change your password as soon as possible.";
							sendmail($email, $message, $email_title, $email_from);
                            $passwordResetOK = TRUE;
						}

                        $smarty->assign('registered', 'true');
						break; // jos maileja l�ytyykin useampia, otetaan vain ensimm�inen
					}
				}
			} else {
				$smarty->assign('invalid_email', 'true');
                $smarty->assign('task', 'newuser');
                $smarty->display('login.tpl');
                logger('error', 'Invalid email: ' . $email);
                die();
			}
		}
	}

	// lähetä salasana
	if($task == "requestpassword" && !is_null($email)) {

        // TODO we should check here that the emp_ID is still valid as user may be deleted

        // check that password request is not too soon once per 5 min
        $requestOK = TRUE; // is request OK according to rate limiting
        // check rate limit per ip
        $query = "SELECT * FROM password_requests_ip WHERE ip='".$_SERVER["REMOTE_ADDR"]."' AND timestamp > '".date("Y-m-d H:i:s", (time()-300))."'";
        $res = $pdb_auth->query($query);
        if ($res->numRows() > 0) {
            $error = true;
            $smarty->assign('password_request_too_soon', 'true');
            logger('info', "Password for ip request too soon: $email from: " . $_SERVER["REMOTE_ADDR"]);
            $requestOK = FALSE;
        } else {
            // check rate limit per email
            $query = "SELECT * FROM password_requests_email WHERE email='$email' AND timestamp > '" . date("Y-m-d H:i:s", (time() - 300)) . "'";
            $res = $pdb_auth->query($query);
            if ($res->numRows() > 0) {
                $error = true;
                $smarty->assign('password_request_too_soon', 'true');
                logger('info', "Password for email request too soon: $email from: " . $_SERVER["REMOTE_ADDR"]);
                $requestOK = FALSE;
            }
        }
        $email = $pdb->escapeString($email);
        if ($requestOK && is_valid_email($email)) {
            logger('info', "Password request: $email from: ".$_SERVER["REMOTE_ADDR"]);
            // delete old entries (if there is one) and log password request into DB
            $query = "DELETE FROM password_requests_ip WHERE ip='".$_SERVER["REMOTE_ADDR"]."'";
            $pdb_auth->query($query);
            $query = "DELETE FROM password_requests_email WHERE email='".$email."'";
            $pdb_auth->query($query);
            $query = "INSERT INTO password_requests_ip (ip, timestamp) VALUES ('".$_SERVER["REMOTE_ADDR"]."', '".date('Y-m-d H:i:s')."')";
            $pdb_auth->query($query);
            $query = "INSERT INTO password_requests_email (email, timestamp) VALUES ('".$email."', '".date('Y-m-d H:i:s')."')";
            $pdb_auth->query($query);

			$query = "SELECT * FROM flusers WHERE username=LOWER('$email')";
			$db_auth_pdb = $pdb_auth->query($query);
			if ($row_db_auth = $db_auth_pdb->fetchRow()) {
                // valid flex user
                // add password reset token to db
                $token = bin2hex(random_bytes(64));
                $email = $row_db_auth['username'];
                // token valid for one hour
                $query = "INSERT INTO password_reset_tokens (email, token, valid_before) VALUES ('$email', '$token', '".date('Y-m-d H:i:s', time()+3600)."')";
                $tokenShort = substr($token, 0, 10).'...';
                logger('info', "Password reset token $tokenShort created for $email");
                $pdb_auth->query($query);
                $smarty->assign('link', $_ENV['BASE_URL'].'resetpassword.php?token=' . $token);
                $smarty->assign('site_name', $_ENV['SITE_NAME']);
                $message = $smarty->fetch('password_reset_email.tpl');
                sendmail($email, $message, $email_title, $email_from);
                logger('info', "Password reset email sent to $email");
                $passwordResetOK = TRUE;
			}
			if (!@$auth_query_ok) {
                $passwordResetOK = TRUE; // fake OK so user will not know if email was valid or not
			}
		} else {
            if ($requestOK) {
                $passwordResetOK = TRUE; // fake OK so user will not know if email was valid or not
                logger('error', "Password request for invalid: $email from: " . $_SERVER["REMOTE_ADDR"]);
            }
		}
	}
	if (!$task){
       		$task = "login";
          $login_page=TRUE;
	}
	$smarty->assign("timestamp", time());
	$smarty->assign("password", $password);
	$smarty->assign("login", $login);
	$smarty->assign("task", $task);
	$smarty->assign("email", $email);
    if (@$login_page) $smarty->assign("login_page", true);
    if (@$passwordResetOK) {
        $smarty->display('sentpassword.tpl');
    } else {
        $smarty->display('login.tpl');
    }
