Extension:PHP-Fusion Auth

From MediaWiki.org
Jump to navigation Jump to search
MediaWiki extensions manual
OOjs UI icon advanced.svg
PHP-Fusion Auth Integration
Release status: beta
Implementation User identity , User activity
Description Authenticate users from PHP-Fusion.
Author(s) NeKit, original author: Ryan Wagoner (NeKittalk)
Latest version 1.00 (2009-12-27)
MediaWiki 1.15
License No license specified
Download Download
Translate the PHP-Fusion Auth extension if it is available at translatewiki.net
Check usage and version matrix.

What can this extension do?[edit]

I've adapded Extension:SMF Auth Integration to work with PHP-Fusion v7. Note, if you site and wiki are situated at different subdomains, for example site.ru and wiki at wiki.site.ru, you need to change some files of Fusion.

Download instructions[edit]

Please cut and paste the code found below and place it in $IP/extensions/Auth_PHPFusion.php. Note: $IP stands for the root directory of your MediaWiki installation, the same directory that holds LocalSettings.php.

Installation[edit]

To install this extension, add the following to LocalSettings.php:

$wgPHPFusionPath = "";
# Relative path to the fusion directory from the wiki, without a trailing /
$wgPHPFusion_domain = "";
# Site domain, where PHP-Fusion is installed, without subdomains, like site.ru (not wiki.site.ru).
require_once("$IP/extensions/Auth_PHPFusion.php");

PHP-Fusion Cookie Hack[edit]

maincore.php[edit]

Find

setcookie(COOKIE_PREFIX."user", $cookie_value, $cookie_exp, "/", "", "0");

Replace

setcookie(COOKIE_PREFIX."user", $cookie_value, $cookie_exp, "/", ".site.ru", "0");

Where site.ru is your site's domain.

setuser.php[edit]

Find

setcookie(COOKIE_PREFIX."user", "", time() - 7200, "/", "", "0");

Replace

setcookie(COOKIE_PREFIX."user", "", time() - 7200, "/", ".site.ru", "0");

Where site.ru is your site's domain.

Code[edit]

<?php
/*
PHP-Fusion and MediaWiki Integration
=============================
Original author: Ryan Wagoner (rswagoner at gmail dot com)
Adapted for PHP-Fusion by NeKit (nekit@sonicscanf.ru)
Version: 1.00, based on 1.13
Place this file in your wiki/extenstions folder. If you
encouter an issue be sure to read the known issues below.

Add to LocalSettings.php
========================
# This requires a user be logged into the wiki to make changes.
$wgGroupPermissions['*']['edit'] = false; // MediaWiki Setting

# If you experience the issue where you appear to be logged in
# eventhough you are logged out then disable the page cache.
#$wgEnableParserCache = false;
#$wgCachePages = false;

# PHP-Fusion Authentication
# To get started you only need to configure wgPHPFusionPath and wgPHPFusionVersion. 
# The rest of the settings are optional for advanced features.

# Relative path to the forum directory from the wiki
# Do not put a trailing /
# Example: /public_html/forum and /public_html/wiki -> ../forum
$wgPHPFusionPath = "../forum"; 

$wgPHPFusion_domain = "site.ru";
# Site domain, where PHP-Fusion is installed, without subdomains, like site.ru (not wiki.site.ru).

# Load up the extension
require_once "$IP/extensions/Auth_PHPFusion.php";
$wgAuth = new Auth_PHPFusion();

Known Issues
============
- The wiki converts underscores in usernames to spaces. For example john_doe becomes 
  John doe. To work around this we check for both cases and use the first registered
  PHPFusion user. You will need to change the usernames of the later registered user.

- When wgPHPFusionLogin is disabled if you register a new account from the wiki you will not
  be redirected back to the wiki. PHP-Fusion does not have support for this in their code.

 */
	error_reporting(E_ALL); // Debug
	if(file_exists("$IP/$wgPHPFusionPath/config.php"))
		require_once("$IP/$wgPHPFusionPath/config.php");
	else
		die('Check to make sure $wgPHPFusionPath is correctly set in LocalSettings.php!');

	$smf_settings['cookiename'] = 'fusion_user';

	$smf_settings['db_server'] = $db_host;
	$smf_settings['db_name'] = $db_name;
	$smf_settings['db_user'] = $db_user;
	$smf_settings['db_passwd'] = $db_pass;
	$smf_settings['db_prefix'] = $db_prefix;
	// Setup the database fields depending on PHPFusion version.
		$smf_map['id_member'] = 'user_id';
		$smf_map['member_name'] = 'user_name';
		$smf_map['date_registered'] = 'user_joined';
		$smf_map['real_name'] = 'user_name';
		$smf_map['passwd'] = 'user_password';
		$smf_map['email_address'] = 'user_email';
		$smf_map['is_activated'] = 'user_status';
		$smf_map['id_group'] = 'user_level';

	/**
	 * Check the PHPFusion cookie and automatically log the user into the wiki.
	 *
	 * @param User $user
	 * @return bool
	 * @public
	 */
	function isnum($value) {
	if (!is_array($value)) {
		return (preg_match("/^[0-9]+$/", $value));
	} else {
		return false;
	}
}
function preg_check($expression, $value) {
	if (!is_array($value)) {
		return preg_match($expression, $value);
	} else {
		return false;
	}
}
	function AutoAuthenticatePHPFusion(&$user) {
		global $wgAuth, $smf_settings, $smf_map;
		$ID_MEMBER = 0;


		if (isset($_COOKIE['fusion_user'])) {
		$cookie_vars = explode(".", $_COOKIE['fusion_user']);
		$cookie_1 = isnum($cookie_vars['0']) ? $cookie_vars['0'] : "0";
		$cookie_2 = (preg_check("/^[0-9a-z]{32}$/", $cookie_vars['1']) ? $cookie_vars['1'] : "");
		$ID_MEMBER = $cookie_1;
		$password = md5($cookie_2);
		unset($cookie_vars,$cookie_1,$cookie_2);
		}

		// Only load this stuff if the user isn't a guest.
		if ($ID_MEMBER != 0)
		{
			$request = $wgAuth->query("		
				SELECT $smf_map[id_member], $smf_map[member_name], $smf_map[email_address], $smf_map[real_name],
					$smf_map[is_activated], $smf_map[passwd]
				FROM $smf_settings[db_prefix]users
				WHERE $smf_map[id_member] = '{$ID_MEMBER}'
					AND $smf_map[is_activated] = 0
				LIMIT 1");

			$user_settings = mysql_fetch_assoc($request);

			// Did we find 'im?  If not, junk it.
			if (mysql_num_rows($request) != 0)
			{
				// SHA-1 passwords should be 40 characters long.
				$check = $user_settings[$smf_map['passwd']] == $password;

					// Wrong password or not activated - either way, you're going nowhere.
				$ID_MEMBER = $check && ($user_settings[$smf_map['is_activated']] == 0 || $user_settings[$smf_map['is_activated']] == 11) ? $user_settings[$smf_map['id_member']] : 0;
				}
			else
				$ID_MEMBER = 0;

			mysql_free_result($request);
		}

		// Log out guests or members with invalid cookie passwords.
		if($ID_MEMBER == 0)
		{
			if($user->isLoggedIn())
				$user->logout();
			return true;
		}

		// If the username has an underscore or space accept the first registered user.
		if(strpos($user_settings[$smf_map['member_name']], ' ') !== false || strpos($user_settings[$smf_map['member_name']], '_') !== false)
		{
			// Format to wiki standards (underscores are converted to spaces).
			$case1 = str_replace('_', ' ', $user_settings[$smf_map['member_name']]);
			// Format the alternative case (spaces converted to underscores).
			$case2 = str_replace(' ', '_', $case1);

			$request = $wgAuth->query("
				SELECT $smf_map[id_member] 
				FROM $smf_settings[db_prefix]users
				WHERE $smf_map[member_name] = '{$case1}' 
					OR $smf_map[member_name] = '{$case2}'
				ORDER BY $smf_map[date_registered] ASC
				LIMIT 1");

			list($id) = mysql_fetch_row($request);
			mysql_free_result($request);

			// Sorry your name was taken already!
			if($id != $ID_MEMBER)
			{
				if($user->isLoggedIn())
					$user->logout();
				return true;
			}
		}

		// Lastly check to see if they are not banned and allowed to login
		if (!($wgAuth->isNotBanned($ID_MEMBER) && $wgAuth->isGroupAllowed($user_settings[$smf_map['member_name']])))
		{
			if($user->isLoggedIn())
				$user->logout();
			return true;
		} 

		// Convert to wiki standards
		$username = ucfirst(str_replace('_', ' ', $user_settings[$smf_map['member_name']]));

		// Only poll the database if no session or username mismatch.
		if(!($user->isLoggedIn() && $user->getName() == $username))
		{
	       	$user->setName($username);
	       	$user->setId($user->idForName());

			// No ID we need to add this member to the wiki database.
			if ($user->getID() == 0)
			{
				// getID clears out the name set above.
				$user->setName($username);

				// Let wiki know that their email has been verified.
				$user->mEmailAuthenticated = wfTimestampNow(); 
				$user->addToDatabase();
			} 
			// Otherwise load their details.
			else
				$user->loadFromDatabase();
		}

		// Keep their email and real name up to date with PHPFusion
		$user->setEmail($user_settings[$smf_map['email_address']]);
		
		$wgAuth->setAdminGroup($user, $user_settings[$smf_map['member_name']]);

		$user->saveSettings();

		// Go ahead and log 'em in
		$user->setupSession();
		$user->setCookies();

		return true;
	}

	/**
	 * Redirect them to the PHPFusion login page.
	 *
	 * @param User $user
	 * @public
	 */
	function UserLoginFormPHPFusion(&$user) {
		smf_sessionSetup();	
		smf_redirectWrapper('old_url', 'login');
	}

	/**
	 * Redirect and utilize the PHPFusion logout function.
	 *
	 * @param User $user
	 * @public
	 */
	/**
	 * Redirect and utilize the PHPFusion register function.
	 *
	 * @public
	 */
	function UserRegisterPHPFusion(&$template) {
		global $wgPHPFusion_domain;
		header ('Location: http://'.$wgPHPFusion_domain.'/register.php');
		exit();
	}

	/**
	 * Wrapper to configure the PHPFusion session and perform the redirect.
	 *
	 * @public
	 */
	/**
	 * If the user has visited the forum during the browser session
	 * then load up the exisiting session. Otherwise start a new
	 * session that PHPFusion can use.
	 *
	 * @public
	 */
	// First check if class has already been defined.
	if (!class_exists('AuthPlugin'))
		require_once "$IP/includes/AuthPlugin.php";

class Auth_PHPFusion extends AuthPlugin {
	var $conn = 0;

	/**
	 * Class constructor that will initialize the hooks and database connection.
	 */
	function Auth_PHPFusion()
	{
		global $wgPHPFusionLogin, $wgHooks;

		// Integrate with PHPFusion login / logout?
		if(isset($wgPHPFusionLogin) && $wgPHPFusionLogin)
		{
			$wgHooks['UserLoginForm'][] = 'UserLoginFormPHPFusion';
			$wgHooks['UserLogout'][] = 'UserLogoutPHPFusion';
		}	
		$wgHooks['UserLoadFromSession'][] = 'AutoAuthenticatePHPFusion';
		// Always redirect registration to PHPFusion.
		$wgHooks['UserCreateForm'][] = 'UserRegisterPHPFusion';

		// Connect to the database.
		$this->connect();
	}

	/**
	 * Check whether there exists a user account with the given name.
	 * The name will be normalized to MediaWiki's requirements, so
	 * you might need to munge it (for instance, for lowercase initial
	 * letters).
	 *
	 * @param $username String: username.
	 * @return bool
	 * @public
	 */
	function userExists( $username ) {
		global $smf_settings, $smf_map;

		$username = $this->fixUsername($username);

		$request = $this->query("
			SELECT $smf_map[member_name]
			FROM $smf_settings[db_prefix]users
			WHERE $smf_map[member_name] = '{$username}'
			LIMIT 1");

		list ($user) = mysql_fetch_row($request);
		mysql_free_result($request);

		// Play it safe and double check the match.
		return strtolower($user) == strtolower($username);
	}

	/**
	 * Check if a username+password pair is a valid login.
	 * The name will be normalized to MediaWiki's requirements, so
	 * you might need to munge it (for instance, for lowercase initial
	 * letters).
	 *
	 * @param $username String: username.
	 * @param $password String: user password.
	 * @return bool
	 * @public
	 */
	function authenticate( $username, $password ) {
		global $smf_settings, $smf_map, $wgPHPFusion_domain;
	
		$username = $this->fixUsername($username);

		$request = $this->query("
			SELECT $smf_map[id_member], $smf_map[passwd]
			FROM $smf_settings[db_prefix]users
			WHERE $smf_map[member_name] = '{$username}'
				AND $smf_map[is_activated] = 0
			LIMIT 1");

		list($id_member, $passwd) = mysql_fetch_row($request);
		mysql_free_result($request);

		$pw = md5(md5($password));
		$cpw = md5($password);
		// Check for password match, the user is not banned, and the user is allowed.
		if($pw == $passwd && $this->isNotBanned($id_member) && $this->isGroupAllowed($username)) {
		$cookie_value = $id_member.".".$cpw;
		$cookie_exp = isset($_POST['wpRemember']) ? time() + 3600 * 24 * 30 : time() + 3600 * 3;
		setcookie("fusion_user", $cookie_value, $cookie_exp, "/", ".".$wgPHPFusion_domain, "0");
		return $wgPHPFusion_domain;
		}
		return false;
	}

	/**
	 * Modify options in the login template.
	 *
	 * @param $template UserLoginTemplate object.
	 * @public
	 */
	function modifyUITemplate( &$template ) {
		$template->set('usedomain',   false); // We do not want a domain name.
		$template->set('create',      false); // Remove option to create new accounts from the wiki.
		$template->set('useemail',    false); // Disable the mail new password box.
	}

	/**
	 * Set the domain this plugin is supposed to use when authenticating.
	 *
	 * @param $domain String: authentication domain.
	 * @public
	 */
	function setDomain( $domain ) {
		$this->domain = $domain;
	}

	/**
	 * Check to see if the specific domain is a valid domain.
	 *
	 * @param $domain String: authentication domain.
	 * @return bool
	 * @public
	 */
	function validDomain( $domain ) {
		return true;
	}

	/**
	 * When a user logs in, optionally fill in preferences and such.
	 * For instance, you might pull the email address or real name from the
	 * external user database.
	 *
	 * The User object is passed by reference so it can be modified; don't
	 * forget the & on your function declaration.
	 *
	 * @param User $user
	 * @public
	 */
	function updateUser( &$user ) {
		global $smf_settings, $smf_map;
		
		$username = $this->fixUsername($user->getName());

		$request = $this->query("
			SELECT $smf_map[email_address], $smf_map[real_name]
			FROM $smf_settings[db_prefix]users
			WHERE $smf_map[member_name] = '{$username}'
			LIMIT 1");

		while($row = mysql_fetch_assoc($request))
		{
			$user->setEmail($row[$smf_map['email_address']]);

			$this->setAdminGroup($user, $username);
			
			$user->saveSettings();
		}

		mysql_free_result($request);
	
		return true;
	}


	/**
	 * Return true if the wiki should create a new local account automatically
	 * when asked to login a user who doesn't exist locally but does in the
	 * external auth database.
	 *
	 * If you don't automatically create accounts, you must still create
	 * accounts in some way. It's not possible to authenticate without
	 * a local account.
	 *
	 * This is just a question, and shouldn't perform any actions.
	 *
	 * @return bool
	 * @public
	 */
	function autoCreate() {
		return true;
	}

	/**
	 * Can users change their passwords?
	 *
	 * @return bool
	 */
	function allowPasswordChange() {
		global $wgPHPFusionLogin;

		// Only allow password change if not using auto login.
		// Otherwise we would need a bunch of code to rewrite
		// the PHPFusion login cookie with the new password.
		if(isset($wgPHPFusionLogin) && $wgPHPFusionLogin)
			return false;

		return true;
	}

	/**
	 * Set the given password in the authentication database.
	 * As a special case, the password may be set to null to request
	 * locking the password to an unusable value, with the expectation
	 * that it will be set later through a mail reset or other method.
	 *
	 * Return true if successful.
	 *
	 * @param $user User object.
	 * @param $password String: password.
	 * @return bool
	 * @public
	 */
	function setPassword( $user, $password ) {
		global $smf_settings, $smf_map;

		$username = $this->fixUsername($user->getName());
		
		$pw = md5(md5($password));

		// Verify that the password is up to par.
		if($this->validatePassword($password, $username, array($user->getRealName(), $user->getEmail())) != null)
			return false;

		// Commit it to the database.
		$this->query("
			UPDATE $smf_settings[db_prefix]users
			SET user_password = '{$pw}'
			WHERE $smf_map[member_name] = '{$username}'
			LIMIT 1");

		return true;
	}

	/**
	 * Update user information in the external authentication database.
	 * Return true if successful.
	 *
	 * @param $user User object.
	 * @return bool
	 * @public
	 */
	function updateExternalDB( $user ) {
		return true;
	}

	/**
	 * Check to see if external accounts can be created.
	 * Return true if external accounts can be created.
	 * @return bool
	 * @public
	 */
	function canCreateAccounts() {
		return false;
	}

	/**
	 * Add a user to the external authentication database.
	 * Return true if successful.
	 *
	 * @param User $user - only the name should be assumed valid at this point
	 * @param string $password
	 * @param string $email
	 * @param string $realname
	 * @return bool
	 * @public
	 */
	function addUser( $user, $password, $email='', $realname='' ) {
		return true;
	}


	/**
	 * Return true to prevent logins that don't authenticate here from being
	 * checked against the local database's password fields.
	 *
	 * This is just a question, and shouldn't perform any actions.
	 *
	 * @return bool
	 * @public
	 */
	function strict() {
		return true;
	}

	/**
	 * When creating a user account, optionally fill in preferences and such.
	 * For instance, you might pull the email address or real name from the
	 * external user database.
	 *
	 * The User object is passed by reference so it can be modified; don't
	 * forget the & on your function declaration.
	 *
	 * @param $user User object.
	 * @param $autocreate bool True if user is being autocreated on login
	 * @public
	 */
	function initUser( $user, $autocreate=false ) {
		global $smf_settings, $smf_map;

		$username = $this->fixUsername($user->getName());

		$request = $this->query("
			SELECT $smf_map[id_member], $smf_map[email_address], $smf_map[real_name]
			FROM $smf_settings[db_prefix]users
			WHERE $smf_map[member_name] = '{$username}'
			LIMIT 1");

		while($row = mysql_fetch_assoc($request))
		{
			$user->setRealName($row[$smf_map['real_name']]);
			$user->setEmail($row[$smf_map['email_address']]);

			// Let wiki know that their email has been verified.
			$user->mEmailAuthenticated = wfTimestampNow(); 

			$this->setAdminGroup($user, $username);

			$user->saveSettings();
		}
			
		mysql_free_result($request);

		return true;
	}

	/**
	 * If you want to munge the case of an account name before the final
	 * check, now is your chance.
	 *
	 * @public
	 */
	function getCanonicalName( $username ) {
		/**
		 * wiki converts username (john_doe -> John doe)
		 * then getCanonicalName is called
		 * user not in wiki database call userExists
		 * lastly call authenticate
		 */
		return $username;
	}

	/**
	 * The wiki converts underscores to spaces. Attempt to work around this
	 * by checking for both cases. Hopefully we'll only get one match.
	 * Otherwise the first registered PHPFusion account takes priority.
	 *
	 * @public
	 */
	function fixUsername ( $username ) {
		global $smf_settings, $smf_map;

		// No space no problem.
		if(strpos($username, ' ') === false)
			return $username;

		// Look for either case sorted by date.
		$request = $this->query("
			SELECT $smf_map[member_name] 
			FROM $smf_settings[db_prefix]users
			WHERE $smf_map[member_name] = '{$username}' 
				OR $smf_map[member_name] = '" . str_replace(' ', '_', $username) . "'
			ORDER BY $smf_map[date_registered] ASC
			LIMIT 1");

		list($user) = mysql_fetch_row($request);
		mysql_free_result($request);

		// No result play it safe and return the original.
		return !isset($user) ? $username : $user;
	}

	/**
	 * Check to see if the user is banned partially
	 * restricting their ability to post or login.
	 *
	 * @public
	 */
	function isNotBanned ( $id_member )	{
		return true;
	}

	/**
	 * Check to see if the user should have sysop rights.
	 * Either they are an administrator or are in one
	 * of the define groups.
	 *
	 * To save database queries the fixed username is used.
	 *
	 * @public
	 */
	function setAdminGroup ( &$user, $username ) {
		global $wgPHPFusionAdminGroupName;

		// Administrator always get admin rights.
		if($this->isGroupMember($username, 103))
		{
			$user->addGroup("sysop");
			return;
		}

		// Search through all groups, if match give them admin rights.
		if(isset($wgPHPFusionAdminGroupName) && !empty($wgPHPFusionAdminGroupName))
		{
			foreach($this->lookupGroupID($wgPHPFusionAdminGroupName) as $id => $name)
				if($this->isGroupMember($username, $id))
				{
					$user->addGroup("sysop");
					return;
				}
		}

		// No go! Make sure they are not a sysop.
		$user->removeGroup("sysop");
		return;
	}


	/**
	 * Check to see if the user is allowed to log in.
	 * Either they are an administrator or are in one
	 * of the define groups.
	 *
	 * @public
	 */
	function isGroupAllowed ( $username ) {
			return true;
	}

	/**
	 * Lookup the IDs for the group names. 
	 * Return array in the format of id => name.
	 *
	 * @public
	 */

	/**
	 * Lookup the user and the groups they are assigned to.
	 * If there is a match return true. Otherwise return false.
	 *
	 * @public
	 */
	function isGroupMember ( $username, $groupID ) {
		global $smf_settings, $smf_map, $smf_cache;

		// Determine all groups the user is a member of.
		if(!isset($smf_cache['groups']))
		{
			$request = $this->query("
				SELECT $smf_map[id_group]
				FROM $smf_settings[db_prefix]users
				WHERE $smf_map[member_name] = '{$username}'
				LIMIT 1");

			$groups = array();
			while($row = mysql_fetch_assoc($request))
			{
				$groups[] = $row[$smf_map['id_group']];
			}
			mysql_free_result($request);

			// Cache results to save redundant queries.
			$smf_cache['groups'] = $groups;
		}

		// Is there a match?
		return in_array($groupID, $smf_cache['groups']);
	}

	/**
	 * Check the password to make sure it is to PHPFusion standards.
	 * Modified from PHPFusion Sources\Subs-Auth.php
	 *
	 * @return null if the password is accepted.
	 * @return string with failure reason.
	 * @public
	 */
	function validatePassword($password, $username, $restrict_in = array())
	{
		global $smf_settings;

		// Perform basic requirements first.
		if (strlen($password) < (empty($modSettings['password_strength']) ? 4 : 8))
			return 'short';

		// Is this enough?
		if (empty($modSettings['password_strength']))
			return null;

		// Otherwise, perform the medium strength test - checking if password appears in the restricted string.
		if (preg_match('~\b' . preg_quote($password, '~') . '\b~', implode(' ', $restrict_in)) != 0)
			return 'restricted_words';
		elseif (strpos($password, $username) !== false)
			return 'restricted_words';

		// !!! If pspell is available, use it on the word, and return restricted_words if it doesn't give "bad spelling"?

		// If just medium, we're done.
		if ($modSettings['password_strength'] == 1)
			return null;

		// Otherwise, hard test next, check for numbers and letters, uppercase too.
		$good = preg_match('~(\D\d|\d\D)~', $password) != 0;
		$good &= strtolower($password) != $password;

		return $good ? null : 'chars';
	}

	/**
	 * Connect to the database. Use the settings from smf.
	 *
	 * {@source}
	 * @return resource
	 */
	function connect()
	{
		global $smf_settings;

		// Connect to database.
		$this->conn = @mysql_connect($smf_settings['db_server'], $smf_settings['db_user'],
	 		$smf_settings['db_passwd'], true);

		// Check if we are connected to the database.
		if (!$this->conn)
		{
			$this->mysqlerror("There was a problem when connecting to the PHPFusion database.<br />\n" .
				'Check your host, username, and password settings.');
		}

		// Select database: this assumes the wiki and smf are in the same database.
		$db_selected = @mysql_select_db($smf_settings['db_name'], $this->conn);

		// Check if we were able to select the database.
		if (!$db_selected)
		{
			$this->mysqlerror("There was a problem when connecting to the PHPFusion database.<br />\n" .
				'The database ' . $smf_settings['db_name'] . ' was not found.');
		}
	}

	/**
	 * Run the query and if applicable display the mysql error.
	 *
	 * @param string $query
	 * @return resource
	 */
	function query($query)
	{
		$request = mysql_query($query, $this->conn);

		if(!$request)
			$this->mysqlerror('Unable to view external table.');

		return $request;
	}

	/**
	 * Display an error when a mysql error is found.
	 *
	 * @param string $message
	 * @access public
	 */
	function mysqlerror($message)
	{
	   echo $message . "<br /><br />\n\n";
	   echo 'mySQL error number: ' . mysql_errno() . "<br />\n";
	   echo 'mySQL error message: ' . mysql_error() . "<br /><br />\n\n";
	   exit;
	}
}