<?php

#
#   FILE:  SPT--UserLogin.php
#
#   FUNCTIONS PROVIDED:
#       FunctionName($Parameter, $Parameter)
#           - brief description of function supplied for use in HTML file
#
#   FUNCTIONS EXPECTED:
#       FunctionName($Parameter, $Parameter)
#           - brief description of function that HTML file must supply
#
#   FORM FIELDS EXPECTED:
#       F_FormFieldName
#           - brief description of form field purpose and/or content
#
#   Part of the Scout Portal Toolkit
#   Copyright 2002 Internet Scout Project
#   http://scout.cs.wisc.edu
#

# try to log user in
$LoginResult = NULL;
if (isset($_POST["F_UserName"]) && isset($_POST["F_Password"]))
{
    $Password = "";
    $UserName = $_POST["F_UserName"];

    # If an encrypted password was sent:
    if (isset($_POST["F_CryptPassword"]))
    {
        $DB = new Database();
        $DB->Query("SELECT CreationTime, KeyPair FROM LoginKeys");

        # Remove the base64 encoding sent on the cyphertext:
        $Cyphertext = base64_decode($_POST["F_CryptPassword"]);

        # Extract recently used login keys from the database
        $Keys = array();
        while ($Row = $DB->FetchRow())
        {
            $Keys [$Row["CreationTime"]] = $Row["KeyPair"];
        }

        # Iterate through them on the cyphertext, trying to decrypt it with
        # each key.  By default, 48 hr worth of keys are kept, and fresh keys
        # are generated every 24 hr.
        foreach ($Keys as $KeyCTime => $DBFormatKeyPair)
        {
            # Extract the OpenSSL format private key from the database key format.
            $Keypair = openssl_pkey_get_private( $DBFormatKeyPair );

            # Attempt to decrypt the cyphertext
            if (openssl_private_decrypt($Cyphertext, $Cleartext, $Keypair))
            {
                # On success, extract the password portion and the random bytes.
                $Data = explode("\t", $Cleartext);
                $Password = array_shift($Data);
                $Token    = array_shift($Data);

                # Check to see if we've seen these particular bytes
                # before for this user and this key.
                $DB->Query("SELECT * FROM UsedLoginTokens WHERE"
                           ." Token=\"".addslashes($Token)."\" AND"
                           ." KeyCTime=\"".$KeyCTime."\" AND"
                           ." UserName=\"".$UserName."\"");

                if ($DB->NumRowsSelected() > 0)
                {
                    # If so, this is probably a replay attack.  Fail the login.
                    $Password="";
                }
                else
                {
                    # Otherwise, record these bytes as seen, and attempt to login
                    # with the plaintext password.
                    $DB->Query("INSERT INTO UsedLoginTokens"
                               ." (Token, KeyCTime, UserName) VALUES ("
                               ."\"".addslashes($Token)."\","
                               ."\"".addslashes($KeyCTime)."\","
                               ."\"".addslashes($UserName)."\")");
                }

                # Exit the loop.  No need to try other keys
                # when we've found one that works.
                break;
            }
        }
    }
    elseif (isset($_POST["F_HashPassword"]) && strlen($_POST["F_HashPassword"])>0)
    {
        $Password = " ".$_POST["F_HashPassword"];
    }
    else
    {
        $Password = $_POST["F_Password"];
    }

    $SignalResult = $AF->SignalEvent("EVENT_USER_AUTHENTICATION", array(
                                         "UserName" => $UserName,
                                         "Password" => $Password));
    if ($SignalResult === NULL)
    {
        $LoginResult = $G_User->Login($UserName, $Password);
    }
    else
    {
        if ($SignalResult)
        {
            $LoginResult = $G_User->Login($UserName, $Password, TRUE);
        }
        else
        {
            $LoginResult = $G_User->Login($UserName, "");
        }
    }
}

# if login was successful
if ($LoginResult === U_OKAY)
{
    # is user account disabled?
    if ($G_User->HasPriv(PRIV_USERDISABLED))
    {
        # log user out
        $G_User->Logout();
        $AF->SetJumpToPage("LoginError");
        return;
    }
    else
    {
        # record successful login event
        $EventLog = new SPTEventLog();
        $EventLog->Log(SptEventLog::SPTEVENT_USERLOGIN, $_SERVER["REMOTE_ADDR"]);

        # signal successful user login
        $AF->SignalEvent("EVENT_USER_LOGIN", array(
                "UserId" => $G_User->Id(), "Password" => $Password));

        # list of pages we do not want to return to
        $DoNotReturnToPages = array(
                "Login",
                "UserLogin",
                "LoginError",
                "ForgottenPasswordComplete",
                "RequestAccount",
                "RequestAccountComplete",
                "ActivateAccount",
                "ResendAccountActivation",
                "ResetPassword",
                "ForgottenPasswordComplete",
                );

        # add in any locally-defined pages not to return to
        if (function_exists("Local_GetDoNotReturnToPages"))
        {
            $DoNotReturnToPages = array_merge($DoNotReturnToPages,
                    Local_GetDoNotReturnToPages());
        }

        # if originating page looks like a valid destination
        $OrigPage = $_SERVER["HTTP_REFERER"];
        $OrigPagePieces = explode("?", basename($OrigPage), 2);
        $OrigPageFile = array_shift($OrigPagePieces);
        if (($OrigPageFile == "index.php") && (!count($OrigPagePieces)
                || preg_match("/P=([a-zA-Z_-]+)/", $OrigPagePieces[0], $OrigParams)))
        {
            $OrigPageFile = isset($OrigParams) ? $OrigParams[1] : "Home";
        }
        if (!in_array($OrigPageFile, $DoNotReturnToPages))
        {
            if (substr($OrigPage, -1) == "/")
            {
                $OrigPage .= "index.php";
            }

            # head to originating page
            $ReturnPage = $OrigPage;
        }
        else
        {
            # head to home page
            $ReturnPage = "Home";
        }

        # pass login return address through any hooked filters via signal
        $SignalResult = $AF->SignalEvent("EVENT_USER_LOGIN_RETURN",
                array("ReturnPage" => $ReturnPage));
        $ReturnPage = $SignalResult["ReturnPage"];

        # set destination to return to after login
        $AF->SetJumpToPage($ReturnPage);
    }
}
elseif (isset($Password) 
        && (preg_match("/^[0-9A-F]{6}([0-9A-F]{4})?$/",$Password) == 1))
{
    # Login failed, but password looks like it was a reset or an
    # activation code.
    # We'll check to see if the code was valid, and redirect if it
    # was.  However, if the PW and the code are both invalid,
    # just report a login error as usual.
    $TargetUser = new SPTUser($UserName);

    if ($TargetUser->IsActivated())
    {
        # Account already activated, see if this was a PW reset
        if ($TargetUser->IsResetCodeGood($Password))
        {
            # And jump to the rest page if so
            $AF->SetJumpToPage("index.php?P=ResetPassword&"
                               ."UN=".$UserName."&"
                               ."RC=".$Password);
        }
        else
        {
            # Or report a login error if not
            $AF->SetJumpToPage("LoginError");
        }
    }
    else
    {
        # Otherwise, we're doing an account activation, check to
        # see if the code was good
        if ($TargetUser->IsActivationCodeGood($Password))
        {
            # Jump to the activation code if so
            $AF->SetJumpToPage("index.php?P=ActivateAccount&"
                               ."UN=".$UserName."&"
                               ."AC=".$Password);
        }
        else
        {
            # Or report a login error if not
            $AF->SetJumpToPage("LoginError");
        }
    }
}
else
{
    # go to login error page
    $AF->SetJumpToPage("LoginError");
}

?>
