<?php

// $Id: /cvsroot/tikiwiki/tiki/tiki-login_openid.php,v 1.9 2007-10-12 07:55:29 nyloth Exp $

// Based on tiki-galleries.php
// Copyright (c) 2002-2007, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.

// As a side note beyond the standard heading. Most of the code in this file was taken
// directly from the OpenID library example files. The code was modified to suit the
// specific needs.

// Initialization
require_once ('tiki-setup.php');

/**
 * Require the OpenID consumer code.
 */
require_once "Auth/OpenID/Consumer.php";

/**
 * Require the "file store" module, which we'll need to store
 * OpenID information.
 */
require_once "Auth/OpenID/FileStore.php";

/**
 * Require the Simple Registration extension API.
 */
require_once "Auth/OpenID/SReg.php";

if ($prefs['auth_method'] != 'openid') {
	$smarty->assign('msg', tra("Authentication method is not OpenID"));

	$smarty->display("error.tpl");
	die;
}

function setupFromAddress() // {{{
{
	global $url_scheme, $url_host, $url_port, $base_url;
	// Remember where the page was requested from (from tiki-login.php)
	if ( ! isset($_SESSION['loginfrom']) ) {
		$_SESSION['loginfrom'] = ( isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : $prefs['tikiIndex'] );
		if ( ! ereg('^http', $_SESSION['loginfrom']) ) {
			if ( $_SESSION['loginfrom']{0} == '/' ) $_SESSION['loginfrom'] = $url_scheme.'://'.$url_host.(($url_port!='')?":$url_port":'').$_SESSION['loginfrom'];
			else $_SESSION['loginfrom'] = $base_url.$_SESSION['loginfrom'];
		}
	}

	if( strpos( $_SESSION['loginfrom'], 'openid' ) !== false )
		$_SESSION['loginfrom'] = $base_url;

} // }}}

function getAccountsMatchingIdentifier( $identifier ) // {{{
{
	global $tikilib;

	$result = $tikilib->query( "SELECT login FROM users_users WHERE openid_url = ?", array( $identifier ) );

	$userlist = array();
	while( $row = $result->fetchRow() )
		$userlist[] = $row['login'];

	return $userlist;
} // }}}

function loginUser( $identifier ) // {{{
{
	global $user_cookie_site;
	
	$_SESSION[$user_cookie_site] = $identifier;
	header('location: '.$_SESSION['loginfrom']);
	unset( $_SESSION['loginfrom'] );
	exit;
} // }}}

function filterExistingInformation( &$data, &$messages ) // {{{
{
	global $tikilib;

	$result = $tikilib->query( "SELECT COUNT(*) FROM users_users WHERE login = ?", array( $data['nickname'] ) );

	$count = reset( $result->fetchRow() );

	if( $count > 0 )
	{
		$data['nickname'] = '';
		$messages[] = tra( 'Your default nickname is already in use. A new one has to be selected.' );
	}
} // }}}

	function displayRegisatrationForms( $data, $messages ) // {{{
	{
		global $smarty;

		// Default values for the registration form
		$smarty->assign( 'username', $data['nickname'] );
		$smarty->assign( 'email', $data['email'] );

		// Changing some system values to get the login box to display properly in the context
		$smarty->assign( 'rememberme', 'disabled' );
		$smarty->assign( 'forgotPass', 'n' );
		$smarty->assign( 'allowRegister', 'n' );
		$smarty->assign( 'change_password', 'n' );
		$smarty->assign( 'auth_method', 'tiki' );
		$smarty->assign( 'feature_switch_ssl_mode', 'n' );

		// Display
		$smarty->assign( 'mid', 'tiki-openid_register.tpl' );
		$smarty->display('tiki.tpl');
	} // }}}

	function displaySelectionList( $data, $messages ) // {{{
	{
		global $smarty;

		// Display
		$smarty->assign( 'mid', 'tiki-openid_select.tpl' );
		$smarty->display('tiki.tpl');
	} // }}}

function displayError($message) { // {{{
	global $smarty;

	$smarty->assign('msg', tra("Failure").": " . $message);

	$smarty->display("error.tpl");
	die;
} // }}}

function &getStore() { // {{{
    /**
     * This is where the example will store its OpenID information.
     * You should change this path if you want the example store to be
     * created elsewhere.  After you're done playing with the example
     * script, you'll have to remove this directory manually.
     */
    $store_path = "/tmp/_php_consumer_test";

    if (!file_exists($store_path) &&
        !mkdir($store_path)) {
        print "Could not create the FileStore directory '$store_path'. ".
            " Please check the effective permissions.";
        exit(0);
    }

    return new Auth_OpenID_FileStore($store_path);
} // }}}

function &getConsumer() { // {{{
    /**
     * Create a consumer object using the store object created
     * earlier.
     */
    $store = getStore();
    return new Auth_OpenID_Consumer($store);
} // }}}

function getOpenIDURL() { // {{{
    // Render a default page if we got a submission without an openid
    // value.
    if (empty($_GET['openid_url'])) {
        displayError( 'Call the page properly' );
    }

    return $_GET['openid_url'];
} // }}}

function getScheme() { // {{{
    $scheme = 'http';
    if (isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == 'on') {
        $scheme .= 's';
    }
    return $scheme;
} // }}}

function getReturnTo() { // {{{
    $string = sprintf("%s://%s:%s%s/tiki-login_openid.php?action=return",
                   getScheme(), $_SERVER['SERVER_NAME'],
                   $_SERVER['SERVER_PORT'],
                   dirname($_SERVER['PHP_SELF']));

	if( isset( $_GET['action'] ) && $_GET['action'] == 'force' )
		$string .= '&force=true';

	return $string;
} // }}}

function getTrustRoot() { // {{{
    return sprintf("%s://%s:%s%s",
                   getScheme(), $_SERVER['SERVER_NAME'],
                   $_SERVER['SERVER_PORT'],
                   dirname($_SERVER['PHP_SELF']));
} // }}}

function runAuth() { // {{{
	setupFromAddress();

    $openid = getOpenIDURL();
    $consumer = getConsumer();

    // Begin the OpenID authentication process.
    $auth_request = $consumer->begin($openid);

    // No auth request means we can't begin OpenID.
    if (!$auth_request) {
        displayError("Authentication error; not a valid OpenID.");
    }

    $sreg_request = Auth_OpenID_SRegRequest::build(
		// Required
		array(),
		// Optional
		array('nickname', 'email'));

    if ($sreg_request) {
        $auth_request->addExtension($sreg_request);
    }

    // Redirect the user to the OpenID server for authentication.
    // Store the token for this authentication so we can verify the
    // response.

    // For OpenID 1, send a redirect.  For OpenID 2, use a Javascript
    // form to send a POST request to the server.
    if ($auth_request->shouldSendRedirect()) {
        $redirect_url = $auth_request->redirectURL(getTrustRoot(),
                                                   getReturnTo());

        // If the redirect URL can't be built, display an error
        // message.
        if (Auth_OpenID::isFailure($redirect_url)) {
            displayError("Could not redirect to server: " . $redirect_url->message);
        } else {
            // Send redirect.
            header("Location: ".$redirect_url);
        }
    } else {
		// Generate form markup and render it.
		$form_id = 'openid_message';
		$form_html = $auth_request->htmlMarkup(getTrustRoot(), getReturnTo(),
		false, array('id' => $form_id));

		// Display an error if the form markup couldn't be generated;
		// otherwise, render the HTML.
		if (Auth_OpenID::isFailure($form_html)) {
			displayError("Could not redirect to server: " . $form_html->message);
		} else {
			print $form_html;
		}

    }
} // }}}

function runFinish() { // {{{
	global $smarty;

    $consumer = getConsumer();

    // Complete the authentication process using the server's
    // response.
    $response = $consumer->complete( getReturnTo() );

    // Check the response status.
    if ($response->status == Auth_OpenID_CANCEL) {
        // This means the authentication was cancelled.
        displayError( 'Verification cancelled.' );
    } else if ($response->status == Auth_OpenID_FAILURE) {
        // Authentication failed; display the error message.
        displayError( "OpenID authentication failed: " . $response->message );
    } else if ($response->status == Auth_OpenID_SUCCESS) {
        // This means the authentication succeeded; extract the
        // identity URL and Simple Registration data (if it was
        // returned).

		$data = array(
			'identifier' => $response->identity_url,
			'email' => '',
			'fullname' => '',
			'nickname' => '',
		);

        $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);

        $sreg = $sreg_resp->contents();

		// Sanitize identifier. Just consider slashes at the end are never good.
		if( substr( $data['identifier'], -1 ) == '/' )
			$data['identifier'] = substr( $data['identifier'], 0, -1 );

        if (@$sreg['email'])
            $data['email'] = $sreg['email'];
        if (@$sreg['nickname'])
            $data['nickname'] = $sreg['nickname'];

		$_SESSION['openid_url'] = $data['identifier'];

		// If OpenID identifier exists in the database
		$list = getAccountsMatchingIdentifier( $data['identifier'] );

		$_SESSION['openid_userlist'] = $list;
		$smarty->assign( 'openid_userlist', $list );

		if( count( $list ) > 0 && !isset($_GET['force']) )
		{
			// If Single account
			if( count( $list ) == 1 )
			{
				// Login the user
				loginUser( $list[0] );
			}
			else // Else Multiple account
			{
				// Display user selection list
				displaySelectionList( $list );
			}
		}
		else
		{
			$messages = array();

			// Check for entries that already exist in the database and filter them out
			filterExistingInformation( $data, $messages );

			// Display register and attach forms
			displayRegisatrationForms( $data, $messages );
		}
    }
} // }}}

function runSelect() // {{{
{
	setupFromAddress();

	$user = $_GET['select'];

	if( in_array( $user, $_SESSION['openid_userlist'] ) )
		loginUser( $user );
	else
		displayError( tra('The selected account is not associated with your identity.') );
} // }}}

if( isset( $_GET['action'] ) )
{
	if( $_GET['action'] == 'return' )
		runFinish();
	elseif( $_GET['action'] == 'select' && isset( $_GET['select'] ) )
		runSelect();
	elseif( $_GET['action'] == 'force' )
		runAuth();
	else
		displayError( tra('unknown action') );
}
else
	runAuth();

?>
