diff --git a/api/js/jsapi/egw_json.js b/api/js/jsapi/egw_json.js index b79517f718..64f2247b59 100644 --- a/api/js/jsapi/egw_json.js +++ b/api/js/jsapi/egw_json.js @@ -149,9 +149,19 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) this.egw.message.call(this.egw, this.egw.lang('A request to the EGroupware server returned with an error')+': '+_xmlhttp.statusText+' ('+_xmlhttp.status+ ")\n\n"+this.egw.lang('Please reload the EGroupware desktop (F5 / Cmd+r).')+"\n"+ this.egw.lang('If the error persists, contact your administrator for help and ask to check the error-log of the webserver.')+ - "\n\nURL: "+this.url+"\n"+(_xmlhttp.getAllResponseHeaders() ? _xmlhttp.getAllResponseHeaders().match(/^Date:.*$/m)[0]:'')); + "\n\nURL: "+this.url+"\n"+(_xmlhttp.getAllResponseHeaders() ? _xmlhttp.getAllResponseHeaders().match(/^Date:.*$/mi)[0]:'')+ + // if EGroupware send JSON payload with error, errno show it here too + (_err === 'error' && _xmlhttp.status === 400 && typeof _xmlhttp.responseJSON === 'object' && _xmlhttp.responseJSON.error ? + "\nError: "+_xmlhttp.responseJSON.error+' ('+_xmlhttp.responseJSON.errno+')' : '')); - this.egw.debug('error', 'Ajax request to', this.url, ' failed: ', _err, _xmlhttp.status, _xmlhttp.statusText); + this.egw.debug('error', 'Ajax request to', this.url, ' failed: ', _err, _xmlhttp.status, _xmlhttp.statusText, _xmlhttp.responseJSON); + + // check of unparsable JSON on server-side, which might be caused by some network problem --> resend max. twice + if (_err === 'error' && _xmlhttp.status === 400 && typeof _xmlhttp.responseJSON === 'object' && + _xmlhttp.responseJSON.errno && _xmlhttp.responseJSON.error.substr(0, 5) === 'JSON ') + { + // ToDo: resend request max. twice + } } }; diff --git a/api/src/Json/Request.php b/api/src/Json/Request.php index f913ee076b..3a2ace84f9 100644 --- a/api/src/Json/Request.php +++ b/api/src/Json/Request.php @@ -54,23 +54,36 @@ class Request * * @param string menuaction to call * @param string $input_data is the RAW input data as it was received from the client + * @throws \InvalidArgumentException if JSON can not be parsed (json_last_error()) + * or did not contain request[parameters] array (999) */ public function parseRequest($menuaction, $input_data) { // Remember that we currently are in a JSON request - e.g. used in the redirect code self::$_hadJSONRequest = true; - if (get_magic_quotes_gpc()) $input_data = stripslashes($input_data); - - $json_data = json_decode($input_data,true); - if (is_array($json_data) && isset($json_data['request']) && isset($json_data['request']['parameters']) && is_array($json_data['request']['parameters'])) + // no or empty payload is eg. used by dynamicly loading tree nodes (uses just GET parameters) + if (!isset($input_data) || $input_data === '') { - //error_log(__METHOD__.__LINE__.array2string($json_data['request']).function_backtrace()); - $parameters =& $json_data['request']['parameters']; + $parameters = array(); } else { - $parameters = array(); + if (get_magic_quotes_gpc()) $input_data = stripslashes($input_data); + + if (($json_data = json_decode($input_data,true)) === null && json_last_error() !== JSON_ERROR_NONE) + { + throw new \InvalidArgumentException('JSON '.json_last_error_msg(), json_last_error()); + } + elseif (is_array($json_data) && isset($json_data['request']) && isset($json_data['request']['parameters']) && is_array($json_data['request']['parameters'])) + { + //error_log(__METHOD__.__LINE__.array2string($json_data['request']).function_backtrace()); + $parameters =& $json_data['request']['parameters']; + } + else + { + throw new \InvalidArgumentException('Missing request:parameters object', 999); + } } // do we have a single request or an array of queued requests if ($menuaction == 'api.queue') diff --git a/json.php b/json.php index 4410cf702f..859572a2a1 100644 --- a/json.php +++ b/json.php @@ -70,8 +70,11 @@ function ajax_exception_handler($e) // set our own exception handler, to not get the html from eGW's default one set_exception_handler('ajax_exception_handler'); -if (isset($_GET['menuaction'])) -{ +try { + if (!isset($_GET['menuaction'])) + { + throw new InvalidArgumentException('Missing menuaction GET parameter', 998); + } if (strpos($_GET['menuaction'],'::') !== false && strpos($_GET['menuaction'],'.') === false) // static method name app_something::method { @list($className,$functionName,$handler) = explode('::',$_GET['menuaction']); @@ -127,5 +130,13 @@ if (isset($_GET['menuaction'])) Json\Response::get(); exit(); } +// missing menuaction GET parameter or request:parameters object or unparsable JSON +catch (\InvalidArgumentException $e) { + if (isset($json)) $json->isJSONRequest(false); // no regular json request processing -throw new Json\Exception($_SERVER['PHP_SELF'] . ' Invalid AJAX JSON Request'); + // give a proper HTTP status 400 Bad Request with some JSON payload explaining the problem + http_response_code(400); + header('Content-Type: application/json'); + echo json_encode(array('error' => $e->getMessage(), 'errno' => $e->getCode())); +} +// other exceptions are handled by our ajax_exception_handler sending them back as alerts to client-side \ No newline at end of file