diff --git a/phpgwapi/inc/xajax/xajax_core/xajaxArgumentManager.inc.php b/phpgwapi/inc/xajax/xajax_core/xajaxArgumentManager.inc.php new file mode 100644 index 0000000000..a520ddd351 --- /dev/null +++ b/phpgwapi/inc/xajax/xajax_core/xajaxArgumentManager.inc.php @@ -0,0 +1,466 @@ + for a detailed description, copyright + and license information. +*/ + +/* + @package xajax + @version $Id: xajaxArgumentManager.inc.php 362 2007-05-29 15:32:24Z calltoconstruct $ + @copyright Copyright (c) 2005-2007 by Jared White & J. Max Wilson + @copyright Copyright (c) 2008-2009 by Joseph Woolley, Steffen Konerow, Jared White & J. Max Wilson + @license http://www.xajaxproject.org/bsd_license.txt BSD License +*/ + +if (!defined('XAJAX_METHOD_UNKNOWN')) define('XAJAX_METHOD_UNKNOWN', 0); +if (!defined('XAJAX_METHOD_GET')) define('XAJAX_METHOD_GET', 1); +if (!defined('XAJAX_METHOD_POST')) define('XAJAX_METHOD_POST', 2); + +/* + Class: xajaxArgumentManager + + This class processes the input arguments from the GET or POST data of + the request. If this is a request for the initial page load, no arguments + will be processed. During a xajax request, any arguments found in the + GET or POST will be converted to a PHP array. +*/ +class xajaxArgumentManager +{ + /* + Array: aArgs + + An array of arguments received via the GET or POST parameter + xjxargs. + */ + var $aArgs; + + /* + Boolean: bDecodeUTF8Input + + A configuration option used to indicate whether input data should be + UTF8 decoded automatically. + */ + var $bDecodeUTF8Input; + + /* + String: sCharacterEncoding + + The character encoding in which the input data will be received. + */ + var $sCharacterEncoding; + + /* + Integer: nMethod + + Stores the method that was used to send the arguments from the client. Will + be one of: XAJAX_METHOD_UNKNOWN, XAJAX_METHOD_GET, XAJAX_METHOD_POST + */ + var $nMethod; + + /* + Array: aSequence + + Stores the decoding sequence table. + */ + var $aSequence; + + /* + Function: convertStringToBool + + Converts a string to a bool var. + + Parameters: + $sValue - (string): + + Returns: + (bool) : true / false + + */ + + function convertStringToBool($sValue) + { + if (0 == strcasecmp($sValue, 'true')) + return true; + if (0 == strcasecmp($sValue, 'false')) + return false; + if (is_numeric($sValue)) + { + if (0 == $sValue) + return false; + return true; + } + return false; + } + + function argumentStripSlashes(&$sArg) + { + if (false == is_string($sArg)) + return; + + $sArg = stripslashes($sArg); + } + + function argumentDecodeXML(&$sArg) + { + if (false == is_string($sArg)) + return; + + if (0 == bytes($sArg)) + return; + + $nStackDepth = 0; + $aStack = array(); + $aArg = array(); + + $nCurrent = 0; + $nLast = 0; + $aExpecting = array(); + $nFound = 0; + list($aExpecting, $nFound) = $this->aSequence['start']; + + $nLength = bytes($sArg); + + $sKey = ''; + $mValue = ''; + + while ($nCurrent < $nLength) + { + $bFound = false; + + foreach ($aExpecting as $sExpecting => $nExpectedLength) + { + if ($sArg[$nCurrent] == $sExpecting[0]) + { + if ($sExpecting == cut_bytes($sArg, $nCurrent, $nExpectedLength)) + { + list($aExpecting, $nFound) = $this->aSequence[$sExpecting]; + + switch ($nFound) + { + case 3: // k + $sKey = ''; + break; + case 4: // /k + $sKey = str_replace( + array('<'.'![CDATA[', ']]>'), + '', + cut_bytes($sArg, $nLast, $nCurrent - $nLast) + ); + break; + case 5: // v + $mValue = ''; + break; + case 6: // /v + if ($nLast < $nCurrent) + { + $mValue = str_replace( + array('<'.'![CDATA[', ']]>'), + '', + cut_bytes($sArg, $nLast, $nCurrent - $nLast) + ); + + $cType = cut_bytes($mValue, 0, 1); + $sValue = cut_bytes($mValue, 1); + switch ($cType) { + case 'S': $mValue = false === $sValue ? '' : $sValue; break; + case 'B': $mValue = $this->convertStringToBool($sValue); break; + case 'N': $mValue = floatval($sValue); break; + case '*': $mValue = null; break; + } + } + break; + case 7: // /e + $aArg[$sKey] = $mValue; + break; + case 1: // xjxobj + ++$nStackDepth; + array_push($aStack, $aArg); + $aArg = array(); + array_push($aStack, $sKey); + $sKey = ''; + break; + case 8: // /xjxobj + if (1 < $nStackDepth) { + $mValue = $aArg; + $sKey = array_pop($aStack); + $aArg = array_pop($aStack); + --$nStackDepth; + } else { + $sArg = $aArg; + return; + } + break; + } + $nCurrent += $nExpectedLength; + $nLast = $nCurrent; + $bFound = true; + break; + } + } + } + + if (false == $bFound) + { + if (0 == $nCurrent) + { + $sArg = str_replace( + array('<'.'![CDATA[', ']]>'), + '', + $sArg + ); + + $cType = cut_bytes($sArg, 0, 1); + $sValue = cut_bytes($sArg, 1); + switch ($cType) { + case 'S': $sArg = false === $sValue ? '' : $sValue; break; + case 'B': $sArg = $this->convertStringToBool($sValue); break; + case 'N': $sArg = floatval($sValue); break; + case '*': $sArg = null; break; + } + + return; + } + +// for larger arg data, performance may suffer using concatenation +// $sText .= $sArg[$nCurrent]; + $nCurrent++; + } + } + + $objLanguageManager =& xajaxLanguageManager::getInstance(); + + trigger_error( + $objLanguageManager->getText('ARGMGR:ERR:01') + . $sExpecting + . $objLanguageManager->getText('ARGMGR:ERR:02') + . $sArg + , E_USER_ERROR + ); + } + + function argumentDecodeUTF8_iconv(&$mArg) + { + if (is_array($mArg)) + { + foreach (array_keys($mArg) as $sKey) + { + $sNewKey = $sKey; + $this->argumentDecodeUTF8_iconv($sNewKey); + + if ($sNewKey != $sKey) + { + $mArg[$sNewKey] = $mArg[$sKey]; + unset($mArg[$sKey]); + $sKey = $sNewKey; + } + + $this->argumentDecodeUTF8_iconv($mArg[$sKey]); + } + } + else if (is_string($mArg)) + $mArg = iconv("UTF-8", $this->sCharacterEncoding.'//TRANSLIT', $mArg); + } + + function argumentDecodeUTF8_mb_convert_encoding(&$mArg) + { + if (is_array($mArg)) + { + foreach (array_keys($mArg) as $sKey) + { + $sNewKey = $sKey; + $this->argumentDecodeUTF8_mb_convert_encoding($sNewKey); + + if ($sNewKey != $sKey) + { + $mArg[$sNewKey] = $mArg[$sKey]; + unset($mArg[$sKey]); + $sKey = $sNewKey; + } + + $this->argumentDecodeUTF8_mb_convert_encoding($mArg[$sKey]); + } + } + else if (is_string($mArg)) + $mArg = mb_convert_encoding($mArg, $this->sCharacterEncoding, "UTF-8"); + } + + function argumentDecodeUTF8_utf8_decode(&$mArg) + { + if (is_array($mArg)) + { + foreach (array_keys($mArg) as $sKey) + { + $sNewKey = $sKey; + $this->argumentDecodeUTF8_utf8_decode($sNewKey); + + if ($sNewKey != $sKey) + { + $mArg[$sNewKey] = $mArg[$sKey]; + unset($mArg[$sKey]); + $sKey = $sNewKey; + } + + $this->argumentDecodeUTF8_utf8_decode($mArg[$sKey]); + } + } + else if (is_string($mArg)) + $mArg = utf8_decode($mArg); + } + + /* + Constructor: xajaxArgumentManager + + Initializes configuration settings to their default values and reads + the argument data from the GET or POST data. + */ + function xajaxArgumentManager() + { + $this->aArgs = array(); + + $this->bDecodeUTF8Input = false; + $this->sCharacterEncoding = 'UTF-8'; + $this->nMethod = XAJAX_METHOD_UNKNOWN; + + $this->aSequence = array( + '<'.'k'.'>' => array(array( + '<'.'/k'.'>' => 4 + ), 3), + '<'.'/k'.'>' => array(array( + '<'.'v'.'>' => 3, + '<'.'/e'.'>' => 4 + ), 4), + '<'.'v'.'>' => array(array( + '<'.'xjxobj'.'>' => 8, + '<'.'/v'.'>' => 4 + ), 5), + '<'.'/v'.'>' => array(array( + '<'.'/e'.'>' => 4, + '<'.'k'.'>' => 3 + ), 6), + '<'.'e'.'>' => array(array( + '<'.'k'.'>' => 3, + '<'.'v'.'>' => 3, + '<'.'/e'.'>' => 4 + ), 2), + '<'.'/e'.'>' => array(array( + '<'.'e'.'>' => 3, + '<'.'/xjxobj'.'>' => 9 + ), 7), + '<'.'xjxobj'.'>' => array(array( + '<'.'e'.'>' => 3, + '<'.'/xjxobj'.'>' => 9 + ), 1), + '<'.'/xjxobj'.'>' => array(array( + '<'.'/v'.'>' => 4 + ), 8), + 'start' => array(array( + '<'.'xjxobj'.'>' => 8 + ), 9) + ); + + if (isset($_POST['xjxargs'])) { + $this->nMethod = XAJAX_METHOD_POST; + $this->aArgs = $_POST['xjxargs']; + } else if (isset($_GET['xjxargs'])) { + $this->nMethod = XAJAX_METHOD_GET; + $this->aArgs = $_GET['xjxargs']; + } + + if (1 == get_magic_quotes_gpc()) + array_walk($this->aArgs, array(&$this, 'argumentStripSlashes')); + + array_walk($this->aArgs, array(&$this, 'argumentDecodeXML')); + } + + /* + Function: getInstance + + Returns: + + object - A reference to an instance of this class. This function is + used to implement the singleton pattern. + */ + function &getInstance() + { + static $obj; + if (!$obj) { + $obj = new xajaxArgumentManager(); + } + return $obj; + } + + /* + Function: configure + + Accepts configuration settings from the main object. + + Parameters: + + + The tracks the following configuration settings: + + - (boolean): See bDecodeUTF8Input> + - (string): See sCharacterEncoding> + */ + function configure($sName, $mValue) + { + if ('decodeUTF8Input' == $sName) { + if (true === $mValue || false === $mValue) + $this->bDecodeUTF8Input = $mValue; + } else if ('characterEncoding' == $sName) { + $this->sCharacterEncoding = $mValue; + } + } + + /* + Function: getRequestMethod + + Returns the method that was used to send the arguments from the client. + */ + function getRequestMethod() + { + return $this->nMethod; + } + + /* + Function: process + + Returns the array of arguments that were extracted and parsed from + the GET or POST data. + */ + function process() + { + if ($this->bDecodeUTF8Input) + { + $sFunction = ''; + + if (function_exists('iconv')) + $sFunction = "iconv"; + else if (function_exists('mb_convert_encoding')) + $sFunction = "mb_convert_encoding"; + else if ($this->sCharacterEncoding == "ISO-8859-1") + $sFunction = "utf8_decode"; + else { + $objLanguageManager =& xajaxLanguageManager::getInstance(); + trigger_error( + $objLanguageManager->getText('ARGMGR:ERR:03') + , E_USER_NOTICE + ); + } + + $mFunction = array(&$this, 'argumentDecodeUTF8_' . $sFunction); + + array_walk($this->aArgs, $mFunction); + + $this->bDecodeUTF8Input = false; + } + + return $this->aArgs; + } +} diff --git a/xajax.php b/xajax.php new file mode 100644 index 0000000000..dfc0a6da34 --- /dev/null +++ b/xajax.php @@ -0,0 +1,166 @@ + + * @version $Id$ + */ + +// include our common functions, to have mbstring.func_overload functions available +require_once('./phpgwapi/inc/common_functions.inc.php'); +require_once('./phpgwapi/inc/xajax/xajax_core/xajax.inc.php'); + +/** + * callback if the session-check fails, redirects via xajax to login.php + * + * @param array &$anon_account anon account_info with keys 'login', 'passwd' and optional 'passwd_type' + * @return boolean/string true if we allow anon access and anon_account is set, a sessionid or false otherwise + */ +function xajax_redirect(&$anon_account) +{ + // now the header is included, we can set the charset + $GLOBALS['xajax']->configure('characterEncoding',translation::charset()); + define('XAJAX_DEFAULT_CHAR_ENCODING',translation::charset()); + + $response = new xajaxResponse(); + $response->redirect($GLOBALS['egw_info']['server']['webserver_url'].'/login.php?cd=10'); + $response->printOutput(); + + common::egw_exit(); +} + +/** + * Exception handler for xajax, return the message (and trace, if enabled) as alert() to the user + * + * Does NOT return! + * + * @param Exception $e + */ +function ajax_exception_handler(Exception $e) +{ + // logging all exceptions to the error_log + if (function_exists('_egw_log_exception')) + { + _egw_log_exception($e,$message); + } + $response = new xajaxResponse(); + $message .= ($message ? "\n\n" : '').$e->getMessage(); + + // only show trace (incl. function arguments) if explicitly enabled, eg. on a development system + if ($GLOBALS['egw_info']['server']['exception_show_trace']) + { + $message .= "\n\n".$e->getTraceAsString(); + } + $response->addAlert($message); + $response->printOutput(); + + if (is_object($GLOBALS['egw'])) + { + common::egw_exit(); + } + exit; +} + +// set our own exception handler, to not get the html from eGW's default one +set_exception_handler('ajax_exception_handler'); + +/** + * Callback called from xajax + * + * Includs the header and set's up the eGW enviroment. + * + * @return xajaxResponse object + */ +function doXMLHTTP() +{ + $numargs = func_num_args(); + if($numargs < 1) + return false; + + $argList = func_get_args(); + $arg0 = array_shift($argList); + + //error_log("xajax_doXMLHTTP('$arg0',...)".print_r($argList,true)); + + if (strpos($arg0,'::') !== false && strpos($arg0,'.') === false) // static method name app_something::method + { + @list($className,$functionName,$handler) = explode('::',$arg0); + list($appName) = explode('_',$className); + } + else + { + @list($appName, $className, $functionName, $handler) = explode('.',$arg0); + } + //error_log("xajax.php: appName=$appName, className=$className, functionName=$functionName, handler=$handler"); + + $GLOBALS['egw_info'] = array( + 'flags' => array( + 'currentapp' => $appName, + 'noheader' => True, + 'disable_Template_class' => True, + 'autocreate_session_callback' => 'xajax_redirect', + 'no_exception_handler' => true, // we already installed our own + 'no_dla_update' => $appName == 'notifications', // otherwise session never time out + ) + ); + include('./header.inc.php'); + + if(get_magic_quotes_gpc()) { + $argList = array_stripslashes($argList); + } + + // now the header is included, we can set the charset + $GLOBALS['xajax']->configure('characterEncoding',translation::charset()); + define('XAJAX_DEFAULT_CHAR_ENCODING',translation::charset()); + + switch($handler) + { + case '/etemplate/process_exec': + $_GET['menuaction'] = $appName.'.'.$className.'.'.$functionName; + $appName = $className = 'etemplate'; + $functionName = 'process_exec'; + $arg0 = 'etemplate.etemplate.process_exec'; + + $argList = array( + $argList[0]['etemplate_exec_id'], + $argList[0]['submit_button'], + $argList[0], + 'xajaxResponse', + ); + //error_log("xajax_doXMLHTTP() /etemplate/process_exec handler: arg0='$arg0', menuaction='$_GET[menuaction]'"); + break; + case 'etemplate': // eg. ajax code in an eTemplate widget + $arg0 = ($appName = 'etemplate').'.'.$className.'.'.$functionName; + break; + } + if(substr($className,0,4) != 'ajax' && substr($className,-4) != 'ajax' && + $arg0 != 'etemplate.etemplate.process_exec' && substr($functionName,0,4) != 'ajax' || + !preg_match('/^[A-Za-z0-9_]+(\.[A-Za-z0-9_]+\.|::)[A-Za-z0-9_]+$/',$arg0)) + { + // stopped for security reasons + error_log($_SERVER['PHP_SELF']. ' stopped for security reason. '.$arg0.' is not valid. class- or function-name must start with ajax!!!'); + // send message also to the user + throw new Exception($_SERVER['PHP_SELF']. ' stopped for security reason. '.$arg0.' is not valid. class- or function-name must start with ajax!!!'); + exit; + } + $ajaxClass =& CreateObject($appName.'.'.$className); + $argList = translation::convert($argList, 'utf-8'); + if ($arg0 == 'etemplate.link_widget.ajax_search' && count($argList)==7) + { + $first = array_shift($argList); + array_unshift($argList,''); + array_unshift($argList,$first); + } + //error_log("xajax_doXMLHTTP('$arg0',...)".print_r($argList,true)); + return call_user_func_array(array(&$ajaxClass, $functionName), (array)$argList ); +} +$xajax = new xajax(); +//$xajax->configure('requestURI',$_SERVER['PHP_SELF']); +$xajax->register(XAJAX_FUNCTION,'doXMLHTTP'); +$xajax->register(XAJAX_FUNCTION,'doXMLHTTP',array('mode' => "'synchronous'",'alias' => 'doXMLHTTPsync')); +$xajax->processRequest();