<?PHP
#
#   FILE:  UserLogin.php
#
#   Part of the Collection Workflow Integration System (CWIS)
#   Copyright 2002-2013 Edward Almasy and Internet Scout Research Group
#   http://scout.wisc.edu/cwis/
#

# 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"]) && isset($_POST["UseSecure"]) )
    {
        $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 = $DB->FetchRows();
 
        # 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 $Key)
        {
            $KeyCTime = $Key["CreationTime"];
            $DBFormatKeyPair = $Key["KeyPair"];
            # 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",
                "EditResourceComplete"
                );

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

        #  if referer isn't available
        if (!isset($_POST["HTTP_REFERER"])
                && !isset($_SERVER["HTTP_REFERER"]))
        {
            # go to front page
            $ReturnPage = "Home";
        }
        else
        {
            # if we know what internal page we are returning to
            $ReturnPage = isset($_POST["HTTP_REFERER"])
                    ? $_POST["HTTP_REFERER"]
                    : $_SERVER["HTTP_REFERER"];
            $UnmappedReturnPage = $GLOBALS["AF"]->GetUncleanUrlForPath($ReturnPage);
            $QueryString = parse_url($UnmappedReturnPage, PHP_URL_QUERY);
            $QueryVars = ParseQueryString($QueryString);
            if (isset($QueryVars["P"]))
            {
                # go to front page if page is on "Do Not Return To" list
                if (in_array($QueryVars["P"], $DoNotReturnToPages))
                {
                    $ReturnPage = "Home";
                }
                # strip out results per page if returning to advanced search
                # (to avoid overwriting user's preferences)
                elseif ($QueryVars["P"] == "AdvancedSearch")
                {
                    $ReturnPage = preg_replace(
                            "/\&(amp;)?RP=[0-9]+/", "", $ReturnPage);
                }
            }
        }

        # give any hooked filters a chance to modify return page
        $SignalResult = $AF->SignalEvent("EVENT_USER_LOGIN_RETURN",
                array("ReturnPage" => $ReturnPage));
        $ReturnPage = $SignalResult["ReturnPage"];

        # set destination to return to after login
        $AF->SetJumpToPage($ReturnPage);
    }
}
elseif ($LoginResult == U_NOTACTIVATED)
{
    # go to "needs activation" page
    $AF->SetJumpToPage("index.php?P=UserNotActivated&"
            ."UN=".urlencode($UserName));
}
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");
}
