diff --git a/docs/source/simplesamlphp-modules.xml b/docs/source/simplesamlphp-modules.xml index 2cf4b527ed94a2951fce8f7b40a7ee4bad990a25..2b08d8d9bb4f925c32eed8ea2d0c2a898147be10 100644 --- a/docs/source/simplesamlphp-modules.xml +++ b/docs/source/simplesamlphp-modules.xml @@ -7,7 +7,7 @@ <articleinfo> <date>2008-08-21</date> - <pubdate>Thu Aug 21 10:32:42 2008</pubdate> + <pubdate>Thu Aug 21 10:45:26 2008</pubdate> <author> <firstname>Olav</firstname> diff --git a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php index 06065913da1fd5f67d4c11dafbf4430816b7b41d..009b55d192092fb2c046973f7d106f3cf9e37af4 100644 --- a/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php +++ b/lib/SimpleSAML/Bindings/SAML20/HTTPRedirect.php @@ -177,7 +177,7 @@ class SimpleSAML_Bindings_SAML20_HTTPRedirect { return $redirectURL; } - + # $request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP' public function sendMessage($request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP') { SimpleSAML_Utilities::validateXMLDocument($request, 'saml20'); diff --git a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php index a866801084bd0fc231e7256e22e2dfc4942d6104..be7d1be84cd3157c317dd01464e38c4bac4a277b 100644 --- a/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php +++ b/lib/SimpleSAML/Metadata/MetaDataStorageHandler.php @@ -87,7 +87,7 @@ class SimpleSAML_Metadata_MetaDataStorageHandler { * @param $set The set we the property comes from. * @return The autogenerated metadata property. */ - public function getGenerated($property, $set = 'saml20-sp-hosted') { + public function getGenerated($property, $set = 'saml20-sp-hosted', $options = array() ) { /* First we check if the user has overridden this property in the metadata. */ try { @@ -115,12 +115,38 @@ class SimpleSAML_Metadata_MetaDataStorageHandler { return $baseurl . 'saml2/sp/SingleLogoutService.php'; } } elseif($set == 'saml20-idp-hosted') { + + $logouttype = 'traditional'; + if (array_key_exists('logouttype', $options)) $logouttype = $options['logouttype']; + if (!in_array($logouttype, array('traditional', 'iframe'))) + throw new Exception('Invalid logout type [' . $logouttype . '] in IdP Hosted Metadata'); + switch ($property) { case 'SingleSignOnService' : return $baseurl . 'saml2/idp/SSOService.php'; case 'SingleLogoutService' : - return $baseurl . 'saml2/idp/SingleLogoutService.php'; + + switch ($logouttype) { + case 'iframe' : + return $baseurl . 'saml2/idp/SingleLogoutServiceiFrame.php'; + + case 'traditional' : + default : + return $baseurl . 'saml2/idp/SingleLogoutService.php'; + } + + case 'SingleLogoutServiceResponse' : + + switch ($logouttype) { + case 'iframe' : + return $baseurl . 'saml2/idp/SingleLogoutServiceiFrameResponse.php'; + + case 'traditional' : + default : + return $baseurl . 'saml2/idp/SingleLogoutService.php'; + } + } } elseif($set == 'shib13-sp-hosted') { switch ($property) { diff --git a/libextinc/xajax/copyright.inc.php b/libextinc/xajax/copyright.inc.php new file mode 100755 index 0000000000000000000000000000000000000000..ce24adff6bc34f564f6afb03b41aeedd723f5e8c --- /dev/null +++ b/libextinc/xajax/copyright.inc.php @@ -0,0 +1,54 @@ +<?php +/* + File: copyright.inc.php + + This file contains detailed information regarding the xajax project, + current version, copyrights, licnese and documentation. + + You do not need to include this file in your project for xajax to + function properly. +*/ + +/* + Section: Current Version + + xajax version 0.5 (beta 4) +*/ + +/* + Section: Copyright + + - copyright (c) 2005-2007 by Jared White & J. Max Wilson + - portions copyright (c) 2006-2007 by Joseph Woolley +*/ + +/* + Section: Description + + xajax is an open source PHP class library for easily creating powerful + PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously + call PHP functions and update the content of your your webpage without + reloading the page. +*/ + +/* + Section: License + + xajax is released under the terms of the BSD license + http://www.xajaxproject.org/bsd_license.txt +*/ + +/* + @package xajax + @version $Id: copyright.inc.php 327 2007-02-28 16:55:26Z calltoconstruct $ + @copyright Copyright (c) 2005-2006 by Jared White & J. Max Wilson + @license http://www.xajaxproject.org/bsd_license.txt BSD License +*/ + +/* + Section: Online documentation + + Online documentation for this class is available on the xajax wiki at: + http://wiki.xajaxproject.org/Documentation:xajax.inc.php +*/ + diff --git a/libextinc/xajax/xajax.inc.php b/libextinc/xajax/xajax.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..602a049302b4921f5fee17a7898a8d4e620eaeeb --- /dev/null +++ b/libextinc/xajax/xajax.inc.php @@ -0,0 +1,1239 @@ +<?php +/** + * xajax.inc.php :: Main xajax class and setup file + * + * xajax version 0.2.4 + * copyright (c) 2005 by Jared White & J. Max Wilson + * http://www.xajaxproject.org + * + * xajax is an open source PHP class library for easily creating powerful + * PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously + * call PHP functions and update the content of your your webpage without + * reloading the page. + * + * xajax is released under the terms of the LGPL license + * http://www.gnu.org/copyleft/lesser.html#SEC3 + * + * This library 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * @package xajax + * @version $Id$ + * @copyright Copyright (c) 2005-2006 by Jared White & J. Max Wilson + * @license http://www.gnu.org/copyleft/lesser.html#SEC3 LGPL License + */ + +/* + ---------------------------------------------------------------------------- + | Online documentation for this class is available on the xajax wiki at: | + | http://wiki.xajaxproject.org/Documentation:xajax.inc.php | + ---------------------------------------------------------------------------- +*/ + +/** + * Define XAJAX_DEFAULT_CHAR_ENCODING that is used by both + * the xajax and xajaxResponse classes + */ +if (!defined ('XAJAX_DEFAULT_CHAR_ENCODING')) +{ + define ('XAJAX_DEFAULT_CHAR_ENCODING', 'utf-8' ); +} + +require_once(dirname(__FILE__)."/xajaxResponse.inc.php"); + +/** + * Communication Method Defines + */ +if (!defined ('XAJAX_GET')) +{ + define ('XAJAX_GET', 0); +} +if (!defined ('XAJAX_POST')) +{ + define ('XAJAX_POST', 1); +} + +/** + * The xajax class generates the xajax javascript for your page including the + * Javascript wrappers for the PHP functions that you want to call from your page. + * It also handles processing and executing the command messages in the XML responses + * sent back to your page from your PHP functions. + * + * @package xajax + */ +class xajax +{ + /**#@+ + * @access protected + */ + /** + * @var array Array of PHP functions that will be callable through javascript wrappers + */ + var $aFunctions; + /** + * @var array Array of object callbacks that will allow Javascript to call PHP methods (key=function name) + */ + var $aObjects; + /** + * @var array Array of RequestTypes to be used with each function (key=function name) + */ + var $aFunctionRequestTypes; + /** + * @var array Array of Include Files for any external functions (key=function name) + */ + var $aFunctionIncludeFiles; + /** + * @var string Name of the PHP function to call if no callable function was found + */ + var $sCatchAllFunction; + /** + * @var string Name of the PHP function to call before any other function + */ + var $sPreFunction; + /** + * @var string The URI for making requests to the xajax object + */ + var $sRequestURI; + /** + * @var string The prefix to prepend to the javascript wraper function name + */ + var $sWrapperPrefix; + /** + * @var boolean Show debug messages (default false) + */ + var $bDebug; + /** + * @var boolean Show messages in the client browser's status bar (default false) + */ + var $bStatusMessages; + /** + * @var boolean Allow xajax to exit after processing a request (default true) + */ + var $bExitAllowed; + /** + * @var boolean Use wait cursor in browser (default true) + */ + var $bWaitCursor; + /** + * @var boolean Use an special xajax error handler so the errors are sent to the browser properly (default false) + */ + var $bErrorHandler; + /** + * @var string Specify what, if any, file xajax should log errors to (and more information in a future release) + */ + var $sLogFile; + /** + * @var boolean Clean all output buffers before outputting response (default false) + */ + var $bCleanBuffer; + /** + * @var string String containing the character encoding used + */ + var $sEncoding; + /** + * @var boolean Decode input request args from UTF-8 (default false) + */ + var $bDecodeUTF8Input; + /** + * @var boolean Convert special characters to HTML entities (default false) + */ + var $bOutputEntities; + /** + * @var array Array for parsing complex objects + */ + var $aObjArray; + /** + * @var integer Position in $aObjArray + */ + var $iPos; + + /**#@-*/ + + /** + * Constructor. You can set some extra xajax options right away or use + * individual methods later to set options. + * + * @param string defaults to the current browser URI + * @param string defaults to "xajax_"; + * @param string defaults to XAJAX_DEFAULT_CHAR_ENCODING defined above + * @param boolean defaults to false + */ + function xajax($sRequestURI="",$sWrapperPrefix="xajax_",$sEncoding=XAJAX_DEFAULT_CHAR_ENCODING,$bDebug=false) + { + $this->aFunctions = array(); + $this->aObjects = array(); + $this->aFunctionIncludeFiles = array(); + $this->sRequestURI = $sRequestURI; + if ($this->sRequestURI == "") + $this->sRequestURI = $this->_detectURI(); + $this->sWrapperPrefix = $sWrapperPrefix; + $this->bDebug = $bDebug; + $this->bStatusMessages = false; + $this->bWaitCursor = true; + $this->bExitAllowed = true; + $this->bErrorHandler = false; + $this->sLogFile = ""; + $this->bCleanBuffer = false; + $this->setCharEncoding($sEncoding); + $this->bDecodeUTF8Input = false; + $this->bOutputEntities = false; + } + + /** + * Sets the URI to which requests will be made. + * <i>Usage:</i> <kbd>$xajax->setRequestURI("http://www.xajaxproject.org");</kbd> + * + * @param string the URI (can be absolute or relative) of the PHP script + * that will be accessed when an xajax request occurs + */ + function setRequestURI($sRequestURI) + { + $this->sRequestURI = $sRequestURI; + } + + /** + * Sets the prefix that will be appended to the Javascript wrapper + * functions (default is "xajax_"). + * + * @param string + */ + // + function setWrapperPrefix($sPrefix) + { + $this->sWrapperPrefix = $sPrefix; + } + + /** + * Enables debug messages for xajax. + * */ + function debugOn() + { + $this->bDebug = true; + } + + /** + * Disables debug messages for xajax (default behavior). + */ + function debugOff() + { + $this->bDebug = false; + } + + /** + * Enables messages in the browser's status bar for xajax. + */ + function statusMessagesOn() + { + $this->bStatusMessages = true; + } + + /** + * Disables messages in the browser's status bar for xajax (default behavior). + */ + function statusMessagesOff() + { + $this->bStatusMessages = false; + } + + /** + * Enables the wait cursor to be displayed in the browser (default behavior). + */ + function waitCursorOn() + { + $this->bWaitCursor = true; + } + + /** + * Disables the wait cursor to be displayed in the browser. + */ + function waitCursorOff() + { + $this->bWaitCursor = false; + } + + /** + * Enables xajax to exit immediately after processing a request and + * sending the response back to the browser (default behavior). + */ + function exitAllowedOn() + { + $this->bExitAllowed = true; + } + + /** + * Disables xajax's default behavior of exiting immediately after + * processing a request and sending the response back to the browser. + */ + function exitAllowedOff() + { + $this->bExitAllowed = false; + } + + /** + * Turns on xajax's error handling system so that PHP errors that occur + * during a request are trapped and pushed to the browser in the form of + * a Javascript alert. + */ + function errorHandlerOn() + { + $this->bErrorHandler = true; + } + + /** + * Turns off xajax's error handling system (default behavior). + */ + function errorHandlerOff() + { + $this->bErrorHandler = false; + } + + /** + * Specifies a log file that will be written to by xajax during a request + * (used only by the error handling system at present). If you don't invoke + * this method, or you pass in "", then no log file will be written to. + * <i>Usage:</i> <kbd>$xajax->setLogFile("/xajax_logs/errors.log");</kbd> + */ + function setLogFile($sFilename) + { + $this->sLogFile = $sFilename; + } + + /** + * Causes xajax to clean out all output buffers before outputting a + * response (default behavior). + */ + function cleanBufferOn() + { + $this->bCleanBuffer = true; + } + /** + * Turns off xajax's output buffer cleaning. + */ + function cleanBufferOff() + { + $this->bCleanBuffer = false; + } + + /** + * Sets the character encoding for the HTTP output based on + * <kbd>$sEncoding</kbd>, which is a string containing the character + * encoding to use. You don't need to use this method normally, since the + * character encoding for the response gets set automatically based on the + * <kbd>XAJAX_DEFAULT_CHAR_ENCODING</kbd> constant. + * <i>Usage:</i> <kbd>$xajax->setCharEncoding("utf-8");</kbd> + * + * @param string the encoding type to use (utf-8, iso-8859-1, etc.) + */ + function setCharEncoding($sEncoding) + { + $this->sEncoding = $sEncoding; + } + + /** + * Causes xajax to decode the input request args from UTF-8 to the current + * encoding if possible. Either the iconv or mb_string extension must be + * present for optimal functionality. + */ + function decodeUTF8InputOn() + { + $this->bDecodeUTF8Input = true; + } + + /** + * Turns off decoding the input request args from UTF-8 (default behavior). + */ + function decodeUTF8InputOff() + { + $this->bDecodeUTF8Input = false; + } + + /** + * Tells the response object to convert special characters to HTML entities + * automatically (only works if the mb_string extension is available). + */ + function outputEntitiesOn() + { + $this->bOutputEntities = true; + } + + /** + * Tells the response object to output special characters intact. (default + * behavior). + */ + function outputEntitiesOff() + { + $this->bOutputEntities = false; + } + + /** + * Registers a PHP function or method to be callable through xajax in your + * Javascript. If you want to register a function, pass in the name of that + * function. If you want to register a static class method, pass in an + * array like so: + * <kbd>array("myFunctionName", "myClass", "myMethod")</kbd> + * For an object instance method, use an object variable for the second + * array element (and in PHP 4 make sure you put an & before the variable + * to pass the object by reference). Note: the function name is what you + * call via Javascript, so it can be anything as long as it doesn't + * conflict with any other registered function name. + * + * <i>Usage:</i> <kbd>$xajax->registerFunction("myFunction");</kbd> + * or: <kbd>$xajax->registerFunction(array("myFunctionName", &$myObject, "myMethod"));</kbd> + * + * @param mixed contains the function name or an object callback array + * @param mixed request type (XAJAX_GET/XAJAX_POST) that should be used + * for this function. Defaults to XAJAX_POST. + */ + function registerFunction($mFunction,$sRequestType=XAJAX_POST) + { + if (is_array($mFunction)) { + $this->aFunctions[$mFunction[0]] = 1; + $this->aFunctionRequestTypes[$mFunction[0]] = $sRequestType; + $this->aObjects[$mFunction[0]] = array_slice($mFunction, 1); + } + else { + $this->aFunctions[$mFunction] = 1; + $this->aFunctionRequestTypes[$mFunction] = $sRequestType; + } + } + + /** + * Registers a PHP function to be callable through xajax which is located + * in some other file. If the function is requested the external file will + * be included to define the function before the function is called. + * + * <i>Usage:</i> <kbd>$xajax->registerExternalFunction("myFunction","myFunction.inc.php",XAJAX_POST);</kbd> + * + * @param string contains the function name or an object callback array + * ({@link xajax::registerFunction() see registerFunction} for + * more info on object callback arrays) + * @param string contains the path and filename of the include file + * @param mixed the RequestType (XAJAX_GET/XAJAX_POST) that should be used + * for this function. Defaults to XAJAX_POST. + */ + function registerExternalFunction($mFunction,$sIncludeFile,$sRequestType=XAJAX_POST) + { + $this->registerFunction($mFunction, $sRequestType); + + if (is_array($mFunction)) { + $this->aFunctionIncludeFiles[$mFunction[0]] = $sIncludeFile; + } + else { + $this->aFunctionIncludeFiles[$mFunction] = $sIncludeFile; + } + } + + /** + * Registers a PHP function to be called when xajax cannot find the + * function being called via Javascript. Because this is technically + * impossible when using "wrapped" functions, the catch-all feature is + * only useful when you're directly using the xajax.call() Javascript + * method. Use the catch-all feature when you want more dynamic ability to + * intercept unknown calls and handle them in a custom way. + * + * <i>Usage:</i> <kbd>$xajax->registerCatchAllFunction("myCatchAllFunction");</kbd> + * + * @param string contains the function name or an object callback array + * ({@link xajax::registerFunction() see registerFunction} for + * more info on object callback arrays) + */ + function registerCatchAllFunction($mFunction) + { + if (is_array($mFunction)) { + $this->sCatchAllFunction = $mFunction[0]; + $this->aObjects[$mFunction[0]] = array_slice($mFunction, 1); + } + else { + $this->sCatchAllFunction = $mFunction; + } + } + + /** + * Registers a PHP function to be called before xajax calls the requested + * function. xajax will automatically add the request function's response + * to the pre-function's response to create a single response. Another + * feature is the ability to return not just a response, but an array with + * the first element being false (a boolean) and the second being the + * response. In this case, the pre-function's response will be returned to + * the browser without xajax calling the requested function. + * + * <i>Usage:</i> <kbd>$xajax->registerPreFunction("myPreFunction");</kbd> + * + * @param string contains the function name or an object callback array + * ({@link xajax::registerFunction() see registerFunction} for + * more info on object callback arrays) + */ + function registerPreFunction($mFunction) + { + if (is_array($mFunction)) { + $this->sPreFunction = $mFunction[0]; + $this->aObjects[$mFunction[0]] = array_slice($mFunction, 1); + } + else { + $this->sPreFunction = $mFunction; + } + } + + /** + * Returns true if xajax can process the request, false if otherwise. + * You can use this to determine if xajax needs to process the request or + * not. + * + * @return boolean + */ + function canProcessRequests() + { + if ($this->getRequestMode() != -1) return true; + return false; + } + + /** + * Returns the current request mode (XAJAX_GET or XAJAX_POST), or -1 if + * there is none. + * + * @return mixed + */ + function getRequestMode() + { + if (!empty($_GET["xajax"])) + return XAJAX_GET; + + if (!empty($_POST["xajax"])) + return XAJAX_POST; + + return -1; + } + + /** + * This is the main communications engine of xajax. The engine handles all + * incoming xajax requests, calls the apporiate PHP functions (or + * class/object methods) and passes the XML responses back to the + * Javascript response handler. If your RequestURI is the same as your Web + * page then this function should be called before any headers or HTML has + * been sent. + */ + function processRequests() + { + + $requestMode = -1; + $sFunctionName = ""; + $bFoundFunction = true; + $bFunctionIsCatchAll = false; + $sFunctionNameForSpecial = ""; + $aArgs = array(); + $sPreResponse = ""; + $bEndRequest = false; + $sResponse = ""; + + $requestMode = $this->getRequestMode(); + if ($requestMode == -1) return; + + if ($requestMode == XAJAX_POST) + { + $sFunctionName = $_POST["xajax"]; + + if (!empty($_POST["xajaxargs"])) + $aArgs = $_POST["xajaxargs"]; + } + else + { + header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + header ("Cache-Control: no-cache, must-revalidate"); + header ("Pragma: no-cache"); + + $sFunctionName = $_GET["xajax"]; + + if (!empty($_GET["xajaxargs"])) + $aArgs = $_GET["xajaxargs"]; + } + + // Use xajax error handler if necessary + if ($this->bErrorHandler) { + $GLOBALS['xajaxErrorHandlerText'] = ""; + set_error_handler("xajaxErrorHandler"); + } + + if ($this->sPreFunction) { + if (!$this->_isFunctionCallable($this->sPreFunction)) { + $bFoundFunction = false; + $objResponse = new xajaxResponse(); + $objResponse->addAlert("Unknown Pre-Function ". $this->sPreFunction); + $sResponse = $objResponse->getXML(); + } + } + //include any external dependencies associated with this function name + if (array_key_exists($sFunctionName,$this->aFunctionIncludeFiles)) + { + ob_start(); + include_once($this->aFunctionIncludeFiles[$sFunctionName]); + ob_end_clean(); + } + + if ($bFoundFunction) { + $sFunctionNameForSpecial = $sFunctionName; + if (!array_key_exists($sFunctionName, $this->aFunctions)) + { + if ($this->sCatchAllFunction) { + $sFunctionName = $this->sCatchAllFunction; + $bFunctionIsCatchAll = true; + } + else { + $bFoundFunction = false; + $objResponse = new xajaxResponse(); + $objResponse->addAlert("Unknown Function $sFunctionName."); + $sResponse = $objResponse->getXML(); + } + } + else if ($this->aFunctionRequestTypes[$sFunctionName] != $requestMode) + { + $bFoundFunction = false; + $objResponse = new xajaxResponse(); + $objResponse->addAlert("Incorrect Request Type."); + $sResponse = $objResponse->getXML(); + } + } + + if ($bFoundFunction) + { + for ($i = 0; $i < sizeof($aArgs); $i++) + { + // If magic quotes is on, then we need to strip the slashes from the args + if (get_magic_quotes_gpc() == 1 && is_string($aArgs[$i])) { + + $aArgs[$i] = stripslashes($aArgs[$i]); + } + if (stristr($aArgs[$i],"<xjxobj>") != false) + { + $aArgs[$i] = $this->_xmlToArray("xjxobj",$aArgs[$i]); + } + else if (stristr($aArgs[$i],"<xjxquery>") != false) + { + $aArgs[$i] = $this->_xmlToArray("xjxquery",$aArgs[$i]); + } + else if ($this->bDecodeUTF8Input) + { + $aArgs[$i] = $this->_decodeUTF8Data($aArgs[$i]); + } + } + + if ($this->sPreFunction) { + $mPreResponse = $this->_callFunction($this->sPreFunction, array($sFunctionNameForSpecial, $aArgs)); + if (is_array($mPreResponse) && $mPreResponse[0] === false) { + $bEndRequest = true; + $sPreResponse = $mPreResponse[1]; + } + else { + $sPreResponse = $mPreResponse; + } + if (is_a($sPreResponse, "xajaxResponse")) { + $sPreResponse = $sPreResponse->getXML(); + } + if ($bEndRequest) $sResponse = $sPreResponse; + } + + if (!$bEndRequest) { + if (!$this->_isFunctionCallable($sFunctionName)) { + $objResponse = new xajaxResponse(); + $objResponse->addAlert("The Registered Function $sFunctionName Could Not Be Found."); + $sResponse = $objResponse->getXML(); + } + else { + if ($bFunctionIsCatchAll) { + $aArgs = array($sFunctionNameForSpecial, $aArgs); + } + $sResponse = $this->_callFunction($sFunctionName, $aArgs); + } + if (is_a($sResponse, "xajaxResponse")) { + $sResponse = $sResponse->getXML(); + } + if (!is_string($sResponse) || strpos($sResponse, "<xjx>") === FALSE) { + $objResponse = new xajaxResponse(); + $objResponse->addAlert("No XML Response Was Returned By Function $sFunctionName."); + $sResponse = $objResponse->getXML(); + } + else if ($sPreResponse != "") { + $sNewResponse = new xajaxResponse($this->sEncoding, $this->bOutputEntities); + $sNewResponse->loadXML($sPreResponse); + $sNewResponse->loadXML($sResponse); + $sResponse = $sNewResponse->getXML(); + } + } + } + + $sContentHeader = "Content-type: text/xml;"; + if ($this->sEncoding && strlen(trim($this->sEncoding)) > 0) + $sContentHeader .= " charset=".$this->sEncoding; + header($sContentHeader); + if ($this->bErrorHandler && !empty( $GLOBALS['xajaxErrorHandlerText'] )) { + $sErrorResponse = new xajaxResponse(); + $sErrorResponse->addAlert("** PHP Error Messages: **" . $GLOBALS['xajaxErrorHandlerText']); + if ($this->sLogFile) { + $fH = @fopen($this->sLogFile, "a"); + if (!$fH) { + $sErrorResponse->addAlert("** Logging Error **\n\nxajax was unable to write to the error log file:\n" . $this->sLogFile); + } + else { + fwrite($fH, "** xajax Error Log - " . strftime("%b %e %Y %I:%M:%S %p") . " **" . $GLOBALS['xajaxErrorHandlerText'] . "\n\n\n"); + fclose($fH); + } + } + + $sErrorResponse->loadXML($sResponse); + $sResponse = $sErrorResponse->getXML(); + + } + if ($this->bCleanBuffer) while (@ob_end_clean()); + print $sResponse; + if ($this->bErrorHandler) restore_error_handler(); + + if ($this->bExitAllowed) + exit(); + } + + /** + * Prints the xajax Javascript header and wrapper code into your page by + * printing the output of the getJavascript() method. It should only be + * called between the <pre><head> </head></pre> tags in your HTML page. + * Remember, if you only want to obtain the result of this function, use + * {@link xajax::getJavascript()} instead. + * + * <i>Usage:</i> + * <code> + * <head> + * ... + * < ?php $xajax->printJavascript(); ? > + * </code> + * + * @param string the relative address of the folder where xajax has been + * installed. For instance, if your PHP file is + * "http://www.myserver.com/myfolder/mypage.php" + * and xajax was installed in + * "http://www.myserver.com/anotherfolder", then $sJsURI + * should be set to "../anotherfolder". Defaults to assuming + * xajax is in the same folder as your PHP file. + * @param string the relative folder/file pair of the xajax Javascript + * engine located within the xajax installation folder. + * Defaults to xajax_js/xajax.js. + */ + function printJavascript($sJsURI="", $sJsFile=NULL) + { + print $this->getJavascript($sJsURI, $sJsFile); + } + + /** + * Returns the xajax Javascript code that should be added to your HTML page + * between the <kbd><head> </head></kbd> tags. + * + * <i>Usage:</i> + * <code> + * < ?php $xajaxJSHead = $xajax->getJavascript(); ? > + * <head> + * ... + * < ?php echo $xajaxJSHead; ? > + * </code> + * + * @param string the relative address of the folder where xajax has been + * installed. For instance, if your PHP file is + * "http://www.myserver.com/myfolder/mypage.php" + * and xajax was installed in + * "http://www.myserver.com/anotherfolder", then $sJsURI + * should be set to "../anotherfolder". Defaults to assuming + * xajax is in the same folder as your PHP file. + * @param string the relative folder/file pair of the xajax Javascript + * engine located within the xajax installation folder. + * Defaults to xajax_js/xajax.js. + * @return string + */ + function getJavascript($sJsURI="", $sJsFile=NULL) + { + $html = $this->getJavascriptConfig(); + $html .= $this->getJavascriptInclude($sJsURI, $sJsFile); + + return $html; + } + + /** + * Returns a string containing inline Javascript that sets up the xajax + * runtime (typically called internally by xajax from get/printJavascript). + * + * @return string + */ + function getJavascriptConfig() + { + $html = "\t<script type=\"text/javascript\">\n"; + $html .= "var xajaxRequestUri=\"".$this->sRequestURI."\";\n"; + $html .= "var xajaxDebug=".($this->bDebug?"true":"false").";\n"; + $html .= "var xajaxStatusMessages=".($this->bStatusMessages?"true":"false").";\n"; + $html .= "var xajaxWaitCursor=".($this->bWaitCursor?"true":"false").";\n"; + $html .= "var xajaxDefinedGet=".XAJAX_GET.";\n"; + $html .= "var xajaxDefinedPost=".XAJAX_POST.";\n"; + $html .= "var xajaxLoaded=false;\n"; + + foreach($this->aFunctions as $sFunction => $bExists) { + $html .= $this->_wrap($sFunction,$this->aFunctionRequestTypes[$sFunction]); + } + + $html .= "\t</script>\n"; + return $html; + } + + /** + * Returns a string containing a Javascript include of the xajax.js file + * along with a check to see if the file loaded after six seconds + * (typically called internally by xajax from get/printJavascript). + * + * @param string the relative address of the folder where xajax has been + * installed. For instance, if your PHP file is + * "http://www.myserver.com/myfolder/mypage.php" + * and xajax was installed in + * "http://www.myserver.com/anotherfolder", then $sJsURI + * should be set to "../anotherfolder". Defaults to assuming + * xajax is in the same folder as your PHP file. + * @param string the relative folder/file pair of the xajax Javascript + * engine located within the xajax installation folder. + * Defaults to xajax_js/xajax.js. + * @return string + */ + function getJavascriptInclude($sJsURI="", $sJsFile=NULL) + { + if ($sJsFile == NULL) $sJsFile = "xajax_js/xajax.js"; + + if ($sJsURI != "" && substr($sJsURI, -1) != "/") $sJsURI .= "/"; + + $html = "\t<script type=\"text/javascript\" src=\"" . $sJsURI . $sJsFile . "\"></script>\n"; + $html .= "\t<script type=\"text/javascript\">\n"; + $html .= "window.setTimeout(function () { if (!xajaxLoaded) { alert('Error: the xajax Javascript file could not be included. Perhaps the URL is incorrect?\\nURL: {$sJsURI}{$sJsFile}'); } }, 6000);\n"; + $html .= "\t</script>\n"; + return $html; + } + + /** + * This method can be used to create a new xajax.js file out of the + * xajax_uncompressed.js file (which will only happen if xajax.js doesn't + * already exist on the filesystem). + * + * @param string an optional argument containing the full server file path + * of xajax.js. + */ + function autoCompressJavascript($sJsFullFilename=NULL) + { + $sJsFile = "xajax_js/xajax.js"; + + if ($sJsFullFilename) { + $realJsFile = $sJsFullFilename; + } + else { + $realPath = realpath(dirname(__FILE__)); + $realJsFile = $realPath . "/". $sJsFile; + } + + // Create a compressed file if necessary + if (!file_exists($realJsFile)) { + $srcFile = str_replace(".js", "_uncompressed.js", $realJsFile); + if (!file_exists($srcFile)) { + trigger_error("The xajax uncompressed Javascript file could not be found in the <b>" . dirname($realJsFile) . "</b> folder. Error ", E_USER_ERROR); + } + require(dirname(__FILE__)."/xajaxCompress.php"); + $javaScript = implode('', file($srcFile)); + $compressedScript = xajaxCompressJavascript($javaScript); + $fH = @fopen($realJsFile, "w"); + if (!$fH) { + trigger_error("The xajax compressed javascript file could not be written in the <b>" . dirname($realJsFile) . "</b> folder. Error ", E_USER_ERROR); + } + else { + fwrite($fH, $compressedScript); + fclose($fH); + } + } + } + + /** + * Returns the current URL based upon the SERVER vars. + * + * @access private + * @return string + */ + function _detectURI() { + $aURL = array(); + + // Try to get the request URL + if (!empty($_SERVER['REQUEST_URI'])) { + $aURL = parse_url($_SERVER['REQUEST_URI']); + } + + // Fill in the empty values + if (empty($aURL['scheme'])) { + if (!empty($_SERVER['HTTP_SCHEME'])) { + $aURL['scheme'] = $_SERVER['HTTP_SCHEME']; + } else { + $aURL['scheme'] = (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') ? 'https' : 'http'; + } + } + + if (empty($aURL['host'])) { + if (!empty($_SERVER['HTTP_HOST'])) { + if (strpos($_SERVER['HTTP_HOST'], ':') > 0) { + list($aURL['host'], $aURL['port']) = explode(':', $_SERVER['HTTP_HOST']); + } else { + $aURL['host'] = $_SERVER['HTTP_HOST']; + } + } else if (!empty($_SERVER['SERVER_NAME'])) { + $aURL['host'] = $_SERVER['SERVER_NAME']; + } else { + print "xajax Error: xajax failed to automatically identify your Request URI."; + print "Please set the Request URI explicitly when you instantiate the xajax object."; + exit(); + } + } + + if (empty($aURL['port']) && !empty($_SERVER['SERVER_PORT'])) { + $aURL['port'] = $_SERVER['SERVER_PORT']; + } + + if (empty($aURL['path'])) { + if (!empty($_SERVER['PATH_INFO'])) { + $sPath = parse_url($_SERVER['PATH_INFO']); + } else { + $sPath = parse_url($_SERVER['PHP_SELF']); + } + $aURL['path'] = $sPath['path']; + unset($sPath); + } + + if (!empty($aURL['query'])) { + $aURL['query'] = '?'.$aURL['query']; + } + + // Build the URL: Start with scheme, user and pass + $sURL = $aURL['scheme'].'://'; + if (!empty($aURL['user'])) { + $sURL.= $aURL['user']; + if (!empty($aURL['pass'])) { + $sURL.= ':'.$aURL['pass']; + } + $sURL.= '@'; + } + + // Add the host + $sURL.= $aURL['host']; + + // Add the port if needed + if (!empty($aURL['port']) && (($aURL['scheme'] == 'http' && $aURL['port'] != 80) || ($aURL['scheme'] == 'https' && $aURL['port'] != 443))) { + $sURL.= ':'.$aURL['port']; + } + + // Add the path and the query string + $sURL.= $aURL['path'].@$aURL['query']; + + // Clean up + unset($aURL); + return $sURL; + } + + /** + * Returns true if the function name is associated with an object callback, + * false if not. + * + * @param string the name of the function + * @access private + * @return boolean + */ + function _isObjectCallback($sFunction) + { + if (array_key_exists($sFunction, $this->aObjects)) return true; + return false; + } + + /** + * Returns true if the function or object callback can be called, false if + * not. + * + * @param string the name of the function + * @access private + * @return boolean + */ + function _isFunctionCallable($sFunction) + { + if ($this->_isObjectCallback($sFunction)) { + if (is_object($this->aObjects[$sFunction][0])) { + return method_exists($this->aObjects[$sFunction][0], $this->aObjects[$sFunction][1]); + } + else { + return is_callable($this->aObjects[$sFunction]); + } + } + else { + return function_exists($sFunction); + } + } + + /** + * Calls the function, class method, or object method with the supplied + * arguments. + * + * @param string the name of the function + * @param array arguments to pass to the function + * @access private + * @return mixed the output of the called function or method + */ + function _callFunction($sFunction, $aArgs) + { + if ($this->_isObjectCallback($sFunction)) { + $mReturn = call_user_func_array($this->aObjects[$sFunction], $aArgs); + } + else { + $mReturn = call_user_func_array($sFunction, $aArgs); + } + return $mReturn; + } + + /** + * Generates the Javascript wrapper for the specified PHP function. + * + * @param string the name of the function + * @param mixed the request type + * @access private + * @return string + */ + function _wrap($sFunction,$sRequestType=XAJAX_POST) + { + $js = "function ".$this->sWrapperPrefix."$sFunction(){return xajax.call(\"$sFunction\", arguments, ".$sRequestType.");}\n"; + return $js; + } + + /** + * Takes a string containing xajax xjxobj XML or xjxquery XML and builds an + * array representation of it to pass as an argument to the PHP function + * being called. + * + * @param string the root tag of the XML + * @param string XML to convert + * @access private + * @return array + */ + function _xmlToArray($rootTag, $sXml) + { + $aArray = array(); + $sXml = str_replace("<$rootTag>","<$rootTag>|~|",$sXml); + $sXml = str_replace("</$rootTag>","</$rootTag>|~|",$sXml); + $sXml = str_replace("<e>","<e>|~|",$sXml); + $sXml = str_replace("</e>","</e>|~|",$sXml); + $sXml = str_replace("<k>","<k>|~|",$sXml); + $sXml = str_replace("</k>","|~|</k>|~|",$sXml); + $sXml = str_replace("<v>","<v>|~|",$sXml); + $sXml = str_replace("</v>","|~|</v>|~|",$sXml); + $sXml = str_replace("<q>","<q>|~|",$sXml); + $sXml = str_replace("</q>","|~|</q>|~|",$sXml); + + $this->aObjArray = explode("|~|",$sXml); + + $this->iPos = 0; + $aArray = $this->_parseObjXml($rootTag); + + return $aArray; + } + + /** + * A recursive function that generates an array from the contents of + * $this->aObjArray. + * + * @param string the root tag of the XML + * @access private + * @return array + */ + function _parseObjXml($rootTag) + { + $aArray = array(); + + if ($rootTag == "xjxobj") + { + while(!stristr($this->aObjArray[$this->iPos],"</xjxobj>")) + { + $this->iPos++; + if(stristr($this->aObjArray[$this->iPos],"<e>")) + { + $key = ""; + $value = null; + + $this->iPos++; + while(!stristr($this->aObjArray[$this->iPos],"</e>")) + { + if(stristr($this->aObjArray[$this->iPos],"<k>")) + { + $this->iPos++; + while(!stristr($this->aObjArray[$this->iPos],"</k>")) + { + $key .= $this->aObjArray[$this->iPos]; + $this->iPos++; + } + } + if(stristr($this->aObjArray[$this->iPos],"<v>")) + { + $this->iPos++; + while(!stristr($this->aObjArray[$this->iPos],"</v>")) + { + if(stristr($this->aObjArray[$this->iPos],"<xjxobj>")) + { + $value = $this->_parseObjXml("xjxobj"); + $this->iPos++; + } + else + { + $value .= $this->aObjArray[$this->iPos]; + if ($this->bDecodeUTF8Input) + { + $value = $this->_decodeUTF8Data($value); + } + } + $this->iPos++; + } + } + $this->iPos++; + } + + $aArray[$key]=$value; + } + } + } + + if ($rootTag == "xjxquery") + { + $sQuery = ""; + $this->iPos++; + while(!stristr($this->aObjArray[$this->iPos],"</xjxquery>")) + { + if (stristr($this->aObjArray[$this->iPos],"<q>") || stristr($this->aObjArray[$this->iPos],"</q>")) + { + $this->iPos++; + continue; + } + $sQuery .= $this->aObjArray[$this->iPos]; + $this->iPos++; + } + + parse_str($sQuery, $aArray); + if ($this->bDecodeUTF8Input) + { + foreach($aArray as $key => $value) + { + $aArray[$key] = $this->_decodeUTF8Data($value); + } + } + // If magic quotes is on, then we need to strip the slashes from the + // array values because of the parse_str pass which adds slashes + if (get_magic_quotes_gpc() == 1) { + $newArray = array(); + foreach ($aArray as $sKey => $sValue) { + if (is_string($sValue)) + $newArray[$sKey] = stripslashes($sValue); + else + $newArray[$sKey] = $sValue; + } + $aArray = $newArray; + } + } + + return $aArray; + } + + /** + * Decodes string data from UTF-8 to the current xajax encoding. + * + * @param string data to convert + * @access private + * @return string converted data + */ + function _decodeUTF8Data($sData) + { + $sValue = $sData; + if ($this->bDecodeUTF8Input) + { + $sFuncToUse = NULL; + + if (function_exists('iconv')) + { + $sFuncToUse = "iconv"; + } + else if (function_exists('mb_convert_encoding')) + { + $sFuncToUse = "mb_convert_encoding"; + } + else if ($this->sEncoding == "ISO-8859-1") + { + $sFuncToUse = "utf8_decode"; + } + else + { + trigger_error("The incoming xajax data could not be converted from UTF-8", E_USER_NOTICE); + } + + if ($sFuncToUse) + { + if (is_string($sValue)) + { + if ($sFuncToUse == "iconv") + { + $sValue = iconv("UTF-8", $this->sEncoding.'//TRANSLIT', $sValue); + } + else if ($sFuncToUse == "mb_convert_encoding") + { + $sValue = mb_convert_encoding($sValue, $this->sEncoding, "UTF-8"); + } + else + { + $sValue = utf8_decode($sValue); + } + } + } + } + return $sValue; + } + +}// end class xajax + +/** + * This function is registered with PHP's set_error_handler() function if + * the xajax error handling system is turned on. + */ +function xajaxErrorHandler($errno, $errstr, $errfile, $errline) +{ + $errorReporting = error_reporting(); + if (($errno & $errorReporting) == 0) return; + + if ($errno == E_NOTICE) { + $errTypeStr = "NOTICE"; + } + else if ($errno == E_WARNING) { + $errTypeStr = "WARNING"; + } + else if ($errno == E_USER_NOTICE) { + $errTypeStr = "USER NOTICE"; + } + else if ($errno == E_USER_WARNING) { + $errTypeStr = "USER WARNING"; + } + else if ($errno == E_USER_ERROR) { + $errTypeStr = "USER FATAL ERROR"; + } + else if ($errno == E_STRICT) { + return; + } + else { + $errTypeStr = "UNKNOWN: $errno"; + } + $GLOBALS['xajaxErrorHandlerText'] .= "\n----\n[$errTypeStr] $errstr\nerror in line $errline of file $errfile"; +} + +?> \ No newline at end of file diff --git a/libextinc/xajax/xajaxCompress.php b/libextinc/xajax/xajaxCompress.php new file mode 100644 index 0000000000000000000000000000000000000000..327a92dff541a0d5d5bcdd6a79059df16939c805 --- /dev/null +++ b/libextinc/xajax/xajaxCompress.php @@ -0,0 +1,182 @@ +<?php +/** + * xajaxCompress.php :: function to compress Javascript + * + * xajax version 0.2.4 + * copyright (c) 2005 by Jared White & J. Max Wilson + * http://www.xajaxproject.org + * + * xajax is an open source PHP class library for easily creating powerful + * PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously + * call PHP functions and update the content of your your webpage without + * reloading the page. + * + * xajax is released under the terms of the LGPL license + * http://www.gnu.org/copyleft/lesser.html#SEC3 + * + * This library 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * @package xajax + * @version $Id$ + * @copyright Copyright (c) 2005-2006 by Jared White & J. Max Wilson + * @license http://www.gnu.org/copyleft/lesser.html#SEC3 LGPL License + */ + +/** + * Compresses the Javascript code for more efficient delivery. + * (used internally) + * + * @param string contains the Javascript code to compress + */ +function xajaxCompressJavascript($sJS) +{ + //remove windows cariage returns + $sJS = str_replace("\r","",$sJS); + + //array to store replaced literal strings + $literal_strings = array(); + + //explode the string into lines + $lines = explode("\n",$sJS); + //loop through all the lines, building a new string at the same time as removing literal strings + $clean = ""; + $inComment = false; + $literal = ""; + $inQuote = false; + $escaped = false; + $quoteChar = ""; + + for($i=0;$i<count($lines);$i++) + { + $line = $lines[$i]; + $inNormalComment = false; + + //loop through line's characters and take out any literal strings, replace them with ___i___ where i is the index of this string + for($j=0;$j<strlen($line);$j++) + { + $c = substr($line,$j,1); + $d = substr($line,$j,2); + + //look for start of quote + if(!$inQuote && !$inComment) + { + //is this character a quote or a comment + if(($c=="\"" || $c=="'") && !$inComment && !$inNormalComment) + { + $inQuote = true; + $inComment = false; + $escaped = false; + $quoteChar = $c; + $literal = $c; + } + else if($d=="/*" && !$inNormalComment) + { + $inQuote = false; + $inComment = true; + $escaped = false; + $quoteChar = $d; + $literal = $d; + $j++; + } + else if($d=="//") //ignore string markers that are found inside comments + { + $inNormalComment = true; + $clean .= $c; + } + else + { + $clean .= $c; + } + } + else //allready in a string so find end quote + { + if($c == $quoteChar && !$escaped && !$inComment) + { + $inQuote = false; + $literal .= $c; + + //subsitute in a marker for the string + $clean .= "___" . count($literal_strings) . "___"; + + //push the string onto our array + array_push($literal_strings,$literal); + + } + else if($inComment && $d=="*/") + { + $inComment = false; + $literal .= $d; + + //subsitute in a marker for the string + $clean .= "___" . count($literal_strings) . "___"; + + //push the string onto our array + array_push($literal_strings,$literal); + + $j++; + } + else if($c == "\\" && !$escaped) + $escaped = true; + else + $escaped = false; + + $literal .= $c; + } + } + if($inComment) $literal .= "\n"; + $clean .= "\n"; + } + //explode the clean string into lines again + $lines = explode("\n",$clean); + + //now process each line at a time + for($i=0;$i<count($lines);$i++) + { + $line = $lines[$i]; + + //remove comments + $line = preg_replace("/\/\/(.*)/","",$line); + + //strip leading and trailing whitespace + $line = trim($line); + + //remove all whitespace with a single space + $line = preg_replace("/\s+/"," ",$line); + + //remove any whitespace that occurs after/before an operator + $line = preg_replace("/\s*([!\}\{;,&=\|\-\+\*\/\)\(:])\s*/","\\1",$line); + + $lines[$i] = $line; + } + + //implode the lines + $sJS = implode("\n",$lines); + + //make sure there is a max of 1 \n after each line + $sJS = preg_replace("/[\n]+/","\n",$sJS); + + //strip out line breaks that immediately follow a semi-colon + $sJS = preg_replace("/;\n/",";",$sJS); + + //curly brackets aren't on their own + $sJS = preg_replace("/[\n]*\{[\n]*/","{",$sJS); + + //finally loop through and replace all the literal strings: + for($i=0;$i<count($literal_strings);$i++) + $sJS = str_replace("___".$i."___",$literal_strings[$i],$sJS); + + return $sJS; +} +?> \ No newline at end of file diff --git a/libextinc/xajax/xajaxResponse.inc.php b/libextinc/xajax/xajaxResponse.inc.php new file mode 100644 index 0000000000000000000000000000000000000000..1c0aeb6022afae1c910365dbd463f290f9365276 --- /dev/null +++ b/libextinc/xajax/xajaxResponse.inc.php @@ -0,0 +1,580 @@ +<?php +/** + * xajaxResponse.inc.php :: xajax XML response class + * + * xajax version 0.2.4 + * copyright (c) 2005 by Jared White & J. Max Wilson + * http://www.xajaxproject.org + * + * xajax is an open source PHP class library for easily creating powerful + * PHP-driven, web-based Ajax Applications. Using xajax, you can asynchronously + * call PHP functions and update the content of your your webpage without + * reloading the page. + * + * xajax is released under the terms of the LGPL license + * http://www.gnu.org/copyleft/lesser.html#SEC3 + * + * This library 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * @package xajax + * @version $Id$ + * @copyright Copyright (c) 2005-2006 by Jared White & J. Max Wilson + * @license http://www.gnu.org/copyleft/lesser.html#SEC3 LGPL License + */ + +/* + ---------------------------------------------------------------------------- + | Online documentation for this class is available on the xajax wiki at: | + | http://wiki.xajaxproject.org/Documentation:xajaxResponse.inc.php | + ---------------------------------------------------------------------------- +*/ + +/** + * The xajaxResponse class is used to create responses to be sent back to your + * Web page. A response contains one or more command messages for updating + * your page. + * Currently xajax supports 21 kinds of command messages, including some common + * ones such as: + * <ul> + * <li>Assign - sets the specified attribute of an element in your page</li> + * <li>Append - appends data to the end of the specified attribute of an + * element in your page</li> + * <li>Prepend - prepends data to the beginning of the specified attribute of + * an element in your page</li> + * <li>Replace - searches for and replaces data in the specified attribute of + * an element in your page</li> + * <li>Script - runs the supplied JavaScript code</li> + * <li>Alert - shows an alert box with the supplied message text</li> + * </ul> + * + * <i>Note:</i> elements are identified by their HTML id, so if you don't see + * your browser HTML display changing from the request, make sure you're using + * the right id names in your response. + * + * @package xajax + */ +class xajaxResponse +{ + /**#@+ + * @access protected + */ + /** + * @var string internal XML storage + */ + var $xml; + /** + * @var string the encoding type to use + */ + var $sEncoding; + /** + * @var boolean if special characters in the XML should be converted to + * entities + */ + var $bOutputEntities; + + /**#@-*/ + + /** + * The constructor's main job is to set the character encoding for the + * response. + * + * <i>Note:</i> to change the character encoding for all of the + * responses, set the XAJAX_DEFAULT_ENCODING constant before you + * instantiate xajax. + * + * @param string contains the character encoding string to use + * @param boolean lets you set if you want special characters in the output + * converted to HTML entities + * + */ + function xajaxResponse($sEncoding=XAJAX_DEFAULT_CHAR_ENCODING, $bOutputEntities=false) + { + $this->setCharEncoding($sEncoding); + $this->bOutputEntities = $bOutputEntities; + } + + /** + * Sets the character encoding for the response based on $sEncoding, which + * is a string containing the character encoding to use. You don't need to + * use this method normally, since the character encoding for the response + * gets set automatically based on the XAJAX_DEFAULT_CHAR_ENCODING + * constant. + * + * @param string + */ + function setCharEncoding($sEncoding) + { + $this->sEncoding = $sEncoding; + } + + /** + * Tells the response object to convert special characters to HTML entities + * automatically (only works if the mb_string extension is available). + */ + function outputEntitiesOn() + { + $this->bOutputEntities = true; + } + + /** + * Tells the response object to output special characters intact. (default + * behavior) + */ + function outputEntitiesOff() + { + $this->bOutputEntities = false; + } + + /** + * Adds a confirm commands command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addConfirmCommands(1, "Do you want to preview the new data?");</kbd> + * + * @param integer the number of commands to skip if the user presses + * Cancel in the browsers's confirm dialog + * @param string the message to show in the browser's confirm dialog + */ + function addConfirmCommands($iCmdNumber, $sMessage) + { + $this->xml .= $this->_cmdXML(array("n"=>"cc","t"=>$iCmdNumber),$sMessage); + } + + /** + * Adds an assign command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addAssign("contentDiv", "innerHTML", "Some Text");</kbd> + * + * @param string contains the id of an HTML element + * @param string the part of the element you wish to modify ("innerHTML", + * "value", etc.) + * @param string the data you want to set the attribute to + */ + function addAssign($sTarget,$sAttribute,$sData) + { + $this->xml .= $this->_cmdXML(array("n"=>"as","t"=>$sTarget,"p"=>$sAttribute),$sData); + } + + /** + * Adds an append command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addAppend("contentDiv", "innerHTML", "Some New Text");</kbd> + * + * @param string contains the id of an HTML element + * @param string the part of the element you wish to modify ("innerHTML", + * "value", etc.) + * @param string the data you want to append to the end of the attribute + */ + function addAppend($sTarget,$sAttribute,$sData) + { + $this->xml .= $this->_cmdXML(array("n"=>"ap","t"=>$sTarget,"p"=>$sAttribute),$sData); + } + + /** + * Adds an prepend command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addPrepend("contentDiv", "innerHTML", "Some Starting Text");</kbd> + * + * @param string contains the id of an HTML element + * @param string the part of the element you wish to modify ("innerHTML", + * "value", etc.) + * @param string the data you want to prepend to the beginning of the + * attribute + */ + function addPrepend($sTarget,$sAttribute,$sData) + { + $this->xml .= $this->_cmdXML(array("n"=>"pp","t"=>$sTarget,"p"=>$sAttribute),$sData); + } + + /** + * Adds a replace command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addReplace("contentDiv", "innerHTML", "text", "<b>text</b>");</kbd> + * + * @param string contains the id of an HTML element + * @param string the part of the element you wish to modify ("innerHTML", + * "value", etc.) + * @param string the string to search for + * @param string the string to replace the search string when found in the + * attribute + */ + function addReplace($sTarget,$sAttribute,$sSearch,$sData) + { + $sDta = "<s><![CDATA[$sSearch]]></s><r><![CDATA[$sData]]></r>"; + $this->xml .= $this->_cmdXML(array("n"=>"rp","t"=>$sTarget,"p"=>$sAttribute),$sDta); + } + + /** + * Adds a clear command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addClear("contentDiv", "innerHTML");</kbd> + * + * @param string contains the id of an HTML element + * @param string the part of the element you wish to clear ("innerHTML", + * "value", etc.) + */ + function addClear($sTarget,$sAttribute) + { + $this->addAssign($sTarget,$sAttribute,''); + } + + /** + * Adds an alert command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addAlert("This is important information");</kbd> + * + * @param string the text to be displayed in the Javascript alert box + */ + function addAlert($sMsg) + { + $this->xml .= $this->_cmdXML(array("n"=>"al"),$sMsg); + } + + /** + * Uses the addScript() method to add a Javascript redirect to another URL. + * + * <i>Usage:</i> <kbd>$objResponse->addRedirect("http://www.xajaxproject.org");</kbd> + * + * @param string the URL to redirect the client browser to + */ + function addRedirect($sURL) + { + //we need to parse the query part so that the values are rawurlencode()'ed + //can't just use parse_url() cos we could be dealing with a relative URL which + // parse_url() can't deal with. + $queryStart = strpos($sURL, '?', strrpos($sURL, '/')); + if ($queryStart !== FALSE) + { + $queryStart++; + $queryEnd = strpos($sURL, '#', $queryStart); + if ($queryEnd === FALSE) + $queryEnd = strlen($sURL); + $queryPart = substr($sURL, $queryStart, $queryEnd-$queryStart); + parse_str($queryPart, $queryParts); + $newQueryPart = ""; + foreach($queryParts as $key => $value) + { + $newQueryPart .= rawurlencode($key).'='.rawurlencode($value).ini_get('arg_separator.output'); + } + $sURL = str_replace($queryPart, $newQueryPart, $sURL); + } + $this->addScript('window.location = "'.$sURL.'";'); + } + + /** + * Adds a Javascript command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addScript("var x = prompt('get some text');");</kbd> + * + * @param string contains Javascript code to be executed + */ + function addScript($sJS) + { + $this->xml .= $this->_cmdXML(array("n"=>"js"),$sJS); + } + + /** + * Adds a Javascript function call command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addScriptCall("myJSFunction", "arg 1", "arg 2", 12345);</kbd> + * + * @param string $sFunc the name of a Javascript function + * @param mixed $args,... optional arguments to pass to the Javascript function + */ + function addScriptCall() { + $arguments = func_get_args(); + $sFunc = array_shift($arguments); + $sData = $this->_buildObjXml($arguments); + $this->xml .= $this->_cmdXML(array("n"=>"jc","t"=>$sFunc),$sData); + } + + /** + * Adds a remove element command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addRemove("Div2");</kbd> + * + * @param string contains the id of an HTML element to be removed + */ + function addRemove($sTarget) + { + $this->xml .= $this->_cmdXML(array("n"=>"rm","t"=>$sTarget),''); + } + + /** + * Adds a create element command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addCreate("parentDiv", "h3", "myid");</kbd> + * + * @param string contains the id of an HTML element to to which the new + * element will be appended. + * @param string the tag to be added + * @param string the id to be assigned to the new element + * @param string deprecated, use the addCreateInput() method instead + */ + function addCreate($sParent, $sTag, $sId, $sType="") + { + if ($sType) + { + trigger_error("The \$sType parameter of addCreate has been deprecated. Use the addCreateInput() method instead.", E_USER_WARNING); + return; + } + $this->xml .= $this->_cmdXML(array("n"=>"ce","t"=>$sParent,"p"=>$sId),$sTag); + } + + /** + * Adds a insert element command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addInsert("childDiv", "h3", "myid");</kbd> + * + * @param string contains the id of the child before which the new element + * will be inserted + * @param string the tag to be added + * @param string the id to be assigned to the new element + */ + function addInsert($sBefore, $sTag, $sId) + { + $this->xml .= $this->_cmdXML(array("n"=>"ie","t"=>$sBefore,"p"=>$sId),$sTag); + } + + /** + * Adds a insert element command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addInsertAfter("childDiv", "h3", "myid");</kbd> + * + * @param string contains the id of the child after which the new element + * will be inserted + * @param string the tag to be added + * @param string the id to be assigned to the new element + */ + function addInsertAfter($sAfter, $sTag, $sId) + { + $this->xml .= $this->_cmdXML(array("n"=>"ia","t"=>$sAfter,"p"=>$sId),$sTag); + } + + /** + * Adds a create input command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addCreateInput("form1", "text", "username", "input1");</kbd> + * + * @param string contains the id of an HTML element to which the new input + * will be appended + * @param string the type of input to be created (text, radio, checkbox, + * etc.) + * @param string the name to be assigned to the new input and the variable + * name when it is submitted + * @param string the id to be assigned to the new input + */ + function addCreateInput($sParent, $sType, $sName, $sId) + { + $this->xml .= $this->_cmdXML(array("n"=>"ci","t"=>$sParent,"p"=>$sId,"c"=>$sType),$sName); + } + + /** + * Adds an insert input command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addInsertInput("input5", "text", "username", "input1");</kbd> + * + * @param string contains the id of the child before which the new element + * will be inserted + * @param string the type of input to be created (text, radio, checkbox, + * etc.) + * @param string the name to be assigned to the new input and the variable + * name when it is submitted + * @param string the id to be assigned to the new input + */ + function addInsertInput($sBefore, $sType, $sName, $sId) + { + $this->xml .= $this->_cmdXML(array("n"=>"ii","t"=>$sBefore,"p"=>$sId,"c"=>$sType),$sName); + } + + /** + * Adds an insert input command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addInsertInputAfter("input7", "text", "email", "input2");</kbd> + * + * @param string contains the id of the child after which the new element + * will be inserted + * @param string the type of input to be created (text, radio, checkbox, + * etc.) + * @param string the name to be assigned to the new input and the variable + * name when it is submitted + * @param string the id to be assigned to the new input + */ + function addInsertInputAfter($sAfter, $sType, $sName, $sId) + { + $this->xml .= $this->_cmdXML(array("n"=>"iia","t"=>$sAfter,"p"=>$sId,"c"=>$sType),$sName); + } + + /** + * Adds an event command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addEvent("contentDiv", "onclick", "alert(\'Hello World\');");</kbd> + * + * @param string contains the id of an HTML element + * @param string the event you wish to set ("onclick", "onmouseover", etc.) + * @param string the Javascript string you want the event to invoke + */ + function addEvent($sTarget,$sEvent,$sScript) + { + $this->xml .= $this->_cmdXML(array("n"=>"ev","t"=>$sTarget,"p"=>$sEvent),$sScript); + } + + /** + * Adds a handler command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addHandler("contentDiv", "onclick", "content_click");</kbd> + * + * @param string contains the id of an HTML element + * @param string the event you wish to set ("onclick", "onmouseover", etc.) + * @param string the name of a Javascript function that will handle the + * event. Multiple handlers can be added for the same event + */ + function addHandler($sTarget,$sEvent,$sHandler) + { + $this->xml .= $this->_cmdXML(array("n"=>"ah","t"=>$sTarget,"p"=>$sEvent),$sHandler); + } + + /** + * Adds a remove handler command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addRemoveHandler("contentDiv", "onclick", "content_click");</kbd> + * + * @param string contains the id of an HTML element + * @param string the event you wish to remove ("onclick", "onmouseover", + * etc.) + * @param string the name of a Javascript handler function that you want to + * remove + */ + function addRemoveHandler($sTarget,$sEvent,$sHandler) + { + $this->xml .= $this->_cmdXML(array("n"=>"rh","t"=>$sTarget,"p"=>$sEvent),$sHandler); + } + + /** + * Adds an include script command message to the XML response. + * + * <i>Usage:</i> <kbd>$objResponse->addIncludeScript("functions.js");</kbd> + * + * @param string URL of the Javascript file to include + */ + function addIncludeScript($sFileName) + { + $this->xml .= $this->_cmdXML(array("n"=>"in"),$sFileName); + } + + /** + * Returns the XML to be returned from your function to the xajax processor + * on your page. Since xajax 0.2, you can also return an xajaxResponse + * object from your function directly, and xajax will automatically request + * the XML using this method call. + * + * <i>Usage:</i> <kbd>return $objResponse->getXML();</kbd> + * + * @return string response XML data + */ + function getXML() + { + $sXML = "<?xml version=\"1.0\""; + if ($this->sEncoding && strlen(trim($this->sEncoding)) > 0) + $sXML .= " encoding=\"".$this->sEncoding."\""; + $sXML .= " ?"."><xjx>" . $this->xml . "</xjx>"; + + return $sXML; + } + + /** + * Adds the commands of the provided response XML output to this response + * object + * + * <i>Usage:</i> + * <code>$r1 = $objResponse1->getXML(); + * $objResponse2->loadXML($r1); + * return $objResponse2->getXML();</code> + * + * @param string the response XML (returned from a getXML() method) to add + * to the end of this response object + */ + function loadXML($mXML) + { + if (is_a($mXML, "xajaxResponse")) { + $mXML = $mXML->getXML(); + } + $sNewXML = ""; + $iStartPos = strpos($mXML, "<xjx>") + 5; + $sNewXML = substr($mXML, $iStartPos); + $iEndPos = strpos($sNewXML, "</xjx>"); + $sNewXML = substr($sNewXML, 0, $iEndPos); + $this->xml .= $sNewXML; + } + + /** + * Generates XML from command data + * + * @access private + * @param array associative array of attributes + * @param string data + * @return string XML command + */ + function _cmdXML($aAttributes, $sData) + { + if ($this->bOutputEntities) { + if (function_exists('mb_convert_encoding')) { + $sData = call_user_func_array('mb_convert_encoding', array(&$sData, 'HTML-ENTITIES', $this->sEncoding)); + } + else { + trigger_error("The xajax XML response output could not be converted to HTML entities because the mb_convert_encoding function is not available", E_USER_NOTICE); + } + } + $xml = "<cmd"; + foreach($aAttributes as $sAttribute => $sValue) + $xml .= " $sAttribute=\"$sValue\""; + if ($sData !== null && !stristr($sData,'<![CDATA[')) + $xml .= "><![CDATA[$sData]]></cmd>"; + else if ($sData !== null) + $xml .= ">$sData</cmd>"; + else + $xml .= "></cmd>"; + + return $xml; + } + + /** + * Recursively serializes a data structure in XML so it can be sent to + * the client. It could be thought of as the opposite of + * {@link xajax::_parseObjXml()}. + * + * @access private + * @param mixed data structure to serialize to XML + * @return string serialized XML + */ + function _buildObjXml($var) { + if (gettype($var) == "object") $var = get_object_vars($var); + if (!is_array($var)) { + return "<![CDATA[$var]]>"; + } + else { + $data = "<xjxobj>"; + foreach ($var as $key => $value) { + $data .= "<e>"; + $data .= "<k>" . htmlspecialchars($key) . "</k>"; + $data .= "<v>" . $this->_buildObjXml($value) . "</v>"; + $data .= "</e>"; + } + $data .= "</xjxobj>"; + return $data; + } + } + +}// end class xajaxResponse +?> diff --git a/templates/default/includes/header.php b/templates/default/includes/header.php index 9e5db4635e9d103dabfe6d576bfcc0db8fdf7e60..61ae8deb84a5a74a8f924d26b1bcdd7c17c8e202 100644 --- a/templates/default/includes/header.php +++ b/templates/default/includes/header.php @@ -14,6 +14,12 @@ if(array_key_exists('header', $this->data)) { <link rel="icon" type="image/icon" href="/<?php echo $this->data['baseurlpath']; ?>resources/icons/favicon.ico" /> <script type="text/javascript" src="/<?php echo $this->data['baseurlpath']; ?>resources/script.js"></script> <meta name="ROBOTS" content="NOINDEX, NOFOLLOW" /> + +<?php +if(array_key_exists('head', $this->data)) { + echo '<!-- head -->' . $this->data['head'] . '<!-- /head -->'; +} +?> </head> <?php $onLoad = ''; diff --git a/templates/default/logout-iframe.php b/templates/default/logout-iframe.php new file mode 100644 index 0000000000000000000000000000000000000000..c6d8f44ec5d72bf39aa463bf0d155d3c20d4c738 --- /dev/null +++ b/templates/default/logout-iframe.php @@ -0,0 +1,44 @@ +<?php + + + $this->data['head'] .= '<script language="JavaScript"> +// use pre-formatted output for this multiplication table +var j; // loop variables + +xajax_updateslostatus(); +for (j=1; j<=10; j++) { + setTimeout(\'xajax_updateslostatus()\',j*1000) +} +</script>'; + + + $this->includeAtTemplateBase('includes/header.php'); + +# $this->includeLanguageFile('consent.php'); +# $this->includeInlineTranslation('spname', $this->data['sp_name']); +# $this->includeInlineTranslation('IDPNAME', $this->data['idp_name']); +?> + + <div id="content"> + + <p style="margin: .2em">[ <a href="<?php echo $this->data['logoutresponse']; ?>">Interrupt logging out and go back to service</a> ]</p> + + <?php + + foreach ($this->data['sparray'] AS $sp) { + echo '<iframe class="hiddeniframe" style="width: 200px; height: 100px" src="' . $sp['url'] . '" ></iframe>'; + } + + foreach ($this->data['sparray'] AS $spentityid => $sp) { + echo '<div class="inprogress" style="border: 1px solid #888; background: #eee; color: #444; padding: .2em; margin: .7em" id="' . $spentityid . '"> + <img style="float: left; margin: 3px" src="/' . $this->data['baseurlpath'] . 'resources/progress.gif" />Wait... is logging out from ' . $spentityid . '</div>'; + } + + + ?> + </div> + + + + +<?php $this->includeAtTemplateBase('includes/footer.php'); ?> diff --git a/www/resources/default.css b/www/resources/default.css index d067b73acfc8ad76f353676215a3228ae429c8e1..64cd821140573f5e7d2bbdac3c573944e0b3cd0e 100644 --- a/www/resources/default.css +++ b/www/resources/default.css @@ -255,3 +255,14 @@ table.attributes tr td { padding-top: 5px; vertical-align: top; } + +div.inprogress { + background: #eee; +} +div.loggedout { + background: #9f9 ! important ; +} +iframe.hiddeniframe { + display: none; +} + diff --git a/www/resources/progress.gif b/www/resources/progress.gif new file mode 100644 index 0000000000000000000000000000000000000000..529e72f45a2395e2eddb0487edbe79b306c92c3a Binary files /dev/null and b/www/resources/progress.gif differ diff --git a/www/saml2/idp/SingleLogoutService.php b/www/saml2/idp/SingleLogoutService.php index b60ce02994e590c80ff89c205ca382093f84a07e..95ae16c0268deaa6d77d04aaf60f43052fbfb6e0 100644 --- a/www/saml2/idp/SingleLogoutService.php +++ b/www/saml2/idp/SingleLogoutService.php @@ -4,15 +4,14 @@ * This SAML 2.0 endpoint can receive incomming LogoutRequests. It will also send LogoutResponses, * and LogoutRequests and also receive LogoutResponses. It is implemeting SLO at the SAML 2.0 IdP. * - * @author Andreas Ĺkre Solberg, UNINETT AS. <andreas.solberg@uninett.no> + * @author Andreas Ă…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no> * @package simpleSAMLphp * @version $Id$ */ // TODO: Show error message, when shibboleth sp is logged in. -// TODO: Propagate HTTP-REDIRECT SLO on SAML 2.0 bridge. -require_once('../../../www/_include.php'); +require_once('../../_include.php'); $config = SimpleSAML_Configuration::getInstance(); $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); @@ -140,7 +139,6 @@ if (isset($_GET['SAMLRequest'])) { $session->doLogout(); - /* Fill in the $logoutInfo associative array with information about this logout request. */ $logoutInfo['Issuer'] = $logoutrequest->getIssuer(); $logoutInfo['RequestID'] = $logoutrequest->getRequestID(); diff --git a/www/saml2/idp/SingleLogoutServiceAjax.php b/www/saml2/idp/SingleLogoutServiceAjax.php new file mode 100644 index 0000000000000000000000000000000000000000..02cd1d15843a198a3a60ee051d5b2bcba51ace5c --- /dev/null +++ b/www/saml2/idp/SingleLogoutServiceAjax.php @@ -0,0 +1,260 @@ +<?php + + +require_once('../../_include.php'); + +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutRequest.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutResponse.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPRedirect.php'); +//require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPPost.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php'); + + + + +session_start(); + +$config = SimpleSAML_Configuration::getInstance(); +$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + + + + +$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); + +$session = SimpleSAML_Session::getInstance(); + + + + +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . '../../xajax/xajax.inc.php'); + + + + + +/* + * This function is called via AJAX and will send LogoutRequest to one single SP by + * sending a LogoutRequest using HTTP-REDIRECT + */ +function httpredirectslo($spentityid) { + + + + $config = SimpleSAML_Configuration::getInstance(); + $metadata = new SimpleSAML_XML_MetaDataStore($config); + + $idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); + + $session = SimpleSAML_Session::getInstance(); + + if (!isset($_GET['loggedoutservice'])) { + + try { + $lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata); + + // ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) { + $req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getNameIDFormat(), $session->getSessionIndex(), 'IdP'); + + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + $relayState = SimpleSAML_Utilities::selfURL(); + + + //$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' + //$httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP'); + + //header('Location: vg.no'); + header('Location: http://vg.no'); + + exit(); + + } catch(Exception $exception) { + + $et = new SimpleSAML_XHTML_Template($config, 'error.php'); + + $et->data['header'] = 'Error sending logout request to service'; + $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; + $et->data['e'] = $exception; + + $et->show(); + exit(); + } + + + } + + + + + $newContent = "Successfully logged out from ".$spentityid; + + // Instantiate the xajaxResponse object + $objResponse = new xajaxResponse(); + + // add a command to the response to assign the innerHTML attribute of + // the element with id="SomeElementId" to whatever the new content is + $objResponse->addAssign($spentityid,"innerHTML", $newContent); + + //return the xajaxResponse object + return $objResponse; +} + + + +$xajax = new xajax(); +$xajax->registerFunction("httpredirectslo", XAJAX_GET); +$xajax->processRequests(); + + + + + + + + + + + +$session->dump_sp_sessions(); + + + + + +$et = new SimpleSAML_XHTML_Template($config, 'logout-ajax.php'); + +$et->data['header'] = 'SAML 2.0 IdP Ajax Logout'; +$et->data['listofsps'] = $session->get_sp_list();; +$et->data['xajax'] = $xajax; +$et->show(); + +exit(0); + + + + + + +/* + * If we get an LogoutRequest then we initiate the logout process. + */ +if (isset($_GET['SAMLRequest'])) { + + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + $logoutrequest = $binding->decodeLogoutRequest($_GET); + + $session->setAuthenticated(false); + + //$requestid = $authnrequest->getRequestID(); + //$session->setAuthnRequest($requestid, $authnrequest); + + //echo '<pre>' . htmlentities($logoutrequest->getXML()) . '</pre>'; + + error_log('IdP LogoutService: got Logoutrequest from ' . $logoutrequest->getIssuer() . ' '); + + $session->set_sp_logout_completed($logoutrequest->getIssuer() ); + $session->setLogoutRequest($logoutrequest); + +/* + * We receive a Logout Response to a Logout Request that we have issued earlier. + */ +} elseif (isset($_GET['SAMLResponse'])) { + + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + $loginresponse = $binding->decodeLogoutResponse($_GET); + + $session->set_sp_logout_completed($loginresponse->getIssuer()); + + error_log('IdP LogoutService: got LogoutResponse from ' . $loginresponse->getIssuer() . ' '); +} + +/* + * We proceed to send logout requests to all remaining SPs. + */ +$spentityid = $session->get_next_sp_logout(); +if ($spentityid) { + + error_log('IdP LogoutService: next SP ' . $spentityid); + + try { + $lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata); + + // ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) { + $req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getNameIDFormat(), $session->getSessionIndex(), 'IdP'); + + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + $relayState = SimpleSAML_Utilities::selfURL(); + if (isset($_GET['RelayState'])) { + $relayState = $_GET['RelayState']; + } + + //$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' + $httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP'); + + exit(); + + } catch(Exception $exception) { + + $et = new SimpleSAML_XHTML_Template($config, 'error.php'); + + $et->data['header'] = 'Error sending logout request to service'; + $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; + $et->data['e'] = $exception; + + $et->show(); + exit(0); + } + + +} + +/* + * Logout procedure is done and we send a Logout Response back to the SP + */ +error_log('IdP LogoutService: SPs done '); +try { + + $logoutrequest = $session->getLogoutRequest(); + if (!$logoutrequest) { + throw new Exception('Could not get reference to the logout request.'); + } + + $rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata); + + // generate($issuer, $receiver, $inresponseto, $mode ) + + $logoutResponseXML = $rg->generate($idpentityid, $logoutrequest->getIssuer(), $logoutrequest->getRequestID(), 'IdP'); + + // echo '<pre>' . htmlentities($logoutResponseXML) . '</pre>'; + // exit(); + + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + $relayState = SimpleSAML_Utilities::selfURL(); + if (isset($_GET['RelayState'])) { + $relayState = $_GET['RelayState']; + } + + //$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' + $httpredirect->sendMessage($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP'); + +} catch(Exception $exception) { + + $et = new SimpleSAML_XHTML_Template($config, 'error.php'); + + $et->data['header'] = 'Error sending response to service'; + $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; + $et->data['e'] = $exception; + + $et->show(); + +} + + + +?> \ No newline at end of file diff --git a/www/saml2/idp/SingleLogoutServiceAjaxResponse.php b/www/saml2/idp/SingleLogoutServiceAjaxResponse.php new file mode 100644 index 0000000000000000000000000000000000000000..a78e47d9e756494119553cdacffa7b4eb4aee8ae --- /dev/null +++ b/www/saml2/idp/SingleLogoutServiceAjaxResponse.php @@ -0,0 +1,52 @@ +<?php + + +require_once('../../_include.php'); + +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutRequest.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutResponse.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPRedirect.php'); +//require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPPost.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php'); + + + + + + +session_start(); + +$config = SimpleSAML_Configuration::getInstance(); +$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + +$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); + +$session = SimpleSAML_Session::getInstance(); + +//$session->dump_sp_sessions(); + + + +if (isset($_GET['SAMLResponse'])) { + + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + $logoutresponse = $binding->decodeLogoutResponse($_GET); + + $session->set_sp_logout_completed($logoutresponse->getIssuer()); + + error_log('IdP LogoutService: got LogoutResponse at SingleLogoutServiceAjaxResponse from ' . $logoutresponse->getIssuer() . ' '); + + $relaystate = $logoutresponse->getRelayState(); + + $redirURL = SimpleSAML_Utilities::addURLparameter($relaystate, 'loggedoutservice=' . $logoutresponse->getIssuer() ); + header('Location: ' . $redirURL); +} else { + + error_log('Error on SingleLogoutServiceAjaxResponse'); + +} + +?> \ No newline at end of file diff --git a/www/saml2/idp/SingleLogoutServiceiFrame.php b/www/saml2/idp/SingleLogoutServiceiFrame.php new file mode 100644 index 0000000000000000000000000000000000000000..4d2c791add20bf6448de3c89f9a79e33b48f05bb --- /dev/null +++ b/www/saml2/idp/SingleLogoutServiceiFrame.php @@ -0,0 +1,537 @@ +<?php + +/** + * This SAML 2.0 endpoint can receive incomming LogoutRequests. It will also send LogoutResponses, + * and LogoutRequests and also receive LogoutResponses. It is implemeting SLO at the SAML 2.0 IdP. + * + * @author Andreas Ă…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no> + * @package simpleSAMLphp + * @version $Id$ + */ + +require_once('../../_include.php'); + +$config = SimpleSAML_Configuration::getInstance(); +$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); +$session = SimpleSAML_Session::getInstance(); + +SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutServiceiFrame: Accessing SAML 2.0 IdP endpoint SingleLogoutService (iFrame version)'); + +if (!$config->getValue('enable.saml20-idp', false)) + SimpleSAML_Utilities::fatalError(isset($session) ? $session->getTrackID() : null, 'NOACCESS'); + +try { + $idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); +} catch (Exception $exception) { + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'METADATA', $exception); +} + +SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutServiceiFrame: Got IdP entity id: ' . $idpentityid); + + +/** + * The $logoutInfo contains information about the current logout operation. + * It can have the following attributes: + * - 'RelayState' - The RelayState which should be returned to the SP which initiated the logout operation. + * - 'Issuer' - The entity id of the SP which initiated the logout operation. + * - 'RequestID' - The id of the LogoutRequest which initiated the logout operation. + */ +$logoutInfo = array(); + + +/** + * This function retrieves the logout info with the given ID. + * + * @param $id The identifier of the logout info. + */ +function fetchLogoutInfo($id) { + global $session; + global $logoutInfo; + + $logoutInfo = $session->getData('idplogoutresponsedata', $id); + + if($logoutInfo === NULL) { + SimpleSAML_Logger::warning('SAML2.0 - IdP.SingleLogoutService: Lost logout information.'); + } +} + + +/** + * This function saves the logout info with the given ID. + * + * @param $id The identifier the logout info should be saved with. + */ +function saveLogoutInfo($id) { + global $session; + global $logoutInfo; + + $session->setData('idplogoutresponsedata', $id, $logoutInfo); +} + + +// Include XAJAX definition. +require_once(SimpleSAML_Utilities::resolvePath('libextinc') . '/xajax/xajax.inc.php'); + + + +/* + * This function is called via AJAX and will send LogoutRequest to one single SP by + * sending a LogoutRequest using HTTP-REDIRECT + */ +function updateslostatus() { + + $config = SimpleSAML_Configuration::getInstance(); + $metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + $session = SimpleSAML_Session::getInstance(); + + $idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); + + $listofsps = $session->get_sp_list(SimpleSAML_Session::STATE_LOGGEDOUT); + + // Instantiate the xajaxResponse object + $objResponse = new xajaxResponse(); + + foreach ($listofsps AS $spentityid) { + + error_log('Completed ' . $spentityid); + // add a command to the response to assign the innerHTML attribute of + // the element with id="SomeElementId" to whatever the new content is + $objResponse->addAssign($spentityid, "className", 'loggedout'); + $objResponse->addAssign($spentityid, "innerHTML", 'Logging out from ' . $spentityid . ' successfully completed'); + + } + + //return the xajaxResponse object + return $objResponse; +} + + + +$xajax = new xajax(); +$xajax->registerFunction("updateslostatus"); +$xajax->processRequests(); + + + + + +/* + * If we get an LogoutRequest then we initiate the logout process. + */ +if (isset($_GET['SAMLRequest'])) { + + SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Got SAML reuqest'); + + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + try { + $logoutrequest = $binding->decodeLogoutRequest($_GET); + + if ($binding->validateQuery($logoutrequest->getIssuer(),'IdP')) { + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Valid signature found for '.$logoutrequest->getRequestID()); + } + + } catch(Exception $exception) { + + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'LOGOUTREQUEST', $exception); + + } + + // Extract some parameters from the logout request + #$requestid = $logoutrequest->getRequestID(); + $requester = $logoutrequest->getIssuer(); + #$relayState = $logoutrequest->getRelayState(); + + //$responder = $config->getValue('saml2-hosted-sp'); + $responder = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); + + + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: got Logoutrequest from ' . $logoutrequest->getIssuer()); + SimpleSAML_Logger::stats('saml20-idp-SLO spinit ' . $requester . ' ' . $responder); + + /* Check if we have a valid session. */ + if($session === NULL) { + + /* Invalid session. To prevent the user from being unable to + * log out from the service provider, we should just return a + * LogoutResponse pretending that the logout was successful to + * the SP that sent the LogoutRequest. + */ + + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Did not find a session here, but we are returning a LogoutResponse anyway.'); + + $spentityid = $logoutrequest->getIssuer(); + + /* Generate the response. */ + $response = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata); + $responseText = $response->generate($idpentityid, $spentityid, $logoutrequest->getRequestID(), 'IdP'); + + /* Retrieve the relay state from the request. */ + $relayState = $logoutrequest->getRelayState(); + + /* Send the response using the HTTP-Redirect binding. */ + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, + $metadata); + $binding->sendMessage($responseText, $idpentityid, $spentityid, $relayState, + 'SingleLogoutService', 'SAMLResponse', 'IdP'); + exit; + } + + + $session->doLogout(); + + + /* Fill in the $logoutInfo associative array with information about this logout request. */ + $logoutInfo['Issuer'] = $logoutrequest->getIssuer(); + $logoutInfo['RequestID'] = $logoutrequest->getRequestID(); + + $relayState = $logoutrequest->getRelayState(); + if($relayState !== NULL) { + $logoutInfo['RelayState'] = $relayState; + } + + + SimpleSAML_Logger::debug('SAML2.0 - IDP.SingleLogoutService: Setting cached request with issuer ' . $logoutrequest->getIssuer()); + + $session->set_sp_logout_completed($logoutrequest->getIssuer() ); + + + +/* + * We receive a Logout Response to a Logout Request that we have issued earlier. + * If so, there is a misconfiguration. + */ +} elseif (isset($_GET['SAMLResponse'])) { + + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'LOGOUTRESPONSE', + new Exception('The SP is likely to be misconfigured. The LogoutResponse is sent to wrong endpoint. This iFrame endpoint only accepts LogoutRequests, and the response is to be sent to a separate endpoint. Please revisit the IdP-Remote metadata on the SP.') + ); + +} else { + /* + * We have no idea what to do here. It is neither a logout request, a logout + * response nor a response from bridged SLO. + */ + SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: No request, response or bridge'); + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'SLOSERVICEPARAMS'); +} + + + +$session->dump_sp_sessions(); + + + +/* + * Generate a list of all service providers, and creat a LogoutRequest message for all these SPs. + */ +$listofsps = $session->get_sp_list(); +$sparray = array(); +foreach ($listofsps AS $spentityid) { + + // ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) { + $nameId = $session->getSessionNameId('saml20-sp-remote', $spentityid); + if($nameId === NULL) { + $nameId = $session->getNameID(); + } + + $lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata); + $req = $lr->generate($idpentityid, $spentityid, $nameId, $session->getSessionIndex(), 'IdP'); + + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + + // $request, $localentityid, $remoteentityid, $relayState = null, $endpoint = 'SingleSignOnService', $direction = 'SAMLRequest', $mode = 'SP' + $url = $httpredirect->getRedirectURL($req, $idpentityid, $spentityid, NULL, 'SingleLogoutService', 'SAMLRequest', 'IdP'); + + $sparray[$spentityid] = array('url' => $url); + +} + + + + + + + +/* +try { + + $logoutrequest = $session->getLogoutRequest(); + if (!$logoutrequest) { + throw new Exception('Could not get reference to the logout request.'); + } + + $rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata); + + + $logoutResponseXML = $rg->generate($idpentityid, $logoutrequest->getIssuer(), $logoutrequest->getRequestID(), 'IdP'); + + + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + $relayState = SimpleSAML_Utilities::selfURL(); + if (isset($_GET['RelayState'])) { + $relayState = $_GET['RelayState']; + } + + + $logoutresponse = $httpredirect->getRedirectURL($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP'); + +} catch(Exception $exception) { + + $et = new SimpleSAML_XHTML_Template($config, 'error.php'); + + $et->data['header'] = 'Error sending response to service'; + $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; + $et->data['e'] = $exception; + + $et->show(); + +} + + + +*/ + + + + + + + + +/* + * Logout procedure is done and we send a Logout Response back to the SP + */ + +try { + + if(!$logoutInfo) { + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'LOGOUTINFOLOST'); + } + + SimpleSAML_Logger::debug('SAML2.0 - IdP.SingleLogoutService: Found logout info with these keys: ' . join(',', array_keys($logoutInfo))); + + /** + * Clean up session object to save storage. + */ + if ($config->getValue('debug', false)) + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size before cleaning: ' . $session->getSize()); + + $session->clean(); + + if ($config->getValue('debug', false)) + SimpleSAML_Logger::info('SAML2.0 - IdP.SingleLogoutService: Session Size after cleaning: ' . $session->getSize()); + + + /* + * Check if the Single Logout procedure is initated by an SP (alternatively IdP initiated SLO + */ + if (array_key_exists('Issuer', $logoutInfo)) { + + /** + * Create a Logot Response. + */ + $rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata); + + // generate($issuer, $receiver, $inresponseto, $mode ) + $logoutResponseXML = $rg->generate($idpentityid, $logoutInfo['Issuer'], $logoutInfo['RequestID'], 'IdP'); + + // Create a HTTP-REDIRECT Binding. + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + // Find the relaystate if cached. + $relayState = isset($logoutInfo['RelayState']) ? $logoutInfo['RelayState'] : null; + + // Parameters: $request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' + $logoutresponse = $httpredirect->getRedirectURL($logoutResponseXML, $idpentityid, $logoutInfo['Issuer'], $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP'); + + + } elseif (array_key_exists('RelayState', $logoutInfo)) { + + SimpleSAML_Utilities::redirect($logoutInfo['RelayState']); + exit; + + } else { + + echo 'You are logged out'; exit; + + } + +} catch(Exception $exception) { + + SimpleSAML_Utilities::fatalError($session->getTrackID(), 'GENERATELOGOUTRESPONSE', $exception); + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +$et = new SimpleSAML_XHTML_Template($config, 'logout-iframe.php'); + +$et->data['header'] = 'SAML 2.0 IdP Ajax Logout'; +$et->data['sparray'] = $sparray; +$et->data['logoutresponse'] = $logoutresponse; +$et->data['xajax'] = $xajax; + +$et->data['head'] = $xajax->printJavascript(); + +$et->show(); + +exit(0); + + + + + + +/* + * If we get an LogoutRequest then we initiate the logout process. + */ +if (isset($_GET['SAMLRequest'])) { + + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + $logoutrequest = $binding->decodeLogoutRequest($_GET); + + $session->setAuthenticated(false); + + //$requestid = $authnrequest->getRequestID(); + //$session->setAuthnRequest($requestid, $authnrequest); + + //echo '<pre>' . htmlentities($logoutrequest->getXML()) . '</pre>'; + + error_log('IdP LogoutService: got Logoutrequest from ' . $logoutrequest->getIssuer() . ' '); + + $session->set_sp_logout_completed($logoutrequest->getIssuer() ); + $session->setLogoutRequest($logoutrequest); + +/* + * We receive a Logout Response to a Logout Request that we have issued earlier. + */ +} elseif (isset($_GET['SAMLResponse'])) { + + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + $loginresponse = $binding->decodeLogoutResponse($_GET); + + $session->set_sp_logout_completed($loginresponse->getIssuer()); + + error_log('IdP LogoutService: got LogoutResponse from ' . $loginresponse->getIssuer() . ' '); +} + +/* + * We proceed to send logout requests to all remaining SPs. + */ +$spentityid = $session->get_next_sp_logout(); +if ($spentityid) { + + error_log('IdP LogoutService: next SP ' . $spentityid); + + try { + $lr = new SimpleSAML_XML_SAML20_LogoutRequest($config, $metadata); + + // ($issuer, $receiver, $nameid, $nameidformat, $sessionindex, $mode) { + $req = $lr->generate($idpentityid, $spentityid, $session->getNameID(), $session->getNameIDFormat(), $session->getSessionIndex(), 'IdP'); + + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + $relayState = SimpleSAML_Utilities::selfURL(); + if (isset($_GET['RelayState'])) { + $relayState = $_GET['RelayState']; + } + + //$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' + $httpredirect->sendMessage($req, $spentityid, $relayState, 'SingleLogoutService', 'SAMLRequest', 'IdP'); + + exit(); + + } catch(Exception $exception) { + + $et = new SimpleSAML_XHTML_Template($config, 'error.php'); + + $et->data['header'] = 'Error sending logout request to service'; + $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; + $et->data['e'] = $exception; + + $et->show(); + exit(0); + } + + +} + +/* + * Logout procedure is done and we send a Logout Response back to the SP + */ +error_log('IdP LogoutService: SPs done '); +try { + + $logoutrequest = $session->getLogoutRequest(); + if (!$logoutrequest) { + throw new Exception('Could not get reference to the logout request.'); + } + + $rg = new SimpleSAML_XML_SAML20_LogoutResponse($config, $metadata); + + // generate($issuer, $receiver, $inresponseto, $mode ) + + $logoutResponseXML = $rg->generate($idpentityid, $logoutrequest->getIssuer(), $logoutrequest->getRequestID(), 'IdP'); + + // echo '<pre>' . htmlentities($logoutResponseXML) . '</pre>'; + // exit(); + + $httpredirect = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + + $relayState = SimpleSAML_Utilities::selfURL(); + if (isset($_GET['RelayState'])) { + $relayState = $_GET['RelayState']; + } + + //$request, $remoteentityid, $relayState = null, $endpoint = 'SingleLogoutService', $direction = 'SAMLRequest', $mode = 'SP' + $httpredirect->sendMessage($logoutResponseXML, $logoutrequest->getIssuer(), $relayState, 'SingleLogoutService', 'SAMLResponse', 'IdP'); + +} catch(Exception $exception) { + + $et = new SimpleSAML_XHTML_Template($config, 'error.php'); + + $et->data['header'] = 'Error sending response to service'; + $et->data['message'] = 'Some error occured when trying to issue the logout response, and send it to the SP.'; + $et->data['e'] = $exception; + + $et->show(); + +} + + + +?> \ No newline at end of file diff --git a/www/saml2/idp/SingleLogoutServiceiFrameResponse.php b/www/saml2/idp/SingleLogoutServiceiFrameResponse.php new file mode 100644 index 0000000000000000000000000000000000000000..12ad063d4ddc01dc6f442737d3d0f59cfc8c6701 --- /dev/null +++ b/www/saml2/idp/SingleLogoutServiceiFrameResponse.php @@ -0,0 +1,50 @@ +<?php + + +require_once('../../_include.php'); + +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Utilities.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Session.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Metadata/MetaDataStorageHandler.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutRequest.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XML/SAML20/LogoutResponse.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPRedirect.php'); +//require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/Bindings/SAML20/HTTPPost.php'); +require_once((isset($SIMPLESAML_INCPREFIX)?$SIMPLESAML_INCPREFIX:'') . 'SimpleSAML/XHTML/Template.php'); + + + +sleep(rand(1,6)); + + +session_start(); + +$config = SimpleSAML_Configuration::getInstance(); +$metadata = SimpleSAML_Metadata_MetaDataStorageHandler::getMetadataHandler(); + +$idpentityid = $metadata->getMetaDataCurrentEntityID('saml20-idp-hosted'); + +$session = SimpleSAML_Session::getInstance(); + +//$session->dump_sp_sessions(); + + + +if (isset($_GET['SAMLResponse'])) { + + $binding = new SimpleSAML_Bindings_SAML20_HTTPRedirect($config, $metadata); + $logoutresponse = $binding->decodeLogoutResponse($_GET); + + $session->set_sp_logout_completed($logoutresponse->getIssuer()); + + error_log('IdP LogoutService: got LogoutResponse at SingleLogoutServiceAjaxResponse from ' . $logoutresponse->getIssuer() . ' '); + + echo 'OK'; + +} else { + + error_log('Error on SingleLogoutServiceAjaxResponse'); + echo 'Not set: SAMLResponse'; +} + +?> \ No newline at end of file diff --git a/www/saml2/idp/metadata.php b/www/saml2/idp/metadata.php index 82ed4d188afa5aaafa094e28b0ce3261e2779990..6c7fb1d44fe6ba70ed1944b8dac4695453b27d2b 100644 --- a/www/saml2/idp/metadata.php +++ b/www/saml2/idp/metadata.php @@ -33,16 +33,25 @@ try { $cert = file_get_contents($publiccert); $data = XMLSecurityDSig::get509XCert($cert, true); + $logouttype = 'traditional'; + if (array_key_exists('logouttype', $idpmeta)) $logouttype = $idpmeta['logouttype']; + + $urlSLO = $metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted', array('logouttype' => $logouttype)); + $urlSLOr = $metadata->getGenerated('SingleLogoutServiceResponse', 'saml20-idp-hosted', array('logouttype' => $logouttype)); $metaflat = " '" . htmlspecialchars($idpentityid) . "' => array( 'name' => 'Type in a name for this entity', 'description' => 'and a proper description that would help users know when to select this IdP.', - 'SingleSignOnService' => '" . htmlspecialchars($metadata->getGenerated('SingleSignOnService', 'saml20-idp-hosted')) . "', - 'SingleLogoutService' => '" . htmlspecialchars($metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted')) . "', + 'SingleSignOnService' => '" . htmlspecialchars($metadata->getGenerated('SingleSignOnService', 'saml20-idp-hosted', array())) . "', + 'SingleLogoutService' => '" . htmlspecialchars($urlSLO) . "'," . + (($urlSLO !== $urlSLOr) ? " + 'SingleLogoutServiceResponse' => '" . htmlspecialchars($urlSLOr) . "'," : "") . " 'certFingerprint' => '" . strtolower(sha1(base64_decode($data))) ."' ), "; + + $metaxml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <EntityDescriptor xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="urn:oasis:names:tc:SAML:2.0:metadata" @@ -64,8 +73,8 @@ try { <!-- Logout endpoints --> <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - Location="' . htmlspecialchars($metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted')) . '" - ResponseLocation="' . htmlspecialchars($metadata->getGenerated('SingleLogoutService', 'saml20-idp-hosted')) . '" + Location="' . htmlspecialchars($urlSLO) . '" + ResponseLocation="' . htmlspecialchars($urlSLOr) . '" /> diff --git a/www/saml2/idp/xajax_js/xajax.js b/www/saml2/idp/xajax_js/xajax.js new file mode 100644 index 0000000000000000000000000000000000000000..0dca8f52005bc720b13eadb61526510fba7f0051 --- /dev/null +++ b/www/saml2/idp/xajax_js/xajax.js @@ -0,0 +1,172 @@ +/* xajax Javascript library :: version 0.2.4 */ +Array.prototype.containsValue=function(valueToCheck){for(var i=0;i<this.length;i++){if(this[i]==valueToCheck)return true;} +return false;} +function Xajax(){this.DebugMessage=function(text){if(text.length > 1000)text=text.substr(0,1000)+"...\n[long response]\n...";try{if(this.debugWindow==undefined||this.debugWindow.closed==true){this.debugWindow=window.open('about:blank','xajax-debug','width=800,height=600,scrollbars=1,resizable,status');this.debugWindow.document.write('<html><head><title>Xajax debug output</title></head><body><h2>Xajax debug output</h2><div id="debugTag"></div></body></html>');} +text=text.replace(/&/g,"&") +text=text.replace(/</g,"<") +text=text.replace(/>/g,">") +debugTag=this.debugWindow.document.getElementById('debugTag');debugTag.innerHTML=('<b>'+(new Date()).toString()+'</b>: '+text+'<hr/>')+debugTag.innerHTML;}catch(e){alert("Xajax Debug:\n "+text);} +};this.workId='xajaxWork'+new Date().getTime();this.depth=0;this.responseErrorsForAlert=["400","401","402","403","404","500","501","502","503"];this.getRequestObject=function(){if(xajaxDebug)this.DebugMessage("Initializing Request Object..");var req=null;if(typeof XMLHttpRequest!="undefined") +req=new XMLHttpRequest();if(!req&&typeof ActiveXObject!="undefined"){try{req=new ActiveXObject("Msxml2.XMLHTTP");} +catch(e){try{req=new ActiveXObject("Microsoft.XMLHTTP");} +catch(e2){try{req=new ActiveXObject("Msxml2.XMLHTTP.4.0");} +catch(e3){req=null;} +} +} +} +if(!req&&window.createRequest) +req=window.createRequest();if(!req)this.DebugMessage("Request Object Instantiation failed.");return req;} +this.$=function(sId){if(!sId){return null;} +var returnObj=document.getElementById(sId);if(!returnObj&&document.all){returnObj=document.all[sId];} +if(xajaxDebug&&!returnObj&&sId!=this.workId){this.DebugMessage("Element with the id \""+sId+"\" not found.");} +return returnObj;} +this.include=function(sFileName){var objHead=document.getElementsByTagName('head');var objScript=document.createElement('script');objScript.type='text/javascript';objScript.src=sFileName;objHead[0].appendChild(objScript);} +this.stripOnPrefix=function(sEventName){sEventName=sEventName.toLowerCase();if(sEventName.indexOf('on')==0){sEventName=sEventName.replace(/on/,'');} +return sEventName;} +this.addOnPrefix=function(sEventName){sEventName=sEventName.toLowerCase();if(sEventName.indexOf('on')!=0){sEventName='on'+sEventName;} +return sEventName;} +this.addHandler=function(sElementId,sEvent,sFunctionName){if(window.addEventListener){sEvent=this.stripOnPrefix(sEvent);eval("this.$('"+sElementId+"').addEventListener('"+sEvent+"',"+sFunctionName+",false);");} +else{sAltEvent=this.addOnPrefix(sEvent);eval("this.$('"+sElementId+"').attachEvent('"+sAltEvent+"',"+sFunctionName+",false);");} +} +this.removeHandler=function(sElementId,sEvent,sFunctionName){if(window.addEventListener){sEvent=this.stripOnPrefix(sEvent);eval("this.$('"+sElementId+"').removeEventListener('"+sEvent+"',"+sFunctionName+",false);");} +else{sAltEvent=this.addOnPrefix(sEvent);eval("this.$('"+sElementId+"').detachEvent('"+sAltEvent+"',"+sFunctionName+",false);");} +} +this.create=function(sParentId,sTag,sId){var objParent=this.$(sParentId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);if(objParent) +objParent.appendChild(objElement);} +this.insert=function(sBeforeId,sTag,sId){var objSibling=this.$(sBeforeId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);objSibling.parentNode.insertBefore(objElement,objSibling);} +this.insertAfter=function(sAfterId,sTag,sId){var objSibling=this.$(sAfterId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);objSibling.parentNode.insertBefore(objElement,objSibling.nextSibling);} +this.getInput=function(sType,sName,sId){var Obj;if(!window.addEventListener){Obj=document.createElement('<input type="'+sType+'" id="'+sId+'" name="'+sName+'">');} +else{Obj=document.createElement('input');Obj.setAttribute('type',sType);Obj.setAttribute('name',sName);Obj.setAttribute('id',sId);} +return Obj;} +this.createInput=function(sParentId,sType,sName,sId){var objParent=this.$(sParentId);var objElement=this.getInput(sType,sName,sId);if(objParent&&objElement) +objParent.appendChild(objElement);} +this.insertInput=function(sBeforeId,sType,sName,sId){var objSibling=this.$(sBeforeId);var objElement=this.getInput(sType,sName,sId);if(objElement&&objSibling&&objSibling.parentNode) +objSibling.parentNode.insertBefore(objElement,objSibling);} +this.insertInputAfter=function(sAfterId,sType,sName,sId){var objSibling=this.$(sAfterId);var objElement=this.getInput(sType,sName,sId);if(objElement&&objSibling&&objSibling.parentNode){objSibling.parentNode.insertBefore(objElement,objSibling.nextSibling);} +} +this.remove=function(sId){objElement=this.$(sId);if(objElement&&objElement.parentNode&&objElement.parentNode.removeChild){objElement.parentNode.removeChild(objElement);} +} +this.replace=function(sId,sAttribute,sSearch,sReplace){var bFunction=false;if(sAttribute=="innerHTML") +sSearch=this.getBrowserHTML(sSearch);eval("var txt=this.$('"+sId+"')."+sAttribute);if(typeof txt=="function"){txt=txt.toString();bFunction=true;} +if(txt.indexOf(sSearch)>-1){var newTxt='';while(txt.indexOf(sSearch)>-1){x=txt.indexOf(sSearch)+sSearch.length+1;newTxt+=txt.substr(0,x).replace(sSearch,sReplace);txt=txt.substr(x,txt.length-x);} +newTxt+=txt;if(bFunction){eval('this.$("'+sId+'").'+sAttribute+'=newTxt;');} +else if(this.willChange(sId,sAttribute,newTxt)){eval('this.$("'+sId+'").'+sAttribute+'=newTxt;');} +} +} +this.getFormValues=function(frm){var objForm;var submitDisabledElements=false;if(arguments.length > 1&&arguments[1]==true) +submitDisabledElements=true;var prefix="";if(arguments.length > 2) +prefix=arguments[2];if(typeof(frm)=="string") +objForm=this.$(frm);else +objForm=frm;var sXml="<xjxquery><q>";if(objForm&&objForm.tagName=='FORM'){var formElements=objForm.elements;for(var i=0;i < formElements.length;i++){if(!formElements[i].name) +continue;if(formElements[i].name.substring(0,prefix.length)!=prefix) +continue;if(formElements[i].type&&(formElements[i].type=='radio'||formElements[i].type=='checkbox')&&formElements[i].checked==false) +continue;if(formElements[i].disabled&&formElements[i].disabled==true&&submitDisabledElements==false) +continue;var name=formElements[i].name;if(name){if(sXml!='<xjxquery><q>') +sXml+='&';if(formElements[i].type=='select-multiple'){for(var j=0;j < formElements[i].length;j++){if(formElements[i].options[j].selected==true) +sXml+=name+"="+encodeURIComponent(formElements[i].options[j].value)+"&";} +} +else{sXml+=name+"="+encodeURIComponent(formElements[i].value);} +} +} +} +sXml+="</q></xjxquery>";return sXml;} +this.objectToXML=function(obj){var sXml="<xjxobj>";for(i in obj){try{if(i=='constructor') +continue;if(obj[i]&&typeof(obj[i])=='function') +continue;var key=i;var value=obj[i];if(value&&typeof(value)=="object"&&this.depth <=50){this.depth++;value=this.objectToXML(value);this.depth--;} +sXml+="<e><k>"+key+"</k><v>"+value+"</v></e>";} +catch(e){if(xajaxDebug)this.DebugMessage(e.name+": "+e.message);} +} +sXml+="</xjxobj>";return sXml;} +this._nodeToObject=function(node){if(node.nodeName=='#cdata-section'){var data="";for(var j=0;j<node.parentNode.childNodes.length;j++){data+=node.parentNode.childNodes[j].data;} +return data;} +else if(node.nodeName=='xjxobj'){var data=new Array();for(var j=0;j<node.childNodes.length;j++){var child=node.childNodes[j];var key;var value;if(child.nodeName=='e'){for(var k=0;k<child.childNodes.length;k++){if(child.childNodes[k].nodeName=='k'){key=child.childNodes[k].firstChild.data;} +else if(child.childNodes[k].nodeName=='v'){value=this._nodeToObject(child.childNodes[k].firstChild);} +} +if(key!=null&&value!=null){data[key]=value;key=value=null;} +} +} +return data;} +} +this.loadingFunction=function(){};this.doneLoadingFunction=function(){};var loadingTimeout;this.call=function(sFunction,aArgs,sRequestType){var i,r,postData;if(document.body&&xajaxWaitCursor) +document.body.style.cursor='wait';if(xajaxStatusMessages==true)window.status='Sending Request...';clearTimeout(loadingTimeout);loadingTimeout=setTimeout("xajax.loadingFunction();",400);if(xajaxDebug)this.DebugMessage("Starting xajax...");if(sRequestType==null){var xajaxRequestType=xajaxDefinedPost;} +else{var xajaxRequestType=sRequestType;} +var uri=xajaxRequestUri;var value;switch(xajaxRequestType){case xajaxDefinedGet:{var uriGet=uri.indexOf("?")==-1?"?xajax="+encodeURIComponent(sFunction):"&xajax="+encodeURIComponent(sFunction);if(aArgs){for(i=0;i<aArgs.length;i++){value=aArgs[i];if(typeof(value)=="object") +value=this.objectToXML(value);uriGet+="&xajaxargs[]="+encodeURIComponent(value);} +} +uriGet+="&xajaxr="+new Date().getTime();uri+=uriGet;postData=null;}break;case xajaxDefinedPost:{postData="xajax="+encodeURIComponent(sFunction);postData+="&xajaxr="+new Date().getTime();if(aArgs){for(i=0;i <aArgs.length;i++){value=aArgs[i];if(typeof(value)=="object") +value=this.objectToXML(value);postData=postData+"&xajaxargs[]="+encodeURIComponent(value);} +} +}break;default: +alert("Illegal request type: "+xajaxRequestType);return false;break;} +r=this.getRequestObject();if(!r)return false;r.open(xajaxRequestType==xajaxDefinedGet?"GET":"POST",uri,true);if(xajaxRequestType==xajaxDefinedPost){try{r.setRequestHeader("Method","POST "+uri+" HTTP/1.1");r.setRequestHeader("Content-Type","application/x-www-form-urlencoded");} +catch(e){alert("Your browser does not appear to support asynchronous requests using POST.");return false;} +} +r.onreadystatechange=function(){if(r.readyState!=4) +return;if(r.status==200){if(xajaxDebug)xajax.DebugMessage("Received:\n"+r.responseText);if(r.responseXML&&r.responseXML.documentElement) +xajax.processResponse(r.responseXML);else{var errorString="Error: the XML response that was returned from the server is invalid.";errorString+="\nReceived:\n"+r.responseText;trimmedResponseText=r.responseText.replace(/^\s+/g,"");trimmedResponseText=trimmedResponseText.replace(/\s+$/g,"");if(trimmedResponseText!=r.responseText) +errorString+="\nYou have whitespace in your response.";alert(errorString);document.body.style.cursor='default';if(xajaxStatusMessages==true)window.status='Invalid XML response error';} +} +else{if(xajax.responseErrorsForAlert.containsValue(r.status)){var errorString="Error: the server returned the following HTTP status: "+r.status;errorString+="\nReceived:\n"+r.responseText;alert(errorString);} +document.body.style.cursor='default';if(xajaxStatusMessages==true)window.status='Invalid XML response error';} +delete r;r=null;} +if(xajaxDebug)this.DebugMessage("Calling "+sFunction+" uri="+uri+" (post:"+postData+")");r.send(postData);if(xajaxStatusMessages==true)window.status='Waiting for data...';delete r;return true;} +this.getBrowserHTML=function(html){tmpXajax=this.$(this.workId);if(!tmpXajax){tmpXajax=document.createElement("div");tmpXajax.setAttribute('id',this.workId);tmpXajax.style.display="none";tmpXajax.style.visibility="hidden";document.body.appendChild(tmpXajax);} +tmpXajax.innerHTML=html;var browserHTML=tmpXajax.innerHTML;tmpXajax.innerHTML='';return browserHTML;} +this.willChange=function(element,attribute,newData){if(!document.body){return true;} +if(attribute=="innerHTML"){newData=this.getBrowserHTML(newData);} +elementObject=this.$(element);if(elementObject){var oldData;eval("oldData=this.$('"+element+"')."+attribute);if(newData!==oldData) +return true;} +return false;} +this.viewSource=function(){return "<html>"+document.getElementsByTagName("HTML")[0].innerHTML+"</html>";} +this.processResponse=function(xml){clearTimeout(loadingTimeout);this.doneLoadingFunction();if(xajaxStatusMessages==true)window.status='Processing...';var tmpXajax=null;xml=xml.documentElement;if(xml==null) +return;var skipCommands=0;for(var i=0;i<xml.childNodes.length;i++){if(skipCommands > 0){skipCommands--;continue;} +if(xml.childNodes[i].nodeName=="cmd"){var cmd;var id;var property;var data;var search;var type;var before;var objElement=null;for(var j=0;j<xml.childNodes[i].attributes.length;j++){if(xml.childNodes[i].attributes[j].name=="n"){cmd=xml.childNodes[i].attributes[j].value;} +else if(xml.childNodes[i].attributes[j].name=="t"){id=xml.childNodes[i].attributes[j].value;} +else if(xml.childNodes[i].attributes[j].name=="p"){property=xml.childNodes[i].attributes[j].value;} +else if(xml.childNodes[i].attributes[j].name=="c"){type=xml.childNodes[i].attributes[j].value;} +} +if(xml.childNodes[i].childNodes.length > 1&&xml.childNodes[i].firstChild.nodeName=="#cdata-section"){data="";for(var j=0;j<xml.childNodes[i].childNodes.length;j++){data+=xml.childNodes[i].childNodes[j].data;} +} +else if(xml.childNodes[i].firstChild&&xml.childNodes[i].firstChild.nodeName=='xjxobj'){data=this._nodeToObject(xml.childNodes[i].firstChild);objElement="XJX_SKIP";} +else if(xml.childNodes[i].childNodes.length > 1){for(var j=0;j<xml.childNodes[i].childNodes.length;j++){if(xml.childNodes[i].childNodes[j].childNodes.length > 1&&xml.childNodes[i].childNodes[j].firstChild.nodeName=="#cdata-section"){var internalData="";for(var k=0;k<xml.childNodes[i].childNodes[j].childNodes.length;k++){internalData+=xml.childNodes[i].childNodes[j].childNodes[k].nodeValue;} +}else{var internalData=xml.childNodes[i].childNodes[j].firstChild.nodeValue;} +if(xml.childNodes[i].childNodes[j].nodeName=="s"){search=internalData;} +if(xml.childNodes[i].childNodes[j].nodeName=="r"){data=internalData;} +} +} +else if(xml.childNodes[i].firstChild) +data=xml.childNodes[i].firstChild.nodeValue;else +data="";if(objElement!="XJX_SKIP")objElement=this.$(id);var cmdFullname;try{if(cmd=="cc"){cmdFullname="addConfirmCommands";var confirmResult=confirm(data);if(!confirmResult){skipCommands=id;} +} +if(cmd=="al"){cmdFullname="addAlert";alert(data);} +else if(cmd=="js"){cmdFullname="addScript/addRedirect";eval(data);} +else if(cmd=="jc"){cmdFullname="addScriptCall";var scr=id+'(';if(data[0]!=null){scr+='data[0]';for(var l=1;l<data.length;l++){scr+=',data['+l+']';} +} +scr+=');';eval(scr);} +else if(cmd=="in"){cmdFullname="addIncludeScript";this.include(data);} +else if(cmd=="as"){cmdFullname="addAssign/addClear";if(this.willChange(id,property,data)){eval("objElement."+property+"=data;");} +} +else if(cmd=="ap"){cmdFullname="addAppend";eval("objElement."+property+"+=data;");} +else if(cmd=="pp"){cmdFullname="addPrepend";eval("objElement."+property+"=data+objElement."+property);} +else if(cmd=="rp"){cmdFullname="addReplace";this.replace(id,property,search,data) +} +else if(cmd=="rm"){cmdFullname="addRemove";this.remove(id);} +else if(cmd=="ce"){cmdFullname="addCreate";this.create(id,data,property);} +else if(cmd=="ie"){cmdFullname="addInsert";this.insert(id,data,property);} +else if(cmd=="ia"){cmdFullname="addInsertAfter";this.insertAfter(id,data,property);} +else if(cmd=="ci"){cmdFullname="addCreateInput";this.createInput(id,type,data,property);} +else if(cmd=="ii"){cmdFullname="addInsertInput";this.insertInput(id,type,data,property);} +else if(cmd=="iia"){cmdFullname="addInsertInputAfter";this.insertInputAfter(id,type,data,property);} +else if(cmd=="ev"){cmdFullname="addEvent";property=this.addOnPrefix(property);eval("this.$('"+id+"')."+property+"= function(){"+data+";}");} +else if(cmd=="ah"){cmdFullname="addHandler";this.addHandler(id,property,data);} +else if(cmd=="rh"){cmdFullname="addRemoveHandler";this.removeHandler(id,property,data);} +} +catch(e){if(xajaxDebug) +alert("While trying to '"+cmdFullname+"' (command number "+i+"), the following error occured:\n" ++e.name+": "+e.message+"\n" ++(id&&!objElement?"Object with id='"+id+"' wasn't found.\n":""));} +delete objElement;delete cmd;delete cmdFullname;delete id;delete property;delete search;delete data;delete type;delete before;delete internalData;delete j;delete k;} +} +delete xml;delete i;document.body.style.cursor='default';if(xajaxStatusMessages==true)window.status='Done';} +} +var xajax=new Xajax();xajaxLoaded=true; \ No newline at end of file diff --git a/www/saml2/idp/xajax_js/xajax_uncompressed.js b/www/saml2/idp/xajax_js/xajax_uncompressed.js new file mode 100644 index 0000000000000000000000000000000000000000..df51ccdeb27dad60cbe2847058eaa683d916a7ec --- /dev/null +++ b/www/saml2/idp/xajax_js/xajax_uncompressed.js @@ -0,0 +1,795 @@ +/* xajax Javascript library :: version 0.2.4 */ + +Array.prototype.containsValue = function(valueToCheck) +{ + for (var i=0;i<this.length;i++) { + if (this[i] == valueToCheck) return true; + } + return false; +} + +function Xajax() +{ + this.DebugMessage = function(text) + { + if (text.length > 1000) text = text.substr(0,1000)+"...\n[long response]\n..."; + try { + if (this.debugWindow == undefined || this.debugWindow.closed == true) { + this.debugWindow = window.open('about:blank', 'xajax-debug', 'width=800,height=600,scrollbars=1,resizable,status'); + this.debugWindow.document.write('<html><head><title>Xajax debug output</title></head><body><h2>Xajax debug output</h2><div id="debugTag"></div></body></html>'); + } + text = text.replace(/&/g, "&") + text = text.replace(/</g, "<") + text = text.replace(/>/g, ">") + debugTag = this.debugWindow.document.getElementById('debugTag'); + debugTag.innerHTML = ('<b>'+(new Date()).toString()+'</b>: ' + text + '<hr/>') + debugTag.innerHTML; + } catch (e) { + alert("Xajax Debug:\n " + text); + } + }; + + this.workId = 'xajaxWork'+ new Date().getTime(); + this.depth = 0; + this.responseErrorsForAlert = ["400","401","402","403","404","500","501","502","503"]; + + //Get the XMLHttpRequest Object + this.getRequestObject = function() + { + if (xajaxDebug) this.DebugMessage("Initializing Request Object.."); + var req = null; + if (typeof XMLHttpRequest != "undefined") + req = new XMLHttpRequest(); + if (!req && typeof ActiveXObject != "undefined") + { + try + { + req=new ActiveXObject("Msxml2.XMLHTTP"); + } + catch (e) + { + try + { + req=new ActiveXObject("Microsoft.XMLHTTP"); + } + catch (e2) + { + try { + req=new ActiveXObject("Msxml2.XMLHTTP.4.0"); + } + catch (e3) + { + req=null; + } + } + } + } + if(!req && window.createRequest) + req = window.createRequest(); + + if (!req) this.DebugMessage("Request Object Instantiation failed."); + + return req; + } + + // xajax.$() is shorthand for document.getElementById() + this.$ = function(sId) + { + if (!sId) { + return null; + } + var returnObj = document.getElementById(sId); + if (!returnObj && document.all) { + returnObj = document.all[sId]; + } + if (xajaxDebug && !returnObj && sId != this.workId) { + this.DebugMessage("Element with the id \"" + sId + "\" not found."); + } + return returnObj; + } + + // xajax.include(sFileName) dynamically includes an external javascript file + this.include = function(sFileName) + { + var objHead = document.getElementsByTagName('head'); + var objScript = document.createElement('script'); + objScript.type = 'text/javascript'; + objScript.src = sFileName; + objHead[0].appendChild(objScript); + } + + this.stripOnPrefix = function(sEventName) + { + sEventName = sEventName.toLowerCase(); + if (sEventName.indexOf('on') == 0) + { + sEventName = sEventName.replace(/on/,''); + } + + return sEventName; + } + + this.addOnPrefix = function(sEventName) + { + sEventName = sEventName.toLowerCase(); + if (sEventName.indexOf('on') != 0) + { + sEventName = 'on' + sEventName; + } + + return sEventName; + } + + // xajax.addHandler adds an event handler to an element + this.addHandler = function(sElementId, sEvent, sFunctionName) + { + if (window.addEventListener) + { + sEvent = this.stripOnPrefix(sEvent); + eval("this.$('"+sElementId+"').addEventListener('"+sEvent+"',"+sFunctionName+",false);"); + } + else + { + sAltEvent = this.addOnPrefix(sEvent); + eval("this.$('"+sElementId+"').attachEvent('"+sAltEvent+"',"+sFunctionName+",false);"); + } + } + + // xajax.removeHandler removes an event handler from an element + this.removeHandler = function(sElementId, sEvent, sFunctionName) + { + if (window.addEventListener) + { + sEvent = this.stripOnPrefix(sEvent); + eval("this.$('"+sElementId+"').removeEventListener('"+sEvent+"',"+sFunctionName+",false);"); + } + else + { + sAltEvent = this.addOnPrefix(sEvent); + eval("this.$('"+sElementId+"').detachEvent('"+sAltEvent+"',"+sFunctionName+",false);"); + } + } + + // xajax.create creates a new child node under a parent + this.create = function(sParentId, sTag, sId) + { + var objParent = this.$(sParentId); + objElement = document.createElement(sTag); + objElement.setAttribute('id',sId); + if (objParent) + objParent.appendChild(objElement); + } + + // xajax.insert inserts a new node before another node + this.insert = function(sBeforeId, sTag, sId) + { + var objSibling = this.$(sBeforeId); + objElement = document.createElement(sTag); + objElement.setAttribute('id',sId); + objSibling.parentNode.insertBefore(objElement, objSibling); + } + + // xajax.insertAfter inserts a new node after another node + this.insertAfter = function(sAfterId, sTag, sId) + { + var objSibling = this.$(sAfterId); + objElement = document.createElement(sTag); + objElement.setAttribute('id',sId); + objSibling.parentNode.insertBefore(objElement, objSibling.nextSibling); + } + + this.getInput = function(sType, sName, sId) + { + var Obj; + if (!window.addEventListener) + { + Obj = document.createElement('<input type="'+sType+'" id="'+sId+'" name="'+sName+'">'); + } + else + { + Obj = document.createElement('input'); + Obj.setAttribute('type',sType); + Obj.setAttribute('name',sName); + Obj.setAttribute('id',sId); + } + return Obj; + } + + // xajax.createInput creates a new input node under a parent + this.createInput = function(sParentId, sType, sName, sId) + { + var objParent = this.$(sParentId); + var objElement = this.getInput(sType, sName, sId); + if (objParent && objElement) + objParent.appendChild(objElement); + } + + // xajax.insertInput creates a new input node before another node + this.insertInput = function(sBeforeId, sType, sName, sId) + { + var objSibling = this.$(sBeforeId); + var objElement = this.getInput(sType, sName, sId); + if (objElement && objSibling && objSibling.parentNode) + objSibling.parentNode.insertBefore(objElement, objSibling); + } + + // xajax.insertInputAfter creates a new input node after another node + this.insertInputAfter = function(sAfterId, sType, sName, sId) + { + var objSibling = this.$(sAfterId); + var objElement = this.getInput(sType, sName, sId); + if (objElement && objSibling && objSibling.parentNode) { + objSibling.parentNode.insertBefore(objElement, objSibling.nextSibling); + } + } + + // xajax.remove deletes an element + this.remove = function(sId) + { + objElement = this.$(sId); + if (objElement && objElement.parentNode && objElement.parentNode.removeChild) + { + objElement.parentNode.removeChild(objElement); + } + } + + //xajax.replace searches for text in an attribute of an element and replaces it + //with a different text + this.replace = function(sId,sAttribute,sSearch,sReplace) + { + var bFunction = false; + + if (sAttribute == "innerHTML") + sSearch = this.getBrowserHTML(sSearch); + + eval("var txt=this.$('"+sId+"')."+sAttribute); + if (typeof txt == "function") + { + txt = txt.toString(); + bFunction = true; + } + if (txt.indexOf(sSearch)>-1) + { + var newTxt = ''; + while (txt.indexOf(sSearch) > -1) + { + x = txt.indexOf(sSearch)+sSearch.length+1; + newTxt += txt.substr(0,x).replace(sSearch,sReplace); + txt = txt.substr(x,txt.length-x); + } + newTxt += txt; + if (bFunction) + { + eval('this.$("'+sId+'").'+sAttribute+'=newTxt;'); + } + else if (this.willChange(sId,sAttribute,newTxt)) + { + eval('this.$("'+sId+'").'+sAttribute+'=newTxt;'); + } + } + } + + // xajax.getFormValues() builds a query string XML message from the elements of a form object + // * The first argument is the id of the form + // * The second argument (optional) can be set to true if you want to submit disabled elements + // * The third argument (optional) allows you to specify a string prefix that a form element + // name must contain if you want that element to be submitted + this.getFormValues = function(frm) + { + var objForm; + var submitDisabledElements = false; + if (arguments.length > 1 && arguments[1] == true) + submitDisabledElements = true; + var prefix=""; + if(arguments.length > 2) + prefix = arguments[2]; + + if (typeof(frm) == "string") + objForm = this.$(frm); + else + objForm = frm; + var sXml = "<xjxquery><q>"; + if (objForm && objForm.tagName == 'FORM') + { + var formElements = objForm.elements; + for( var i=0; i < formElements.length; i++) + { + if (!formElements[i].name) + continue; + if (formElements[i].name.substring(0, prefix.length) != prefix) + continue; + if (formElements[i].type && (formElements[i].type == 'radio' || formElements[i].type == 'checkbox') && formElements[i].checked == false) + continue; + if (formElements[i].disabled && formElements[i].disabled == true && submitDisabledElements == false) + continue; + var name = formElements[i].name; + if (name) + { + if (sXml != '<xjxquery><q>') + sXml += '&'; + if(formElements[i].type=='select-multiple') + { + for (var j = 0; j < formElements[i].length; j++) + { + if (formElements[i].options[j].selected == true) + sXml += name+"="+encodeURIComponent(formElements[i].options[j].value)+"&"; + } + } + else + { + sXml += name+"="+encodeURIComponent(formElements[i].value); + } + } + } + } + + sXml +="</q></xjxquery>"; + + return sXml; + } + + // Generates an XML message that xajax can understand from a javascript object + this.objectToXML = function(obj) + { + var sXml = "<xjxobj>"; + for (i in obj) + { + try + { + if (i == 'constructor') + continue; + if (obj[i] && typeof(obj[i]) == 'function') + continue; + + var key = i; + var value = obj[i]; + if (value && typeof(value)=="object" && this.depth <= 50) + { + this.depth++; + value = this.objectToXML(value); + this.depth--; + } + + sXml += "<e><k>"+key+"</k><v>"+value+"</v></e>"; + + } + catch(e) + { + if (xajaxDebug) this.DebugMessage(e.name+": "+e.message); + } + } + sXml += "</xjxobj>"; + + return sXml; + } + + // unserializes data structure from xajaxResponse::_buildObjXml() + this._nodeToObject = function(node) { + // parentNode here is weird, have to tune + if (node.nodeName == '#cdata-section') { + var data = ""; + for (var j=0; j<node.parentNode.childNodes.length; j++) { + data += node.parentNode.childNodes[j].data; + } + return data; + } + else if (node.nodeName == 'xjxobj') { + var data = new Array(); + for (var j=0; j<node.childNodes.length; j++) { + var child = node.childNodes[j]; + var key; + var value; + if (child.nodeName == 'e') { + for (var k=0; k<child.childNodes.length; k++) { + if (child.childNodes[k].nodeName == 'k') { + key = child.childNodes[k].firstChild.data; + } + else if (child.childNodes[k].nodeName == 'v') { + value = this._nodeToObject(child.childNodes[k].firstChild); + } + } + if (key != null && value != null) { + data[key] = value; + key = value = null; + } + } + } + return data; + } + } + + this.loadingFunction = function(){}; + this.doneLoadingFunction = function(){}; + var loadingTimeout; + + // Sends a XMLHttpRequest to call the specified PHP function on the server + // * sRequestType is optional -- defaults to POST + this.call = function(sFunction, aArgs, sRequestType) + { + var i,r,postData; + if (document.body && xajaxWaitCursor) + document.body.style.cursor = 'wait'; + if (xajaxStatusMessages == true) window.status = 'Sending Request...'; + clearTimeout(loadingTimeout); + loadingTimeout = setTimeout("xajax.loadingFunction();",400); + if (xajaxDebug) this.DebugMessage("Starting xajax..."); + if (sRequestType == null) { + var xajaxRequestType = xajaxDefinedPost; + } + else { + var xajaxRequestType = sRequestType; + } + var uri = xajaxRequestUri; + var value; + switch(xajaxRequestType) + { + case xajaxDefinedGet:{ + var uriGet = uri.indexOf("?")==-1?"?xajax="+encodeURIComponent(sFunction):"&xajax="+encodeURIComponent(sFunction); + if (aArgs) { + for (i = 0; i<aArgs.length; i++) + { + value = aArgs[i]; + if (typeof(value)=="object") + value = this.objectToXML(value); + uriGet += "&xajaxargs[]="+encodeURIComponent(value); + } + } + uriGet += "&xajaxr=" + new Date().getTime(); + uri += uriGet; + postData = null; + } break; + case xajaxDefinedPost:{ + postData = "xajax="+encodeURIComponent(sFunction); + postData += "&xajaxr="+new Date().getTime(); + if (aArgs) { + for (i = 0; i <aArgs.length; i++) + { + value = aArgs[i]; + if (typeof(value)=="object") + value = this.objectToXML(value); + postData = postData+"&xajaxargs[]="+encodeURIComponent(value); + } + } + } break; + default: + alert("Illegal request type: " + xajaxRequestType); return false; break; + } + r = this.getRequestObject(); + if (!r) return false; + r.open(xajaxRequestType==xajaxDefinedGet?"GET":"POST", uri, true); + if (xajaxRequestType == xajaxDefinedPost) + { + try + { + r.setRequestHeader("Method", "POST " + uri + " HTTP/1.1"); + r.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + } + catch(e) + { + alert("Your browser does not appear to support asynchronous requests using POST."); + return false; + } + } + r.onreadystatechange = function() + { + if (r.readyState != 4) + return; + + if (r.status==200) + { + if (xajaxDebug) xajax.DebugMessage("Received:\n" + r.responseText); + if (r.responseXML && r.responseXML.documentElement) + xajax.processResponse(r.responseXML); + else { + var errorString = "Error: the XML response that was returned from the server is invalid."; + errorString += "\nReceived:\n" + r.responseText; + trimmedResponseText = r.responseText.replace( /^\s+/g, "" );// strip leading space + trimmedResponseText = trimmedResponseText.replace( /\s+$/g, "" );// strip trailing + if (trimmedResponseText != r.responseText) + errorString += "\nYou have whitespace in your response."; + alert(errorString); + document.body.style.cursor = 'default'; + if (xajaxStatusMessages == true) window.status = 'Invalid XML response error'; + } + } + else { + if (xajax.responseErrorsForAlert.containsValue(r.status)) { + var errorString = "Error: the server returned the following HTTP status: " + r.status; + errorString += "\nReceived:\n" + r.responseText; + alert(errorString); + } + document.body.style.cursor = 'default'; + if (xajaxStatusMessages == true) window.status = 'Invalid XML response error'; + } + + delete r; + r = null; + } + if (xajaxDebug) this.DebugMessage("Calling "+sFunction +" uri="+uri+" (post:"+ postData +")"); + r.send(postData); + if (xajaxStatusMessages == true) window.status = 'Waiting for data...'; + delete r; + return true; + } + + //Gets the text as it would be if it were being retrieved from + //the innerHTML property in the current browser + this.getBrowserHTML = function(html) + { + tmpXajax = this.$(this.workId); + if (!tmpXajax) + { + tmpXajax = document.createElement("div"); + tmpXajax.setAttribute('id',this.workId); + tmpXajax.style.display = "none"; + tmpXajax.style.visibility = "hidden"; + document.body.appendChild(tmpXajax); + } + tmpXajax.innerHTML = html; + var browserHTML = tmpXajax.innerHTML; + tmpXajax.innerHTML = ''; + + return browserHTML; + } + + // Tests if the new Data is the same as the extant data + this.willChange = function(element, attribute, newData) + { + if (!document.body) + { + return true; + } + if (attribute == "innerHTML") + { + newData = this.getBrowserHTML(newData); + } + elementObject = this.$(element); + if (elementObject) { + var oldData; + eval("oldData=this.$('"+element+"')."+attribute); + if (newData !== oldData) + return true; + } + + return false; + } + + //Returns the source code of the page after it's been modified by xajax + this.viewSource = function() + { + return "<html>"+document.getElementsByTagName("HTML")[0].innerHTML+"</html>"; + } + + //Process XML xajaxResponses returned from the request + this.processResponse = function(xml) + { + clearTimeout(loadingTimeout); + this.doneLoadingFunction(); + if (xajaxStatusMessages == true) window.status = 'Processing...'; + var tmpXajax = null; + xml = xml.documentElement; + if (xml == null) + return; + + var skipCommands = 0; + for (var i=0; i<xml.childNodes.length; i++) + { + if (skipCommands > 0) { + skipCommands--; + continue; + } + if (xml.childNodes[i].nodeName == "cmd") + { + var cmd; + var id; + var property; + var data; + var search; + var type; + var before; + var objElement = null; + + for (var j=0; j<xml.childNodes[i].attributes.length; j++) + { + if (xml.childNodes[i].attributes[j].name == "n") + { + cmd = xml.childNodes[i].attributes[j].value; + } + else if (xml.childNodes[i].attributes[j].name == "t") + { + id = xml.childNodes[i].attributes[j].value; + } + else if (xml.childNodes[i].attributes[j].name == "p") + { + property = xml.childNodes[i].attributes[j].value; + } + else if (xml.childNodes[i].attributes[j].name == "c") + { + type = xml.childNodes[i].attributes[j].value; + } + } + if (xml.childNodes[i].childNodes.length > 1 && xml.childNodes[i].firstChild.nodeName == "#cdata-section") + { + data = ""; + for (var j=0; j<xml.childNodes[i].childNodes.length; j++) + { + data += xml.childNodes[i].childNodes[j].data; + } + } + else if (xml.childNodes[i].firstChild && xml.childNodes[i].firstChild.nodeName == 'xjxobj') { + data = this._nodeToObject(xml.childNodes[i].firstChild); + objElement = "XJX_SKIP"; + } + else if (xml.childNodes[i].childNodes.length > 1) + { + for (var j=0; j<xml.childNodes[i].childNodes.length; j++) + { + if (xml.childNodes[i].childNodes[j].childNodes.length > 1 && xml.childNodes[i].childNodes[j].firstChild.nodeName == "#cdata-section") + { + var internalData = ""; + for (var k=0; k<xml.childNodes[i].childNodes[j].childNodes.length;k++) + { + internalData+=xml.childNodes[i].childNodes[j].childNodes[k].nodeValue; + } + } else { + var internalData = xml.childNodes[i].childNodes[j].firstChild.nodeValue; + } + + if (xml.childNodes[i].childNodes[j].nodeName == "s") + { + search = internalData; + } + if (xml.childNodes[i].childNodes[j].nodeName == "r") + { + data = internalData; + } + } + } + else if (xml.childNodes[i].firstChild) + data = xml.childNodes[i].firstChild.nodeValue; + else + data = ""; + + if (objElement != "XJX_SKIP") objElement = this.$(id); + var cmdFullname; + try + { + if (cmd=="cc") { + cmdFullname = "addConfirmCommands"; + var confirmResult = confirm(data); + if (!confirmResult) { + skipCommands = id; + } + } + if (cmd=="al") + { + cmdFullname = "addAlert"; + alert(data); + } + else if (cmd=="js") + { + cmdFullname = "addScript/addRedirect"; + eval(data); + } + else if (cmd=="jc") + { + cmdFullname = "addScriptCall"; + var scr = id + '('; + if (data[0] != null) { + scr += 'data[0]'; + for (var l=1; l<data.length; l++) { + scr += ',data['+l+']'; + } + } + scr += ');'; + eval(scr); + } + else if (cmd=="in") + { + cmdFullname = "addIncludeScript"; + this.include(data); + } + else if (cmd=="as") + { + cmdFullname = "addAssign/addClear"; + if (this.willChange(id,property,data)) + { + eval("objElement."+property+"=data;"); + } + } + else if (cmd=="ap") + { + cmdFullname = "addAppend"; + eval("objElement."+property+"+=data;"); + } + else if (cmd=="pp") + { + cmdFullname = "addPrepend"; + eval("objElement."+property+"=data+objElement."+property); + } + else if (cmd=="rp") + { + cmdFullname = "addReplace"; + this.replace(id,property,search,data) + } + else if (cmd=="rm") + { + cmdFullname = "addRemove"; + this.remove(id); + } + else if (cmd=="ce") + { + cmdFullname = "addCreate"; + this.create(id,data,property); + } + else if (cmd=="ie") + { + cmdFullname = "addInsert"; + this.insert(id,data,property); + } + else if (cmd=="ia") + { + cmdFullname = "addInsertAfter"; + this.insertAfter(id,data,property); + } + else if (cmd=="ci") + { + cmdFullname = "addCreateInput"; + this.createInput(id,type,data,property); + } + else if (cmd=="ii") + { + cmdFullname = "addInsertInput"; + this.insertInput(id,type,data,property); + } + else if (cmd=="iia") + { + cmdFullname = "addInsertInputAfter"; + this.insertInputAfter(id,type,data,property); + } + else if (cmd=="ev") + { + cmdFullname = "addEvent"; + property = this.addOnPrefix(property); + eval("this.$('"+id+"')."+property+"= function(){"+data+";}"); + } + else if (cmd=="ah") + { + cmdFullname = "addHandler"; + this.addHandler(id, property, data); + } + else if (cmd=="rh") + { + cmdFullname = "addRemoveHandler"; + this.removeHandler(id, property, data); + } + } + catch(e) + { + if (xajaxDebug) + alert("While trying to '"+cmdFullname+"' (command number "+i+"), the following error occured:\n" + + e.name+": "+e.message+"\n" + + (id&&!objElement?"Object with id='"+id+"' wasn't found.\n":"")); + } + delete objElement; + delete cmd; + delete cmdFullname; + delete id; + delete property; + delete search; + delete data; + delete type; + delete before; + delete internalData; + delete j; + delete k; + } + } + delete xml; + delete i; + document.body.style.cursor = 'default'; + if (xajaxStatusMessages == true) window.status = 'Done'; + } +} + +var xajax = new Xajax(); +xajaxLoaded = true;