<?php

require_once("RestAPIHelper.php");

global $cwis_user_in_rest_call;
$cwis_user_in_rest_call = FALSE;

/**
* Check if a specified REST API call is a duplicate.
* @param int $timestamp UNIX time when the remote site sent the call.
* @param string $cookie Random cookie included with the original call.
* @return TRUE for duplicate messages, FALSE otherwise.
*/
function cwis_user_check_for_dup($timestamp, $cookie)
{
    // clean out messages older than 630 seconds
    db_delete('cwis_user_messages')
        ->condition('timestamp', time()-630, "<")
        ->execute();

    // look for duplicate messages
    $result = db_query(
        'SELECT COUNT(*) FROM {cwis_user_messages} '
        .'WHERE timestamp = :t AND cookie = :c',
        array(
            ':t' => $timestamp,
            ':c' => $cookie,
        ));

    return ($result->fetchField() > 0);
}

/**
* Record that REST API message has been seen.
* @param int $timestamp UNIX time when the remote site sent the call.
* @param string $cookie Random cookie included with the original call.
*/
function cwis_user_register_message($timestamp, $cookie)
{
    db_insert('cwis_user_messages')
        ->fields(array(
            'timestamp' => $timestamp,
            'cookie' => $cookie,
        ))
        ->execute();
}

/**
* Get a REST API helper.
*/
function cwis_user_get_helper()
{
    static $helper = NULL;

    if ($helper === NULL)
    {
        $helper = new RestAPIHelper(
            variable_get('cwis_user_cwis_url','').'/index.php?P=P_DrupalSync_API',
            variable_get('cwis_user_api_password',''),
            'cwis_user_check_for_dup',
            'cwis_user_register_message' );
    }

    return $helper;
}

/**
* implement hook_init()
*/
function cwis_user_init()
{
    global $cwis_user_in_rest_call;
    if ($cwis_user_in_rest_call)
    {
        return;
    }

    // if this pageload happens to be a user login request for a user
    // that does not exist in drupal
    If (isset($_POST["form_id"] ) &&
        ($_POST["form_id"] == "user_login_block" ||
         $_POST["form_id"] == "user_login") &&
        $_POST["op"] == "Log in"  &&
        user_load_by_name($_POST["name"]) === FALSE)
    {
        // check if a user w/ the same name and password exists in cwis
        $helper = cwis_user_get_helper();
        $result = $helper->DoRestCommand(array(
            "Cmd" => "GetUserInfo",
            "UserName" => $_POST["name"],
            "UserPassword" => $_POST["pass"],
        ));

        // if so, and if we don't have a different account already for this
        // email address, create a corresponding local account
        if ($result["Status"] == "OK" &&
            user_load_by_mail($result["Data"]["UserEmail"]) === FALSE)
        {
            user_save(NULL, array(
                'name' => $_POST["name"],
                'pass' => $_POST["pass"],
                'mail' => $result["Data"]["UserEmail"],
                "status" => 1));
        }
    }
}

/**
* implement hook_user_insert(), for handling user creation.
*/
function cwis_user_user_insert(&$edit, $account, $category)
{
    global $cwis_user_in_rest_call;
    if ($cwis_user_in_rest_call)
    {
        return;
    }

    $helper = cwis_user_get_helper();

    $helper->DoRestCommand(array(
        "Cmd" => "CreateUser",
        "UserName" => $account->name,
        "UserEmail" => $account->mail,
        "Password" => $edit['input']['pass']));
}

/**
* implement hook_user_delete(), for handling user deletion.
*/
function cwis_user_user_delete($account)
{
    global $cwis_user_in_rest_call;
    if ($cwis_user_in_rest_call)
    {
        return;
    }

    $helper = cwis_user_get_helper();
    $helper->DoRestCommand(array(
        "Cmd" => "DeleteUser",
        "UserEmail" => $account->mail,
    ));
}

/**
* implement hook_user_update(), for handling updates to user email and password
*/
function cwis_user_user_update(&$edit, $account, $category)
{
    global $cwis_user_in_rest_call;
    if ($cwis_user_in_rest_call)
    {
        return;
    }

    $helper = cwis_user_get_helper();

    // bail if no password was provided
    if (strlen($_POST["current_pass"]) == 0)
    {
        return;
    }

    // if email was changed, propagate that change
    if ($account->mail != $account->original->mail)
    {
        $helper->DoRestCommand(array(
            "Cmd" => "UpdateEmail",
            "UserEmail" => $account->original->mail,
            "NewUserEmail" => $account->mail,
        ));
    }

    // if password was changed, propagate that as well
    if (strlen($_POST["pass"]["pass1"])>0 &&
        $_POST["pass"]["pass1"] == $_POST["pass"]["pass2"])
    {
        $helper->DoRestCommand(array(
            "Cmd" => "UpdatePassword",
            "UserEmail" => $account->mail,
            "OldPassword" => $_POST["current_pass"],
            "NewPassword" => $_POST["pass"]["pass1"],
        ));
    }
}

/**
* implement hook_user_login(), for handling user logins
*/
function cwis_user_user_login(&$edit, $account)
{
    global $cwis_user_in_rest_call;
    if ($cwis_user_in_rest_call)
    {
        return;
    }

    $helper = cwis_user_get_helper();

    // cookies the remote will need to unset to log us out
    $cookies = array(
        session_name() => session_id(),
        "DRUPAL_UID" => $account->uid,
    );

    // attempt to log in on CWIS
    $result = $helper->DoRestCommand(
        array("Cmd" => "UserLogin",
              "UserEmail" => $account->mail,
              "UserPassword" => $_POST["pass"],
              "Cookies" => $cookies,
        ));

    // check that the login was successful, if so set the provided cookies
    if ($result["Status"] != "OK" ||
        $result["Message"] != "Login successful.")
    {
        drupal_set_message(
            t("cwis_user: problem synchronizing user login with CWIS. "
              ."Status='@status' Message='@message'",
              array(
                  '@status' => $result['Status'],
                  '@message' => $result['Message'])),
            'error');
        return;
    }

    $Hostname = parse_url(variable_get('cwis_user_cwis_url',''), PHP_URL_HOST);
    $Proto = parse_url(
        variable_get('cwis_user_cwis_url',''), PHP_URL_SCHEME);
    foreach ($result["Cookies"] as $Name => $Val)
    {
        setcookie($Name, $Val, REQUEST_TIME + 3600, "/", $Hostname,
                  ($Proto == "https" ? TRUE : FALSE), TRUE);
    }

    $_SESSION["cwis_user_needs_logout"] = $result["Cookies"];
}

/**
* implement hook_user_logout(), for handling user logout.
*/
function cwis_user_user_logout($account)
{
    global $cwis_user_in_rest_call;
    if ($cwis_user_in_rest_call)
    {
        return;
    }

    $helper = cwis_user_get_helper();

    if (!isset($_SESSION["cwis_user_needs_logout"]))
    {
        return;
    }

    $result = $helper->DoRestCommand(array(
        "Cmd" => "UserLogout",
        "UserEmail" => $account->mail,
        "Cookies" => $_SESSION["cwis_user_needs_logout"],
    ));

    if ($result["Status"] != "OK" ||
        $result["Message"] != "User successfully logged out.")
    {
        drupal_set_message(
            t("cwis_user: problem synchronizing user login with CWIS. "
              ."Status='@status' Message='@message'",
              array(
                  '@status' => $result['Status'],
                  '@message' => $result['Message'])),
            'error');
        return;
    }

    $Hostname = parse_url(variable_get('cwis_user_cwis_url',''), PHP_URL_HOST);
    $Proto = parse_url(
        variable_get('cwis_user_cwis_url',''), PHP_URL_SCHEME);

    foreach ($_SESSION["cwis_user_needs_logout"] as $Name => $Val)
    {
        setcookie($Name, NULL, REQUEST_TIME - 3600, "/", $Hostname,
                  ($Proto == "https" ? TRUE : FALSE), TRUE);
    }

    unset($_SESSION["cwis_user_needs_logout"]);
}

// Create a settings page for our plugin
function cwis_user_admin(){
    $form = array();

    $form['cwis_user_cwis_url'] = array(
        '#type' => 'textfield',
        '#title' => t("CWIS site URL"),
        '#default_value' => variable_get('cwis_user_cwis_url',''),
        '#required' => TRUE
        );
    $form['cwis_user_api_password'] = array(
        '#type' => 'textfield',
        '#title' => t("Synchronization password"),
        '#default_value' => variable_get('cwis_user_api_password',''),
        '#required' => TRUE
        );

    return system_settings_form($form);
}

// Link our settings page into the menu system
function cwis_user_menu(){
    $items = array();

    $items['admin/config/people/cwis_user'] = array(
        'title' => 'CWIS User',
        'description' => 'CWIS database configuration information',
        'page callback' => 'drupal_get_form',
        'page arguments' => array('cwis_user_admin'),
        'access arguments' => array('administer users'),
        'type' => MENU_NORMAL_ITEM,
        );

    $items['cwis_user/api'] = array(
        'page callback' => 'cwis_user_api_call',
        'access callback' => TRUE,
        'type' => MENU_CALLBACK,
        'delivery callback' => 'drupal_json_output',
    );

    return $items;
}

/**
* Process a decoded REST command.
* @param array $message Decoded REST command.
* @return array command status
*/
function cwis_user_process_rest_command($message)
{
    global $user;
    // load user by email where appropriate based on command
    switch ($message["Data"]["Cmd"])
    {
        case "UserLogin":
        case "UserLogout":
        case "DeleteUser":
        case "UpdateEmail":
        case "UpdatePassword":
            $user = user_load_by_mail($message["Data"]["UserEmail"]);
            if ($user === FALSE)
            {
                return array(
                    "Status" => "Error",
                    "Message" => "User not found.",
                );
            }

        default:
            break;
    }

    switch ($message["Data"]["Cmd"])
    {
        case "UserLogin":
            // attempt to log in w/ the provided password
            $result = user_authenticate(
                $user->name, $message["Data"]["UserPassword"]);
            if ($result === FALSE)
            {
                // if unsuccessful, return an error
                return array(
                    "Status" => "Error",
                    "Message" => "Login failed.");
            }

            // otherwise, return success and the necessary cookies
            user_login_finalize();
            $sname = session_name();

            $reply = array(
                "Status" => "OK",
                "Message" => "Login successful.",
                "Cookies" => array(
                    $sname => session_id(),
                    "DRUPAL_UID" => $result,
                ),
            );

            // and note the cookies that we'd need to nuke here
            $_SESSION["cwis_user_needs_logout"] = $message["Data"]["Cookies"];

            return $reply;
            break;

        case "UserLogout":
            // connect to the user's session
            if (session_status() == PHP_SESSION_ACTIVE)
            {
                session_abort();
            }
            session_id($message["Data"]["Cookies"][session_name()]);
            session_start();

            // delete it to log the user out
            session_destroy();

            // return success
            return array(
                "Status" => "OK",
                "Message" => "User successfully logged out.");
            break;

        case "CreateUser":
            $user = user_load_by_mail(
                $message["Data"]["UserEmail"]);
            if ($user !== FALSE)
            {
                return array(
                    "Status" => "Error",
                    "Message" => "User already exists.");
            }

            // get the desired name from the request
            $tgt_name = $message["Data"]["UserName"];

            // if the desired name is in use, append a sequence number
            // to make it unique
            $count = 0;
            while (user_load_by_name($tgt_name) !== FALSE)
            {
                $tgt_name = $message["Data"]["UserName"].$count;
                $count++;
            }

            user_save(NULL, array(
                'name' => $tgt_name,
                'pass' => $message["Data"]["UserPassword"],
                'mail' => $message["Data"]["UserEmail"],
                "status" => 1));

            return array(
                "Status" => "OK",
                "Message" => "User successfully created.",
                "UserName" => $tgt_name);
            break;

        case "DeleteUser":
            user_delete($user->uid);
            return  array(
                "Status" => "OK",
                "Message" => "User successfully deleted.");
            break;

        case "UpdatePassword":
            $result = user_authenticate(
                $user->name, $message["Data"]["UserPassword"]);

            if ($result === FLASE)
            {
                return array(
                    "Status" => "Error",
                    "Message" => "Unable to change password, bad value for current password.");
            }

            user_save($user, array(
                "pass" => $message["Data"]["NewPassword"]));
            return array(
                "Status" => "OK",
                "Message" => "Password successfully changed.",
            );
            break;

        case "UpdateEmail":
            if (user_load_by_mail($message["Data"]["NewEmail"]) !== FALSE)
            {
                $reply = array(
                    "Status" => "Error",
                    "Message" => "Another user already exists with this email address.");
            }

            user_save($user, array(
                "mail" => $message["Data"]["NewEmail"]));
            return array(
                "Status" => "Error",
                "Message" => "Email successfully changed.");
            break;

        case "GetUserInfo":
            $user = user_load_by_name($message["Data"]["UserName"]);
            if ($user === FALSE)
            {
                return array(
                    "Status" => "Error",
                    "Message" => "User not found.");
            }

            // attempt to log in w/ the provided password
            $result = user_authenticate(
                $user->name, $message["Data"]["UserPassword"]);
            if ($result === FALSE)
            {
                // if unsuccessful, return an error
                return array(
                    "Status" => "Error",
                    "Message" => "Incorrect password.");
            }

            return array(
                "Status" => "OK",
                "UserEmail" => $user->mail );

            break;

        default:
            return array(
                "Status" => "Error",
                "Message" => "Command not understood.",
            );
            break;
    }
}

/**
* Handle REST API calls from CWIS.
*/
function cwis_user_api_call()
{
    global $cwis_user_in_rest_call;
    $cwis_user_in_rest_call = TRUE;

    $helper = cwis_user_get_helper();

    $message = $helper->DecodeEncryptedMessage($_POST);
    if ($message["Status"] != "OK")
    {
        $reply = $message;
    }
    else
    {
        $reply = cwis_user_process_rest_command($message);
    }

    $cwis_user_in_rest_call = FALSE;

    return $helper->EncodeEncryptedMessage(
        $reply);
}
