<?php
    /**
     * Elgg UNL CAS authentication
     * 
     * @package cas_auth_unl
     * @license BSD http://www1.unl.edu/wdn/wiki/Software_License
     * @author University of Nebraska-Lincoln
     * @copyright 2010 Regents of the University of Nebraska 
     * @link http://www.unl.edu/
     */

    global $CONFIG;

    require_once __DIR__ . '/vendor/autoload.php';
 
    function cas_auth_unl_init() {
        global $CONFIG;
        
        $auth = new UnlCAS();
        $auth->autoLogin();
        $auth->singleLogOut();

        // Set up login page, this creates the url /login to be used as our login page
        elgg_register_page_handler('login', 'cas_auth_unl_login_page_handler');
        elgg_register_page_handler('logout', 'cas_auth_unl_logout_page_handler');
        elgg_register_page_handler('register', 'cas_auth_unl_registration_page_handler');
        
        /* set up getemail page */
        elgg_register_page_handler('getemail', 'cas_auth_unl_getemail_page_handler');

        elgg_register_action('getemail', $CONFIG->pluginspath . 'cas_auth_unl/actions/getemail.php', 'public');
        elgg_register_action('logout', $CONFIG->pluginspath . 'cas_auth_unl/actions/logout.php', 'public');
    }

    // Fire up the plugin initialization using the elgg handler
    elgg_register_event_handler('init','system','cas_auth_unl_init');

    function cas_auth_unl_login_page_handler($page) {
        // If we're not logged in, display the login page
        if (!elgg_is_logged_in()) {
            $auth = new UnlCAS();
            $auth->forceLogin();
        }

        // Otherwise, forward to the index page
        forward();
    }
    
    function cas_auth_unl_logout_page_handler($page) {
        if (elgg_is_logged_in()) {
            // If we're not logged in, display the login page
            $auth = new UnlCAS();
            $auth->forceLogout();
        }
        
        // Otherwise, forward to the index page
        forward();
    }

    function cas_auth_unl_registration_page_handler($page) {
        if (elgg_is_logged_in()) {
            // Otherwise, forward to the index page
            forward();
        }

        if (!isset($_SESSION['cas_auth_unl']['register']['uid'])) {
            //They are not already in the registration process
            forward();
        }

        if (!isset($_POST['email'])) {
            forward();
        }
        
        // If we're not logged in, display the login page
        $auth = new UnlCAS();
        $unl_uid = $_SESSION['cas_auth_unl']['register']['uid'];
        $auth->register($unl_uid, $_POST['email']);
        $auth->login($unl_uid);
        forward();
    }

    function cas_auth_unl_getemail_page_handler($page) {
        if (!elgg_get_logged_in_user_entity()) {
            echo elgg_view_page(elgg_echo('Your Email'), elgg_view("account/forms/getemail")); 
        } else {
            forward();
        }
    }
    
class UnlCAS {
    
    var $client;  
    var $casInitialized = false;
    
    const DIRECTORY_URL = 'http://directory.unl.edu/';
    
    public static $cert_path = '/etc/pki/tls/cert.pem';
    
    function __construct() {
        if (!\phpCAS::isInitialized()) {
            \phpCAS::client(CAS_VERSION_2_0, 'login.unl.edu', 443, 'cas');
            \phpCAS::setCasServerCACert(self::$cert_path);
            
            \phpCAS::setPostAuthenticateCallback(function($logoutTicket) {
                $auth = new UnlCAS();
                $auth->loginOrRegister(\phpCAS::getUser());

                $session = elgg_get_session();

                $pool = $auth->getSessionMapPool();
                $item = $pool->getItem($logoutTicket);
                $item->set(array(
                    'session_id' => $session->getId(),
                    'date_created' => time()
                ));
            });
            
            \phpCAS::setSingleSignoutCallback(function ($logoutTicket) {
                $auth = new UnlCAS();
                $pool = $auth->getSessionMapPool();
                $item = $pool->getItem($logoutTicket);

                if ($item->isMiss()) {
                    return null;
                }
                
                $data = $item->get();

                $handler = new Elgg\Http\DatabaseSessionHandler(_elgg_services()->db);
                if (!$handler->destroy($data['session_id'])) {
                    throw new \Exception('unable to destroy session on single sign out');
                }
                
                //Remove the cached item
                $item->clear();
            });
            
        }
    }
    
    public function getSessionMapPool()
    {
        $driver = new Stash\Driver\FileSystem();

        // Setting a custom path is done by passing an options array to the constructor.
        $options = array('path' => __DIR__ .'/../../tmp/elgg_session_map');
        $driver->setOptions($options);

        $pool = new Stash\Pool($driver);
        
        return $pool;
    }
    
    public function singleLogOut()
    {
        \phpCAS::handleLogoutRequests(false);
    }
    
    public function autoLogin()
    {
        if (!array_key_exists('unl_sso', $_COOKIE)) {
            //No unl_sso cookie was found, no need to auto-login.
            return;
        }
        
        if (elgg_get_logged_in_user_entity()) {
            //We are already logged in, no need to auto-login
            return;
        }
        
        //Everything looks good.  Log in!
        $result = \phpCAS::checkAuthentication();

        if ($result) {
            //Make sure we are still authenticated with CAS
            //setPostAuthenticateCallback() will actually do the work
            \phpCAS::renewAuthentication();
        }
    }
    
    protected function loginOrRegister($unl_uid)
    {
        $elgg_uid = $this->toElggUID($unl_uid);
        $user = get_user_by_username($elgg_uid);
        
        if (!$user) {
            $this->register($unl_uid);
            $this->login($unl_uid);
        } else {
            $this->login($unl_uid);
        }
    }
    
    public function login($unl_uid)
    {
        $user = get_user_by_username($this->toElggUID($unl_uid));

        //Log the user into elgg
        login($user);
    }
    
    public function register($unl_uid, $student_email = false)
    {
        $elgg_uid = $this->toElggUID($unl_uid);
        $directory_info = $this->getDirectoryInfo($unl_uid);
        
        $name = 'unknown';
        $email = false;
        
        if ($student_email) {
            $email = $student_email;
        }
        
        if (isset($directory_info['name'])) {
            $name = $directory_info['name'];
        }
        
        if (isset($directory_info['email'])) {
            //Override the student email with the one from the directory
            $email = $directory_info['email'];
        }
        
        if (false === $email) {
            $_SESSION['cas_auth_unl']['register']['uid'] = $unl_uid;
            forward('getemail');
        }
        
        $password = generate_random_cleartext_password();
        
        try {
            $user_guid = register_user($elgg_uid, $password, $name, $email, false);
        } catch (RegistrationException $e) {
            //Looks like we had an invalid email address... ask for it again.
            register_error(elgg_echo($e->getMessage()));
            forward('getemail');
        }
        
        if (!$user_guid) {
            register_error(elgg_echo("registerbad"));
        }
        
        //Make sure that this gets unset
        unset($_SESSION['cas_auth_unl']);
        
        return true;
    }
    
    protected function getDirectoryInfo($uid)
    {
        $info = array();

        if (!$json = @file_get_contents(self::DIRECTORY_URL . '?uid=' . $uid . '&format=json')) {
            return $info;
        }

        if (!$json = json_decode($json, true)) {
            return $info;
        }

        $map = array(
            'givenName' => 'first_name',
            'sn' => 'last_name',
            'mail' => 'email'
        );

        foreach ($map as $from => $to) {
            if (isset($json[$from][0])) {
                $info[$to] = $json[$from][0];
            }
        }
        
        $info['name'] = $info['first_name'] . ' ' . $info['last_name'];

        return $info;
    }
    
    protected function toElggUID($unl_uid)
    {
        $unl_uid = str_replace('-','_',$unl_uid);
        return 'unl_' . $unl_uid;
    }
    
    public function forceLogin() {
        //Due to the weirdness of phpCAS, it might think we are still logged in (when we are not)
        $_SESSION['phpCAS'] = array();

        \phpCAS::forceAuthentication();
    }
    
    public function forceLogout()
    {
        //log out of elgg
        logout();
        
        //log out of CAS
        \phpCAS::logout();
    }
}