Arnaud Ligot 14 rokov pred
rodič
commit
7b46ca86c8

+ 105 - 0
main/auth/cas/authcas.php

@@ -0,0 +1,105 @@
+<?php
+/*Written by Noel Dieschburg <noel@cblue.be> for the paris5 university
+
+* Checks if the user is already logged in via the cas system
+* Gets all the info via the ldap module (ldap has to work)
+
+*/
+require_once(api_get_path(SYS_PATH).'main/auth/cas/cas_var.inc.php');
+require_once(api_get_path(SYS_PATH).'main/auth/ldap/authldap.php');
+
+/**
+* checks if the user already get a session
+* @return the user login if the user already has a session ,false otherwise
+**/
+
+
+function cas_is_authenticated()
+{
+	global $cas_auth_ver, $cas_auth_server, $cas_auth_port, $cas_auth_uri; 
+	global $PHPCAS_CLIENT;
+	global $logout;
+
+
+	if (!is_object($PHPCAS_CLIENT) ) 
+	{
+		phpCAS::client($cas_auth_ver,$cas_auth_server,$cas_auth_port,$cas_auth_uri);
+//		die("phpCAS::client($cas_auth_ver,$cas_auth_server,$cas_auth_port,$cas_auth_uri);");
+		phpCAS::setNoCasServerValidation();
+	}
+	$auth = phpCAS::checkAuthentication(); 
+  
+	if ($auth) {
+		$login= trim(phpCAS::getUser());
+		/*
+		   Get user  attributes. Here are the attributes for crdp platform
+		   sn => name
+		   ENTPersonMailInterne => mail
+		   ENTPersonAlias => login
+		   ENTPersonProfils => profil
+		   givenName => first name
+		 */
+		/*$user=phpCAS::getAttributes();
+		$firstName = trim($user['givenName']);
+		$lastName = trim($user['sn']);
+		$login = trim($user['ENTPersonAlias']);
+		$profil = trim($user['ENTPersonProfils']);
+		$email = trim($user['ENTPersonMailInterne']);
+		$satus=5;
+		switch ($profil){
+			case 'admin_etab':
+				$status=3; //Session admin
+				break;
+			case 'admin_sie':
+				$status=3; //Session admin
+				break;
+			case 'National_3':
+				$status=1; // Teacher
+				break;
+			case 'National_1':
+				$status=5; // Student
+				break;
+			default:
+				$status=5; // Student
+		}*/
+		//If the user is in the dokeos database and we are ,not in a logout request, we upgrade his infomration by ldap
+		if (! $logout){
+			$user_table = Database::get_main_table(TABLE_MAIN_USER);
+			$sql = "SELECT user_id, username, password, auth_source, active, expiration_date ".
+				"FROM $user_table ".
+				"WHERE username = '$login' ";
+
+			$result = api_sql_query($sql,__FILE__,__LINE__);
+			if(mysql_num_rows($result) == 0) { 
+				require_once(api_get_path(SYS_PATH).'main/inc/lib/usermanager.lib.php');
+				$rnumber=rand(0,256000);
+				UserManager::create_user($firstName, $lastName, $status, $email, $login, md5('casplaceholder'.$rnumber), $official_code='',$language='',$phone='',$picture_uri='',$auth_source = PLATFORM_AUTH_SOURCE);
+			}
+			else {
+				$user = mysql_fetch_assoc($result);
+				$user_id = intval($user['user_id']);
+				//echo "deb : $status";
+				UserManager::update_user ($user_id, $firstname, $lastname, $login, null, null, $email, $status, '', '', '', '', 1, null, 0, null,'') ;
+
+			}
+		}
+		return($login);
+	}
+	else 
+	{ 
+		return(false);
+	}
+}
+
+/**
+* Logs out the user of the cas 
+* The user MUST be logged in with cas to use this function 
+**/ 
+function cas_logout()
+{
+	
+	//phpCAS::logoutWithRedirectService("fmc.univ-paris5.fr");		
+	phpCAS::logoutWithRedirectService(api_get_path(WEB_PATH));		
+}
+
+?>

+ 20 - 0
main/auth/cas/cas_var.inc.php

@@ -0,0 +1,20 @@
+<?
+/* This file contains all the configuration variable for the cas module
+ * In the future, these will be in the database
+*/
+require_once('lib/CAS.php');
+define("CAS_VERSION_2_0",'2.0');
+define("SAML_VERSION_1_1", 'S1'); 
+
+global $cas_auth_ver, $cas_auth_server, $cas_auth_port, $cas_auth_uri; 
+
+$cas_auth_server = api_get_setting('cas_server');
+$cas_auth_uri = api_get_setting('cas_server_uri');
+$cas_auth_port = intval(api_get_setting('cas_port'));
+
+$cas_auth_uri = api_get_setting('cas_server_uri');
+if ( ! is_string($cas_auth_uri)) $cas_auth_uri = ''; 
+	
+$cas_auth_ver = '2.0';
+//$cas_auth_ver = SAML_VERSION_1_1;
+?>

+ 1610 - 0
main/auth/cas/lib/CAS.php

@@ -0,0 +1,1610 @@
+<?php
+
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//
+// hack by Vangelis Haniotakis to handle the absence of $_SERVER['REQUEST_URI'] in IIS
+//
+if (!$_SERVER['REQUEST_URI']) {
+	$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
+}
+
+//
+// another one by Vangelis Haniotakis also to make phpCAS work with PHP5
+//
+if (version_compare(PHP_VERSION, '5', '>=')) {
+	require_once (dirname(__FILE__) . '/CAS/domxml-php4-to-php5.php');
+}
+
+/**
+ * @file CAS/CAS.php
+ * Interface class of the phpCAS library
+ *
+ * @ingroup public
+ */
+
+// ########################################################################
+//  CONSTANTS
+// ########################################################################
+
+// ------------------------------------------------------------------------
+//  CAS VERSIONS
+// ------------------------------------------------------------------------
+
+/**
+ * phpCAS version. accessible for the user by phpCAS::getVersion().
+ */
+define('PHPCAS_VERSION', '1.1.1');
+
+// ------------------------------------------------------------------------
+//  CAS VERSIONS
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup public
+ * @{
+ */
+
+/**
+ * CAS version 1.0
+ */
+define("CAS_VERSION_1_0", '1.0');
+/*!
+ * CAS version 2.0
+ */
+define("CAS_VERSION_2_0", '2.0');
+
+// ------------------------------------------------------------------------
+//  SAML defines
+// ------------------------------------------------------------------------
+
+/**
+ * SAML protocol
+ */
+define("SAML_VERSION_1_1", 'S1');
+
+/**
+ * XML header for SAML POST
+ */
+define("SAML_XML_HEADER", '<?xml version="1.0" encoding="UTF-8"?>');
+
+/**
+ * SOAP envelope for SAML POST
+ */
+define("SAML_SOAP_ENV", '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/>');
+
+/**
+ * SOAP body for SAML POST
+ */
+define("SAML_SOAP_BODY", '<SOAP-ENV:Body>');
+
+/**
+ * SAMLP request
+ */
+define("SAMLP_REQUEST", '<samlp:Request xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"  MajorVersion="1" MinorVersion="1" RequestID="_192.168.16.51.1024506224022" IssueInstant="2002-06-19T17:03:44.022Z">');
+define("SAMLP_REQUEST_CLOSE", '</samlp:Request>');
+
+/**
+ * SAMLP artifact tag (for the ticket)
+ */
+define("SAML_ASSERTION_ARTIFACT", '<samlp:AssertionArtifact>');
+
+/**
+ * SAMLP close
+ */
+define("SAML_ASSERTION_ARTIFACT_CLOSE", '</samlp:AssertionArtifact>');
+
+/**
+ * SOAP body close
+ */
+define("SAML_SOAP_BODY_CLOSE", '</SOAP-ENV:Body>');
+
+/**
+ * SOAP envelope close
+ */
+define("SAML_SOAP_ENV_CLOSE", '</SOAP-ENV:Envelope>');
+
+/**
+ * SAML Attributes
+ */
+define("SAML_ATTRIBUTES", 'SAMLATTRIBS');
+
+/** @} */
+/**
+ * @addtogroup publicPGTStorage
+ * @{
+ */
+// ------------------------------------------------------------------------
+//  FILE PGT STORAGE
+// ------------------------------------------------------------------------
+/**
+ * Default path used when storing PGT's to file
+ */
+define("CAS_PGT_STORAGE_FILE_DEFAULT_PATH", '/tmp');
+/**
+ * phpCAS::setPGTStorageFile()'s 2nd parameter to write plain text files
+ */
+define("CAS_PGT_STORAGE_FILE_FORMAT_PLAIN", 'plain');
+/**
+ * phpCAS::setPGTStorageFile()'s 2nd parameter to write xml files
+ */
+define("CAS_PGT_STORAGE_FILE_FORMAT_XML", 'xml');
+/**
+ * Default format used when storing PGT's to file
+ */
+define("CAS_PGT_STORAGE_FILE_DEFAULT_FORMAT", CAS_PGT_STORAGE_FILE_FORMAT_PLAIN);
+// ------------------------------------------------------------------------
+//  DATABASE PGT STORAGE
+// ------------------------------------------------------------------------
+/**
+ * default database type when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_DATABASE_TYPE", 'mysql');
+/**
+ * default host when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_HOSTNAME", 'localhost');
+/**
+ * default port when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_PORT", '');
+/**
+ * default database when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_DATABASE", 'phpCAS');
+/**
+ * default table when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_TABLE", 'pgt');
+
+/** @} */
+// ------------------------------------------------------------------------
+// SERVICE ACCESS ERRORS
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup publicServices
+ * @{
+ */
+
+/**
+ * phpCAS::service() error code on success
+ */
+define("PHPCAS_SERVICE_OK", 0);
+/**
+ * phpCAS::service() error code when the PT could not retrieve because
+ * the CAS server did not respond.
+ */
+define("PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE", 1);
+/**
+ * phpCAS::service() error code when the PT could not retrieve because
+ * the response of the CAS server was ill-formed.
+ */
+define("PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE", 2);
+/**
+ * phpCAS::service() error code when the PT could not retrieve because
+ * the CAS server did not want to.
+ */
+define("PHPCAS_SERVICE_PT_FAILURE", 3);
+/**
+ * phpCAS::service() error code when the service was not available.
+ */
+define("PHPCAS_SERVICE_NOT AVAILABLE", 4);
+
+/** @} */
+// ------------------------------------------------------------------------
+//  LANGUAGES
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup publicLang
+ * @{
+ */
+
+define("PHPCAS_LANG_ENGLISH", 'english');
+define("PHPCAS_LANG_FRENCH", 'french');
+define("PHPCAS_LANG_GREEK", 'greek');
+define("PHPCAS_LANG_GERMAN", 'german');
+define("PHPCAS_LANG_JAPANESE", 'japanese');
+define("PHPCAS_LANG_SPANISH", 'spanish');
+define("PHPCAS_LANG_CATALAN", 'catalan');
+
+/** @} */
+
+/**
+ * @addtogroup internalLang
+ * @{
+ */
+
+/**
+ * phpCAS default language (when phpCAS::setLang() is not used)
+ */
+define("PHPCAS_LANG_DEFAULT", PHPCAS_LANG_ENGLISH);
+
+/** @} */
+// ------------------------------------------------------------------------
+//  DEBUG
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup publicDebug
+ * @{
+ */
+
+/**
+ * The default directory for the debug file under Unix.
+ */
+define('DEFAULT_DEBUG_DIR', '/tmp/');
+
+/** @} */
+// ------------------------------------------------------------------------
+//  MISC
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup internalMisc
+ * @{
+ */
+
+/**
+ * This global variable is used by the interface class phpCAS.
+ *
+ * @hideinitializer
+ */
+$GLOBALS['PHPCAS_CLIENT'] = null;
+
+/**
+ * This global variable is used to store where the initializer is called from 
+ * (to print a comprehensive error in case of multiple calls).
+ *
+ * @hideinitializer
+ */
+$GLOBALS['PHPCAS_INIT_CALL'] = array (
+	'done' => FALSE,
+	'file' => '?',
+	'line' => -1,
+	'method' => '?'
+);
+
+/**
+ * This global variable is used to store where the method checking
+ * the authentication is called from (to print comprehensive errors)
+ *
+ * @hideinitializer
+ */
+$GLOBALS['PHPCAS_AUTH_CHECK_CALL'] = array (
+	'done' => FALSE,
+	'file' => '?',
+	'line' => -1,
+	'method' => '?',
+	'result' => FALSE
+);
+
+/**
+ * This global variable is used to store phpCAS debug mode.
+ *
+ * @hideinitializer
+ */
+$GLOBALS['PHPCAS_DEBUG'] = array (
+	'filename' => '/tmp/cas.log',
+	'indent' => 0,
+	'unique_id' => ''
+);
+
+/** @} */
+
+// ########################################################################
+//  CLIENT CLASS
+// ########################################################################
+
+// include client class
+include_once (dirname(__FILE__) . '/CAS/client.php');
+
+// ########################################################################
+//  INTERFACE CLASS
+// ########################################################################
+
+/**
+ * @class phpCAS
+ * The phpCAS class is a simple container for the phpCAS library. It provides CAS
+ * authentication for web applications written in PHP.
+ *
+ * @ingroup public
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ *
+ * \internal All its methods access the same object ($PHPCAS_CLIENT, declared 
+ * at the end of CAS/client.php).
+ */
+
+class phpCAS {
+
+	// ########################################################################
+	//  INITIALIZATION
+	// ########################################################################
+
+	/**
+	 * @addtogroup publicInit
+	 * @{
+	 */
+
+	/**
+	 * phpCAS client initializer.
+	 * @note Only one of the phpCAS::client() and phpCAS::proxy functions should be
+	 * called, only once, and before all other methods (except phpCAS::getVersion()
+	 * and phpCAS::setDebug()).
+	 *
+	 * @param $server_version the version of the CAS server
+	 * @param $server_hostname the hostname of the CAS server
+	 * @param $server_port the port the CAS server is running on
+	 * @param $server_uri the URI the CAS server is responding on
+	 * @param $start_session Have phpCAS start PHP sessions (default true)
+	 *
+	 * @return a newly created CASClient object
+	 */
+	function client($server_version, $server_hostname, $server_port, $server_uri, $start_session = true) {
+		global $PHPCAS_CLIENT, $PHPCAS_INIT_CALL;
+
+		phpCAS :: traceBegin();
+		if (is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error($PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . $PHPCAS_INIT_CALL['file'] . ':' . $PHPCAS_INIT_CALL['line'] . ')');
+		}
+		if (gettype($server_version) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_version (should be `string\')');
+		}
+		if (gettype($server_hostname) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_hostname (should be `string\')');
+		}
+		if (gettype($server_port) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $server_port (should be `integer\')');
+		}
+		if (gettype($server_uri) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_uri (should be `string\')');
+		}
+
+		// store where the initializer is called from
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_INIT_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__
+		);
+
+		// initialize the global object $PHPCAS_CLIENT
+		$PHPCAS_CLIENT = new CASClient($server_version, FALSE /*proxy*/
+		, $server_hostname, $server_port, $server_uri, $start_session);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * phpCAS proxy initializer.
+	 * @note Only one of the phpCAS::client() and phpCAS::proxy functions should be
+	 * called, only once, and before all other methods (except phpCAS::getVersion()
+	 * and phpCAS::setDebug()).
+	 *
+	 * @param $server_version the version of the CAS server
+	 * @param $server_hostname the hostname of the CAS server
+	 * @param $server_port the port the CAS server is running on
+	 * @param $server_uri the URI the CAS server is responding on
+	 * @param $start_session Have phpCAS start PHP sessions (default true)
+	 *
+	 * @return a newly created CASClient object
+	 */
+	function proxy($server_version, $server_hostname, $server_port, $server_uri, $start_session = true) {
+		global $PHPCAS_CLIENT, $PHPCAS_INIT_CALL;
+
+		phpCAS :: traceBegin();
+		if (is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error($PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . $PHPCAS_INIT_CALL['file'] . ':' . $PHPCAS_INIT_CALL['line'] . ')');
+		}
+		if (gettype($server_version) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_version (should be `string\')');
+		}
+		if (gettype($server_hostname) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_hostname (should be `string\')');
+		}
+		if (gettype($server_port) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $server_port (should be `integer\')');
+		}
+		if (gettype($server_uri) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_uri (should be `string\')');
+		}
+
+		// store where the initialzer is called from
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_INIT_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__
+		);
+
+		// initialize the global object $PHPCAS_CLIENT
+		$PHPCAS_CLIENT = new CASClient($server_version, TRUE /*proxy*/
+		, $server_hostname, $server_port, $server_uri, $start_session);
+		phpCAS :: traceEnd();
+	}
+
+	/** @} */
+	// ########################################################################
+	//  DEBUGGING
+	// ########################################################################
+
+	/**
+	 * @addtogroup publicDebug
+	 * @{
+	 */
+
+	/**
+	 * Set/unset debug mode
+	 *
+	 * @param $filename the name of the file used for logging, or FALSE to stop debugging.
+	 */
+	function setDebug($filename = '') {
+		global $PHPCAS_DEBUG;
+
+		if ($filename != FALSE && gettype($filename) != 'string') {
+			phpCAS :: error('type mismatched for parameter $dbg (should be FALSE or the name of the log file)');
+		}
+
+		if (empty ($filename)) {
+			if (preg_match('/^Win.*/', getenv('OS'))) {
+				if (isset ($_ENV['TMP'])) {
+					$debugDir = $_ENV['TMP'] . '/';
+				} else
+					if (isset ($_ENV['TEMP'])) {
+						$debugDir = $_ENV['TEMP'] . '/';
+					} else {
+						$debugDir = '';
+					}
+			} else {
+				$debugDir = DEFAULT_DEBUG_DIR;
+			}
+			$filename = $debugDir . 'phpCAS.log';
+		}
+
+		if (empty ($PHPCAS_DEBUG['unique_id'])) {
+			$PHPCAS_DEBUG['unique_id'] = substr(strtoupper(md5(uniqid(''))), 0, 4);
+		}
+
+		$PHPCAS_DEBUG['filename'] = $filename;
+
+		phpCAS :: trace('START ******************');
+	}
+
+	/** @} */
+	/**
+	 * @addtogroup internalDebug
+	 * @{
+	 */
+
+	/**
+	 * This method is a wrapper for debug_backtrace() that is not available 
+	 * in all PHP versions (>= 4.3.0 only)
+	 */
+	function backtrace() {
+		if (function_exists('debug_backtrace')) {
+			return debug_backtrace();
+		} else {
+			// poor man's hack ... but it does work ...
+			return array ();
+		}
+	}
+
+	/**
+	 * Logs a string in debug mode.
+	 *
+	 * @param $str the string to write
+	 *
+	 * @private
+	 */
+	function log($str) {
+		$indent_str = ".";
+		global $PHPCAS_DEBUG;
+
+		if ($PHPCAS_DEBUG['filename']) {
+			for ($i = 0; $i < $PHPCAS_DEBUG['indent']; $i++) {
+				$indent_str .= '|    ';
+			}
+			error_log($PHPCAS_DEBUG['unique_id'] . ' ' . $indent_str . $str . "\n", 3, $PHPCAS_DEBUG['filename']);
+		}
+
+	}
+
+	/**
+	 * This method is used by interface methods to print an error and where the function
+	 * was originally called from.
+	 *
+	 * @param $msg the message to print
+	 *
+	 * @private
+	 */
+	function error($msg) {
+		$dbg = phpCAS :: backtrace();
+		$function = '?';
+		$file = '?';
+		$line = '?';
+		if (is_array($dbg)) {
+			for ($i = 1; $i < sizeof($dbg); $i++) {
+				if (is_array($dbg[$i])) {
+					if ($dbg[$i]['class'] == __CLASS__) {
+						$function = $dbg[$i]['function'];
+						$file = $dbg[$i]['file'];
+						$line = $dbg[$i]['line'];
+					}
+				}
+			}
+		}
+		echo "<br />\n<b>phpCAS error</b>: <font color=\"FF0000\"><b>" . __CLASS__ . "::" . $function . '(): ' . htmlentities($msg) . "</b></font> in <b>" . $file . "</b> on line <b>" . $line . "</b><br />\n";
+		phpCAS :: trace($msg);
+		phpCAS :: traceExit();
+		exit ();
+	}
+
+	/**
+	 * This method is used to log something in debug mode.
+	 */
+	function trace($str) {
+		$dbg = phpCAS :: backtrace();
+		phpCAS :: log($str . ' [' . basename($dbg[1]['file']) . ':' . $dbg[1]['line'] . ']');
+	}
+
+	/**
+	 * This method is used to indicate the start of the execution of a function in debug mode.
+	 */
+	function traceBegin() {
+		global $PHPCAS_DEBUG;
+
+		$dbg = phpCAS :: backtrace();
+		$str = '=> ';
+		if (!empty ($dbg[2]['class'])) {
+			$str .= $dbg[2]['class'] . '::';
+		}
+		$str .= $dbg[2]['function'] . '(';
+		if (is_array($dbg[2]['args'])) {
+			foreach ($dbg[2]['args'] as $index => $arg) {
+				if ($index != 0) {
+					$str .= ', ';
+				}
+				$str .= str_replace("\n", "", var_export($arg, TRUE));
+			}
+		}
+		$str .= ') [' . basename($dbg[2]['file']) . ':' . $dbg[2]['line'] . ']';
+		phpCAS :: log($str);
+		$PHPCAS_DEBUG['indent']++;
+	}
+
+	/**
+	 * This method is used to indicate the end of the execution of a function in debug mode.
+	 *
+	 * @param $res the result of the function
+	 */
+	function traceEnd($res = '') {
+		global $PHPCAS_DEBUG;
+
+		$PHPCAS_DEBUG['indent']--;
+		$dbg = phpCAS :: backtrace();
+		$str = '';
+		$str .= '<= ' . str_replace("\n", "", var_export($res, TRUE));
+		phpCAS :: log($str);
+	}
+
+	/**
+	 * This method is used to indicate the end of the execution of the program
+	 */
+	function traceExit() {
+		global $PHPCAS_DEBUG;
+
+		phpCAS :: log('exit()');
+		while ($PHPCAS_DEBUG['indent'] > 0) {
+			phpCAS :: log('-');
+			$PHPCAS_DEBUG['indent']--;
+		}
+	}
+
+	/** @} */
+	// ########################################################################
+	//  INTERNATIONALIZATION
+	// ########################################################################
+	/**
+	 * @addtogroup publicLang
+	 * @{
+	 */
+
+	/**
+	 * This method is used to set the language used by phpCAS. 
+	 * @note Can be called only once.
+	 *
+	 * @param $lang a string representing the language.
+	 *
+	 * @sa PHPCAS_LANG_FRENCH, PHPCAS_LANG_ENGLISH
+	 */
+	function setLang($lang) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($lang) != 'string') {
+			phpCAS :: error('type mismatched for parameter $lang (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setLang($lang);
+	}
+
+	/** @} */
+	// ########################################################################
+	//  VERSION
+	// ########################################################################
+	/**
+	 * @addtogroup public
+	 * @{
+	 */
+
+	/**
+	 * This method returns the phpCAS version.
+	 *
+	 * @return the phpCAS version.
+	 */
+	function getVersion() {
+		return PHPCAS_VERSION;
+	}
+
+	/** @} */
+	// ########################################################################
+	//  HTML OUTPUT
+	// ########################################################################
+	/**
+	 * @addtogroup publicOutput
+	 * @{
+	 */
+
+	/**
+	 * This method sets the HTML header used for all outputs.
+	 *
+	 * @param $header the HTML header.
+	 */
+	function setHTMLHeader($header) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($header) != 'string') {
+			phpCAS :: error('type mismatched for parameter $header (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setHTMLHeader($header);
+	}
+
+	/**
+	 * This method sets the HTML footer used for all outputs.
+	 *
+	 * @param $footer the HTML footer.
+	 */
+	function setHTMLFooter($footer) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($footer) != 'string') {
+			phpCAS :: error('type mismatched for parameter $footer (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setHTMLFooter($footer);
+	}
+
+	/** @} */
+	// ########################################################################
+	//  PGT STORAGE
+	// ########################################################################
+	/**
+	 * @addtogroup publicPGTStorage
+	 * @{
+	 */
+
+	/**
+	 * This method is used to tell phpCAS to store the response of the
+	 * CAS server to PGT requests onto the filesystem. 
+	 *
+	 * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
+	 * @param $path the path where the PGT's should be stored
+	 */
+	function setPGTStorageFile($format = '', $path = '') {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if ($PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called before ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() (called at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ')');
+		}
+		if (gettype($format) != 'string') {
+			phpCAS :: error('type mismatched for parameter $format (should be `string\')');
+		}
+		if (gettype($path) != 'string') {
+			phpCAS :: error('type mismatched for parameter $format (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setPGTStorageFile($format, $path);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to tell phpCAS to store the response of the
+	 * CAS server to PGT requests into a database. 
+	 * @note The connection to the database is done only when needed. 
+	 * As a consequence, bad parameters are detected only when 
+	 * initializing PGT storage, except in debug mode.
+	 *
+	 * @param $user the user to access the data with
+	 * @param $password the user's password
+	 * @param $database_type the type of the database hosting the data
+	 * @param $hostname the server hosting the database
+	 * @param $port the port the server is listening on
+	 * @param $database the name of the database
+	 * @param $table the name of the table storing the data
+	 */
+	function setPGTStorageDB($user, $password, $database_type = '', $hostname = '', $port = 0, $database = '', $table = '') {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if ($PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called before ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() (called at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ')');
+		}
+		if (gettype($user) != 'string') {
+			phpCAS :: error('type mismatched for parameter $user (should be `string\')');
+		}
+		if (gettype($password) != 'string') {
+			phpCAS :: error('type mismatched for parameter $password (should be `string\')');
+		}
+		if (gettype($database_type) != 'string') {
+			phpCAS :: error('type mismatched for parameter $database_type (should be `string\')');
+		}
+		if (gettype($hostname) != 'string') {
+			phpCAS :: error('type mismatched for parameter $hostname (should be `string\')');
+		}
+		if (gettype($port) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $port (should be `integer\')');
+		}
+		if (gettype($database) != 'string') {
+			phpCAS :: error('type mismatched for parameter $database (should be `string\')');
+		}
+		if (gettype($table) != 'string') {
+			phpCAS :: error('type mismatched for parameter $table (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setPGTStorageDB($user, $password, $database_type, $hostname, $port, $database, $table);
+		phpCAS :: traceEnd();
+	}
+
+	/** @} */
+	// ########################################################################
+	// ACCESS TO EXTERNAL SERVICES
+	// ########################################################################
+	/**
+	 * @addtogroup publicServices
+	 * @{
+	 */
+
+	/**
+	 * This method is used to access an HTTP[S] service.
+	 * 
+	 * @param $url the service to access.
+	 * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
+	 * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
+	 * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
+	 * @param $output the output of the service (also used to give an error
+	 * message on failure).
+	 *
+	 * @return TRUE on success, FALSE otherwise (in this later case, $err_code
+	 * gives the reason why it failed and $output contains an error message).
+	 */
+	function serviceWeb($url, & $err_code, & $output) {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['result']) {
+			phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+
+		$res = $PHPCAS_CLIENT->serviceWeb($url, $err_code, $output);
+
+		phpCAS :: traceEnd($res);
+		return $res;
+	}
+
+	/**
+	 * This method is used to access an IMAP/POP3/NNTP service.
+	 * 
+	 * @param $url a string giving the URL of the service, including the mailing box
+	 * for IMAP URLs, as accepted by imap_open().
+	 * @param $service a string giving for CAS retrieve Proxy ticket
+	 * @param $flags options given to imap_open().
+	 * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
+	 * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
+	 * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
+	 * @param $err_msg an error message on failure
+	 * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
+	 * on success, FALSE on error).
+	 *
+	 * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
+	 * gives the reason why it failed and $err_msg contains an error message).
+	 */
+	function serviceMail($url, $service, $flags, & $err_code, & $err_msg, & $pt) {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['result']) {
+			phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+
+		if (gettype($flags) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $flags (should be `integer\')');
+		}
+
+		$res = $PHPCAS_CLIENT->serviceMail($url, $service, $flags, $err_code, $err_msg, $pt);
+
+		phpCAS :: traceEnd($res);
+		return $res;
+	}
+
+	/** @} */
+	// ########################################################################
+	//  AUTHENTICATION
+	// ########################################################################
+	/**
+	 * @addtogroup publicAuth
+	 * @{
+	 */
+
+	/**
+	 * Set the times authentication will be cached before really accessing the CAS server in gateway mode: 
+	 * - -1: check only once, and then never again (until you pree login)
+	 * - 0: always check
+	 * - n: check every "n" time
+	 *
+	 * @param $n an integer.
+	 */
+	function setCacheTimesForAuthRecheck($n) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($n) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $header (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setCacheTimesForAuthRecheck($n);
+	}
+
+	/**
+	 * This method is called to check if the user is authenticated (use the gateway feature).
+	 * @return TRUE when the user is authenticated; otherwise FALSE.
+	 */
+	function checkAuthentication() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+
+		$auth = $PHPCAS_CLIENT->checkAuthentication();
+
+		// store where the authentication has been checked and the result
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_AUTH_CHECK_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__,
+			'result' => $auth
+		);
+		phpCAS :: traceEnd($auth);
+		return $auth;
+	}
+
+	/**
+	 * This method is called to force authentication if the user was not already 
+	 * authenticated. If the user is not authenticated, halt by redirecting to 
+	 * the CAS server.
+	 */
+	function forceAuthentication() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+
+		$auth = $PHPCAS_CLIENT->forceAuthentication();
+
+		// store where the authentication has been checked and the result
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_AUTH_CHECK_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__,
+			'result' => $auth
+		);
+
+		if (!$auth) {
+			phpCAS :: trace('user is not authenticated, redirecting to the CAS server');
+			$PHPCAS_CLIENT->forceAuthentication();
+		} else {
+			phpCAS :: trace('no need to authenticate (user `' . phpCAS :: getUser() . '\' is already authenticated)');
+		}
+
+		phpCAS :: traceEnd();
+		return $auth;
+	}
+
+	/**
+	 * This method is called to renew the authentication.
+	 **/
+	function renewAuthentication() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+
+		// store where the authentication has been checked and the result
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_AUTH_CHECK_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__,
+			'result' => $auth
+		);
+
+		$PHPCAS_CLIENT->renewAuthentication();
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method has been left from version 0.4.1 for compatibility reasons.
+	 */
+	function authenticate() {
+		phpCAS :: error('this method is deprecated. You should use ' . __CLASS__ . '::forceAuthentication() instead');
+	}
+
+	/**
+	 * This method is called to check if the user is authenticated (previously or by
+	 * tickets given in the URL).
+	 *
+	 * @return TRUE when the user is authenticated.
+	 */
+	function isAuthenticated() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+
+		// call the isAuthenticated method of the global $PHPCAS_CLIENT object
+		$auth = $PHPCAS_CLIENT->isAuthenticated();
+
+		// store where the authentication has been checked and the result
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_AUTH_CHECK_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__,
+			'result' => $auth
+		);
+		phpCAS :: traceEnd($auth);
+		return $auth;
+	}
+
+	/**
+	 * Checks whether authenticated based on $_SESSION. Useful to avoid
+	 * server calls.
+	 * @return true if authenticated, false otherwise.
+	 * @since 0.4.22 by Brendan Arnold
+	 */
+	function isSessionAuthenticated() {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		return ($PHPCAS_CLIENT->isSessionAuthenticated());
+	}
+
+	/**
+	 * This method returns the CAS user's login name.
+	 * @warning should not be called only after phpCAS::forceAuthentication()
+	 * or phpCAS::checkAuthentication().
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getUser() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['result']) {
+			phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE');
+		}
+		return $PHPCAS_CLIENT->getUser();
+	}
+
+	/**
+	 * This method returns the CAS user's login name.
+	 * @warning should not be called only after phpCAS::forceAuthentication()
+	 * or phpCAS::checkAuthentication().
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getAttributes() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['result']) {
+			phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE');
+		}
+		return $PHPCAS_CLIENT->getAttributes();
+	}
+	/**
+	 * Handle logout requests.
+	 */
+	function handleLogoutRequests($check_client = true, $allowed_clients = false) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		return ($PHPCAS_CLIENT->handleLogoutRequests($check_client, $allowed_clients));
+	}
+
+	/**
+	 * This method returns the URL to be used to login.
+	 * or phpCAS::isAuthenticated().
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getServerLoginURL() {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		return $PHPCAS_CLIENT->getServerLoginURL();
+	}
+
+	/**
+	 * Set the login URL of the CAS server.
+	 * @param $url the login URL
+	 * @since 0.4.21 by Wyman Chan
+	 */
+	function setServerLoginURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerLoginURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the serviceValidate URL of the CAS server.
+	 * Used only in CAS 1.0 validations
+	 * @param $url the serviceValidate URL
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerServiceValidateURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerServiceValidateURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the proxyValidate URL of the CAS server.
+	 * Used for all CAS 2.0 validations
+	 * @param $url the proxyValidate URL
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerProxyValidateURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerProxyValidateURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the samlValidate URL of the CAS server.
+	 * @param $url the samlValidate URL
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerSamlValidateURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerSamlValidateURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method returns the URL to be used to login.
+	 * or phpCAS::isAuthenticated().
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getServerLogoutURL() {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		return $PHPCAS_CLIENT->getServerLogoutURL();
+	}
+
+	/**
+	 * Set the logout URL of the CAS server.
+	 * @param $url the logout URL
+	 * @since 0.4.21 by Wyman Chan
+	 */
+	function setServerLogoutURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerLogoutURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to logout from CAS.
+	 * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server
+	 * @public
+	 */
+	function logout($params = "") {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		$parsedParams = array ();
+		if ($params != "") {
+			if (is_string($params)) {
+				phpCAS :: error('method `phpCAS::logout($url)\' is now deprecated, use `phpCAS::logoutWithUrl($url)\' instead');
+			}
+			if (!is_array($params)) {
+				phpCAS :: error('type mismatched for parameter $params (should be `array\')');
+			}
+			foreach ($params as $key => $value) {
+				if ($key != "service" && $key != "url") {
+					phpCAS :: error('only `url\' and `service\' parameters are allowed for method `phpCAS::logout($params)\'');
+				}
+				$parsedParams[$key] = $value;
+			}
+		}
+		$PHPCAS_CLIENT->logout($parsedParams);
+		// never reached
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to logout from CAS. Halts by redirecting to the CAS server.
+	 * @param $service a URL that will be transmitted to the CAS server
+	 */
+	function logoutWithRedirectService($service) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (!is_string($service)) {
+			phpCAS :: error('type mismatched for parameter $service (should be `string\')');
+		}
+		$PHPCAS_CLIENT->logout(array (
+			"service" => $service
+		));
+		// never reached
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to logout from CAS. Halts by redirecting to the CAS server.
+	 * @param $url a URL that will be transmitted to the CAS server
+	 */
+	function logoutWithUrl($url) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (!is_string($url)) {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+		$PHPCAS_CLIENT->logout(array (
+			"url" => $url
+		));
+		// never reached
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to logout from CAS. Halts by redirecting to the CAS server.
+	 * @param $service a URL that will be transmitted to the CAS server
+	 * @param $url a URL that will be transmitted to the CAS server
+	 */
+	function logoutWithRedirectServiceAndUrl($service, $url) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (!is_string($service)) {
+			phpCAS :: error('type mismatched for parameter $service (should be `string\')');
+		}
+		if (!is_string($url)) {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+		$PHPCAS_CLIENT->logout(array (
+			"service" => $service,
+			"url" => $url
+		));
+		// never reached
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the fixed URL that will be used by the CAS server to transmit the PGT.
+	 * When this method is not called, a phpCAS script uses its own URL for the callback.
+	 *
+	 * @param $url the URL
+	 */
+	function setFixedCallbackURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setCallbackURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the fixed URL that will be set as the CAS service parameter. When this
+	 * method is not called, a phpCAS script uses its own URL.
+	 *
+	 * @param $url the URL
+	 */
+	function setFixedServiceURL($url) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Get the URL that is set as the CAS service parameter.
+	 */
+	function getServiceURL() {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		return ($PHPCAS_CLIENT->getURL());
+	}
+
+	/**
+	 * Retrieve a Proxy Ticket from the CAS server.
+	 */
+	function retrievePT($target_service, & $err_code, & $err_msg) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($target_service) != 'string') {
+			phpCAS :: error('type mismatched for parameter $target_service(should be `string\')');
+		}
+		return ($PHPCAS_CLIENT->retrievePT($target_service, $err_code, $err_msg));
+	}
+
+	/**
+	 * Set the certificate of the CAS server.
+	 *
+	 * @param $cert the PEM certificate
+	 */
+	function setCasServerCert($cert) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($cert) != 'string') {
+			phpCAS :: error('type mismatched for parameter $cert (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setCasServerCert($cert);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the certificate of the CAS server CA.
+	 *
+	 * @param $cert the CA certificate
+	 */
+	function setCasServerCACert($cert) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($cert) != 'string') {
+			phpCAS :: error('type mismatched for parameter $cert (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setCasServerCACert($cert);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set no SSL validation for the CAS server.
+	 */
+	function setNoCasServerValidation() {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		$PHPCAS_CLIENT->setNoCasServerValidation();
+		phpCAS :: traceEnd();
+	}
+
+	/** @} */
+
+	/**
+	 * Change CURL options.
+	 * CURL is used to connect through HTTPS to CAS server
+	 * @param $key the option key
+	 * @param $value the value to set
+	 */
+	function setExtraCurlOption($key, $value) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		$PHPCAS_CLIENT->setExtraCurlOption($key, $value);
+		phpCAS :: traceEnd();
+	}
+
+}
+
+// ########################################################################
+// DOCUMENTATION
+// ########################################################################
+
+// ########################################################################
+//  MAIN PAGE
+
+/**
+ * @mainpage
+ *
+ * The following pages only show the source documentation.
+ *
+ */
+
+// ########################################################################
+//  MODULES DEFINITION
+
+/** @defgroup public User interface */
+
+/** @defgroup publicInit Initialization
+ *  @ingroup public */
+
+/** @defgroup publicAuth Authentication
+ *  @ingroup public */
+
+/** @defgroup publicServices Access to external services
+ *  @ingroup public */
+
+/** @defgroup publicConfig Configuration
+ *  @ingroup public */
+
+/** @defgroup publicLang Internationalization
+ *  @ingroup publicConfig */
+
+/** @defgroup publicOutput HTML output
+ *  @ingroup publicConfig */
+
+/** @defgroup publicPGTStorage PGT storage
+ *  @ingroup publicConfig */
+
+/** @defgroup publicDebug Debugging
+ *  @ingroup public */
+
+/** @defgroup internal Implementation */
+
+/** @defgroup internalAuthentication Authentication
+ *  @ingroup internal */
+
+/** @defgroup internalBasic CAS Basic client features (CAS 1.0, Service Tickets)
+ *  @ingroup internal */
+
+/** @defgroup internalProxy CAS Proxy features (CAS 2.0, Proxy Granting Tickets)
+ *  @ingroup internal */
+
+/** @defgroup internalPGTStorage PGT storage
+ *  @ingroup internalProxy */
+
+/** @defgroup internalPGTStorageDB PGT storage in a database
+ *  @ingroup internalPGTStorage */
+
+/** @defgroup internalPGTStorageFile PGT storage on the filesystem
+ *  @ingroup internalPGTStorage */
+
+/** @defgroup internalCallback Callback from the CAS server
+ *  @ingroup internalProxy */
+
+/** @defgroup internalProxied CAS proxied client features (CAS 2.0, Proxy Tickets)
+ *  @ingroup internal */
+
+/** @defgroup internalConfig Configuration
+ *  @ingroup internal */
+
+/** @defgroup internalOutput HTML output
+ *  @ingroup internalConfig */
+
+/** @defgroup internalLang Internationalization
+ *  @ingroup internalConfig
+ *
+ * To add a new language:
+ * - 1. define a new constant PHPCAS_LANG_XXXXXX in CAS/CAS.php
+ * - 2. copy any file from CAS/languages to CAS/languages/XXXXXX.php
+ * - 3. Make the translations
+ */
+
+/** @defgroup internalDebug Debugging
+ *  @ingroup internal */
+
+/** @defgroup internalMisc Miscellaneous
+ *  @ingroup internal */
+
+// ########################################################################
+//  EXAMPLES
+
+/**
+ * @example example_simple.php
+ */
+/**
+ * @example example_proxy.php
+ */
+/**
+ * @example example_proxy2.php
+ */
+/**
+ * @example example_lang.php
+ */
+/**
+ * @example example_html.php
+ */
+/**
+ * @example example_file.php
+ */
+/**
+ * @example example_db.php
+ */
+/**
+ * @example example_service.php
+ */
+/**
+ * @example example_session_proxy.php
+ */
+/**
+ * @example example_session_service.php
+ */
+/**
+ * @example example_gateway.php
+ */
+/**
+ * @example example_custom_urls.php
+ */
+?>

+ 218 - 0
main/auth/cas/lib/CAS/PGTStorage/pgt-db.php

@@ -0,0 +1,218 @@
+<?php
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file CAS/PGTStorage/pgt-db.php
+ * Basic class for PGT database storage
+ */
+
+/**
+ * @class PGTStorageDB
+ * The PGTStorageDB class is a class for PGT database storage. An instance of 
+ * this class is returned by CASClient::SetPGTStorageDB().
+ *
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ *
+ * @ingroup internalPGTStorageDB
+ */
+
+class PGTStorageDB extends PGTStorage
+{
+  /** 
+   * @addtogroup internalPGTStorageDB
+   * @{ 
+   */
+
+  /**
+   * a string representing a PEAR DB URL to connect to the database. Written by
+   * PGTStorageDB::PGTStorageDB(), read by getURL().
+   *
+   * @hideinitializer
+   * @private
+   */
+  var $_url='';
+
+  /**
+   * This method returns the PEAR DB URL to use to connect to the database.
+   *
+   * @return a PEAR DB URL
+   *
+   * @private
+   */
+  function getURL()
+    {
+      return $this->_url;
+    }
+
+  /**
+   * The handle of the connection to the database where PGT's are stored. Written by
+   * PGTStorageDB::init(), read by getLink().
+   *
+   * @hideinitializer
+   * @private
+   */
+  var $_link = null;
+
+  /**
+   * This method returns the handle of the connection to the database where PGT's are 
+   * stored.
+   *
+   * @return a handle of connection.
+   *
+   * @private
+   */
+  function getLink()
+    {
+      return $this->_link;
+    }
+
+  /**
+   * The name of the table where PGT's are stored. Written by 
+   * PGTStorageDB::PGTStorageDB(), read by getTable().
+   *
+   * @hideinitializer
+   * @private
+   */
+  var $_table = '';
+
+  /**
+   * This method returns the name of the table where PGT's are stored.
+   *
+   * @return the name of a table.
+   *
+   * @private
+   */
+  function getTable()
+    {
+      return $this->_table;
+    }
+
+  // ########################################################################
+  //  DEBUGGING
+  // ########################################################################
+  
+  /**
+   * This method returns an informational string giving the type of storage
+   * used by the object (used for debugging purposes).
+   *
+   * @return an informational string.
+   * @public
+   */
+  function getStorageType()
+    {
+      return "database";
+    }
+
+  /**
+   * This method returns an informational string giving informations on the
+   * parameters of the storage.(used for debugging purposes).
+   *
+   * @public
+   */
+  function getStorageInfo()
+    {
+      return 'url=`'.$this->getURL().'\', table=`'.$this->getTable().'\'';
+    }
+
+  // ########################################################################
+  //  CONSTRUCTOR
+  // ########################################################################
+  
+  /**
+   * The class constructor, called by CASClient::SetPGTStorageDB().
+   *
+   * @param $cas_parent the CASClient instance that creates the object.
+   * @param $user the user to access the data with
+   * @param $password the user's password
+   * @param $database_type the type of the database hosting the data
+   * @param $hostname the server hosting the database
+   * @param $port the port the server is listening on
+   * @param $database the name of the database
+   * @param $table the name of the table storing the data
+   *
+   * @public
+   */
+  function PGTStorageDB($cas_parent,$user,$password,$database_type,$hostname,$port,$database,$table)
+    {
+      phpCAS::traceBegin();
+
+      // call the ancestor's constructor
+      $this->PGTStorage($cas_parent);
+
+      if ( empty($database_type) ) $database_type = CAS_PGT_STORAGE_DB_DEFAULT_DATABASE_TYPE;
+      if ( empty($hostname) ) $hostname = CAS_PGT_STORAGE_DB_DEFAULT_HOSTNAME;
+      if ( $port==0 ) $port = CAS_PGT_STORAGE_DB_DEFAULT_PORT;
+      if ( empty($database) ) $database = CAS_PGT_STORAGE_DB_DEFAULT_DATABASE;
+      if ( empty($table) ) $table = CAS_PGT_STORAGE_DB_DEFAULT_TABLE;
+
+      // build and store the PEAR DB URL
+      $this->_url = $database_type.':'.'//'.$user.':'.$password.'@'.$hostname.':'.$port.'/'.$database;
+
+      // XXX should use setURL and setTable
+      phpCAS::traceEnd();
+    }
+  
+  // ########################################################################
+  //  INITIALIZATION
+  // ########################################################################
+  
+  /**
+   * This method is used to initialize the storage. Halts on error.
+   *
+   * @public
+   */
+  function init()
+    {
+      phpCAS::traceBegin();
+      // if the storage has already been initialized, return immediatly
+      if ( $this->isInitialized() )
+		return;
+      // call the ancestor's method (mark as initialized)
+      parent::init();
+      
+	  //include phpDB library (the test was introduced in release 0.4.8 for 
+	  //the integration into Tikiwiki).
+	  if (!class_exists('DB')) {
+		include_once('DB.php');
+	  }
+
+      // try to connect to the database
+      $this->_link = DB::connect($this->getURL());
+      if ( DB::isError($this->_link) ) {
+	phpCAS::error('could not connect to database ('.DB::errorMessage($this->_link).')');
+      }
+      var_dump($this->_link);
+      phpCAS::traceBEnd();
+    }
+
+  /** @} */
+}
+
+?>

+ 276 - 0
main/auth/cas/lib/CAS/PGTStorage/pgt-file.php

@@ -0,0 +1,276 @@
+<?php
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file CAS/PGTStorage/pgt-file.php
+ * Basic class for PGT file storage
+ */
+
+/**
+ * @class PGTStorageFile
+ * The PGTStorageFile class is a class for PGT file storage. An instance of 
+ * this class is returned by CASClient::SetPGTStorageFile().
+ *
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ *
+ * @ingroup internalPGTStorageFile
+ */
+
+class PGTStorageFile extends PGTStorage
+{
+  /** 
+   * @addtogroup internalPGTStorageFile 
+   * @{ 
+   */
+
+  /**
+   * a string telling where PGT's should be stored on the filesystem. Written by
+   * PGTStorageFile::PGTStorageFile(), read by getPath().
+   *
+   * @private
+   */
+  var $_path;
+
+  /**
+   * This method returns the name of the directory where PGT's should be stored 
+   * on the filesystem.
+   *
+   * @return the name of a directory (with leading and trailing '/')
+   *
+   * @private
+   */
+  function getPath()
+    {
+      return $this->_path;
+    }
+
+  /**
+   * a string telling the format to use to store PGT's (plain or xml). Written by
+   * PGTStorageFile::PGTStorageFile(), read by getFormat().
+   *
+   * @private
+   */
+  var $_format;
+
+  /**
+   * This method returns the format to use when storing PGT's on the filesystem.
+   *
+   * @return a string corresponding to the format used (plain or xml).
+   *
+   * @private
+   */
+  function getFormat()
+    {
+      return $this->_format;
+    }
+
+  // ########################################################################
+  //  DEBUGGING
+  // ########################################################################
+  
+  /**
+   * This method returns an informational string giving the type of storage
+   * used by the object (used for debugging purposes).
+   *
+   * @return an informational string.
+   * @public
+   */
+  function getStorageType()
+    {
+      return "file";
+    }
+
+  /**
+   * This method returns an informational string giving informations on the
+   * parameters of the storage.(used for debugging purposes).
+   *
+   * @return an informational string.
+   * @public
+   */
+  function getStorageInfo()
+    {
+      return 'path=`'.$this->getPath().'\', format=`'.$this->getFormat().'\'';
+    }
+
+  // ########################################################################
+  //  CONSTRUCTOR
+  // ########################################################################
+  
+  /**
+   * The class constructor, called by CASClient::SetPGTStorageFile().
+   *
+   * @param $cas_parent the CASClient instance that creates the object.
+   * @param $format the format used to store the PGT's (`plain' and `xml' allowed).
+   * @param $path the path where the PGT's should be stored
+   *
+   * @public
+   */
+  function PGTStorageFile($cas_parent,$format,$path)
+    {
+      phpCAS::traceBegin();
+      // call the ancestor's constructor
+      $this->PGTStorage($cas_parent);
+
+      if (empty($format) ) $format = CAS_PGT_STORAGE_FILE_DEFAULT_FORMAT;
+      if (empty($path) ) $path = CAS_PGT_STORAGE_FILE_DEFAULT_PATH;
+
+      // check that the path is an absolute path
+      if (getenv("OS")=="Windows_NT"){
+      	
+      	 if (!preg_match('`^[a-zA-Z]:`', $path)) {
+	     	phpCAS::error('an absolute path is needed for PGT storage to file');
+      	}
+      	
+      }
+      else
+      {
+      
+      	if ( $path[0] != '/' ) {
+			phpCAS::error('an absolute path is needed for PGT storage to file');
+      	}
+
+      	// store the path (with a leading and trailing '/')      
+      	$path = preg_replace('|[/]*$|','/',$path);
+      	$path = preg_replace('|^[/]*|','/',$path);
+      }
+      
+      $this->_path = $path;
+      // check the format and store it
+      switch ($format) {
+      case CAS_PGT_STORAGE_FILE_FORMAT_PLAIN:
+      case CAS_PGT_STORAGE_FILE_FORMAT_XML:
+	$this->_format = $format;
+	break;
+      default:
+	phpCAS::error('unknown PGT file storage format (`'.CAS_PGT_STORAGE_FILE_FORMAT_PLAIN.'\' and `'.CAS_PGT_STORAGE_FILE_FORMAT_XML.'\' allowed)');
+      }
+      phpCAS::traceEnd();      
+    }
+
+  // ########################################################################
+  //  INITIALIZATION
+  // ########################################################################
+  
+  /**
+   * This method is used to initialize the storage. Halts on error.
+   *
+   * @public
+   */
+  function init()
+    {
+      phpCAS::traceBegin();
+      // if the storage has already been initialized, return immediatly
+      if ( $this->isInitialized() )
+	return;
+      // call the ancestor's method (mark as initialized)
+      parent::init();
+      phpCAS::traceEnd();      
+    }
+
+  // ########################################################################
+  //  PGT I/O
+  // ########################################################################
+
+  /**
+   * This method returns the filename corresponding to a PGT Iou.
+   *
+   * @param $pgt_iou the PGT iou.
+   *
+   * @return a filename
+   * @private
+   */
+  function getPGTIouFilename($pgt_iou)
+    {
+      phpCAS::traceBegin();
+      $filename = $this->getPath().$pgt_iou.'.'.$this->getFormat();
+      phpCAS::traceEnd($filename);
+      return $filename;
+    }
+  
+  /**
+   * This method stores a PGT and its corresponding PGT Iou into a file. Echoes a
+   * warning on error.
+   *
+   * @param $pgt the PGT
+   * @param $pgt_iou the PGT iou
+   *
+   * @public
+   */
+  function write($pgt,$pgt_iou)
+    {
+      phpCAS::traceBegin();
+      $fname = $this->getPGTIouFilename($pgt_iou);
+      if ( $f=fopen($fname,"w") ) {
+	if ( fputs($f,$pgt) === FALSE ) {
+	  phpCAS::error('could not write PGT to `'.$fname.'\'');
+	}
+	fclose($f);
+      } else {
+	phpCAS::error('could not open `'.$fname.'\'');
+      }
+      phpCAS::traceEnd();      
+    }
+
+  /**
+   * This method reads a PGT corresponding to a PGT Iou and deletes the 
+   * corresponding file.
+   *
+   * @param $pgt_iou the PGT iou
+   *
+   * @return the corresponding PGT, or FALSE on error
+   *
+   * @public
+   */
+  function read($pgt_iou)
+    {
+      phpCAS::traceBegin();
+      $pgt = FALSE;
+      $fname = $this->getPGTIouFilename($pgt_iou);
+      if ( !($f=fopen($fname,"r")) ) {
+	phpCAS::trace('could not open `'.$fname.'\'');
+      } else {
+	if ( ($pgt=fgets($f)) === FALSE ) {
+	  phpCAS::trace('could not read PGT from `'.$fname.'\'');
+	} 
+	fclose($f);
+      }
+
+      // delete the PGT file
+      @unlink($fname);
+
+      phpCAS::traceEnd($pgt);
+      return $pgt;
+    }
+  
+  /** @} */
+  
+}
+
+  
+?>

+ 215 - 0
main/auth/cas/lib/CAS/PGTStorage/pgt-main.php

@@ -0,0 +1,215 @@
+<?php
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file CAS/PGTStorage/pgt-main.php
+ * Basic class for PGT storage
+ */
+
+/**
+ * @class PGTStorage
+ * The PGTStorage class is a generic class for PGT storage. This class should
+ * not be instanciated itself but inherited by specific PGT storage classes.
+ *
+ * @author   Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ *
+ * @ingroup internalPGTStorage
+ */
+
+class PGTStorage
+{
+  /** 
+   * @addtogroup internalPGTStorage
+   * @{ 
+   */
+
+  // ########################################################################
+  //  CONSTRUCTOR
+  // ########################################################################
+  
+  /**
+   * The constructor of the class, should be called only by inherited classes.
+   *
+   * @param $cas_parent the CASclient instance that creates the current object.
+   *
+   * @protected
+   */
+  function PGTStorage($cas_parent)
+    {
+      phpCAS::traceBegin();
+      if ( !$cas_parent->isProxy() ) {
+	phpCAS::error('defining PGT storage makes no sense when not using a CAS proxy'); 
+      }
+      phpCAS::traceEnd();
+    }
+
+  // ########################################################################
+  //  DEBUGGING
+  // ########################################################################
+  
+  /**
+   * This virtual method returns an informational string giving the type of storage
+   * used by the object (used for debugging purposes).
+   *
+   * @public
+   */
+  function getStorageType()
+    {
+      phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); 
+    }
+
+  /**
+   * This virtual method returns an informational string giving informations on the
+   * parameters of the storage.(used for debugging purposes).
+   *
+   * @public
+   */
+  function getStorageInfo()
+    {
+      phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); 
+    }
+
+  // ########################################################################
+  //  ERROR HANDLING
+  // ########################################################################
+  
+  /**
+   * string used to store an error message. Written by PGTStorage::setErrorMessage(),
+   * read by PGTStorage::getErrorMessage().
+   *
+   * @hideinitializer
+   * @private
+   * @deprecated not used.
+   */
+  var $_error_message=FALSE;
+
+  /**
+   * This method sets en error message, which can be read later by 
+   * PGTStorage::getErrorMessage().
+   *
+   * @param $error_message an error message
+   *
+   * @protected
+   * @deprecated not used.
+   */
+  function setErrorMessage($error_message)
+    {
+      $this->_error_message = $error_message;
+    }
+
+  /**
+   * This method returns an error message set by PGTStorage::setErrorMessage().
+   *
+   * @return an error message when set by PGTStorage::setErrorMessage(), FALSE
+   * otherwise.
+   *
+   * @public
+   * @deprecated not used.
+   */
+  function getErrorMessage()
+    {
+      return $this->_error_message;
+    }
+
+  // ########################################################################
+  //  INITIALIZATION
+  // ########################################################################
+
+  /**
+   * a boolean telling if the storage has already been initialized. Written by 
+   * PGTStorage::init(), read by PGTStorage::isInitialized().
+   *
+   * @hideinitializer
+   * @private
+   */
+  var $_initialized = FALSE;
+
+  /**
+   * This method tells if the storage has already been intialized.
+   *
+   * @return a boolean
+   *
+   * @protected
+   */
+  function isInitialized()
+    {
+      return $this->_initialized;
+    }
+
+  /**
+   * This virtual method initializes the object.
+   *
+   * @protected
+   */
+  function init()
+    {
+      $this->_initialized = TRUE;
+    }
+
+  // ########################################################################
+  //  PGT I/O
+  // ########################################################################
+
+  /**
+   * This virtual method stores a PGT and its corresponding PGT Iuo.
+   * @note Should never be called.
+   *
+   * @param $pgt the PGT
+   * @param $pgt_iou the PGT iou
+   *
+   * @protected
+   */
+  function write($pgt,$pgt_iou)
+    {
+      phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); 
+    }
+
+  /**
+   * This virtual method reads a PGT corresponding to a PGT Iou and deletes
+   * the corresponding storage entry.
+   * @note Should never be called.
+   *
+   * @param $pgt_iou the PGT iou
+   *
+   * @protected
+   */
+  function read($pgt_iou)
+    {
+      phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); 
+    }
+
+  /** @} */
+  
+} 
+
+// include specific PGT storage classes
+include_once(dirname(__FILE__).'/pgt-file.php'); 
+include_once(dirname(__FILE__).'/pgt-db.php');
+  
+?>

+ 2700 - 0
main/auth/cas/lib/CAS/client.php

@@ -0,0 +1,2700 @@
+<?php
+
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file CAS/client.php
+ * Main class of the phpCAS library
+ */
+
+// include internationalization stuff
+include_once(dirname(__FILE__).'/languages/languages.php');
+
+// include PGT storage classes
+include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
+
+/**
+ * @class CASClient
+ * The CASClient class is a client interface that provides CAS authentication
+ * to PHP applications.
+ *
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ */
+
+class CASClient
+{
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                          CONFIGURATION                             XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	// ########################################################################
+	//  HTML OUTPUT
+	// ########################################################################
+	/**
+	 * @addtogroup internalOutput
+	 * @{
+	 */  
+	
+	/**
+	 * This method filters a string by replacing special tokens by appropriate values
+	 * and prints it. The corresponding tokens are taken into account:
+	 * - __CAS_VERSION__
+	 * - __PHPCAS_VERSION__
+	 * - __SERVER_BASE_URL__
+	 *
+	 * Used by CASClient::PrintHTMLHeader() and CASClient::printHTMLFooter().
+	 *
+	 * @param $str the string to filter and output
+	 *
+	 * @private
+	 */
+	function HTMLFilterOutput($str)
+		{
+		$str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str);
+		$str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str);
+		$str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str);
+		echo $str;
+		}
+	
+	/**
+	 * A string used to print the header of HTML pages. Written by CASClient::setHTMLHeader(),
+	 * read by CASClient::printHTMLHeader().
+	 *
+	 * @hideinitializer
+	 * @private
+	 * @see CASClient::setHTMLHeader, CASClient::printHTMLHeader()
+	 */
+	var $_output_header = '';
+	
+	/**
+	 * This method prints the header of the HTML output (after filtering). If
+	 * CASClient::setHTMLHeader() was not used, a default header is output.
+	 *
+	 * @param $title the title of the page
+	 *
+	 * @see HTMLFilterOutput()
+	 * @private
+	 */
+	function printHTMLHeader($title)
+		{
+		$this->HTMLFilterOutput(str_replace('__TITLE__',
+			$title,
+			(empty($this->_output_header)
+					? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
+							: $this->_output_header)
+		)
+		);
+		}
+	
+	/**
+	 * A string used to print the footer of HTML pages. Written by CASClient::setHTMLFooter(),
+	 * read by printHTMLFooter().
+	 *
+	 * @hideinitializer
+	 * @private
+	 * @see CASClient::setHTMLFooter, CASClient::printHTMLFooter()
+	 */
+	var $_output_footer = '';
+	
+	/**
+	 * This method prints the footer of the HTML output (after filtering). If
+	 * CASClient::setHTMLFooter() was not used, a default footer is output.
+	 *
+	 * @see HTMLFilterOutput()
+	 * @private
+	 */
+	function printHTMLFooter()
+		{
+		$this->HTMLFilterOutput(empty($this->_output_footer)
+			?('<hr><address>phpCAS __PHPCAS_VERSION__ '.$this->getString(CAS_STR_USING_SERVER).' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>')
+					:$this->_output_footer);
+		}
+	
+	/**
+	 * This method set the HTML header used for all outputs.
+	 *
+	 * @param $header the HTML header.
+	 *
+	 * @public
+	 */
+	function setHTMLHeader($header)
+		{
+		$this->_output_header = $header;
+		}
+	
+	/**
+	 * This method set the HTML footer used for all outputs.
+	 *
+	 * @param $footer the HTML footer.
+	 *
+	 * @public
+	 */
+	function setHTMLFooter($footer)
+		{
+		$this->_output_footer = $footer;
+		}
+	
+	/** @} */
+	// ########################################################################
+	//  INTERNATIONALIZATION
+	// ########################################################################
+	/**
+	 * @addtogroup internalLang
+	 * @{
+	 */  
+	/**
+	 * A string corresponding to the language used by phpCAS. Written by 
+	 * CASClient::setLang(), read by CASClient::getLang().
+	 
+	 * @note debugging information is always in english (debug purposes only).
+	 *
+	 * @hideinitializer
+	 * @private
+	 * @sa CASClient::_strings, CASClient::getString()
+	 */
+	var $_lang = '';
+	
+	/**
+	 * This method returns the language used by phpCAS.
+	 *
+	 * @return a string representing the language
+	 *
+	 * @private
+	 */
+	function getLang()
+		{
+		if ( empty($this->_lang) )
+			$this->setLang(PHPCAS_LANG_DEFAULT);
+		return $this->_lang;
+		}
+	
+	/**
+	 * array containing the strings used by phpCAS. Written by CASClient::setLang(), read by 
+	 * CASClient::getString() and used by CASClient::setLang().
+	 *
+	 * @note This array is filled by instructions in CAS/languages/<$this->_lang>.php
+	 *
+	 * @private
+	 * @see CASClient::_lang, CASClient::getString(), CASClient::setLang(), CASClient::getLang()
+	 */
+	var $_strings;
+	
+	/**
+	 * This method returns a string depending on the language.
+	 *
+	 * @param $str the index of the string in $_string.
+	 *
+	 * @return the string corresponding to $index in $string.
+	 *
+	 * @private
+	 */
+	function getString($str)
+		{
+		// call CASclient::getLang() to be sure the language is initialized
+		$this->getLang();
+		
+		if ( !isset($this->_strings[$str]) ) {
+			trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR);
+		}
+		return $this->_strings[$str];
+		}
+	
+	/**
+	 * This method is used to set the language used by phpCAS. 
+	 * @note Can be called only once.
+	 *
+	 * @param $lang a string representing the language.
+	 *
+	 * @public
+	 * @sa CAS_LANG_FRENCH, CAS_LANG_ENGLISH
+	 */
+	function setLang($lang)
+		{
+		// include the corresponding language file
+		include_once(dirname(__FILE__).'/languages/'.$lang.'.php');
+		
+		if ( !is_array($this->_strings) ) {
+			trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR);
+		}
+		$this->_lang = $lang;
+		}
+	
+	/** @} */
+	// ########################################################################
+	//  CAS SERVER CONFIG
+	// ########################################################################
+	/**
+	 * @addtogroup internalConfig
+	 * @{
+	 */  
+	
+	/**
+	 * a record to store information about the CAS server.
+	 * - $_server["version"]: the version of the CAS server
+	 * - $_server["hostname"]: the hostname of the CAS server
+	 * - $_server["port"]: the port the CAS server is running on
+	 * - $_server["uri"]: the base URI the CAS server is responding on
+	 * - $_server["base_url"]: the base URL of the CAS server
+	 * - $_server["login_url"]: the login URL of the CAS server
+	 * - $_server["service_validate_url"]: the service validating URL of the CAS server
+	 * - $_server["proxy_url"]: the proxy URL of the CAS server
+	 * - $_server["proxy_validate_url"]: the proxy validating URL of the CAS server
+	 * - $_server["logout_url"]: the logout URL of the CAS server
+	 *
+	 * $_server["version"], $_server["hostname"], $_server["port"] and $_server["uri"]
+	 * are written by CASClient::CASClient(), read by CASClient::getServerVersion(), 
+	 * CASClient::getServerHostname(), CASClient::getServerPort() and CASClient::getServerURI().
+	 *
+	 * The other fields are written and read by CASClient::getServerBaseURL(), 
+	 * CASClient::getServerLoginURL(), CASClient::getServerServiceValidateURL(), 
+	 * CASClient::getServerProxyValidateURL() and CASClient::getServerLogoutURL().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_server = array(
+		'version' => -1,
+		'hostname' => 'none',
+		'port' => -1,
+		'uri' => 'none'
+	);
+	
+	/**
+	 * This method is used to retrieve the version of the CAS server.
+	 * @return the version of the CAS server.
+	 * @private
+	 */
+	function getServerVersion()
+		{ 
+		return $this->_server['version']; 
+		}
+	
+	/**
+	 * This method is used to retrieve the hostname of the CAS server.
+	 * @return the hostname of the CAS server.
+	 * @private
+	 */
+	function getServerHostname()
+		{ return $this->_server['hostname']; }
+	
+	/**
+	 * This method is used to retrieve the port of the CAS server.
+	 * @return the port of the CAS server.
+	 * @private
+	 */
+	function getServerPort()
+		{ return $this->_server['port']; }
+	
+	/**
+	 * This method is used to retrieve the URI of the CAS server.
+	 * @return a URI.
+	 * @private
+	 */
+	function getServerURI()
+		{ return $this->_server['uri']; }
+	
+	/**
+	 * This method is used to retrieve the base URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerBaseURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['base_url']) ) {
+			$this->_server['base_url'] = 'https://'
+				.$this->getServerHostname()
+				.':'
+				.$this->getServerPort()
+				.$this->getServerURI();
+		}
+		return $this->_server['base_url']; 
+		}
+	
+	/**
+	 * This method is used to retrieve the login URL of the CAS server.
+	 * @param $gateway true to check authentication, false to force it
+	 * @param $renew true to force the authentication with the CAS server
+	 * NOTE : It is recommended that CAS implementations ignore the
+	 "gateway" parameter if "renew" is set
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerLoginURL($gateway=false,$renew=false) {
+		phpCAS::traceBegin();
+		// the URL is build only when needed
+		if ( empty($this->_server['login_url']) ) {
+			$this->_server['login_url'] = $this->getServerBaseURL();
+			$this->_server['login_url'] .= 'login?service=';
+			// $this->_server['login_url'] .= preg_replace('/&/','%26',$this->getURL());
+			$this->_server['login_url'] .= urlencode($this->getURL());
+			if($renew) {
+				// It is recommended that when the "renew" parameter is set, its value be "true"
+				$this->_server['login_url'] .= '&renew=true';
+			} elseif ($gateway) {
+				// It is recommended that when the "gateway" parameter is set, its value be "true"
+				$this->_server['login_url'] .= '&gateway=true';
+			}
+		}
+		phpCAS::traceEnd($this->_server['login_url']);
+		return $this->_server['login_url'];
+	} 
+	
+	/**
+	 * This method sets the login URL of the CAS server.
+	 * @param $url the login URL
+	 * @private
+	 * @since 0.4.21 by Wyman Chan
+	 */
+	function setServerLoginURL($url)
+		{
+		return $this->_server['login_url'] = $url;
+		}
+	
+	
+	/**
+	 * This method sets the serviceValidate URL of the CAS server.
+	 * @param $url the serviceValidate URL
+	 * @private
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerServiceValidateURL($url)
+		{
+		return $this->_server['service_validate_url'] = $url;
+		}
+	
+	
+	/**
+	 * This method sets the proxyValidate URL of the CAS server.
+	 * @param $url the proxyValidate URL
+	 * @private
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerProxyValidateURL($url)
+		{
+		return $this->_server['proxy_validate_url'] = $url;
+		}
+	
+	
+	/**
+	 * This method sets the samlValidate URL of the CAS server.
+	 * @param $url the samlValidate URL
+	 * @private
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerSamlValidateURL($url)
+		{
+		return $this->_server['saml_validate_url'] = $url;
+		}
+	
+	
+	/**
+	 * This method is used to retrieve the service validating URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerServiceValidateURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['service_validate_url']) ) {
+			switch ($this->getServerVersion()) {
+				case CAS_VERSION_1_0:
+					$this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
+					break;
+				case CAS_VERSION_2_0:
+					$this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
+					break;
+			}
+		}
+		//      return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
+		return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL()); 
+		}
+	/**
+	 * This method is used to retrieve the SAML validating URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerSamlValidateURL()
+		{
+		phpCAS::traceBegin();
+		// the URL is build only when needed
+		if ( empty($this->_server['saml_validate_url']) ) {
+			switch ($this->getServerVersion()) {
+				case SAML_VERSION_1_1:
+					$this->_server['saml_validate_url'] = $this->getServerBaseURL().'samlValidate';
+					break;
+			}
+		}
+		phpCAS::traceEnd($this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL()));
+		return $this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL());
+		}
+	/**
+	 * This method is used to retrieve the proxy validating URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerProxyValidateURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['proxy_validate_url']) ) {
+			switch ($this->getServerVersion()) {
+				case CAS_VERSION_1_0:
+					$this->_server['proxy_validate_url'] = '';
+					break;
+				case CAS_VERSION_2_0:
+					$this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
+					break;
+			}
+		}
+		//      return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
+		return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL()); 
+		}
+	
+	/**
+	 * This method is used to retrieve the proxy URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerProxyURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['proxy_url']) ) {
+			switch ($this->getServerVersion()) {
+				case CAS_VERSION_1_0:
+					$this->_server['proxy_url'] = '';
+					break;
+				case CAS_VERSION_2_0:
+					$this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
+					break;
+			}
+		}
+		return $this->_server['proxy_url']; 
+		}
+	
+	/**
+	 * This method is used to retrieve the logout URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerLogoutURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['logout_url']) ) {
+			$this->_server['logout_url'] = $this->getServerBaseURL().'logout';
+		}
+		return $this->_server['logout_url']; 
+		}
+	
+	/**
+	 * This method sets the logout URL of the CAS server.
+	 * @param $url the logout URL
+	 * @private
+	 * @since 0.4.21 by Wyman Chan
+	 */
+	function setServerLogoutURL($url)
+		{
+		return $this->_server['logout_url'] = $url;
+		}
+	
+	/**
+	 * An array to store extra curl options.
+	 */	
+	var $_curl_options = array();
+	
+	/**
+	 * This method is used to set additional user curl options.
+	 */
+	function setExtraCurlOption($key, $value)
+		{
+		$this->_curl_options[$key] = $value;
+		}
+	
+	/**
+	 * This method checks to see if the request is secured via HTTPS
+	 * @return true if https, false otherwise
+	 * @private
+	 */
+	function isHttps() {
+		//if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ) {
+		//0.4.24 by Hinnack
+		if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
+			return true;
+		} else {
+			return false;
+		}
+	}
+	
+	// ########################################################################
+	//  CONSTRUCTOR
+	// ########################################################################
+	/**
+	 * CASClient constructor.
+	 *
+	 * @param $server_version the version of the CAS server
+	 * @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise
+	 * @param $server_hostname the hostname of the CAS server
+	 * @param $server_port the port the CAS server is running on
+	 * @param $server_uri the URI the CAS server is responding on
+	 * @param $start_session Have phpCAS start PHP sessions (default true)
+	 *
+	 * @return a newly created CASClient object
+	 *
+	 * @public
+	 */
+	function CASClient(
+					   $server_version,
+					   $proxy,
+					   $server_hostname,
+					   $server_port,
+					   $server_uri,
+					   $start_session = true) {
+		
+		phpCAS::traceBegin();
+		
+		// the redirect header() call and DOM parsing code from domxml-php4-php5.php won't work in PHP4 compatibility mode
+		if (version_compare(PHP_VERSION,'5','>=') && ini_get('zend.ze1_compatibility_mode')) {
+			phpCAS::error('phpCAS cannot support zend.ze1_compatibility_mode. Sorry.');
+		}
+		// skip Session Handling for logout requests and if don't want it'
+		if ($start_session && !$this->isLogoutRequest()) {
+			phpCAS::trace("Starting session handling");
+			// Check for Tickets from the CAS server
+			if (empty($_GET['ticket'])){
+				phpCAS::trace("No ticket found");
+				// only create a session if necessary
+				if (!session_id()) {
+					phpCAS::trace("No session found, creating new session");
+					session_start();
+				}
+			}else{
+				phpCAS::trace("Ticket found");
+				// We have to copy any old data before renaming the session
+				if (session_id()) {
+					phpCAS::trace("Old active session found, saving old data and destroying session");
+					$old_session = $_SESSION;
+					session_destroy();	
+				}else{
+					session_start();
+					phpCAS::trace("Starting possible old session to copy variables");
+					$old_session = $_SESSION;
+					session_destroy();	
+				}
+				// set up a new session, of name based on the ticket
+				$session_id = preg_replace('/[^\w]/','',$_GET['ticket']);
+				phpCAS::LOG("Session ID: " . $session_id);
+				session_id($session_id);
+				session_start();
+				// restore old session vars
+				if(isset($old_session)){
+					phpCAS::trace("Restoring old session vars");
+					$_SESSION = $old_session;
+				}
+			}
+		}else{
+			phpCAS::trace("Skipping session creation");
+		}
+		
+		
+		// are we in proxy mode ?
+		$this->_proxy = $proxy;
+		
+		//check version
+		switch ($server_version) {
+			case CAS_VERSION_1_0:
+				if ( $this->isProxy() )
+					phpCAS::error('CAS proxies are not supported in CAS '
+						.$server_version);
+				break;
+			case CAS_VERSION_2_0:
+				break;
+			case SAML_VERSION_1_1:
+				break;
+			default:
+				phpCAS::error('this version of CAS (`'
+					.$server_version
+					.'\') is not supported by phpCAS '
+					.phpCAS::getVersion());
+		}
+		$this->_server['version'] = $server_version;
+		
+		// check hostname
+		if ( empty($server_hostname) 
+				|| !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
+			phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
+		}
+		$this->_server['hostname'] = $server_hostname;
+		
+		// check port
+		if ( $server_port == 0 
+			|| !is_int($server_port) ) {
+			phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
+		}
+		$this->_server['port'] = $server_port;
+		
+		// check URI
+		if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
+			phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
+		}
+		// add leading and trailing `/' and remove doubles      
+		$server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
+		$this->_server['uri'] = $server_uri;
+		
+		// set to callback mode if PgtIou and PgtId CGI GET parameters are provided 
+		if ( $this->isProxy() ) {
+			$this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
+		}
+		
+		if ( $this->isCallbackMode() ) {
+			//callback mode: check that phpCAS is secured
+			if ( !$this->isHttps() ) {
+				phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
+			}
+		} else {
+			//normal mode: get ticket and remove it from CGI parameters for developpers
+			$ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
+			switch ($this->getServerVersion()) {
+				case CAS_VERSION_1_0: // check for a Service Ticket
+					if( preg_match('/^ST-/',$ticket) ) {
+						phpCAS::trace('ST \''.$ticket.'\' found');
+						//ST present
+						$this->setST($ticket);
+						//ticket has been taken into account, unset it to hide it to applications
+						unset($_GET['ticket']);
+					} else if ( !empty($ticket) ) {
+						//ill-formed ticket, halt
+						phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
+					}
+					break;
+				case CAS_VERSION_2_0: // check for a Service or Proxy Ticket
+					if( preg_match('/^[SP]T-/',$ticket) ) {
+						phpCAS::trace('ST or PT \''.$ticket.'\' found');
+						$this->setPT($ticket);
+						unset($_GET['ticket']);
+					} else if ( !empty($ticket) ) {
+						//ill-formed ticket, halt
+						phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
+					} 
+					break;
+				case SAML_VERSION_1_1: // SAML just does Service Tickets
+					if( preg_match('/^[SP]T-/',$ticket) ) {
+						phpCAS::trace('SA \''.$ticket.'\' found');
+						$this->setSA($ticket);
+						unset($_GET['ticket']);
+					} else if ( !empty($ticket) ) {
+						//ill-formed ticket, halt
+						phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
+					}
+					break;
+			}
+		}
+		phpCAS::traceEnd();
+	}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                           AUTHENTICATION                           XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	/**
+	 * @addtogroup internalAuthentication
+	 * @{
+	 */  
+	
+	/**
+	 * The Authenticated user. Written by CASClient::setUser(), read by CASClient::getUser().
+	 * @attention client applications should use phpCAS::getUser().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_user = '';
+	
+	/**
+	 * This method sets the CAS user's login name.
+	 *
+	 * @param $user the login name of the authenticated user.
+	 *
+	 * @private
+	 */
+	function setUser($user)
+		{
+		$this->_user = $user;
+		}
+	
+	/**
+	 * This method returns the CAS user's login name.
+	 * @warning should be called only after CASClient::forceAuthentication() or 
+	 * CASClient::isAuthenticated(), otherwise halt with an error.
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getUser()
+		{
+		if ( empty($this->_user) ) {
+			phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
+		}
+		return $this->_user;
+		}
+	
+	
+	
+	/***********************************************************************************************************************
+	 * Atrributes section
+	 * 
+	 * @author Matthias Crauwels <matthias.crauwels@ugent.be>, Ghent University, Belgium
+	 * 
+	 ***********************************************************************************************************************/
+	/**
+	 * The Authenticated users attributes. Written by CASClient::setAttributes(), read by CASClient::getAttributes().
+	 * @attention client applications should use phpCAS::getAttributes().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */	
+	var $_attributes = array();
+	
+	function setAttributes($attributes)	
+		{ $this->_attributes = $attributes; }
+	
+	function getAttributes() {
+		if ( empty($this->_user) ) { // if no user is set, there shouldn't be any attributes also...
+			phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
+		}
+		return $this->_attributes;
+	}
+	
+	function hasAttributes()
+		{ return !empty($this->_attributes); }
+	
+	function hasAttribute($key)
+		{ return (is_array($this->_attributes) && array_key_exists($key, $this->_attributes)); }
+	
+	function getAttribute($key)	{
+		if($this->hasAttribute($key)) {
+			return $this->_attributes[$key];
+		}
+	}
+	
+	/**
+	 * This method is called to renew the authentication of the user
+	 * If the user is authenticated, renew the connection
+	 * If not, redirect to CAS
+	 * @public
+	 */
+	function renewAuthentication(){
+		phpCAS::traceBegin();
+		// Either way, the user is authenticated by CAS
+		if( isset( $_SESSION['phpCAS']['auth_checked'] ) )
+			unset($_SESSION['phpCAS']['auth_checked']);
+		if ( $this->isAuthenticated() ) {
+			phpCAS::trace('user already authenticated; renew');
+			$this->redirectToCas(false,true);
+		} else {
+			$this->redirectToCas();
+		}
+		phpCAS::traceEnd();
+	}
+	
+	/**
+	 * This method is called to be sure that the user is authenticated. When not 
+	 * authenticated, halt by redirecting to the CAS server; otherwise return TRUE.
+	 * @return TRUE when the user is authenticated; otherwise halt.
+	 * @public
+	 */
+	function forceAuthentication()
+		{
+		phpCAS::traceBegin();
+		
+		if ( $this->isAuthenticated() ) {
+			// the user is authenticated, nothing to be done.
+			phpCAS::trace('no need to authenticate');
+			$res = TRUE;
+		} else {
+			// the user is not authenticated, redirect to the CAS server
+			if (isset($_SESSION['phpCAS']['auth_checked'])) {
+				unset($_SESSION['phpCAS']['auth_checked']);
+			}
+			$this->redirectToCas(FALSE/* no gateway */);	
+			// never reached
+			$res = FALSE;
+		}
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * An integer that gives the number of times authentication will be cached before rechecked.
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_cache_times_for_auth_recheck = 0;
+	
+	/**
+	 * Set the number of times authentication will be cached before rechecked.
+	 *
+	 * @param $n an integer.
+	 *
+	 * @public
+	 */
+	function setCacheTimesForAuthRecheck($n)
+		{
+		$this->_cache_times_for_auth_recheck = $n;
+		}
+	
+	/**
+	 * This method is called to check whether the user is authenticated or not.
+	 * @return TRUE when the user is authenticated, FALSE otherwise.
+	 * @public
+	 */
+	function checkAuthentication()
+		{
+		phpCAS::traceBegin();
+		
+		if ( $this->isAuthenticated() ) {
+			phpCAS::trace('user is authenticated');
+			$res = TRUE;
+		} else if (isset($_SESSION['phpCAS']['auth_checked'])) {
+			// the previous request has redirected the client to the CAS server with gateway=true
+			unset($_SESSION['phpCAS']['auth_checked']);
+			$res = FALSE;
+		} else {
+			//        $_SESSION['phpCAS']['auth_checked'] = true;
+			//	    $this->redirectToCas(TRUE/* gateway */);	
+			//	    // never reached
+			//	    $res = FALSE;
+			// avoid a check against CAS on every request
+			if (! isset($_SESSION['phpCAS']['unauth_count']) )
+				$_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized
+			
+			if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1) 
+					|| ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
+			{
+				$res = FALSE;
+				
+				if ($this->_cache_times_for_auth_recheck != -1)
+				{
+					$_SESSION['phpCAS']['unauth_count']++;
+					phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
+				}
+				else
+				{
+					phpCAS::trace('user is not authenticated (cached for until login pressed)');
+				}
+			}
+			else
+			{
+				$_SESSION['phpCAS']['unauth_count'] = 0;
+				$_SESSION['phpCAS']['auth_checked'] = true;
+				phpCAS::trace('user is not authenticated (cache reset)');
+				$this->redirectToCas(TRUE/* gateway */);	
+				// never reached
+				$res = FALSE;
+			}
+		}
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * This method is called to check if the user is authenticated (previously or by
+	 * tickets given in the URL).
+	 *
+	 * @return TRUE when the user is authenticated. Also may redirect to the same URL without the ticket.
+	 *
+	 * @public
+	 */
+	function isAuthenticated()
+		{
+		phpCAS::traceBegin();
+		$res = FALSE;
+		$validate_url = '';
+		
+		if ( $this->wasPreviouslyAuthenticated() ) {
+			// the user has already (previously during the session) been
+			// authenticated, nothing to be done.
+			phpCAS::trace('user was already authenticated, no need to look for tickets');
+			$res = TRUE;
+		}
+		else {
+			if ( $this->hasST() ) {
+				// if a Service Ticket was given, validate it
+				phpCAS::trace('ST `'.$this->getST().'\' is present');
+				$this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
+				phpCAS::trace('ST `'.$this->getST().'\' was validated');
+				if ( $this->isProxy() ) {
+					$this->validatePGT($validate_url,$text_response,$tree_response); // idem
+					phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
+					$_SESSION['phpCAS']['pgt'] = $this->getPGT();
+				}
+				$_SESSION['phpCAS']['user'] = $this->getUser();
+				$res = TRUE;
+			}
+			elseif ( $this->hasPT() ) {
+				// if a Proxy Ticket was given, validate it
+				phpCAS::trace('PT `'.$this->getPT().'\' is present');
+				$this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts
+				phpCAS::trace('PT `'.$this->getPT().'\' was validated');
+				if ( $this->isProxy() ) {
+					$this->validatePGT($validate_url,$text_response,$tree_response); // idem
+					phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
+					$_SESSION['phpCAS']['pgt'] = $this->getPGT();
+				}
+				$_SESSION['phpCAS']['user'] = $this->getUser();
+				$res = TRUE;
+			}
+			elseif ( $this->hasSA() ) {
+				// if we have a SAML ticket, validate it.
+				phpCAS::trace('SA `'.$this->getSA().'\' is present');
+				$this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts
+				phpCAS::trace('SA `'.$this->getSA().'\' was validated');
+				$_SESSION['phpCAS']['user'] = $this->getUser();
+				$_SESSION['phpCAS']['attributes'] = $this->getAttributes();
+				$res = TRUE;
+			}
+			else {
+				// no ticket given, not authenticated
+				phpCAS::trace('no ticket found');
+			}
+			if ($res) {
+				// if called with a ticket parameter, we need to redirect to the app without the ticket so that CAS-ification is transparent to the browser (for later POSTS)
+				// most of the checks and errors should have been made now, so we're safe for redirect without masking error messages.
+				header('Location: '.$this->getURL());
+				phpCAS::log( "Prepare redirect to : ".$this->getURL() );
+			}
+		}
+		
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * This method tells if the current session is authenticated.
+	 * @return true if authenticated based soley on $_SESSION variable
+	 * @since 0.4.22 by Brendan Arnold
+	 */
+	function isSessionAuthenticated ()
+		{
+		return !empty($_SESSION['phpCAS']['user']);
+		}
+	
+	/**
+	 * This method tells if the user has already been (previously) authenticated
+	 * by looking into the session variables.
+	 *
+	 * @note This function switches to callback mode when needed.
+	 *
+	 * @return TRUE when the user has already been authenticated; FALSE otherwise.
+	 *
+	 * @private
+	 */
+	function wasPreviouslyAuthenticated()
+		{
+		phpCAS::traceBegin();
+		
+		if ( $this->isCallbackMode() ) {
+			$this->callback();
+		}
+		
+		$auth = FALSE;
+		
+		if ( $this->isProxy() ) {
+			// CAS proxy: username and PGT must be present
+			if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
+				// authentication already done
+				$this->setUser($_SESSION['phpCAS']['user']);
+				$this->setPGT($_SESSION['phpCAS']['pgt']);
+				phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\''); 
+				$auth = TRUE;
+			} elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
+				// these two variables should be empty or not empty at the same time
+				phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
+				// unset all tickets to enforce authentication
+				unset($_SESSION['phpCAS']);
+				$this->setST('');
+				$this->setPT('');
+			} elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
+				// these two variables should be empty or not empty at the same time
+				phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty'); 
+				// unset all tickets to enforce authentication
+				unset($_SESSION['phpCAS']);
+				$this->setST('');
+				$this->setPT('');
+			} else {
+				phpCAS::trace('neither user not PGT found'); 
+			}
+		} else {
+			// `simple' CAS client (not a proxy): username must be present
+			if ( $this->isSessionAuthenticated() ) {
+				// authentication already done
+				$this->setUser($_SESSION['phpCAS']['user']);
+				if(isset($_SESSION['phpCAS']['attributes'])){
+					$this->setAttributes($_SESSION['phpCAS']['attributes']);
+				}
+				phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\''); 
+				$auth = TRUE;
+			} else {
+				phpCAS::trace('no user found');
+			}
+		}
+		
+		phpCAS::traceEnd($auth);
+		return $auth;
+		}
+	
+	/**
+	 * This method is used to redirect the client to the CAS server.
+	 * It is used by CASClient::forceAuthentication() and CASClient::checkAuthentication().
+	 * @param $gateway true to check authentication, false to force it
+	 * @param $renew true to force the authentication with the CAS server
+	 * @public
+	 */
+	function redirectToCas($gateway=false,$renew=false){
+		phpCAS::traceBegin();
+		$cas_url = $this->getServerLoginURL($gateway,$renew);
+		header('Location: '.$cas_url);
+		phpCAS::log( "Redirect to : ".$cas_url );
+		
+		$this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
+		
+		printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
+		$this->printHTMLFooter();
+		
+		phpCAS::traceExit();
+		exit();
+	}
+	
+	
+	/**
+	 * This method is used to logout from CAS.
+	 * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server
+	 * @public
+	 */
+	function logout($params) {
+		phpCAS::traceBegin();
+		$cas_url = $this->getServerLogoutURL();
+		$paramSeparator = '?';
+		if (isset($params['url'])) {
+			$cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']); 
+			$paramSeparator = '&';
+		}
+		if (isset($params['service'])) {
+			$cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']); 
+		}
+		header('Location: '.$cas_url);
+		phpCAS::log( "Prepare redirect to : ".$cas_url );
+		
+		session_unset();
+		session_destroy();
+		
+		$this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
+		printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
+		$this->printHTMLFooter();
+		
+		phpCAS::traceExit();
+		exit();
+	}
+	
+	/**
+	 * @return true if the current request is a logout request.
+	 * @private
+	 */
+	function isLogoutRequest() {
+		return !empty($_POST['logoutRequest']);
+	}
+	
+	/**
+	 * @return true if a logout request is allowed.
+	 * @private
+	 */
+	function isLogoutRequestAllowed() {
+	}
+	
+	/**
+	 * This method handles logout requests.
+	 * @param $check_client true to check the client bofore handling the request, 
+	 * false not to perform any access control. True by default.
+	 * @param $allowed_clients an array of host names allowed to send logout requests. 
+	 * By default, only the CAs server (declared in the constructor) will be allowed.
+	 * @public
+	 */
+	function handleLogoutRequests($check_client=true, $allowed_clients=false) {
+		phpCAS::traceBegin();
+		if (!$this->isLogoutRequest()) {
+			phpCAS::log("Not a logout request");
+			phpCAS::traceEnd();
+			return;
+		}
+		phpCAS::log("Logout requested");
+		phpCAS::log("SAML REQUEST: ".$_POST['logoutRequest']);
+		if ($check_client) {
+			if (!$allowed_clients) {
+				$allowed_clients = array( $this->getServerHostname() ); 
+			}
+			$client_ip = $_SERVER['REMOTE_ADDR'];
+			$client = gethostbyaddr($client_ip);
+			phpCAS::log("Client: ".$client."/".$client_ip); 
+			$allowed = false;
+			foreach ($allowed_clients as $allowed_client) {
+				if (($client == $allowed_client) or ($client_ip == $allowed_client)) { 
+					phpCAS::log("Allowed client '".$allowed_client."' matches, logout request is allowed");
+					$allowed = true;
+					break;
+				} else {
+					phpCAS::log("Allowed client '".$allowed_client."' does not match");
+				}
+			}
+			if (!$allowed) {
+				phpCAS::error("Unauthorized logout request from client '".$client."'");
+				printf("Unauthorized!");
+				phpCAS::traceExit();
+				exit();
+			}
+		} else {
+			phpCAS::log("No access control set");
+		}
+		// Extract the ticket from the SAML Request
+		preg_match("|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", $_POST['logoutRequest'], $tick, PREG_OFFSET_CAPTURE, 3);
+		$wrappedSamlSessionIndex = preg_replace('|<samlp:SessionIndex>|','',$tick[0][0]);
+		$ticket2logout = preg_replace('|</samlp:SessionIndex>|','',$wrappedSamlSessionIndex);
+		phpCAS::log("Ticket to logout: ".$ticket2logout);
+		$session_id = preg_replace('/[^\w]/','',$ticket2logout);
+		phpCAS::log("Session id: ".$session_id);
+		
+		// destroy a possible application session created before phpcas
+		if(session_id()){
+			session_unset();
+			session_destroy();
+		}
+		// fix session ID
+		session_id($session_id);
+		$_COOKIE[session_name()]=$session_id;
+		$_GET[session_name()]=$session_id;
+		
+		// Overwrite session
+		session_start();	
+		session_unset();
+		session_destroy();
+		printf("Disconnected!");
+		phpCAS::traceExit();
+		exit();
+	}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                  BASIC CLIENT FEATURES (CAS 1.0)                   XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	// ########################################################################
+	//  ST
+	// ########################################################################
+	/**
+	 * @addtogroup internalBasic
+	 * @{
+	 */  
+	
+	/**
+	 * the Service Ticket provided in the URL of the request if present
+	 * (empty otherwise). Written by CASClient::CASClient(), read by 
+	 * CASClient::getST() and CASClient::hasPGT().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_st = '';
+	
+	/**
+	 * This method returns the Service Ticket provided in the URL of the request.
+	 * @return The service ticket.
+	 * @private
+	 */
+	function getST()
+		{ return $this->_st; }
+	
+	/**
+	 * This method stores the Service Ticket.
+	 * @param $st The Service Ticket.
+	 * @private
+	 */
+	function setST($st)
+		{ $this->_st = $st; }
+	
+	/**
+	 * This method tells if a Service Ticket was stored.
+	 * @return TRUE if a Service Ticket has been stored.
+	 * @private
+	 */
+	function hasST()
+		{ return !empty($this->_st); }
+	
+	/** @} */
+	
+	// ########################################################################
+	//  ST VALIDATION
+	// ########################################################################
+	/**
+	 * @addtogroup internalBasic
+	 * @{
+	 */  
+	
+	/**
+	 * the certificate of the CAS server.
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_cas_server_cert = '';
+	
+	/**
+	 * the certificate of the CAS server CA.
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_cas_server_ca_cert = '';
+	
+	/**
+	 * Set to true not to validate the CAS server.
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_no_cas_server_validation = false;
+	
+	/**
+	 * Set the certificate of the CAS server.
+	 *
+	 * @param $cert the PEM certificate
+	 */
+	function setCasServerCert($cert)
+		{
+		$this->_cas_server_cert = $cert;
+		}
+	
+	/**
+	 * Set the CA certificate of the CAS server.
+	 *
+	 * @param $cert the PEM certificate of the CA that emited the cert of the server
+	 */
+	function setCasServerCACert($cert)
+		{
+		$this->_cas_server_ca_cert = $cert;
+		}
+	
+	/**
+	 * Set no SSL validation for the CAS server.
+	 */
+	function setNoCasServerValidation()
+		{
+		$this->_no_cas_server_validation = true;
+		}
+	
+	/**
+	 * This method is used to validate a ST; halt on failure, and sets $validate_url,
+	 * $text_reponse and $tree_response on success. These parameters are used later
+	 * by CASClient::validatePGT() for CAS proxies.
+	 * Used for all CAS 1.0 validations
+	 * @param $validate_url the URL of the request to the CAS server.
+	 * @param $text_response the response of the CAS server, as is (XML text).
+	 * @param $tree_response the response of the CAS server, as a DOM XML tree.
+	 *
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function validateST($validate_url,&$text_response,&$tree_response)
+		{
+		phpCAS::traceBegin();
+		// build the URL to validate the ticket
+		$validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
+		if ( $this->isProxy() ) {
+			// pass the callback url for CAS proxies
+			$validate_url .= '&pgtUrl='.$this->getCallbackURL();
+		}
+		
+		// open and read the URL
+		if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
+			phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
+			$this->authError('ST not validated',
+				$validate_url,
+				TRUE/*$no_response*/);
+		}
+		
+		// analyze the result depending on the version
+		switch ($this->getServerVersion()) {
+			case CAS_VERSION_1_0:
+				if (preg_match('/^no\n/',$text_response)) {
+					phpCAS::trace('ST has not been validated');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						FALSE/*$bad_response*/,
+						$text_response);
+				}
+				if (!preg_match('/^yes\n/',$text_response)) {
+					phpCAS::trace('ill-formed response');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// ST has been validated, extract the user name
+				$arr = preg_split('/\n/',$text_response);
+				$this->setUser(trim($arr[1]));
+				break;
+			case CAS_VERSION_2_0:
+				// read the response of the CAS server into a DOM object
+				if ( !($dom = domxml_open_mem($text_response))) {
+					phpCAS::trace('domxml_open_mem() failed');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// read the root node of the XML tree
+				if ( !($tree_response = $dom->document_element()) ) {
+					phpCAS::trace('document_element() failed');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// insure that tag name is 'serviceResponse'
+				if ( $tree_response->node_name() != 'serviceResponse' ) {
+					phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
+					// authentication succeded, extract the user name
+					if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
+						phpCAS::trace('<authenticationSuccess> found, but no <user>');
+						$this->authError('ST not validated',
+							$validate_url,
+							FALSE/*$no_response*/,
+							TRUE/*$bad_response*/,
+							$text_response);
+					}
+					$user = trim($user_elements[0]->get_content());
+					phpCAS::trace('user = `'.$user);
+					$this->setUser($user);
+					
+				} else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
+					phpCAS::trace('<authenticationFailure> found');
+					// authentication failed, extract the error code and message
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						FALSE/*$bad_response*/,
+						$text_response,
+						$failure_elements[0]->get_attribute('code')/*$err_code*/,
+						trim($failure_elements[0]->get_content())/*$err_msg*/);
+				} else {
+					phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				break;
+		}
+		
+		// at this step, ST has been validated and $this->_user has been set,
+		phpCAS::traceEnd(TRUE);
+		return TRUE;
+		}
+	
+	// ########################################################################
+	//  SAML VALIDATION
+	// ########################################################################
+	/**
+	 * @addtogroup internalBasic
+	 * @{
+	 */
+	
+	/**
+	 * This method is used to validate a SAML TICKET; halt on failure, and sets $validate_url,
+	 * $text_reponse and $tree_response on success. These parameters are used later
+	 * by CASClient::validatePGT() for CAS proxies.
+	 *
+	 * @param $validate_url the URL of the request to the CAS server.
+	 * @param $text_response the response of the CAS server, as is (XML text).
+	 * @param $tree_response the response of the CAS server, as a DOM XML tree.
+	 *
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function validateSA($validate_url,&$text_response,&$tree_response)
+		{
+		phpCAS::traceBegin();
+		
+		// build the URL to validate the ticket
+		$validate_url = $this->getServerSamlValidateURL();
+		
+		// open and read the URL
+		if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
+			phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
+			$this->authError('SA not validated', $validate_url, TRUE/*$no_response*/);
+		}
+		
+		phpCAS::trace('server version: '.$this->getServerVersion());
+		
+		// analyze the result depending on the version
+		switch ($this->getServerVersion()) {
+			case SAML_VERSION_1_1:
+				
+				// read the response of the CAS server into a DOM object
+				if ( !($dom = domxml_open_mem($text_response))) {
+					phpCAS::trace('domxml_open_mem() failed');
+					$this->authError('SA not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// read the root node of the XML tree
+				if ( !($tree_response = $dom->document_element()) ) {
+					phpCAS::trace('document_element() failed');
+					$this->authError('SA not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// insure that tag name is 'Envelope'
+				if ( $tree_response->node_name() != 'Envelope' ) {
+					phpCAS::trace('bad XML root node (should be `Envelope\' instead of `'.$tree_response->node_name().'\'');
+					$this->authError('SA not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// check for the NameIdentifier tag in the SAML response
+				if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("NameIdentifier")) != 0) {
+					phpCAS::trace('NameIdentifier found');
+					$user = trim($success_elements[0]->get_content());
+					phpCAS::trace('user = `'.$user.'`');
+					$this->setUser($user);
+					$this->setSessionAttributes($text_response);
+				} else {
+					phpCAS::trace('no <NameIdentifier> tag found in SAML payload');
+					$this->authError('SA not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				break;
+		}
+		
+		// at this step, ST has been validated and $this->_user has been set,
+		phpCAS::traceEnd(TRUE);
+		return TRUE;
+		}
+	
+	/**
+	 * This method will parse the DOM and pull out the attributes from the SAML
+	 * payload and put them into an array, then put the array into the session.
+	 *
+	 * @param $text_response the SAML payload.
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function setSessionAttributes($text_response)
+		{
+		phpCAS::traceBegin();
+		
+		$result = FALSE;
+		
+		if (isset($_SESSION[SAML_ATTRIBUTES])) {
+			phpCAS::trace("session attrs already set.");  //testbml - do we care?
+		}
+		
+		$attr_array = array();
+		
+		if (($dom = domxml_open_mem($text_response))) {
+			$xPath = $dom->xpath_new_context();
+			$xPath->xpath_register_ns('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
+			$xPath->xpath_register_ns('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
+			$nodelist = $xPath->xpath_eval("//saml:Attribute");
+			$attrs = $nodelist->nodeset;
+			phpCAS::trace($text_response);
+			foreach($attrs as $attr){
+				$xres = $xPath->xpath_eval("saml:AttributeValue", $attr);
+				$name = $attr->get_attribute("AttributeName");
+				$value_array = array();
+				foreach($xres->nodeset as $node){
+					$value_array[] = $node->get_content();
+					
+				}
+				phpCAS::trace("* " . $name . "=" . $value_array);
+				$attr_array[$name] = $value_array;
+			}
+			$_SESSION[SAML_ATTRIBUTES] = $attr_array;
+			// UGent addition...
+			foreach($attr_array as $attr_key => $attr_value) {
+				if(count($attr_value) > 1) {
+					$this->_attributes[$attr_key] = $attr_value;
+				}
+				else {
+					$this->_attributes[$attr_key] = $attr_value[0];
+				}
+			}
+			$result = TRUE;
+		}
+		phpCAS::traceEnd($result);
+		return $result;
+		}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                     PROXY FEATURES (CAS 2.0)                       XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	// ########################################################################
+	//  PROXYING
+	// ########################################################################
+	/**
+	 * @addtogroup internalProxy
+	 * @{
+	 */
+	
+	/**
+	 * A boolean telling if the client is a CAS proxy or not. Written by CASClient::CASClient(), 
+	 * read by CASClient::isProxy().
+	 *
+	 * @private
+	 */
+	var $_proxy;
+	
+	/**
+	 * Tells if a CAS client is a CAS proxy or not
+	 *
+	 * @return TRUE when the CAS client is a CAs proxy, FALSE otherwise
+	 *
+	 * @private
+	 */
+	function isProxy()
+		{
+		return $this->_proxy;
+		}
+	
+	/** @} */
+	// ########################################################################
+	//  PGT
+	// ########################################################################
+	/**
+	 * @addtogroup internalProxy
+	 * @{
+	 */  
+	
+	/**
+	 * the Proxy Grnting Ticket given by the CAS server (empty otherwise). 
+	 * Written by CASClient::setPGT(), read by CASClient::getPGT() and CASClient::hasPGT().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_pgt = '';
+	
+	/**
+	 * This method returns the Proxy Granting Ticket given by the CAS server.
+	 * @return The Proxy Granting Ticket.
+	 * @private
+	 */
+	function getPGT()
+		{ return $this->_pgt; }
+	
+	/**
+	 * This method stores the Proxy Granting Ticket.
+	 * @param $pgt The Proxy Granting Ticket.
+	 * @private
+	 */
+	function setPGT($pgt)
+		{ $this->_pgt = $pgt; }
+	
+	/**
+	 * This method tells if a Proxy Granting Ticket was stored.
+	 * @return TRUE if a Proxy Granting Ticket has been stored.
+	 * @private
+	 */
+	function hasPGT()
+		{ return !empty($this->_pgt); }
+	
+	/** @} */
+	
+	// ########################################################################
+	//  CALLBACK MODE
+	// ########################################################################
+	/**
+	 * @addtogroup internalCallback
+	 * @{
+	 */  
+	/**
+	 * each PHP script using phpCAS in proxy mode is its own callback to get the
+	 * PGT back from the CAS server. callback_mode is detected by the constructor
+	 * thanks to the GET parameters.
+	 */
+	
+	/**
+	 * a boolean to know if the CAS client is running in callback mode. Written by
+	 * CASClient::setCallBackMode(), read by CASClient::isCallbackMode().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_callback_mode = FALSE;
+	
+	/**
+	 * This method sets/unsets callback mode.
+	 *
+	 * @param $callback_mode TRUE to set callback mode, FALSE otherwise.
+	 *
+	 * @private
+	 */
+	function setCallbackMode($callback_mode)
+		{
+		$this->_callback_mode = $callback_mode;
+		}
+	
+	/**
+	 * This method returns TRUE when the CAs client is running i callback mode, 
+	 * FALSE otherwise.
+	 *
+	 * @return A boolean.
+	 *
+	 * @private
+	 */
+	function isCallbackMode()
+		{
+		return $this->_callback_mode;
+		}
+	
+	/**
+	 * the URL that should be used for the PGT callback (in fact the URL of the 
+	 * current request without any CGI parameter). Written and read by 
+	 * CASClient::getCallbackURL().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_callback_url = '';
+	
+	/**
+	 * This method returns the URL that should be used for the PGT callback (in
+	 * fact the URL of the current request without any CGI parameter, except if
+	 * phpCAS::setFixedCallbackURL() was used).
+	 *
+	 * @return The callback URL
+	 *
+	 * @private
+	 */
+	function getCallbackURL()
+		{
+		// the URL is built when needed only
+		if ( empty($this->_callback_url) ) {
+			$final_uri = '';
+			// remove the ticket if present in the URL
+			$final_uri = 'https://';
+			/* replaced by Julien Marchal - v0.4.6
+			 * $this->uri .= $_SERVER['SERVER_NAME'];
+			 */
+			if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
+				/* replaced by teedog - v0.4.12
+				 * $final_uri .= $_SERVER['SERVER_NAME'];
+				 */
+				if (empty($_SERVER['SERVER_NAME'])) {
+					$final_uri .= $_SERVER['HTTP_HOST'];
+				} else {
+					$final_uri .= $_SERVER['SERVER_NAME'];
+				}
+			} else {
+				$final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
+			}
+			if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
+					|| (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
+				$final_uri .= ':';
+				$final_uri .= $_SERVER['SERVER_PORT'];
+			}
+			$request_uri = $_SERVER['REQUEST_URI'];
+			$request_uri = preg_replace('/\?.*$/','',$request_uri);
+			$final_uri .= $request_uri;
+			$this->setCallbackURL($final_uri);
+		}
+		return $this->_callback_url;
+		}
+	
+	/**
+	 * This method sets the callback url.
+	 *
+	 * @param $callback_url url to set callback 
+	 *
+	 * @private
+	 */
+	function setCallbackURL($url)
+		{
+		return $this->_callback_url = $url;
+		}
+	
+	/**
+	 * This method is called by CASClient::CASClient() when running in callback
+	 * mode. It stores the PGT and its PGT Iou, prints its output and halts.
+	 *
+	 * @private
+	 */
+	function callback()
+		{
+		phpCAS::traceBegin();
+		$this->printHTMLHeader('phpCAS callback');
+		$pgt_iou = $_GET['pgtIou'];
+		$pgt = $_GET['pgtId'];
+		phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
+		echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
+		$this->storePGT($pgt,$pgt_iou);
+		$this->printHTMLFooter();
+		phpCAS::traceExit();
+		exit();
+		}
+	
+	/** @} */
+	
+	// ########################################################################
+	//  PGT STORAGE
+	// ########################################################################
+	/**
+	 * @addtogroup internalPGTStorage
+	 * @{
+	 */  
+	
+	/**
+	 * an instance of a class inheriting of PGTStorage, used to deal with PGT
+	 * storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used 
+	 * by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_pgt_storage = null;
+	
+	/**
+	 * This method is used to initialize the storage of PGT's.
+	 * Halts on error.
+	 *
+	 * @private
+	 */
+	function initPGTStorage()
+		{
+		// if no SetPGTStorageXxx() has been used, default to file
+		if ( !is_object($this->_pgt_storage) ) {
+			$this->setPGTStorageFile();
+		}
+		
+		// initializes the storage
+		$this->_pgt_storage->init();
+		}
+	
+	/**
+	 * This method stores a PGT. Halts on error.
+	 *
+	 * @param $pgt the PGT to store
+	 * @param $pgt_iou its corresponding Iou
+	 *
+	 * @private
+	 */
+	function storePGT($pgt,$pgt_iou)
+		{
+		// ensure that storage is initialized
+		$this->initPGTStorage();
+		// writes the PGT
+		$this->_pgt_storage->write($pgt,$pgt_iou);
+		}
+	
+	/**
+	 * This method reads a PGT from its Iou and deletes the corresponding storage entry.
+	 *
+	 * @param $pgt_iou the PGT Iou
+	 *
+	 * @return The PGT corresponding to the Iou, FALSE when not found.
+	 *
+	 * @private
+	 */
+	function loadPGT($pgt_iou)
+		{
+		// ensure that storage is initialized
+		$this->initPGTStorage();
+		// read the PGT
+		return $this->_pgt_storage->read($pgt_iou);
+		}
+	
+	/**
+	 * This method is used to tell phpCAS to store the response of the
+	 * CAS server to PGT requests onto the filesystem. 
+	 *
+	 * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
+	 * @param $path the path where the PGT's should be stored
+	 *
+	 * @public
+	 */
+	function setPGTStorageFile($format='',
+		$path='')
+		{
+		// check that the storage has not already been set
+		if ( is_object($this->_pgt_storage) ) {
+			phpCAS::error('PGT storage already defined');
+		}
+		
+		// create the storage object
+		$this->_pgt_storage = new PGTStorageFile($this,$format,$path);
+		}
+	
+	/**
+	 * This method is used to tell phpCAS to store the response of the
+	 * CAS server to PGT requests into a database. 
+	 * @note The connection to the database is done only when needed. 
+	 * As a consequence, bad parameters are detected only when 
+	 * initializing PGT storage.
+	 *
+	 * @param $user the user to access the data with
+	 * @param $password the user's password
+	 * @param $database_type the type of the database hosting the data
+	 * @param $hostname the server hosting the database
+	 * @param $port the port the server is listening on
+	 * @param $database the name of the database
+	 * @param $table the name of the table storing the data
+	 *
+	 * @public
+	 */
+	function setPGTStorageDB($user,
+							 $password,
+							 $database_type,
+							 $hostname,
+							 $port,
+							 $database,
+							 $table)
+		{
+		// check that the storage has not already been set
+		if ( is_object($this->_pgt_storage) ) {
+			phpCAS::error('PGT storage already defined');
+		}
+		
+		// warn the user that he should use file storage...
+		trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING);
+		
+		// create the storage object
+		$this->_pgt_storage = new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table);
+		}
+	
+	// ########################################################################
+	//  PGT VALIDATION
+	// ########################################################################
+	/**
+	 * This method is used to validate a PGT; halt on failure.
+	 * 
+	 * @param $validate_url the URL of the request to the CAS server.
+	 * @param $text_response the response of the CAS server, as is (XML text); result
+	 * of CASClient::validateST() or CASClient::validatePT().
+	 * @param $tree_response the response of the CAS server, as a DOM XML tree; result
+	 * of CASClient::validateST() or CASClient::validatePT().
+	 *
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function validatePGT(&$validate_url,$text_response,$tree_response)
+		{
+		// here cannot use phpCAS::traceBegin(); alongside domxml-php4-to-php5.php
+		phpCAS::log('start validatePGT()');
+		if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
+			phpCAS::trace('<proxyGrantingTicket> not found');
+			// authentication succeded, but no PGT Iou was transmitted
+			$this->authError('Ticket validated but no PGT Iou transmitted',
+				$validate_url,
+				FALSE/*$no_response*/,
+				FALSE/*$bad_response*/,
+				$text_response);
+		} else {
+			// PGT Iou transmitted, extract it
+			$pgt_iou = trim($arr[0]->get_content());
+			$pgt = $this->loadPGT($pgt_iou);
+			if ( $pgt == FALSE ) {
+				phpCAS::trace('could not load PGT');
+				$this->authError('PGT Iou was transmitted but PGT could not be retrieved',
+					$validate_url,
+					FALSE/*$no_response*/,
+					FALSE/*$bad_response*/,
+					$text_response);
+			}
+			$this->setPGT($pgt);
+		}
+		// here, cannot use	phpCAS::traceEnd(TRUE); alongside domxml-php4-to-php5.php
+		phpCAS::log('end validatePGT()');
+		return TRUE;
+		}
+	
+	// ########################################################################
+	//  PGT VALIDATION
+	// ########################################################################
+	
+	/**
+	 * This method is used to retrieve PT's from the CAS server thanks to a PGT.
+	 * 
+	 * @param $target_service the service to ask for with the PT.
+	 * @param $err_code an error code (PHPCAS_SERVICE_OK on success).
+	 * @param $err_msg an error message (empty on success).
+	 *
+	 * @return a Proxy Ticket, or FALSE on error.
+	 *
+	 * @private
+	 */
+	function retrievePT($target_service,&$err_code,&$err_msg)
+		{
+		phpCAS::traceBegin();
+		
+		// by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
+		// set to false and $err_msg to an error message. At the end, if $pt is FALSE 
+		// and $error_msg is still empty, it is set to 'invalid response' (the most
+		// commonly encountered error).
+		$err_msg = '';
+		
+		// build the URL to retrieve the PT
+		//      $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
+		$cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
+		
+		// open and read the URL
+		if ( !$this->readURL($cas_url,''/*cookies*/,$headers,$cas_response,$err_msg) ) {
+			phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
+			$err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
+			$err_msg = 'could not retrieve PT (no response from the CAS server)';
+			phpCAS::traceEnd(FALSE);
+			return FALSE;
+		}
+		
+		$bad_response = FALSE;
+		
+		if ( !$bad_response ) {
+			// read the response of the CAS server into a DOM object
+			if ( !($dom = @domxml_open_mem($cas_response))) {
+				phpCAS::trace('domxml_open_mem() failed');
+				// read failed
+				$bad_response = TRUE;
+			} 
+		}
+		
+		if ( !$bad_response ) {
+			// read the root node of the XML tree
+			if ( !($root = $dom->document_element()) ) {
+				phpCAS::trace('document_element() failed');
+				// read failed
+				$bad_response = TRUE;
+			} 
+		}
+		
+		if ( !$bad_response ) {
+			// insure that tag name is 'serviceResponse'
+			if ( $root->node_name() != 'serviceResponse' ) {
+				phpCAS::trace('node_name() failed');
+				// bad root node
+				$bad_response = TRUE;
+			} 
+		}
+		
+		if ( !$bad_response ) {
+			// look for a proxySuccess tag
+			if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
+				// authentication succeded, look for a proxyTicket tag
+				if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
+					$err_code = PHPCAS_SERVICE_OK;
+					$err_msg = '';
+					phpCAS::trace('original PT: '.trim($arr[0]->get_content()));
+					$pt = trim($arr[0]->get_content());
+					phpCAS::traceEnd($pt);
+					return $pt;
+				} else {
+					phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
+				}
+			} 
+			// look for a proxyFailure tag
+			else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
+				// authentication failed, extract the error
+				$err_code = PHPCAS_SERVICE_PT_FAILURE;
+				$err_msg = 'PT retrieving failed (code=`'
+					.$arr[0]->get_attribute('code')
+					.'\', message=`'
+					.trim($arr[0]->get_content())
+					.'\')';
+				phpCAS::traceEnd(FALSE);
+				return FALSE;
+			} else {
+				phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
+			}
+		}
+		
+		// at this step, we are sure that the response of the CAS server was ill-formed
+		$err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
+		$err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
+		
+		phpCAS::traceEnd(FALSE);
+		return FALSE;
+		}
+	
+	// ########################################################################
+	// ACCESS TO EXTERNAL SERVICES
+	// ########################################################################
+	
+	/**
+	 * This method is used to acces a remote URL.
+	 *
+	 * @param $url the URL to access.
+	 * @param $cookies an array containing cookies strings such as 'name=val'
+	 * @param $headers an array containing the HTTP header lines of the response
+	 * (an empty array on failure).
+	 * @param $body the body of the response, as a string (empty on failure).
+	 * @param $err_msg an error message, filled on failure.
+	 *
+	 * @return TRUE on success, FALSE otherwise (in this later case, $err_msg
+	 * contains an error message).
+	 *
+	 * @private
+	 */
+	function readURL($url,$cookies,&$headers,&$body,&$err_msg)
+		{
+		phpCAS::traceBegin();
+		$headers = '';
+		$body = '';
+		$err_msg = '';
+		
+		$res = TRUE;
+		
+		// initialize the CURL session
+		$ch = curl_init($url);
+		
+		if (version_compare(PHP_VERSION,'5.1.3','>=')) {
+			//only avaible in php5
+			curl_setopt_array($ch, $this->_curl_options);
+		} else {
+			foreach ($this->_curl_options as $key => $value) {
+				curl_setopt($ch, $key, $value);
+			}
+		}
+		
+		if ($this->_cas_server_cert == '' && $this->_cas_server_ca_cert == '' && !$this->_no_cas_server_validation) {
+			phpCAS::error('one of the methods phpCAS::setCasServerCert(), phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.');
+		}
+		if ($this->_cas_server_cert != '' && $this->_cas_server_ca_cert != '') {
+			// This branch added by IDMS. Seems phpCAS implementor got a bit confused about the curl options CURLOPT_SSLCERT and CURLOPT_CAINFO
+			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
+			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
+			curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
+			curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
+			curl_setopt($ch, CURLOPT_VERBOSE, '1');
+			phpCAS::trace('CURL: Set all required opts for mutual authentication ------');
+		} else if ($this->_cas_server_cert != '' ) {
+			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
+			curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
+		} else if ($this->_cas_server_ca_cert != '') {
+			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
+			curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
+		} else {
+			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
+			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
+		}
+		
+		// return the CURL output into a variable
+		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+		// get the HTTP header with a callback
+		$this->_curl_headers = array(); // empty the headers array
+		curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, '_curl_read_headers'));
+		// add cookies headers
+		if ( is_array($cookies) ) {
+			curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
+		}
+		// add extra stuff if SAML
+		if ($this->hasSA()) {
+			$more_headers = array ("soapaction: http://www.oasis-open.org/committees/security",
+				"cache-control: no-cache",
+				"pragma: no-cache",
+				"accept: text/xml",
+				"connection: keep-alive",
+			"content-type: text/xml");
+			
+			curl_setopt($ch, CURLOPT_HTTPHEADER, $more_headers);
+			curl_setopt($ch, CURLOPT_POST, 1);
+			$data = $this->buildSAMLPayload();
+			//phpCAS::trace('SAML Payload: '.print_r($data, TRUE));
+			curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+		}
+		// perform the query
+		$buf = curl_exec ($ch);
+		//phpCAS::trace('CURL: Call completed. Response body is: \''.$buf.'\'');
+		if ( $buf === FALSE ) {
+			phpCAS::trace('curl_exec() failed');
+			$err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
+			//phpCAS::trace('curl error: '.$err_msg);
+			// close the CURL session
+			curl_close ($ch);
+			$res = FALSE;
+		} else {
+			// close the CURL session
+			curl_close ($ch);
+			
+			$headers = $this->_curl_headers;
+			$body = $buf;
+		}
+		
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * This method is used to build the SAML POST body sent to /samlValidate URL.
+	 *
+	 * @return the SOAP-encased SAMLP artifact (the ticket).
+	 *
+	 * @private
+	 */
+	function buildSAMLPayload()
+		{
+		phpCAS::traceBegin();
+		
+		//get the ticket
+		$sa = $this->getSA();
+		//phpCAS::trace("SA: ".$sa);
+		
+		$body=SAML_SOAP_ENV.SAML_SOAP_BODY.SAMLP_REQUEST.SAML_ASSERTION_ARTIFACT.$sa.SAML_ASSERTION_ARTIFACT_CLOSE.SAMLP_REQUEST_CLOSE.SAML_SOAP_BODY_CLOSE.SAML_SOAP_ENV_CLOSE;
+		
+		phpCAS::traceEnd($body);
+		return ($body);
+		}
+	
+	/**
+	 * This method is the callback used by readURL method to request HTTP headers.
+	 */
+	var $_curl_headers = array();
+	function _curl_read_headers($ch, $header)
+		{
+		$this->_curl_headers[] = $header;
+		return strlen($header);
+		}
+	
+	/**
+	 * This method is used to access an HTTP[S] service.
+	 * 
+	 * @param $url the service to access.
+	 * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
+	 * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
+	 * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
+	 * @param $output the output of the service (also used to give an error
+	 * message on failure).
+	 *
+	 * @return TRUE on success, FALSE otherwise (in this later case, $err_code
+	 * gives the reason why it failed and $output contains an error message).
+	 *
+	 * @public
+	 */
+	function serviceWeb($url,&$err_code,&$output)
+		{
+		phpCAS::traceBegin();
+		// at first retrieve a PT
+		$pt = $this->retrievePT($url,$err_code,$output);
+		
+		$res = TRUE;
+		
+		// test if PT was retrieved correctly
+		if ( !$pt ) {
+			// note: $err_code and $err_msg are filled by CASClient::retrievePT()
+			phpCAS::trace('PT was not retrieved correctly');
+			$res = FALSE;
+		} else {
+			// add cookies if necessary
+			if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
+				foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) { 
+					$cookies[] = $name.'='.$val;
+				}
+			}
+			
+			// build the URL including the PT
+			if ( strstr($url,'?') === FALSE ) {
+				$service_url = $url.'?ticket='.$pt;
+			} else {
+				$service_url = $url.'&ticket='.$pt;
+			}
+			
+			phpCAS::trace('reading URL`'.$service_url.'\'');
+			if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
+				phpCAS::trace('could not read URL`'.$service_url.'\'');
+				$err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
+				// give an error message
+				$output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
+					$service_url,
+					$err_msg);
+				$res = FALSE;
+			} else {
+				// URL has been fetched, extract the cookies
+				phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
+				foreach ( $headers as $header ) {
+					// test if the header is a cookie
+					if ( preg_match('/^Set-Cookie:/',$header) ) {
+						// the header is a cookie, remove the beginning
+						$header_val = preg_replace('/^Set-Cookie: */','',$header);
+						// extract interesting information
+						$name_val = strtok($header_val,'; ');
+						// extract the name and the value of the cookie
+						$cookie_name = strtok($name_val,'=');
+						$cookie_val = strtok('=');
+						// store the cookie 
+						$_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
+						phpCAS::trace($cookie_name.' -> '.$cookie_val);
+					}
+				}
+			}
+		}
+		
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * This method is used to access an IMAP/POP3/NNTP service.
+	 * 
+	 * @param $url a string giving the URL of the service, including the mailing box
+	 * for IMAP URLs, as accepted by imap_open().
+	 * @param $service a string giving for CAS retrieve Proxy ticket
+	 * @param $flags options given to imap_open().
+	 * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
+	 * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
+	 * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
+	 * @param $err_msg an error message on failure
+	 * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
+	 * on success, FALSE on error).
+	 *
+	 * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
+	 * gives the reason why it failed and $err_msg contains an error message).
+	 *
+	 * @public
+	 */
+	function serviceMail($url,$service,$flags,&$err_code,&$err_msg,&$pt)
+		{
+		phpCAS::traceBegin();
+		// at first retrieve a PT
+		$pt = $this->retrievePT($service,$err_code,$output);
+		
+		$stream = FALSE;
+		
+		// test if PT was retrieved correctly
+		if ( !$pt ) {
+			// note: $err_code and $err_msg are filled by CASClient::retrievePT()
+			phpCAS::trace('PT was not retrieved correctly');
+		} else {
+			phpCAS::trace('opening IMAP URL `'.$url.'\'...');
+			$stream = @imap_open($url,$this->getUser(),$pt,$flags);
+			if ( !$stream ) {
+				phpCAS::trace('could not open URL');
+				$err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
+				// give an error message
+				$err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
+					$service_url,
+					var_export(imap_errors(),TRUE));
+				$pt = FALSE;
+				$stream = FALSE;
+			} else {
+				phpCAS::trace('ok');
+			}
+		}
+		
+		phpCAS::traceEnd($stream);
+		return $stream;
+		}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                  PROXIED CLIENT FEATURES (CAS 2.0)                 XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	// ########################################################################
+	//  PT
+	// ########################################################################
+	/**
+	 * @addtogroup internalProxied
+	 * @{
+	 */  
+	
+	/**
+	 * the Proxy Ticket provided in the URL of the request if present
+	 * (empty otherwise). Written by CASClient::CASClient(), read by 
+	 * CASClient::getPT() and CASClient::hasPGT().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_pt = '';
+	
+	/**
+	 * This method returns the Proxy Ticket provided in the URL of the request.
+	 * @return The proxy ticket.
+	 * @private
+	 */
+	function getPT()
+		{
+		//      return 'ST'.substr($this->_pt, 2);
+		return $this->_pt;
+		}
+	
+	/**
+	 * This method stores the Proxy Ticket.
+	 * @param $pt The Proxy Ticket.
+	 * @private
+	 */
+	function setPT($pt)
+		{ $this->_pt = $pt; }
+	
+	/**
+	 * This method tells if a Proxy Ticket was stored.
+	 * @return TRUE if a Proxy Ticket has been stored.
+	 * @private
+	 */
+	function hasPT()
+		{ return !empty($this->_pt); }
+	/**
+	 * This method returns the SAML Ticket provided in the URL of the request.
+	 * @return The SAML ticket.
+	 * @private
+	 */
+	function getSA()
+		{ return 'ST'.substr($this->_sa, 2); }
+	
+	/**
+	 * This method stores the SAML Ticket.
+	 * @param $sa The SAML Ticket.
+	 * @private
+	 */
+	function setSA($sa)
+		{ $this->_sa = $sa; }
+	
+	/**
+	 * This method tells if a SAML Ticket was stored.
+	 * @return TRUE if a SAML Ticket has been stored.
+	 * @private
+	 */
+	function hasSA()
+		{ return !empty($this->_sa); }
+	
+	/** @} */
+	// ########################################################################
+	//  PT VALIDATION
+	// ########################################################################
+	/**
+	 * @addtogroup internalProxied
+	 * @{
+	 */  
+	
+	/**
+	 * This method is used to validate a ST or PT; halt on failure
+	 * Used for all CAS 2.0 validations
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function validatePT(&$validate_url,&$text_response,&$tree_response)
+		{
+		phpCAS::traceBegin();
+		// build the URL to validate the ticket
+		$validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
+		
+		if ( $this->isProxy() ) {
+			// pass the callback url for CAS proxies
+			$validate_url .= '&pgtUrl='.$this->getCallbackURL();
+		}
+		
+		// open and read the URL
+		if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
+			phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
+			$this->authError('PT not validated',
+				$validate_url,
+				TRUE/*$no_response*/);
+		}
+		
+		// read the response of the CAS server into a DOM object
+		if ( !($dom = domxml_open_mem($text_response))) {
+			// read failed
+			$this->authError('PT not validated',
+				$validate_url,
+				FALSE/*$no_response*/,
+				TRUE/*$bad_response*/,
+				$text_response);
+		}
+		// read the root node of the XML tree
+		if ( !($tree_response = $dom->document_element()) ) {
+			// read failed
+			$this->authError('PT not validated',
+				$validate_url,
+				FALSE/*$no_response*/,
+				TRUE/*$bad_response*/,
+				$text_response);
+		}
+		// insure that tag name is 'serviceResponse'
+		if ( $tree_response->node_name() != 'serviceResponse' ) {
+			// bad root node
+			$this->authError('PT not validated',
+				$validate_url,
+				FALSE/*$no_response*/,
+				TRUE/*$bad_response*/,
+				$text_response);
+		}
+		if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
+			// authentication succeded, extract the user name
+			if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
+				// no user specified => error
+				$this->authError('PT not validated',
+					$validate_url,
+					FALSE/*$no_response*/,
+					TRUE/*$bad_response*/,
+					$text_response);
+			}
+			$this->setUser(trim($arr[0]->get_content()));
+			
+		} else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
+			// authentication succeded, extract the error code and message
+			$this->authError('PT not validated',
+				$validate_url,
+				FALSE/*$no_response*/,
+				FALSE/*$bad_response*/,
+				$text_response,
+				$arr[0]->get_attribute('code')/*$err_code*/,
+				trim($arr[0]->get_content())/*$err_msg*/);
+		} else {
+			$this->authError('PT not validated',
+				$validate_url,	
+				FALSE/*$no_response*/,
+				TRUE/*$bad_response*/,
+				$text_response);
+		}
+		
+		// at this step, PT has been validated and $this->_user has been set,
+		
+		phpCAS::traceEnd(TRUE);
+		return TRUE;
+		}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                               MISC                                 XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	/**
+	 * @addtogroup internalMisc
+	 * @{
+	 */  
+	
+	// ########################################################################
+	//  URL
+	// ########################################################################
+	/**
+	 * the URL of the current request (without any ticket CGI parameter). Written 
+	 * and read by CASClient::getURL().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_url = '';
+	
+	/**
+	 * This method returns the URL of the current request (without any ticket
+	 * CGI parameter).
+	 *
+	 * @return The URL
+	 *
+	 * @private
+	 */
+	function getURL()
+		{
+		phpCAS::traceBegin();
+		// the URL is built when needed only
+		if ( empty($this->_url) ) {
+			$final_uri = '';
+			// remove the ticket if present in the URL
+			$final_uri = ($this->isHttps()) ? 'https' : 'http';
+			$final_uri .= '://';
+			/* replaced by Julien Marchal - v0.4.6
+			 * $this->_url .= $_SERVER['SERVER_NAME'];
+			 */
+			if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
+				/* replaced by teedog - v0.4.12
+				 * $this->_url .= $_SERVER['SERVER_NAME'];
+				 */
+				if (empty($_SERVER['SERVER_NAME'])) {
+					$server_name = $_SERVER['HTTP_HOST'];
+				} else {
+					$server_name = $_SERVER['SERVER_NAME'];
+				}
+			} else {
+				$server_name = $_SERVER['HTTP_X_FORWARDED_SERVER'];
+			}
+			$final_uri .= $server_name;
+			if (!strpos($server_name, ':')) {
+				if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
+						|| (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
+					$final_uri .= ':';
+					$final_uri .= $_SERVER['SERVER_PORT'];
+				}
+			}
+			
+			$request_uri	= explode('?', $_SERVER['REQUEST_URI'], 2);
+			$final_uri		.= $request_uri[0];
+			
+			if (isset($request_uri[1]) && $request_uri[1])
+			{
+				$query_string	= $this->removeParameterFromQueryString('ticket', $request_uri[1]);
+				
+				// If the query string still has anything left, append it to the final URI
+				if ($query_string !== '')
+					$final_uri	.= "?$query_string";
+				
+			}
+			
+			phpCAS::trace("Final URI: $final_uri");
+			$this->setURL($final_uri);
+		}
+		phpCAS::traceEnd($this->_url);
+		return $this->_url;
+	}
+	
+
+		
+	/**
+	 * Removes a parameter from a query string
+	 * 
+	 * @param string $parameterName 
+	 * @param string $queryString
+	 * @return string
+	 *
+	 * @link http://stackoverflow.com/questions/1842681/regular-expression-to-remove-one-parameter-from-query-string
+	 */
+	function removeParameterFromQueryString($parameterName, $queryString)
+	{
+		$parameterName	= preg_quote($parameterName);
+		return preg_replace("/&$parameterName(=[^&]*)?|^$parameterName(=[^&]*)?&?/", '', $queryString);
+	}
+
+	
+	/**
+	 * This method sets the URL of the current request 
+	 *
+	 * @param $url url to set for service
+	 *
+	 * @private
+	 */
+	function setURL($url)
+		{
+		$this->_url = $url;
+		}
+	
+	// ########################################################################
+	//  AUTHENTICATION ERROR HANDLING
+	// ########################################################################
+	/**
+	 * This method is used to print the HTML output when the user was not authenticated.
+	 *
+	 * @param $failure the failure that occured
+	 * @param $cas_url the URL the CAS server was asked for
+	 * @param $no_response the response from the CAS server (other 
+	 * parameters are ignored if TRUE)
+	 * @param $bad_response bad response from the CAS server ($err_code
+	 * and $err_msg ignored if TRUE)
+	 * @param $cas_response the response of the CAS server
+	 * @param $err_code the error code given by the CAS server
+	 * @param $err_msg the error message given by the CAS server
+	 *
+	 * @private
+	 */
+	function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
+		{
+		phpCAS::traceBegin();
+		
+		$this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
+		printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),htmlentities($this->getURL()),$_SERVER['SERVER_ADMIN']);
+		phpCAS::trace('CAS URL: '.$cas_url);
+		phpCAS::trace('Authentication failure: '.$failure);
+		if ( $no_response ) {
+			phpCAS::trace('Reason: no response from the CAS server');
+		} else {
+			if ( $bad_response ) {
+				phpCAS::trace('Reason: bad response from the CAS server');
+			} else {
+				switch ($this->getServerVersion()) {
+					case CAS_VERSION_1_0:
+						phpCAS::trace('Reason: CAS error');
+						break;
+					case CAS_VERSION_2_0:
+						if ( empty($err_code) )
+							phpCAS::trace('Reason: no CAS error');
+						else
+							phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
+						break;
+				}
+			}
+			phpCAS::trace('CAS response: '.$cas_response);
+		}
+		$this->printHTMLFooter();
+		phpCAS::traceExit();
+		exit();
+		}
+	
+	/** @} */
+}
+
+?>

+ 499 - 0
main/auth/cas/lib/CAS/domxml-php4-to-php5.php

@@ -0,0 +1,499 @@
+<?php
+/*
+	Requires PHP5, uses built-in DOM extension.
+	To be used in PHP4 scripts using DOMXML extension: allows PHP4/DOMXML scripts to run on PHP5/DOM.
+	(Optional: requires PHP5/XSL extension for domxml_xslt functions, PHP>=5.1 for XPath evaluation functions, and PHP>=5.1/libxml for DOMXML error reports)
+
+	Typical use:
+	{
+		if (PHP_VERSION>='5')
+			require_once('domxml-php4-to-php5.php');
+	}
+
+	Version 1.21, 2008-12-05, http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/
+
+	------------------------------------------------------------------
+	Written by Alexandre Alapetite, http://alexandre.alapetite.net/cv/
+
+	Copyright 2004-2008, GNU Lesser General Public License,
+	http://www.gnu.org/licenses/lgpl.html
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU Lesser General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU Lesser General Public License for more details.
+	You should have received a copy of the GNU Lesser General Public License
+	along with this program. If not, see <http://www.gnu.org/licenses/lgpl.html>
+
+	== Rights and obligations ==
+	- Attribution: You must give the original author credit.
+	- Share Alike: If you alter or transform this library,
+	   you may distribute the resulting library only under the same license GNU/LGPL.
+	- In case of jurisdiction dispute, the French law is authoritative.
+	- Any of these conditions can be waived if you get permission from Alexandre Alapetite.
+	- Not required, but please send to Alexandre Alapetite the modifications you make,
+	   in order to improve this file for the benefit of everybody.
+
+	If you want to distribute this code, please do it as a link to:
+	http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/
+*/
+
+define('DOMXML_LOAD_PARSING',0);
+define('DOMXML_LOAD_VALIDATING',1);
+define('DOMXML_LOAD_RECOVERING',2);
+define('DOMXML_LOAD_SUBSTITUTE_ENTITIES',4);
+//define('DOMXML_LOAD_COMPLETE_ATTRS',8);
+define('DOMXML_LOAD_DONT_KEEP_BLANKS',16);
+
+function domxml_new_doc($version) {return new php4DOMDocument();}
+function domxml_new_xmldoc($version) {return new php4DOMDocument();}
+function domxml_open_file($filename,$mode=DOMXML_LOAD_PARSING,&$error=null)
+{
+	$dom=new php4DOMDocument($mode);
+	$errorMode=(func_num_args()>2)&&defined('LIBXML_VERSION');
+	if ($errorMode) libxml_use_internal_errors(true);
+	if (!$dom->myDOMNode->load($filename)) $dom=null;
+	if ($errorMode)
+	{
+		$error=array_map('_error_report',libxml_get_errors());
+		libxml_clear_errors();
+	}
+	return $dom;
+}
+function domxml_open_mem($str,$mode=DOMXML_LOAD_PARSING,&$error=null)
+{
+	$dom=new php4DOMDocument($mode);
+	$errorMode=(func_num_args()>2)&&defined('LIBXML_VERSION');
+	if ($errorMode) libxml_use_internal_errors(true);
+	if (!$dom->myDOMNode->loadXML($str)) $dom=null;
+	if ($errorMode)
+	{
+		$error=array_map('_error_report',libxml_get_errors());
+		libxml_clear_errors();
+	}
+	return $dom;
+}
+function html_doc($html_doc,$from_file=false)
+{
+	$dom=new php4DOMDocument();
+	if ($from_file) $result=$dom->myDOMNode->loadHTMLFile($html_doc);
+	else $result=$dom->myDOMNode->loadHTML($html_doc);
+	return $result ? $dom : null;
+}
+function html_doc_file($filename) {return html_doc($filename,true);}
+function xmldoc($str) {return domxml_open_mem($str);}
+function xmldocfile($filename) {return domxml_open_file($filename);}
+function xpath_eval($xpath_context,$eval_str,$contextnode=null) {return $xpath_context->xpath_eval($eval_str,$contextnode);}
+function xpath_new_context($dom_document) {return new php4DOMXPath($dom_document);}
+function xpath_register_ns($xpath_context,$prefix,$namespaceURI) {return $xpath_context->myDOMXPath->registerNamespace($prefix,$namespaceURI);}
+function _entityDecode($text) {return html_entity_decode(strtr($text,array('&apos;'=>'\'')),ENT_QUOTES,'UTF-8');}
+function _error_report($error) {return array('errormessage'=>$error->message,'nodename'=>'','line'=>$error->line,'col'=>$error->column)+($error->file==''?array():array('directory'=>dirname($error->file),'file'=>basename($error->file)));}
+
+class php4DOMAttr extends php4DOMNode
+{
+	function __get($name)
+	{
+		if ($name==='name') return $this->myDOMNode->name;
+		else return parent::__get($name);
+	}
+	function name() {return $this->myDOMNode->name;}
+	function set_content($text) {}
+	//function set_value($content) {return $this->myDOMNode->value=htmlspecialchars($content,ENT_QUOTES);}
+	function specified() {return $this->myDOMNode->specified;}
+	function value() {return $this->myDOMNode->value;}
+}
+
+class php4DOMDocument extends php4DOMNode
+{
+	function php4DOMDocument($mode=DOMXML_LOAD_PARSING)
+	{
+		$this->myDOMNode=new DOMDocument();
+		$this->myOwnerDocument=$this;
+		if ($mode & DOMXML_LOAD_VALIDATING) $this->myDOMNode->validateOnParse=true;
+		if ($mode & DOMXML_LOAD_RECOVERING) $this->myDOMNode->recover=true;
+		if ($mode & DOMXML_LOAD_SUBSTITUTE_ENTITIES) $this->myDOMNode->substituteEntities=true;
+		if ($mode & DOMXML_LOAD_DONT_KEEP_BLANKS) $this->myDOMNode->preserveWhiteSpace=false;
+	}
+	function add_root($name)
+	{
+		if ($this->myDOMNode->hasChildNodes()) $this->myDOMNode->removeChild($this->myDOMNode->firstChild);
+		return new php4DOMElement($this->myDOMNode->appendChild($this->myDOMNode->createElement($name)),$this->myOwnerDocument);
+	}
+	function create_attribute($name,$value)
+	{
+		$myAttr=$this->myDOMNode->createAttribute($name);
+		$myAttr->value=htmlspecialchars($value,ENT_QUOTES);
+		return new php4DOMAttr($myAttr,$this);
+	}
+	function create_cdata_section($content) {return new php4DOMNode($this->myDOMNode->createCDATASection($content),$this);}
+	function create_comment($data) {return new php4DOMNode($this->myDOMNode->createComment($data),$this);}
+	function create_element($name) {return new php4DOMElement($this->myDOMNode->createElement($name),$this);}
+	function create_element_ns($uri,$name,$prefix=null)
+	{
+		if ($prefix==null) $prefix=$this->myDOMNode->lookupPrefix($uri);
+		if (($prefix==null)&&(($this->myDOMNode->documentElement==null)||(!$this->myDOMNode->documentElement->isDefaultNamespace($uri)))) $prefix='a'.sprintf('%u',crc32($uri));
+		return new php4DOMElement($this->myDOMNode->createElementNS($uri,$prefix==null ? $name : $prefix.':'.$name),$this);
+	}
+	function create_entity_reference($content) {return new php4DOMNode($this->myDOMNode->createEntityReference($content),$this);} //By Walter Ebert 2007-01-22
+	function create_processing_instruction($target,$data=''){return new php4DomProcessingInstruction($this->myDOMNode->createProcessingInstruction($target,$data),$this);}
+	function create_text_node($content) {return new php4DOMText($this->myDOMNode->createTextNode($content),$this);}
+	function document_element() {return parent::_newDOMElement($this->myDOMNode->documentElement,$this);}
+	function dump_file($filename,$compressionmode=false,$format=false)
+	{
+		$format0=$this->myDOMNode->formatOutput;
+		$this->myDOMNode->formatOutput=$format;
+		$res=$this->myDOMNode->save($filename);
+		$this->myDOMNode->formatOutput=$format0;
+		return $res;
+	}
+	function dump_mem($format=false,$encoding=false)
+	{
+		$format0=$this->myDOMNode->formatOutput;
+		$this->myDOMNode->formatOutput=$format;
+		$encoding0=$this->myDOMNode->encoding;
+		if ($encoding) $this->myDOMNode->encoding=$encoding;
+		$dump=$this->myDOMNode->saveXML();
+		$this->myDOMNode->formatOutput=$format0;
+		if ($encoding) $this->myDOMNode->encoding= $encoding0=='' ? 'UTF-8' : $encoding0; //UTF-8 is XML default encoding
+		return $dump;
+	}
+	function free()
+	{
+		if ($this->myDOMNode->hasChildNodes()) $this->myDOMNode->removeChild($this->myDOMNode->firstChild);
+		$this->myDOMNode=null;
+		$this->myOwnerDocument=null;
+	}
+	function get_element_by_id($id) {return parent::_newDOMElement($this->myDOMNode->getElementById($id),$this);}
+	function get_elements_by_tagname($name)
+	{
+		$myDOMNodeList=$this->myDOMNode->getElementsByTagName($name);
+		$nodeSet=array();
+		$i=0;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMElement($node,$this);
+		return $nodeSet;
+	}
+	function html_dump_mem() {return $this->myDOMNode->saveHTML();}
+	function root() {return parent::_newDOMElement($this->myDOMNode->documentElement,$this);}
+	function xinclude() {return $this->myDOMNode->xinclude();}
+	function xpath_new_context() {return new php4DOMXPath($this);}
+}
+
+class php4DOMElement extends php4DOMNode
+{
+	function add_namespace($uri,$prefix)
+	{
+		if ($this->myDOMNode->hasAttributeNS('http://www.w3.org/2000/xmlns/',$prefix)) return false;
+		else
+		{
+			$this->myDOMNode->setAttributeNS('http://www.w3.org/2000/xmlns/','xmlns:'.$prefix,$uri); //By Daniel Walker 2006-09-08
+			return true;
+		}
+	}
+	function get_attribute($name) {return $this->myDOMNode->getAttribute($name);}
+	function get_attribute_node($name) {return parent::_newDOMElement($this->myDOMNode->getAttributeNode($name),$this->myOwnerDocument);}
+	function get_elements_by_tagname($name)
+	{
+		$myDOMNodeList=$this->myDOMNode->getElementsByTagName($name);
+		$nodeSet=array();
+		$i=0;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMElement($node,$this->myOwnerDocument);
+		return $nodeSet;
+	}
+	function has_attribute($name) {return $this->myDOMNode->hasAttribute($name);}
+	function remove_attribute($name) {return $this->myDOMNode->removeAttribute($name);}
+	function set_attribute($name,$value)
+	{
+		//return $this->myDOMNode->setAttribute($name,$value); //Does not return a DomAttr
+		$myAttr=$this->myDOMNode->ownerDocument->createAttribute($name);
+		$myAttr->value=htmlspecialchars($value,ENT_QUOTES); //Entity problem reported by AL-DesignWorks 2007-09-07
+		$this->myDOMNode->setAttributeNode($myAttr);
+		return new php4DOMAttr($myAttr,$this->myOwnerDocument);
+	}
+	/*function set_attribute_node($attr)
+	{
+		$this->myDOMNode->setAttributeNode($this->_importNode($attr));
+		return $attr;
+	}*/
+	function set_name($name)
+	{
+		if ($this->myDOMNode->prefix=='') $newNode=$this->myDOMNode->ownerDocument->createElement($name);
+		else $newNode=$this->myDOMNode->ownerDocument->createElementNS($this->myDOMNode->namespaceURI,$this->myDOMNode->prefix.':'.$name);
+		$myDOMNodeList=$this->myDOMNode->attributes;
+		$i=0;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item($i++))
+				if ($node->namespaceURI=='') $newNode->setAttribute($node->name,$node->value);
+				else $newNode->setAttributeNS($node->namespaceURI,$node->nodeName,$node->value);
+		$myDOMNodeList=$this->myDOMNode->childNodes;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item(0)) $newNode->appendChild($node);
+		$this->myDOMNode->parentNode->replaceChild($newNode,$this->myDOMNode);
+		$this->myDOMNode=$newNode;
+		return true;
+	}
+	function tagname() {return $this->tagname;}
+}
+
+class php4DOMNode
+{
+	public $myDOMNode;
+	public $myOwnerDocument;
+	function php4DOMNode($aDomNode,$aOwnerDocument)
+	{
+		$this->myDOMNode=$aDomNode;
+		$this->myOwnerDocument=$aOwnerDocument;
+	}
+	function __get($name)
+	{
+		switch ($name)
+		{
+			case 'type': return $this->myDOMNode->nodeType;
+			case 'tagname': return ($this->myDOMNode->nodeType===XML_ELEMENT_NODE) ? $this->myDOMNode->localName : $this->myDOMNode->tagName; //Avoid namespace prefix for DOMElement
+			case 'content': return $this->myDOMNode->textContent;
+			case 'value': return $this->myDOMNode->value;
+			default:
+				$myErrors=debug_backtrace();
+				trigger_error('Undefined property: '.get_class($this).'::$'.$name.' ['.$myErrors[0]['file'].':'.$myErrors[0]['line'].']',E_USER_NOTICE);
+				return false;
+		}
+	}
+	function add_child($newnode) {return append_child($newnode);}
+	function add_namespace($uri,$prefix) {return false;}
+	function append_child($newnode) {return self::_newDOMElement($this->myDOMNode->appendChild($this->_importNode($newnode)),$this->myOwnerDocument);}
+	function append_sibling($newnode) {return self::_newDOMElement($this->myDOMNode->parentNode->appendChild($this->_importNode($newnode)),$this->myOwnerDocument);}
+	function attributes()
+	{
+		$myDOMNodeList=$this->myDOMNode->attributes;
+		if (!(isset($myDOMNodeList)&&$this->myDOMNode->hasAttributes())) return null;
+		$nodeSet=array();
+		$i=0;
+		while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMAttr($node,$this->myOwnerDocument);
+		return $nodeSet;
+	}
+	function child_nodes()
+	{
+		$myDOMNodeList=$this->myDOMNode->childNodes;
+		$nodeSet=array();
+		$i=0;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=self::_newDOMElement($node,$this->myOwnerDocument);
+		return $nodeSet;
+	}
+	function children() {return $this->child_nodes();}
+	function clone_node($deep=false) {return self::_newDOMElement($this->myDOMNode->cloneNode($deep),$this->myOwnerDocument);}
+	//dump_node($node) should only be called on php4DOMDocument
+	function dump_node($node=null) {return $node==null ? $this->myOwnerDocument->myDOMNode->saveXML($this->myDOMNode) : $this->myOwnerDocument->myDOMNode->saveXML($node->myDOMNode);}
+	function first_child() {return self::_newDOMElement($this->myDOMNode->firstChild,$this->myOwnerDocument);}
+	function get_content() {return $this->myDOMNode->textContent;}
+	function has_attributes() {return $this->myDOMNode->hasAttributes();}
+	function has_child_nodes() {return $this->myDOMNode->hasChildNodes();}
+	function insert_before($newnode,$refnode) {return self::_newDOMElement($this->myDOMNode->insertBefore($this->_importNode($newnode),$refnode==null?null:$refnode->myDOMNode),$this->myOwnerDocument);}
+	function is_blank_node() {return ($this->myDOMNode->nodeType===XML_TEXT_NODE)&&preg_match('%^\s*$%',$this->myDOMNode->nodeValue);}
+	function last_child() {return self::_newDOMElement($this->myDOMNode->lastChild,$this->myOwnerDocument);}
+	function new_child($name,$content)
+	{
+		$mySubNode=$this->myDOMNode->ownerDocument->createElement($name);
+		$mySubNode->appendChild($this->myDOMNode->ownerDocument->createTextNode(_entityDecode($content)));
+		$this->myDOMNode->appendChild($mySubNode);
+		return new php4DOMElement($mySubNode,$this->myOwnerDocument);
+	}
+	function next_sibling() {return self::_newDOMElement($this->myDOMNode->nextSibling,$this->myOwnerDocument);}
+	function node_name() {return ($this->myDOMNode->nodeType===XML_ELEMENT_NODE) ? $this->myDOMNode->localName : $this->myDOMNode->nodeName;} //Avoid namespace prefix for DOMElement
+	function node_type() {return $this->myDOMNode->nodeType;}
+	function node_value() {return $this->myDOMNode->nodeValue;}
+	function owner_document() {return $this->myOwnerDocument;}
+	function parent_node() {return self::_newDOMElement($this->myDOMNode->parentNode,$this->myOwnerDocument);}
+	function prefix() {return $this->myDOMNode->prefix;}
+	function previous_sibling() {return self::_newDOMElement($this->myDOMNode->previousSibling,$this->myOwnerDocument);}
+	function remove_child($oldchild) {return self::_newDOMElement($this->myDOMNode->removeChild($oldchild->myDOMNode),$this->myOwnerDocument);}
+	function replace_child($newnode,$oldnode) {return self::_newDOMElement($this->myDOMNode->replaceChild($this->_importNode($newnode),$oldnode->myDOMNode),$this->myOwnerDocument);}
+	function replace_node($newnode) {return self::_newDOMElement($this->myDOMNode->parentNode->replaceChild($this->_importNode($newnode),$this->myDOMNode),$this->myOwnerDocument);}
+	function set_content($text) {return $this->myDOMNode->appendChild($this->myDOMNode->ownerDocument->createTextNode(_entityDecode($text)));} //Entity problem reported by AL-DesignWorks 2007-09-07
+	//function set_name($name) {return $this->myOwnerDocument->renameNode($this->myDOMNode,$this->myDOMNode->namespaceURI,$name);}
+	function set_namespace($uri,$prefix=null)
+	{//Contributions by Daniel Walker 2006-09-08
+		$nsprefix=$this->myDOMNode->lookupPrefix($uri);
+		if ($nsprefix==null)
+		{
+			$nsprefix= $prefix==null ? $nsprefix='a'.sprintf('%u',crc32($uri)) : $prefix;
+			if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE)
+			{
+				if (($prefix!=null)&&$this->myDOMNode->ownerElement->hasAttributeNS('http://www.w3.org/2000/xmlns/',$nsprefix)&&
+					($this->myDOMNode->ownerElement->getAttributeNS('http://www.w3.org/2000/xmlns/',$nsprefix)!=$uri))
+				{//Remove namespace
+					$parent=$this->myDOMNode->ownerElement;
+					$parent->removeAttributeNode($this->myDOMNode);
+					$parent->setAttribute($this->myDOMNode->localName,$this->myDOMNode->nodeValue);
+					$this->myDOMNode=$parent->getAttributeNode($this->myDOMNode->localName);
+					return;
+				}
+				$this->myDOMNode->ownerElement->setAttributeNS('http://www.w3.org/2000/xmlns/','xmlns:'.$nsprefix,$uri);
+			}
+		}
+		if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE)
+		{
+			$parent=$this->myDOMNode->ownerElement;
+			$parent->removeAttributeNode($this->myDOMNode);
+			$parent->setAttributeNS($uri,$nsprefix.':'.$this->myDOMNode->localName,$this->myDOMNode->nodeValue);
+			$this->myDOMNode=$parent->getAttributeNodeNS($uri,$this->myDOMNode->localName);
+		}
+		elseif ($this->myDOMNode->nodeType===XML_ELEMENT_NODE)
+		{
+			$NewNode=$this->myDOMNode->ownerDocument->createElementNS($uri,$nsprefix.':'.$this->myDOMNode->localName);
+			foreach ($this->myDOMNode->attributes as $n) $NewNode->appendChild($n->cloneNode(true));
+			foreach ($this->myDOMNode->childNodes as $n) $NewNode->appendChild($n->cloneNode(true));
+			$xpath=new DOMXPath($this->myDOMNode->ownerDocument);
+			$myDOMNodeList=$xpath->query('namespace::*[name()!="xml"]',$this->myDOMNode); //Add old namespaces
+			foreach ($myDOMNodeList as $n) $NewNode->setAttributeNS('http://www.w3.org/2000/xmlns/',$n->nodeName,$n->nodeValue); 
+			$this->myDOMNode->parentNode->replaceChild($NewNode,$this->myDOMNode);
+			$this->myDOMNode=$NewNode;
+		}
+	}
+	function unlink_node()
+	{
+		if ($this->myDOMNode->parentNode!=null)
+		{
+			if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE) $this->myDOMNode->parentNode->removeAttributeNode($this->myDOMNode);
+			else $this->myDOMNode->parentNode->removeChild($this->myDOMNode);
+		}
+	}
+	protected function _importNode($newnode) {return $this->myOwnerDocument===$newnode->myOwnerDocument ? $newnode->myDOMNode : $this->myOwnerDocument->myDOMNode->importNode($newnode->myDOMNode,true);} //To import DOMNode from another DOMDocument
+	static function _newDOMElement($aDOMNode,$aOwnerDocument)
+	{//Check the PHP5 DOMNode before creating a new associated PHP4 DOMNode wrapper
+		if ($aDOMNode==null) return null;
+		switch ($aDOMNode->nodeType)
+		{
+			case XML_ELEMENT_NODE: return new php4DOMElement($aDOMNode,$aOwnerDocument);
+			case XML_TEXT_NODE: return new php4DOMText($aDOMNode,$aOwnerDocument);
+			case XML_ATTRIBUTE_NODE: return new php4DOMAttr($aDOMNode,$aOwnerDocument);
+			case XML_PI_NODE: return new php4DomProcessingInstruction($aDOMNode,$aOwnerDocument);
+			default: return new php4DOMNode($aDOMNode,$aOwnerDocument);
+		}
+	}
+}
+
+class php4DomProcessingInstruction extends php4DOMNode
+{
+	function data() {return $this->myDOMNode->data;}
+	function target() {return $this->myDOMNode->target;}
+}
+
+class php4DOMText extends php4DOMNode
+{
+	function __get($name)
+	{
+		if ($name==='tagname') return '#text';
+		else return parent::__get($name);
+	}
+	function tagname() {return '#text';}
+	function set_content($text) {$this->myDOMNode->nodeValue=$text; return true;}
+}
+
+if (!defined('XPATH_NODESET'))
+{
+	define('XPATH_UNDEFINED',0);
+	define('XPATH_NODESET',1);
+	define('XPATH_BOOLEAN',2);
+	define('XPATH_NUMBER',3);
+	define('XPATH_STRING',4);
+	/*define('XPATH_POINT',5);
+	define('XPATH_RANGE',6);
+	define('XPATH_LOCATIONSET',7);
+	define('XPATH_USERS',8);
+	define('XPATH_XSLT_TREE',9);*/
+}
+
+class php4DOMNodelist
+{
+	private $myDOMNodelist;
+	public $nodeset;
+	public $type=XPATH_UNDEFINED;
+	public $value;
+	function php4DOMNodelist($aDOMNodelist,$aOwnerDocument)
+	{
+		if (!isset($aDOMNodelist)) return; 
+		elseif (is_object($aDOMNodelist)||is_array($aDOMNodelist))
+		{
+			if ($aDOMNodelist->length>0)
+			{
+				$this->myDOMNodelist=$aDOMNodelist;
+				$this->nodeset=array();
+				$this->type=XPATH_NODESET;
+				$i=0;
+				while ($node=$this->myDOMNodelist->item($i++)) $this->nodeset[]=php4DOMNode::_newDOMElement($node,$aOwnerDocument);
+			}
+		}
+		elseif (is_int($aDOMNodelist)||is_float($aDOMNodelist))
+		{
+			$this->type=XPATH_NUMBER;
+			$this->value=$aDOMNodelist;
+		}
+		elseif (is_bool($aDOMNodelist))
+		{
+			$this->type=XPATH_BOOLEAN;
+			$this->value=$aDOMNodelist;
+		}
+		elseif (is_string($aDOMNodelist))
+		{
+			$this->type=XPATH_STRING;
+			$this->value=$aDOMNodelist;
+		}
+	}
+}
+
+class php4DOMXPath
+{
+	public $myDOMXPath;
+	private $myOwnerDocument;
+	function php4DOMXPath($dom_document)
+	{
+		//TODO: If $dom_document is a DomElement, make that default $contextnode and modify XPath. Ex: '/test'
+		$this->myOwnerDocument=$dom_document->myOwnerDocument;
+		$this->myDOMXPath=new DOMXPath($this->myOwnerDocument->myDOMNode);
+	}
+	function xpath_eval($eval_str,$contextnode=null)
+	{
+		if (method_exists($this->myDOMXPath,'evaluate')) $xp=isset($contextnode) ? $this->myDOMXPath->evaluate($eval_str,$contextnode->myDOMNode) : $this->myDOMXPath->evaluate($eval_str);
+		else $xp=isset($contextnode) ? $this->myDOMXPath->query($eval_str,$contextnode->myDOMNode) : $this->myDOMXPath->query($eval_str);
+		$xp=new php4DOMNodelist($xp,$this->myOwnerDocument);
+		return ($xp->type===XPATH_UNDEFINED) ? false : $xp;
+	}
+	function xpath_register_ns($prefix,$namespaceURI) {return $this->myDOMXPath->registerNamespace($prefix,$namespaceURI);}
+}
+
+if (extension_loaded('xsl'))
+{//See also: http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/
+	function domxml_xslt_stylesheet($xslstring) {return new php4DomXsltStylesheet(DOMDocument::loadXML($xslstring));}
+	function domxml_xslt_stylesheet_doc($dom_document) {return new php4DomXsltStylesheet($dom_document);}
+	function domxml_xslt_stylesheet_file($xslfile) {return new php4DomXsltStylesheet(DOMDocument::load($xslfile));}
+	class php4DomXsltStylesheet
+	{
+		private $myxsltProcessor;
+		function php4DomXsltStylesheet($dom_document)
+		{
+			$this->myxsltProcessor=new xsltProcessor();
+			$this->myxsltProcessor->importStyleSheet($dom_document);
+		}
+		function process($dom_document,$xslt_parameters=array(),$param_is_xpath=false)
+		{
+			foreach ($xslt_parameters as $param=>$value) $this->myxsltProcessor->setParameter('',$param,$value);
+			$myphp4DOMDocument=new php4DOMDocument();
+			$myphp4DOMDocument->myDOMNode=$this->myxsltProcessor->transformToDoc($dom_document->myDOMNode);
+			return $myphp4DOMDocument;
+		}
+		function result_dump_file($dom_document,$filename)
+		{
+			$html=$dom_document->myDOMNode->saveHTML();
+			file_put_contents($filename,$html);
+			return $html;
+		}
+		function result_dump_mem($dom_document) {return $dom_document->myDOMNode->saveHTML();}
+	}
+}
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/catalan.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/spanish.php
+ * @author Iván-Benjamín García Torà <ivaniclixx AT gmail DOT com>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'usant servidor',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'Autentificació CAS necessària!',
+ CAS_STR_LOGOUT 
+ => 'Sortida de CAS necessària!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'Ja hauria d\ haver estat redireccionat al servidor CAS. Feu click <a href="%s">aquí</a> per a continuar.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'Autentificació CAS fallida!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>No estàs autentificat.</p><p>Pots tornar a intentar-ho fent click <a href="%s">aquí</a>.</p><p>Si el problema persisteix hauría de contactar amb l\'<a href="mailto:%s">administrador d\'aquest llocc</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'El servei `<b>%s</b>\' no està disponible (<b>%s</b>).'
+);
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/english.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/english.php
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'using server',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'CAS Authentication wanted!',
+ CAS_STR_LOGOUT 
+ => 'CAS logout wanted!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'You should already have been redirected to the CAS server. Click <a href="%s">here</a> to continue.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'CAS Authentication failed!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>You were not authenticated.</p><p>You may submit your request again by clicking <a href="%s">here</a>.</p><p>If the problem persists, you may contact <a href="mailto:%s">the administrator of this site</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'The service `<b>%s</b>\' is not available (<b>%s</b>).'
+);
+
+?>

+ 28 - 0
main/auth/cas/lib/CAS/languages/french.php

@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file languages/english.php
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'utilisant le serveur',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'Authentication CAS n�cessaire&nbsp;!',
+ CAS_STR_LOGOUT 
+ => 'D�connexion demand�e&nbsp;!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'Vous auriez du etre redirig�(e) vers le serveur CAS. Cliquez <a href="%s">ici</a> pour continuer.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'Authentification CAS infructueuse&nbsp;!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>Vous n\'avez pas �t� authentifi�(e).</p><p>Vous pouvez soumettre votre requete � nouveau en cliquant <a href="%s">ici</a>.</p><p>Si le probl�me persiste, vous pouvez contacter <a href="mailto:%s">l\'administrateur de ce site</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'Le service `<b>%s</b>\' est indisponible (<b>%s</b>)'
+
+);
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/german.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/german.php
+ * @author Henrik Genssen <hg at mediafactory.de>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'via Server',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'CAS Authentifizierung erforderlich!',
+ CAS_STR_LOGOUT 
+ => 'CAS Abmeldung!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'eigentlich h&auml;ten Sie zum CAS Server weitergeleitet werden sollen. Dr&uuml;cken Sie <a href="%s">hier</a> um fortzufahren.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'CAS Anmeldung fehlgeschlagen!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>Sie wurden nicht angemeldet.</p><p>Um es erneut zu versuchen klicken Sie <a href="%s">hier</a>.</p><p>Wenn das Problem bestehen bleibt, kontkatieren Sie den <a href="mailto:%s">Administrator</a> dieser Seite.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'Der Dienst `<b>%s</b>\' ist nicht verf&uuml;gbar (<b>%s</b>).'
+);
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/greek.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/greek.php
+ * @author Vangelis Haniotakis <haniotak at ucnet.uoc.gr>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => '��������������� � ������������',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => '���������� � ����������� CAS!',
+ CAS_STR_LOGOUT 
+ => '���������� � ���������� ��� CAS!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => '�� ������ �� ������ �������������� ���� ����������� CAS. ����� ���� <a href="%s">���</a> ��� �� ����������.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => '� ����������� CAS �������!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>��� ���������������.</p><p>�������� �� ����������������, �������� ���� <a href="%s">���</a>.</p><p>��� �� �������� ���������, ����� �� ����� �� ��� <a href="mailto:%s">�����������</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => '� �������� `<b>%s</b>\' ��� ����� ��������� (<b>%s</b>).'
+);
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/japanese.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/japanese.php
+ * @author fnorif (fnorif@yahoo.co.jp)
+ * 
+ * Now Encoding is EUC-JP and LF
+ **/
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'using server',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'CAS�ˤ��ǧ�ڤ�Ԥ��ޤ�',
+ CAS_STR_LOGOUT 
+ => 'CAS����?�����Ȥ��ޤ�!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'CAS�����Ф˹Ԥ�ɬ�פ�����ޤ�����ưŪ��ž������ʤ����� <a href="%s">������</a> �򥯥�å�����³�Ԥ��ޤ���',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'CAS�ˤ��ǧ�ڤ˼��Ԥ��ޤ���',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>ǧ�ڤǤ��ޤ���Ǥ���.</p><p>�⤦���٥ꥯ�����Ȥ������������<a href="%s">������</a>�򥯥�å�.</p><p>���꤬��褷�ʤ����� <a href="mailto:%s">���Υ����Ȥδ����</a>���䤤��碌�Ƥ�������.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => '�����ӥ� `<b>%s</b>\' �����ѤǤ��ޤ��� (<b>%s</b>).'
+);
+
+?>

+ 24 - 0
main/auth/cas/lib/CAS/languages/languages.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file languages/languages.php
+ * Internationalization constants
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+//@{
+/**
+ * a phpCAS string index
+ */
+define("CAS_STR_USING_SERVER",                1);
+define("CAS_STR_AUTHENTICATION_WANTED",       2);
+define("CAS_STR_LOGOUT",                      3);
+define("CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED", 4);
+define("CAS_STR_AUTHENTICATION_FAILED",       5);
+define("CAS_STR_YOU_WERE_NOT_AUTHENTICATED",  6);
+define("CAS_STR_SERVICE_UNAVAILABLE",         7);
+//@}
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/spanish.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/spanish.php
+ * @author Iván-Benjamín García Torà <ivaniclixx AT gmail DOT com>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'usando servidor',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => '¡Autentificación CAS necesaria!',
+ CAS_STR_LOGOUT 
+ => '¡Salida CAS necesaria!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'Ya debería haber sido redireccionado al servidor CAS. Haga click <a href="%s">aquí</a> para continuar.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => '¡Autentificación CAS fallida!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>No estás autentificado.</p><p>Puedes volver a intentarlo haciendo click <a href="%s">aquí</a>.</p><p>Si el problema persiste debería contactar con el <a href="mailto:%s">administrador de este sitio</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'El servicio `<b>%s</b>\' no está disponible (<b>%s</b>).'
+);
+
+?>

+ 25 - 0
main/auth/cas/logincas.php

@@ -0,0 +1,25 @@
+<?php
+//----------------------------------------------------------------------
+// CAS PLUGIN  
+//----------------------------------------------------------------------
+// Copyright (c) 2006-2007 University Marc Bloch (UMB)
+//----------------------------------------------------------------------
+// This program is under the terms of the GENERAL PUBLIC LICENSE (GPL)
+// as published by the FREE SOFTWARE FOUNDATION. The GPL is available
+// through the world-wide-web at http://www.gnu.org/copyleft/gpl.html
+//----------------------------------------------------------------------
+// Authors: Pierre Cahard
+//----------------------------------------------------------------------
+// Load required 
+require('../..//inc/global.inc.php');
+require_once (api_get_path(LIBRARY_PATH).'events.lib.inc.php');
+require_once('authcas.php');
+global $cas_auth_ver, $cas_auth_server, $cas_auth_port, $cas_auth_uri; 
+// phpCAS
+if (!is_object($PHPCAS_CLIENT) ) {
+	phpCAS::client($cas_auth_ver,$cas_auth_server,$cas_auth_port,$cas_auth_uri);
+	phpCAS::setNoCasServerValidation();
+}
+phpCAS::forceAuthentication();
+//echo 'ici';
+header('Location: '.api_get_path(WEB_PATH).api_get_setting('page_after_login'));

+ 31 - 2
main/install/db_main.sql

@@ -762,7 +762,17 @@ VALUES
 ('timezone_value', 'timezones', 'select', 'Timezones', '', 'TimezoneValueTitle','TimezoneValueComment',NULL,'Timezones', 1),
 ('timezone_value', 'timezones', 'select', 'Timezones', '', 'TimezoneValueTitle','TimezoneValueComment',NULL,'Timezones', 1),
 ('allow_user_course_subscription_by_course_admin', NULL, 'radio', 'Security', 'true', 'AllowUserCourseSubscriptionByCourseAdminTitle', 'AllowUserCourseSubscriptionByCourseAdminComment', NULL, NULL, 1),
 ('allow_user_course_subscription_by_course_admin', NULL, 'radio', 'Security', 'true', 'AllowUserCourseSubscriptionByCourseAdminTitle', 'AllowUserCourseSubscriptionByCourseAdminComment', NULL, NULL, 1),
 ('show_link_bug_notification', NULL, 'radio', 'Platform', 'true', 'ShowLinkBugNotificationTitle', 'ShowLinkBugNotificationComment', NULL, NULL, 0),
 ('show_link_bug_notification', NULL, 'radio', 'Platform', 'true', 'ShowLinkBugNotificationTitle', 'ShowLinkBugNotificationComment', NULL, NULL, 0),
-('dokeos_database_version', NULL, 'textfield', NULL,'1.8.7.11571','DokeosDatabaseVersion','',NULL,NULL,0);
+('dokeos_database_version', NULL, 'textfield', NULL,'1.8.7.11571','DokeosDatabaseVersion','',NULL,NULL,0),
+('cas_activate', NULL, 'radio', 'CAS', 'false', 'CasMainActivateTitle', 'CasMainActivateComment', NULL, NULL, 0),
+('cas_server', NULL, 'textfield', 'CAS', '', 'CasMainServerTitle', 'CasMainServerComment', NULL, NULL, 0),
+('cas_server_uri', NULL, 'textfield', 'CAS', '', 'CasMainServerURITitle', 'CasMainServerURIComment', NULL, NULL, 0),
+('cas_port', NULL, 'textfield', 'CAS', '', 'CasMainPortTitle', 'CasMainPortComment', NULL, NULL, 0),
+('cas_protocol', NULL, 'radio', 'CAS', '', 'CasMainProtocolTitle', 'CasMainProtocolComment', NULL, NULL, 0),
+('cas_add_user_activate', NULL, 'radio', 'CAS', '', 'CasUserAddActivateTitle', 'CasUserAddActivateComment', NULL, NULL, 0),
+('cas_add_user_login_attr', NULL, 'textfield', 'CAS', '', 'CasUserAddLoginAttributeTitle', 'CasUserAddLoginAttributeComment', NULL, NULL, 0),
+('cas_add_user_email_attr', NULL, 'textfield', 'CAS', '', 'CasUserAddEmailAttributeTitle', 'CasUserAddEmailAttributeComment', NULL, NULL, 0),
+('cas_add_user_firstname_attr', NULL, 'textfield', 'CAS', '', 'CasUserAddFirstnameAttributeTitle', 'CasUserAddFirstnameAttributeComment', NULL, NULL, 0),
+('cas_add_user_lastname_attr', NULL, 'textfield', 'CAS', '', 'CasUserAddLastnameAttributeTitle', 'CasUserAddLastnameAttributeComment', NULL, NULL, 0);
 
 
 
 
 UNLOCK TABLES;
 UNLOCK TABLES;
@@ -976,7 +986,15 @@ VALUES
 ('allow_user_course_subscription_by_course_admin', 'true', 'Yes'),
 ('allow_user_course_subscription_by_course_admin', 'true', 'Yes'),
 ('allow_user_course_subscription_by_course_admin', 'false', 'No'),
 ('allow_user_course_subscription_by_course_admin', 'false', 'No'),
 ('show_link_bug_notification', 'true', 'Yes'),
 ('show_link_bug_notification', 'true', 'Yes'),
-('show_link_bug_notification', 'false', 'No');
+('show_link_bug_notification', 'false', 'No'),
+('cas_activate', 'true', 'Yes'),
+('cas_activate', 'false', 'No'),
+('cas_protocol', 'CAS1', 'CAS1Text'),
+('cas_protocol', 'CAS2', 'CAS2Text'),
+('cas_protocol', 'SAML', 'SAMLText'),
+('cas_add_user_activate', 'true', 'Yes'),
+('cas_add_user_activate', 'false', 'No');
+
 
 
 UNLOCK TABLES;
 UNLOCK TABLES;
 
 
@@ -2382,6 +2400,17 @@ CREATE TABLE group_rel_user (
 ALTER TABLE group_rel_user ADD INDEX ( group_id );
 ALTER TABLE group_rel_user ADD INDEX ( group_id );
 ALTER TABLE group_rel_user ADD INDEX ( user_id );
 ALTER TABLE group_rel_user ADD INDEX ( user_id );
 ALTER TABLE group_rel_user ADD INDEX ( relation_type );
 ALTER TABLE group_rel_user ADD INDEX ( relation_type );
+
+CREATE TABLE group_rel_group (
+	id int NOT NULL AUTO_INCREMENT,
+	group_id int NOT NULL,
+	subgroup_id int NOT NULL,
+	relation_type int NOT NULL,
+	PRIMARY KEY (id)
+);
+ALTER TABLE group_rel_group ADD INDEX ( group_id );
+ALTER TABLE group_rel_group ADD INDEX ( subgroup_id );
+ALTER TABLE group_rel_group ADD INDEX ( relation_type );
 --
 --
 -- Table structure for table message attachment
 -- Table structure for table message attachment
 --
 --

+ 23 - 0
main/install/migrate-db-1.8.6.2-1.8.7-pre.sql

@@ -85,6 +85,24 @@ INSERT INTO settings_options (variable, value, display_text) VALUES ('allow_user
 INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES ('show_link_bug_notification', NULL, 'radio', 'Platform', 'true', 'ShowLinkBugNotificationTitle', 'ShowLinkBugNotificationComment', NULL, NULL, 0);
 INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES ('show_link_bug_notification', NULL, 'radio', 'Platform', 'true', 'ShowLinkBugNotificationTitle', 'ShowLinkBugNotificationComment', NULL, NULL, 0);
 INSERT INTO settings_options (variable, value, display_text) VALUES ('show_link_bug_notification', 'true', 'Yes');
 INSERT INTO settings_options (variable, value, display_text) VALUES ('show_link_bug_notification', 'true', 'Yes');
 INSERT INTO settings_options (variable, value, display_text) VALUES ('show_link_bug_notification', 'false', 'No');
 INSERT INTO settings_options (variable, value, display_text) VALUES ('show_link_bug_notification', 'false', 'No');
+-- CAS
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_activate', NULL, 'radio', 'CAS', 'false', 'CasMainActivateTitle', 'CasMainActivateComment', NULL, NULL, 0);
+INSERT INTO settings_options (variable, value, display_text) values ('cas_activate', 'true', 'Yes');
+INSERT INTO settings_options (variable, value, display_text) values ('cas_activate', 'false', 'No');
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_server', NULL, 'textfield', 'CAS', '', 'CasMainServerTitle', 'CasMainServerComment', NULL, NULL, 0);
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_server_uri', NULL, 'textfield', 'CAS', '', 'CasMainServerURITitle', 'CasMainServerURIComment', NULL, NULL, 0);
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_port', NULL, 'textfield', 'CAS', '', 'CasMainPortTitle', 'CasMainPortComment', NULL, NULL, 0);
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_protocol', NULL, 'radio', 'CAS', '', 'CasMainProtocolTitle', 'CasMainProtocolComment', NULL, NULL, 0);
+INSERT INTO settings_options (variable, value, display_text) values ('cas_protocol', 'CAS1', 'CAS1Text');
+INSERT INTO settings_options (variable, value, display_text) values ('cas_protocol', 'CAS2', 'CAS2Text');
+INSERT INTO settings_options (variable, value, display_text) values ('cas_protocol', 'SAML', 'SAMLText');
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_add_user_activate', NULL, 'radio', 'CAS', '', 'CasUserAddActivateTitle', 'CasUserAddActivateComment', NULL, NULL, 0);
+INSERT INTO settings_options (variable, value, display_text) values ('cas_add_user_activate', 'true', 'Yes');
+INSERT INTO settings_options (variable, value, display_text) values ('cas_add_user_activate', 'false', 'No');
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_add_user_login_attr', NULL, 'textfield', 'CAS', '', 'CasUserAddLoginAttributeTitle', 'CasUserAddLoginAttributeComment', NULL, NULL, 0);
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_add_user_email_attr', NULL, 'textfield', 'CAS', '', 'CasUserAddEmailAttributeTitle', 'CasUserAddEmailAttributeComment', NULL, NULL, 0);
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_add_user_firstname_attr', NULL, 'textfield', 'CAS', '', 'CasUserAddFirstnameAttributeTitle', 'CasUserAddFirstnameAttributeComment', NULL, NULL, 0);
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES('cas_add_user_lastname_attr', NULL, 'textfield', 'CAS', '', 'CasUserAddLastnameAttributeTitle', 'CasUserAddLastnameAttributeComment', NULL, NULL, 0);
 
 
 ALTER TABLE gradebook_score_display ADD category_id int NOT NULL DEFAULT 0;
 ALTER TABLE gradebook_score_display ADD category_id int NOT NULL DEFAULT 0;
 ALTER TABLE gradebook_score_display ADD INDEX (category_id);
 ALTER TABLE gradebook_score_display ADD INDEX (category_id);
@@ -111,6 +129,11 @@ ALTER TABLE track_e_attempt_recording ADD session_id INT NOT NULL DEFAULT 0;
 ALTER TABLE track_e_attempt_recording ADD INDEX (question_id);
 ALTER TABLE track_e_attempt_recording ADD INDEX (question_id);
 ALTER TABLE track_e_attempt_recording ADD INDEX (session_id);
 ALTER TABLE track_e_attempt_recording ADD INDEX (session_id);
 ALTER TABLE track_e_online ADD COLUMN access_url_id INT NOT NULL DEFAULT 1;
 ALTER TABLE track_e_online ADD COLUMN access_url_id INT NOT NULL DEFAULT 1;
+-- Groups can have subgroups
+CREATE TABLE group_rel_group ( id int NOT NULL AUTO_INCREMENT, group_id int NOT NULL, subgroup_id int NOT NULL, relation_type int NOT NULL, PRIMARY KEY (id));
+ALTER TABLE group_rel_group ADD INDEX ( group_id );
+ALTER TABLE group_rel_group ADD INDEX ( subgroup_id );
+ALTER TABLE group_rel_group ADD INDEX ( relation_type );
 
 
 -- xxUSERxx
 -- xxUSERxx
 
 

+ 25 - 1
main/lang/english/admin.inc.php

@@ -1269,4 +1269,28 @@ $ErrorReadingZip = "Error reading ZIP file";
 $ErrorStylesheetFilesExtensionsInsideZip = "The only accepted extensions in the ZIP file are jpg, jpeg, png, gif and css.";
 $ErrorStylesheetFilesExtensionsInsideZip = "The only accepted extensions in the ZIP file are jpg, jpeg, png, gif and css.";
 $MyTextHere = "Enter your text here...";
 $MyTextHere = "Enter your text here...";
 $FieldTypeSocialProfile = "Social network link";
 $FieldTypeSocialProfile = "Social network link";
-?>
+$CAS = "CAS";
+$CasMainActivateTitle = "CAS activation";
+$CasMainActivateComment = "Do you want to activate the CAS Authentication?";
+$CasMainServerTitle = "Main CAS server address";
+$CasMainServerComment = "The ip address or the hostname of your CAS server";
+$CasMainServerURITitle = "Root location of your cas installation";
+$CasMainServerURIComment = "The root location of your cas installation without the host name";
+$CasMainPortTitle = "CAS port";
+$CasMainPortComment = "CAS server port";
+$CAS1Text = "CASV1";
+$CAS2Text = "CASV2";
+$SAMLText = "SAML";
+$CasMainProtocolTitle = "CAS Protocol used";
+$CasMainProtocolComment = "CAS Protocol used. If you want to use your cas server to pass information within the cas ticket, choose SAML";
+$CasUserAddActivateTitle = "User add by CAS activation";
+$CasUserAddActivateComment = "Choose this oprtion if you want dokeos to automatically add user with information sent by the CAS server (using SAML)";
+$CasUserAddLoginAttributeTitle = "XML field for user login";
+$CasUserAddLoginAttributeComment = "The XML field of the CAS ticket containing the user login";
+$CasUserAddEmailAttributeTitle = "XML field for user email";
+$CasUserAddEmailAttributeComment = "The XML field of the CAS ticket containing the user email";
+$CasUserAddFirstnameAttributeTitle = "XML field for user firstname";
+$CasUserAddFirstnameAttributeComment = "The XML field of the CAS ticket containing the user firstname";
+$CasUserAddLastnameAttributeTitle = "XML field for user firsstname";
+$CasUserAddLastnameAttributeComment = "The XML field of the CAS ticket containing the user firstname";
+?>