

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.0.0.170915_Beta4 (15 September 2017)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2017
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
#include "RESTAPI.h"

namespace cmlabs {

RESTParser::RESTParser() {
	mainNode = XMLNode::emptyXMLNode;
}
RESTParser::~RESTParser() {
}


bool RESTParser::init(const char* restDefinition) {

	lastErrorString.clear();
	XMLResults xmlResults;
	mainNode=XMLNode::parseString(restDefinition,"restapi", &xmlResults);

	if (xmlResults.error != eXMLErrorNone) {
		if (xmlResults.error == eXMLErrorFirstTagNotFound) {
			lastErrorString = utils::StringFormat("REST API Definition format error, main tag <restapi> not found");
		}
		else {
			lastErrorString = utils::StringFormat("REST API Definition parsing error at line %i, column %i:\n   %s\n",
				xmlResults.nLine,xmlResults.nColumn, XMLNode::getError(xmlResults.error));
		}
		mainNode = XMLNode::emptyXMLNode;
		return false;
	}
	if(mainNode.isEmpty()) {
		lastErrorString = utils::StringFormat("REST API Definition format error, main tag <restapi> empty");
		mainNode = XMLNode::emptyXMLNode;
		return false;
	}

	// read any defined objects
	std::map<std::string, RESTParam> paramMap;
	XMLNode node;
	const char* name;
	uint32 i, count = mainNode.nChildNode("paramset");
	for (i = 0; i < count; i++) {
		if (!(node = mainNode.getChildNode("paramset", i)).isEmpty()) {
			if (!(name = node.getAttribute("name")) || !strlen(name))
				continue;
			paramMap = parseParamSet(node);
			if (paramMap.size())
				paramSets[name] = paramMap;
		}
	}

	return (lastErrorString.length() == 0);
}


std::map<std::string, RESTParam> RESTParser::parseParamSet(XMLNode objectNode) {
	std::map<std::string, RESTParam> paramMap;
	const char* optional, *type, *name, *objects, *allowEmpty;
	RESTParam restParam;
	XMLNode node;
	uint32 i, count = objectNode.nChildNode("param");
	for (i = 0; i < count; i++) {
		if (!(node = objectNode.getChildNode("param", i)).isEmpty()) {
			type = node.getAttribute("type");
			name = node.getAttribute("name");
			optional = node.getAttribute("optional");
			allowEmpty = node.getAttribute("allowempty");
			objects = node.getAttribute("objects");

			if (!type || !name || !strlen(name)) {
				lastErrorString = utils::StringFormat("REST API Definition format error, <param> tag name or type error: %s", name);
				break;
			}

			if (strcmp(type, "integer") == 0) restParam.type = RESTParam::INTEGER;
			else if (strcmp(type, "text") == 0) restParam.type = RESTParam::TEXT;
			else if (strcmp(type, "float") == 0) restParam.type = RESTParam::FLOAT;
			else if (strcmp(type, "double") == 0) restParam.type = RESTParam::FLOAT;
			else if (strcmp(type, "array") == 0) restParam.type = RESTParam::ARRAY;
			else if (strcmp(type, "image") == 0) restParam.type = RESTParam::IMAGE;
			else if (strcmp(type, "video") == 0) restParam.type = RESTParam::VIDEO;
			else if (strcmp(type, "binary") == 0) restParam.type = RESTParam::BINARY;
			else {
				lastErrorString = utils::StringFormat("REST API Definition format error, <param> type error: %s", type);
				break;
			}

			restParam.optional = false | (optional && strcmp(optional, "yes") == 0);
			restParam.allowEmpty = false | (allowEmpty && strcmp(allowEmpty, "yes") == 0);
			if (objects && strlen(objects)) restParam.objects = objects; else restParam.objects.clear();
			restParam.name = name;
			paramMap[restParam.name] = restParam;
		}
	}
	return paramMap;
}

RESTRequest* RESTParser::parseRequest(HTTPRequest* req) {
	RESTRequest* restReq = new RESTRequest();
	std::map<std::string, RESTParam> paramSet;
	std::string signature;

//	XMLNode node = parseRequest(req->getRequest(), paramSet, restReq, signature);
	XMLNode node = parseRequest(req->getURI(), paramSet, restReq, signature);
	if (node.isEmpty())
		return restReq;

	const char* auth;
	if (restReq->getRequireAuthentication()) {
		if (!(auth = req->getHeaderEntry("Authorization")) || !strlen(auth)) {
			restReq->addToErrorString(utils::StringFormat("No authentication provided, required for this operation").c_str());
			restReq->setStatus(RESTRequest::FAILED_AUTH);
			return restReq;
		}
		restReq->setString("Authentication", auth);
	}

	// Now the node is the one at the correct level
	// and if an id we have the id (int or text) in the restReq already
	const char* ops = node.getAttribute("ops");

	if ( (req->type == HTTP_GET) && (utils::stristr(ops, "get")) ) {
		// Dont expect any more parameters, we are fine now
		signature = utils::StringFormat("GET%s", signature.c_str());
		restReq->setRequestSignature(signature.c_str());
		restReq->setStatus(RESTRequest::SUCCESS);
		return restReq;
	}
	else if ( (req->type == HTTP_POST) && (utils::stristr(ops, "post")) ) {
		// A post needs the parameters described in paramSet, check if they are there
		if (!extractParameters(req, paramSet, restReq)) {
			restReq->setStatus(RESTRequest::FAILED_PARAM);
			return restReq;
		}
		signature = utils::StringFormat("POST%s", signature.c_str());
		restReq->setRequestSignature(signature.c_str());
		restReq->setStatus(RESTRequest::SUCCESS);
		return restReq;
	}
	else if ( (req->type == HTTP_PUT) && (utils::stristr(ops, "put")) ) {
		// A put needs an ID (already there) and the parameters described in paramSet, check if they are there
		if (!extractParameters(req, paramSet, restReq)) {
			// If they are not all there, that's ok, only the updating ones need be there
			//restReq->setStatus(RESTRequest::FAILED_PARAM);
			//return restReq;
		}
		signature = utils::StringFormat("PUT%s", signature.c_str());
		restReq->setRequestSignature(signature.c_str());
		restReq->setStatus(RESTRequest::SUCCESS);
		return restReq;
	}
	else if ( (req->type == HTTP_DELETE) && (utils::stristr(ops, "delete")) ) {
		// Dont expect any more parameters, we are fine now
		signature = utils::StringFormat("DELETE%s", signature.c_str());
		restReq->setRequestSignature(signature.c_str());
		restReq->setStatus(RESTRequest::SUCCESS);
		return restReq;
	}
	else if (req->type == HTTP_OPTIONS) {
		//restReq->addToErrorString(utils::StringFormat("Operation type %s not supported by the requested API", HTTP_Type[req->type]).c_str());
		signature = ops;
		restReq->setStatus(RESTRequest::SUCCESS_OPTIONS);
		return restReq;
	}
	else {
		restReq->addToErrorString(utils::StringFormat("Operation type %s not supported by the requested API", HTTP_Type[req->type]).c_str());
		restReq->setStatus(RESTRequest::FAILED_FORMAT);
		return restReq;
	}
}

RESTRequest* RESTParser::parseRequest(DataMessage* msg) {
	RESTRequest* restReq = new RESTRequest();
	std::map<std::string, RESTParam> paramSet;
	std::string signature;

//	XMLNode node = parseRequest(msg->getString("REQUEST"), paramSet, restReq, signature);
	XMLNode node = parseRequest(msg->getString("URI"), paramSet, restReq, signature);
	if (node.isEmpty())
		return restReq;

	int64 httpOperation = 0;
	msg->getInt("HTTP_OPERATION", httpOperation);

	const char* auth;
	if (restReq->getRequireAuthentication()) {
		if (!(auth = msg->getString("Authorization")) || !strlen(auth)) {
			restReq->addToErrorString(utils::StringFormat("No authentication provided, required for this operation").c_str());
			restReq->setStatus(RESTRequest::FAILED_AUTH);
			return restReq;
		}
		restReq->setString("Authentication", auth);
	}

	// Now the node is the one at the correct level
	// and if an id we have the id (int or text) in the restReq already
	const char* ops = node.getAttribute("ops");

	if ( (httpOperation == HTTP_GET) && (utils::stristr(ops, "get")) ) {
		// Dont expect any more parameters, we are fine now
		signature = utils::StringFormat("GET%s", signature.c_str());
		restReq->setRequestSignature(signature.c_str());
		restReq->setStatus(RESTRequest::SUCCESS);
		return restReq;
	}
	else if ( (httpOperation == HTTP_POST) && (utils::stristr(ops, "post")) ) {
		// A post needs the parameters described in paramSet, check if they are there
		if (!extractParameters(msg, paramSet, restReq)) {
			restReq->setStatus(RESTRequest::FAILED_PARAM);
			return restReq;
		}
		signature = utils::StringFormat("POST%s", signature.c_str());
		restReq->setRequestSignature(signature.c_str());
		restReq->setStatus(RESTRequest::SUCCESS);
		return restReq;
	}
	else if ( (httpOperation == HTTP_PUT) && (utils::stristr(ops, "put")) ) {
		// A put needs an ID (already there) and the parameters described in paramSet, check if they are there
		if (!extractParameters(msg, paramSet, restReq)) {
			// If they are not all there, that's ok, only the updating ones need be there
			//restReq->setStatus(RESTRequest::FAILED_PARAM);
			//return restReq;
		}
		signature = utils::StringFormat("PUT%s", signature.c_str());
		restReq->setRequestSignature(signature.c_str());
		restReq->setStatus(RESTRequest::SUCCESS);
		return restReq;
	}
	else if ( (httpOperation == HTTP_DELETE) && (utils::stristr(ops, "delete")) ) {
		// Dont expect any more parameters, we are fine now
		signature = utils::StringFormat("DELETE%s", signature.c_str());
		restReq->setRequestSignature(signature.c_str());
		restReq->setStatus(RESTRequest::SUCCESS);
		return restReq;
	}
	else if (httpOperation == HTTP_OPTIONS) {
		//restReq->addToErrorString(utils::StringFormat("Operation type %s not supported by the requested API", HTTP_Type[req->type]).c_str());
		restReq->setRequestSignature(ops);
		restReq->setStatus(RESTRequest::SUCCESS_OPTIONS);
		return restReq;
	}
	else {
		restReq->addToErrorString(utils::StringFormat("Operation type %s not supported by the requested API", HTTP_Type[httpOperation]).c_str());
		restReq->setStatus(RESTRequest::FAILED_FORMAT);
		return restReq;
	}
}





XMLNode RESTParser::parseRequest(const char* requestString, std::map<std::string, RESTParam> &paramSet, RESTRequest* restReq, std::string &signature) {

	XMLNode emptyNode = XMLNode::emptyNode();

	if (mainNode.isEmpty()) {
		restReq->addToErrorString(utils::StringFormat("API not initialised").c_str());
		restReq->setStatus(RESTRequest::FAILED_DESCRIPTION);
		return emptyNode;
	}

	if (!requestString || !strlen(requestString)) {
		restReq->addToErrorString(utils::StringFormat("Empty request provided").c_str());
		restReq->setStatus(RESTRequest::FAILED_EMPTY);
		return emptyNode;
	}

	std::string filterString;
	std::string requestPart;
	const char* filterDiv = strstr(requestString, "?");
	if (filterDiv) {
		filterString = filterDiv+1;
		requestPart = utils::TextListSplit(requestString, "?", true, true).at(0);
	}
	else
		requestPart = requestString;

	std::vector<std::string> reqParts = utils::TextListSplit(requestPart.c_str(), "/", false, true);

	XMLNode topNode = findTopNodeForRequest(reqParts.at(0).c_str());
	if (topNode.isEmpty()) {
		restReq->addToErrorString(utils::StringFormat("Requested API is not known: %s", reqParts.at(0).c_str()).c_str());
		restReq->setStatus(RESTRequest::FAILED_UNKNOWN_REQUEST);
		return emptyNode;
	}

	if (filterString.length()) {
		std::multimap<std::string, std::string> paramMap = utils::TextMultiMapSplit(filterString.c_str(), "&", "=");
		std::multimap<std::string, std::string>::iterator ip = paramMap.begin(), ep = paramMap.end();
		while (ip != ep) {
			//restReq->setFilter(ip->first.c_str(), ip->second.c_str());
			restReq->setFilter(utils::TextTrimQuotes(html::DecodeHTML(ip->first).c_str()).c_str(), utils::TextTrimQuotes(html::DecodeHTML(ip->second).c_str()).c_str());
			ip++;
		}
	}

	const char* tag, *type, *name, *subName, *set, *auth, *subapi;
	XMLNode node = topNode, subNode;
	uint32 level = 0;
	int subNodeCount = 0, i;

	//signature.clear();

	while (level < reqParts.size()) {
		// first read any params available
		if (!(name = node.getAttribute("name")) || !strlen(name)) {
			restReq->addToErrorString(utils::StringFormat("API description error: Node name not provided").c_str());
			restReq->setStatus(RESTRequest::FAILED_DESCRIPTION);
			return emptyNode;
		}

		if (set = node.getAttribute("paramset")) {
			if (!strlen(set))
				paramSet.clear();
			else if (paramSets.find(set) != paramSets.end())
				paramSet = paramSets[set];
			else {
				restReq->addToErrorString(utils::StringFormat("API description error: Paramset %s unknown", set).c_str());
				restReq->setStatus(RESTRequest::FAILED_DESCRIPTION);
				return emptyNode;
			}
		}

		if ((auth = node.getAttribute("auth")) && stricmp(auth, "yes") == 0)
			restReq->setRequireAuthentication(true);

		if (!(tag = node.getName()) || !strlen(tag)) {
			restReq->addToErrorString(utils::StringFormat("API description error: Empty tag name").c_str());
			restReq->setStatus(RESTRequest::FAILED_DESCRIPTION);
			return emptyNode;
		}

		if (strcmp(tag, "id") == 0) {
			if (strcmp(type, "integer") == 0) {
				restReq->setInt(name, utils::Ascii2Int64(reqParts.at(level).c_str()));
				signature = utils::StringFormat("%s_IDINTEGER", signature.c_str());
			}
			else if (strcmp(type, "text") == 0) {
				restReq->setString(name, reqParts.at(level).c_str());
				signature = utils::StringFormat("%s_IDTEXT", signature.c_str());
			}
		}
		else if (strcmp(tag, "api") == 0) {
			signature = utils::StringFormat("%s_%s", signature.c_str(), reqParts.at(level).c_str());
		}

		// Check for subapi nodes
		// For now we assume subapi="*", may in the future restrict to list of subapis
		if ((subapi = node.getAttribute("subapi")) && strlen(subapi)) {
			// create a new request with just the sub part from here
			std::string newRequest;
			std::string oldSignature = signature;
			for (uint32 n=level+1; n<reqParts.size(); n++) {
				if (n == level+1)
					newRequest = reqParts.at(n);
				else
					newRequest = utils::StringFormat("%s/%s", newRequest.c_str(), reqParts.at(n).c_str());
			}
			XMLNode subapiNode = parseRequest(newRequest.c_str(), paramSet, restReq, signature);
			if (!subapiNode.isEmpty())
				return subapiNode;
			else {
				signature = oldSignature;
				restReq->clearErrorState();
			}
		}

		if (level == reqParts.size() - 1)
			break;

		if (node.nChildNode("id") > 0) {
			if (!(subNode=node.getChildNode("id")).isEmpty()) {
				if (!(type = subNode.getAttribute("type"))) {
					restReq->addToErrorString(utils::StringFormat("API description error: ID type not provided").c_str());
					restReq->setStatus(RESTRequest::FAILED_DESCRIPTION);
					return emptyNode;
				}
				else if (strcmp(type, "integer") == 0) {
					// check if next part is integer
					if (!utils::IsTextNumeric(reqParts.at(level+1).c_str())) {
						restReq->addToErrorString(utils::StringFormat("API ID %s provided is not numeric", subNode.getAttribute("name")).c_str());
						restReq->setStatus(RESTRequest::FAILED_FORMAT);
						return emptyNode;
					}
					level++;
					node = subNode;
					continue;
				}
				else if (strcmp(type, "text") == 0) {
					// check if next part is text
					if (!reqParts.at(level+1).length()) {
						restReq->addToErrorString(utils::StringFormat("API ID %s provided is empty", subNode.getAttribute("name")).c_str());
						restReq->setStatus(RESTRequest::FAILED_FORMAT);
						return emptyNode;
					}
					level++;
					node = subNode;
					continue;
				}
				else {
					restReq->addToErrorString(utils::StringFormat("API description error: Type %s not supported", type).c_str());
					restReq->setStatus(RESTRequest::FAILED_DESCRIPTION);
					return emptyNode;
				}
			}
			else {
					restReq->addToErrorString(utils::StringFormat("API description error: No ID node found").c_str());
				restReq->setStatus(RESTRequest::FAILED_DESCRIPTION);
				return emptyNode;
			}
		}
		else if ( (subNodeCount = node.nChildNode("api")) > 0) {
			// There might be multiple api child nodes, we need to find the one with name = reqParts.at(level)
			for (i = 0; i < subNodeCount; i++) {
				if (!(subNode=node.getChildNode("api", i)).isEmpty() && (subName = subNode.getAttribute("name")) && (stricmp(reqParts.at(level+1).c_str(), subName) == 0)) {
					level++;
					node = subNode;
					break;
				}
			}
			if (i == subNodeCount) {
				// If we haven't found a matching subnode
				restReq->addToErrorString(utils::StringFormat("API description error: No API node found").c_str());
				restReq->setStatus(RESTRequest::FAILED_DESCRIPTION);
				return emptyNode;
			}
			else {
				// We did find a matching subnode
				continue;
			}
		}
		else {
			restReq->addToErrorString(utils::StringFormat("API error: No API found for signature %s_%s", signature.c_str(), reqParts.at(level+1).c_str()).c_str());
			restReq->setStatus(RESTRequest::FAILED_UNKNOWN_REQUEST);
			return emptyNode;
		}
	}
	return node;
}









bool RESTParser::extractParameters(HTTPRequest* req, std::map<std::string, RESTParam>& paramSet, RESTRequest* restReq) {
	// First find out whether the parameters are stored inside the HTTPRequest structure or as JSON/XML content
	uint32 size;
	const char* postType = req->getPostDataType("POST");
	if (!postType) {
		postType = req->getHeaderEntry("Content-Type");
	}
	const char* val, *data, *json = NULL, *xml = NULL;

	jsmn_parser parser;
	jsmntok_t tokens[256];
	int tokenCount = 0;
	int valSize;
	XMLResults xmlResults;
	XMLNode paramNode, node;

	//if (utils::stristr(req->getHeaderEntry("Content-Type"), "multipart/form-data")) {
	//	// parse all non-POST entries by name
	// #############################

	//}

	// POST entry contains JSON
	if (utils::stristr(postType, "json")) {
		if (!(json = req->getPostData("POST", size)) || !size) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: No JSON POST found").c_str());
			return false;
		}
		jsmn_init(&parser);
		if ( (tokenCount = jsmn_parse(&parser, json, size, tokens, 256)) <= 0) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Error parsing JSON").c_str());
			return false;
		}
	}
	// JSON entry contains JSON
	else if ((json = req->getPostData("JSON", size)) && size) {
		jsmn_init(&parser);
		if ( (tokenCount = jsmn_parse(&parser, json, size, tokens, 256)) <= 0) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Error parsing JSON").c_str());
			return false;
		}
	}
	// POST entry contains XML
	else if (utils::stristr(postType, "xml")) {
		if (!(xml = req->getPostData("POST", size)) || !size) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: No XML POST found").c_str());
			return false;
		}
		paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
		if ((xmlResults.error != eXMLErrorNone) || (paramNode.isEmpty())) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Error parsing XML").c_str());
			return false;
		}
	}
	// XML entry contains XML
	else if ((xml = req->getPostData("XML", size)) && size) {
		paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
		if ((xmlResults.error != eXMLErrorNone) || (paramNode.isEmpty())) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Error parsing XML").c_str());
			return false;
		}
	}
	// POST entry contains something and content type doesn't specify
	else if ((val = req->getPostData("POST", size)) && size) {
		// first try to see if it is JSON
		jsmn_init(&parser);
		if ( (tokenCount = jsmn_parse(&parser, val, size, tokens, 256)) > 0) {
			// great, we found valid JSON
			json = val;
		}
		// and if not then try XML
		else {
			paramNode=XMLNode::parseString(val, NULL, &xmlResults);
			if ((xmlResults.error != eXMLErrorNone) || (paramNode.isEmpty())) {
				restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Cannot find either JSON or XML content").c_str());
				return false;
			}
			else {
				// great, we found valid XML
				xml = val;
			}
		}
	}

	uint32 errorCount = 0;
	std::string value;
	std::map<std::string, RESTParam>::iterator i = paramSet.begin(), e = paramSet.end();
	while (i != e) {
		value.clear();
		if (json) {
			if (!(val = GetJSONValue(tokens, tokenCount, json, i->second.name.c_str(), JSMN_UNDEFINED, valSize))) {
				if (!i->second.optional) {
					restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Parameter %s not found in JSON", i->second.name.c_str()).c_str());
					errorCount++;
					i++;
					continue;
				}
			}
			else if (!valSize && !i->second.allowEmpty) {
				restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Parameter %s empty in JSON, not allowed", i->second.name.c_str()).c_str());
				// Continue anyway, but log the error
				value.clear();
				errorCount++;
			}
			else
				value = std::string(val, valSize);
		}
		else if (xml) {
			// #############################
			return false;
			//node = paramNode.
			//if (!(val = extractXMLValue(req->getPostData("POST", size), i->second.name.c_str(), size)) || !size)
			//	return false;
		}
		else if (utils::stristr(postType, "form")) { // both multipart/form-data and application/x-www-form-urlencoded
			if (i->second.type == RESTParam::IMAGE || i->second.type == RESTParam::VIDEO || i->second.type == RESTParam::BINARY) {
				if (!(data = req->getPostData(i->second.name.c_str(), size)) || !size) {
					restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Binary parameter %s not found in form", i->second.name.c_str()).c_str());
					errorCount++;
					i++;
					continue;
				}
				// and now get the content-type
				if (!(val = req->getPostDataType(i->second.name.c_str()))) {
					restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Binary parameter %s content type not found in form", i->second.name.c_str()).c_str());
					errorCount++;
					i++;
					continue;
				}
				restReq->setBinary(i->second.name.c_str(), data, size, val);
				i++;
				continue;
			}
			else {
				if (!(val = req->getPostData(i->second.name.c_str(), size)) || !size) {
					if (!i->second.optional && (i->second.type != RESTParam::ARRAY)) {
						restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Parameter %s not found in form", i->second.name.c_str()).c_str());
						errorCount++;
						i++;
						continue;
					}
					else if (i->second.type != RESTParam::ARRAY) {
						i++;
						continue;
					}
				}
				else
					value = val;
			}
		}
		else {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: No form, JSON or XML found in request", i->second.name.c_str()).c_str());
			return false;
		}
		uint32 num;
		if (i->second.type == RESTParam::ARRAY) {
			std::map<std::string, RESTParam> arrayParamSet = paramSets[i->second.objects];
			if (!arrayParamSet.size()) {
				restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Array objects %s unknown", i->second.objects.c_str()).c_str());
				return false;
			}
			if (json) {
				// call with sub node 1, 2, ...
				num = 1;
				std::vector<int> jsonArrayIdx = GetJSONArrayIndexes(tokens, tokenCount, json, i->second.name.c_str(), JSMN_UNDEFINED);
				std::vector<int>::iterator ia = jsonArrayIdx.begin(), ea = jsonArrayIdx.end();
				while (ia != ea) {
					value = std::string(json+tokens[*ia].start, tokens[*ia].end - tokens[*ia].start);
					// call array function...
					extractArrayParameters(req, arrayParamSet, restReq, num, value.c_str(), (uint32)value.length(), "json");
					num++;
					ia++;
				}
			}
			else if (xml) {
				// call with subnode 1, 2, ...
				return false;
			}
			else if (utils::stristr(postType, "form")) { // both multipart/form-data and application/x-www-form-urlencoded
				num = 1;
				while (extractArrayParameters(req, arrayParamSet, restReq, num, NULL, 0, NULL))
					num++;
				if (num > 1)
					restReq->clearErrorState();
				// call self with param?
				// setArrayValuesPOST();
			}
		}
		else if (value.length()) {
			if (!setParameterValue(req, i->second, value, restReq))
				return false;
		}
		i++;
	}
	return (errorCount == 0);
}






bool RESTParser::extractParameters(DataMessage* msg, std::map<std::string, RESTParam>& paramSet, RESTRequest* restReq) {
	// First find out whether the parameters are stored inside the HTTPRequest structure or as JSON/XML content
	uint32 size, size2;

	const char* postType = msg->getString("POST_CONTENT_TYPE_");
	const char* val, *data, *json = NULL, *xml = NULL;

	jsmn_parser parser;
	jsmntok_t tokens[256];
	int tokenCount = 0;
	int valSize;
	XMLResults xmlResults;
	XMLNode paramNode, node;

	// POST entry contains JSON
	if (utils::stristr(postType, "json")) {
		if (!(json = msg->getString("POST", size)) || !size) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: No JSON POST found").c_str());
			return false;
		}
		jsmn_init(&parser);
		if ( (tokenCount = jsmn_parse(&parser, json, size, tokens, 256)) <= 0) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Error parsing JSON").c_str());
			return false;
		}
	}
	// JSON entry contains JSON
	else if ((json = msg->getString("JSON", size)) && size) {
		jsmn_init(&parser);
		if ( (tokenCount = jsmn_parse(&parser, json, size, tokens, 256)) <= 0) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Error parsing JSON").c_str());
			return false;
		}
	}
	// POST entry contains XML
	else if (utils::stristr(postType, "xml")) {
		if (!(xml = msg->getString("POST", size)) || !size) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: No XML POST found").c_str());
			return false;
		}
		paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
		if ((xmlResults.error != eXMLErrorNone) || (paramNode.isEmpty())) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Error parsing XML").c_str());
			return false;
		}
	}
	// XML entry contains XML
	else if ((xml = msg->getString("XML", size)) && size) {
		paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
		if ((xmlResults.error != eXMLErrorNone) || (paramNode.isEmpty())) {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Error parsing XML").c_str());
			return false;
		}
	}
	// POST entry contains something and content type doesn't specify
	else if ((val = msg->getString("POST", size)) && size) {
		// first try to see if it is JSON
		jsmn_init(&parser);
		if ( (tokenCount = jsmn_parse(&parser, val, size, tokens, 256)) > 0) {
			// great, we found valid JSON
			json = val;
		}
		// and if not then try XML
		else {
			paramNode=XMLNode::parseString(val, NULL, &xmlResults);
			if ((xmlResults.error != eXMLErrorNone) || (paramNode.isEmpty())) {
				restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Cannot find either JSON or XML content").c_str());
				return false;
			}
			else {
				// great, we found valid XML
				xml = val;
			}
		}
	}

	uint32 errorCount = 0;
	std::string value;
	std::map<std::string, RESTParam>::iterator i = paramSet.begin(), e = paramSet.end();
	while (i != e) {
		value.clear();
		if (json) {
			if (!(val = GetJSONValue(tokens, tokenCount, json, i->second.name.c_str(), JSMN_UNDEFINED, valSize))) {
				if (!i->second.optional) {
					restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Parameter %s not found in JSON", i->second.name.c_str()).c_str());
					errorCount++;
					i++;
					continue;
				}
			}
			else if (!valSize && !i->second.allowEmpty) {
				restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Parameter %s empty in JSON, not allowed", i->second.name.c_str()).c_str());
				// Continue anyway, but log the error
				value.clear();
				errorCount++;
			}
			else
				value = std::string(val, valSize);
		}
		else if (xml) {
			// #############################
			return false;
			//node = paramNode.
			//if (!(val = extractXMLValue(req->getPostData("POST", size), i->second.name.c_str(), size)) || !size)
			//	return false;
		}
		else if (!postType || utils::stristr(postType, "form")) { // both multipart/form-data and application/x-www-form-urlencoded
			if (i->second.type == RESTParam::IMAGE || i->second.type == RESTParam::VIDEO || i->second.type == RESTParam::BINARY) {
				if (!(data = msg->getData(i->second.name.c_str(), size)) || !size) {
					restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Binary parameter %s not found in form", i->second.name.c_str()).c_str());
					errorCount++;
					i++;
					continue;
				}
				// and now get the content-type
				if (!(val = msg->getString((i->second.name + "_CONTENT_TYPE_").c_str(), size2)) || !size2) {
					restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Binary parameter %s content type not found in form", i->second.name.c_str()).c_str());
					errorCount++;
					i++;
					continue;
				}
				restReq->setBinary(i->second.name.c_str(), data, size, val);
				i++;
				continue;
			}
			else {
				if (!(val = msg->getString(i->second.name.c_str(), size)) || !size) {
					if (!i->second.optional && (i->second.type != RESTParam::ARRAY)) {
						restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Parameter %s not found in form", i->second.name.c_str()).c_str());
						errorCount++;
						i++;
						continue;
					}
					else if (i->second.type != RESTParam::ARRAY) {
						i++;
						continue;
					}
				}
				else
					value = val;
			}
		}
		else {
			restReq->addToErrorString(utils::StringFormat("Parameter extraction error: No form, JSON or XML found in request", i->second.name.c_str()).c_str());
			return false;
		}

		uint32 num;
		if (i->second.type == RESTParam::ARRAY) {
			std::map<std::string, RESTParam> arrayParamSet = paramSets[i->second.objects];
			if (!arrayParamSet.size()) {
				restReq->addToErrorString(utils::StringFormat("Parameter extraction error: Array objects %s unknown", i->second.objects.c_str()).c_str());
				return false;
			}
			if (json) {
				// call with sub node 1, 2, ...
				num = 1;
				std::vector<int> jsonArrayIdx = GetJSONArrayIndexes(tokens, tokenCount, json, i->second.name.c_str(), JSMN_UNDEFINED);
				std::vector<int>::iterator ia = jsonArrayIdx.begin(), ea = jsonArrayIdx.end();
				while (ia != ea) {
					value = std::string(json+tokens[*ia].start, tokens[*ia].end - tokens[*ia].start);
					// call array function...
					extractArrayParameters(msg, arrayParamSet, restReq, num, value.c_str(), (uint32)value.length(), "json");
					num++;
					ia++;
				}
			}
			else if (xml) {
				// call with subnode 1, 2, ...
				return false;
			}
			else if (!postType || utils::stristr(postType, "form")) { // both multipart/form-data and application/x-www-form-urlencoded
				num = 1;
				while (extractArrayParameters(msg, arrayParamSet, restReq, num, NULL, 0, NULL))
					num++;
				if (num > 1)
					restReq->clearErrorState();
				// call self with param?
				// setArrayValuesPOST();
			}
		}
		else if (value.length()) {
			if (!setParameterValue(msg, i->second, value, restReq))
				return false;
		}
		i++;
	}
	return (errorCount == 0);
}







bool RESTParser::extractArrayParameters(HTTPRequest* req, std::map<std::string, RESTParam>& paramSet, RESTRequest* restReq, int entryNum, const char* content, uint32 contentSize, const char* contentType) {
	// Called from extractParameters if any parameter is of type ARRAY
	// content can either be JSON or XML or NULL if parameters are posted as PARAM-1, PARAM-2, etc.
	// paramSet is the set inside the ARRAY

	uint32 size;
	const char* val, *data, *json = NULL, *xml = NULL;

	jsmn_parser parser;
	jsmntok_t tokens[256];
	int tokenCount = 0;
	int valSize;
	XMLResults xmlResults;
	XMLNode paramNode, node;

	if (utils::stristr(contentType, "json")) {
		if (!(json = content) || !contentSize) {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: No JSON found").c_str());
			return false;
		}
		jsmn_init(&parser);
		if ( (tokenCount = jsmn_parse(&parser, json, contentSize, tokens, 256)) <= 0) {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Error parsing JSON").c_str());
			return false;
		}
	}
	else if (utils::stristr(contentType, "xml")) {
		if (!(xml = content) || !contentSize) {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: No XML found").c_str());
			return false;
		}
		paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
		if ((xmlResults.error != eXMLErrorNone) || (paramNode.isEmpty())) {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Error parsing XML").c_str());
			return false;
		}
	}

	std::string paddedParamName;
	std::string value;
	std::map<std::string, RESTParam>::iterator i = paramSet.begin(), e = paramSet.end();
	while (i != e) {
		paddedParamName = utils::StringFormat("%s_%u", i->second.name.c_str(), entryNum);
		value.clear();
		if (json) {
			// First look for the raw parameter name
			if (!(val = GetJSONValue(tokens, tokenCount, json, i->second.name.c_str(), JSMN_UNDEFINED, valSize))) {
				// then look for the padded PARAM_N name
				if (!(val = GetJSONValue(tokens, tokenCount, json, paddedParamName.c_str(), JSMN_UNDEFINED, valSize))) {
					if (!i->second.optional) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s not found in JSON", i->second.name.c_str()).c_str());
						return false;
					}
				}
			}
			else {
				if (!valSize && !i->second.allowEmpty) {
					restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s empty in JSON, not allowed", i->second.name.c_str()).c_str());
					return false;
				}
				value = std::string(val, valSize);
				// if this is a binary reference, val has the name of the binary attachment
				if (i->second.type == RESTParam::IMAGE || i->second.type == RESTParam::VIDEO || i->second.type == RESTParam::BINARY) {
					data = req->getPostData(value.c_str(), size);
					if (!data || !size) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s (from %s) not found in form", value.c_str(), paddedParamName.c_str()).c_str());
						return false;
					}
					// and now get the content-type
					if (!(val = req->getPostDataType(i->second.name.c_str()))) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Binary parameter %s content type not found in form", i->second.name.c_str()).c_str());
						return false;
					}
					restReq->setBinary(paddedParamName.c_str(), data, size, val);
					i++;
					continue;
				}
				// else leave value as text
			}
		}
		else if (xml) {
			// #############################
			return false;
			//node = paramNode.
			//if (!(val = extractXMLValue(req->getPostData("POST", size), i->second.name.c_str(), size)) || !size)
			//	return false;
		}
		else if (utils::stristr(contentType, "form")) { // both multipart/form-data and application/x-www-form-urlencoded
			if (!(data = req->getPostData(paddedParamName.c_str(), size)) || !size) {
				if (!i->second.optional) {
					restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s not found in form", paddedParamName.c_str()).c_str());
					return false;
				}
			}
			else {
				// if this is a binary reference, val has the name of the binary attachment
				if (i->second.type == RESTParam::IMAGE || i->second.type == RESTParam::VIDEO || i->second.type == RESTParam::BINARY) {
					// and now get the content-type
					if (!(val = req->getPostDataType(i->second.name.c_str()))) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Binary parameter %s content type not found in form", i->second.name.c_str()).c_str());
						return false;
					}
					// for this don't go into the string-based setParameterValue
					restReq->setBinary(paddedParamName.c_str(), data, size, val);
					i++;
					continue;
				}
				// else leave value as text
				else
					value = data;
			}
		}
		else {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: No form, JSON or XML found in request", i->second.name.c_str()).c_str());
			return false;
		}

		if (i->second.type == RESTParam::ARRAY) {
			// For now we do not support nested arrays
		}
		else if (value.length()) {
			if (!setParameterValue(req, i->second, value, restReq, entryNum))
				return false;
		}
		i++;
	}
	return true;
}



bool RESTParser::extractArrayParameters(DataMessage* msg, std::map<std::string, RESTParam>& paramSet, RESTRequest* restReq, int entryNum, const char* content, uint32 contentSize, const char* contentType) {
	// Called from extractParameters if any parameter is of type ARRAY
	// content can either be JSON or XML or NULL if parameters are posted as PARAM-1, PARAM-2, etc.
	// paramSet is the set inside the ARRAY

	uint32 size, size2;
	const char* val, *data, *json = NULL, *xml = NULL;

	jsmn_parser parser;
	jsmntok_t tokens[256];
	int tokenCount = 0;
	int valSize;
	XMLResults xmlResults;
	XMLNode paramNode, node;

	if (utils::stristr(contentType, "json")) {
		if (!(json = content) || !contentSize) {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: No JSON found").c_str());
			return false;
		}
		jsmn_init(&parser);
		if ( (tokenCount = jsmn_parse(&parser, json, contentSize, tokens, 256)) <= 0) {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Error parsing JSON").c_str());
			return false;
		}
	}
	else if (utils::stristr(contentType, "xml")) {
		if (!(xml = content) || !contentSize) {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: No XML found").c_str());
			return false;
		}
		paramNode=XMLNode::parseString(xml, NULL, &xmlResults);
		if ((xmlResults.error != eXMLErrorNone) || (paramNode.isEmpty())) {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Error parsing XML").c_str());
			return false;
		}
	}

	std::string paddedParamName;
	std::string value;
	std::map<std::string, RESTParam>::iterator i = paramSet.begin(), e = paramSet.end();
	while (i != e) {
		paddedParamName = utils::StringFormat("%s_%u", i->second.name.c_str(), entryNum);
		value.clear();
		if (json) {
			// First look for the raw parameter name
			if (!(val = GetJSONValue(tokens, tokenCount, json, i->second.name.c_str(), JSMN_UNDEFINED, valSize))) {
				// then look for the padded PARAM_N name
				if (!(val = GetJSONValue(tokens, tokenCount, json, paddedParamName.c_str(), JSMN_UNDEFINED, valSize))) {
					if (!i->second.optional) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s not found in JSON", i->second.name.c_str()).c_str());
						return false;
					}
				}
			}
			else {
				if (!valSize && !i->second.allowEmpty) {
					restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s empty in JSON, not allowed", i->second.name.c_str()).c_str());
					return false;
				}
				value = std::string(val, valSize);
				// if this is a binary reference, val has the name of the binary attachment
				if (i->second.type == RESTParam::IMAGE || i->second.type == RESTParam::VIDEO || i->second.type == RESTParam::BINARY) {
					data = msg->getData(value.c_str(), size);
					if (!data || !size) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s (from %s) not found in form", value.c_str(), paddedParamName.c_str()).c_str());
						return false;
					}
					// and now get the content-type
					if (!(val = msg->getString((value + "_CONTENT_TYPE_").c_str(), size2)) || !size2) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Binary parameter %s content type not found in form", i->second.name.c_str()).c_str());
						return false;
					}
					restReq->setBinary(paddedParamName.c_str(), data, size, val);
					i++;
					continue;
				}
				// else leave value as text
			}
		}
		else if (xml) {
			// #############################
			return false;
			//node = paramNode.
			//if (!(val = extractXMLValue(req->getPostData("POST", size), i->second.name.c_str(), size)) || !size)
			//	return false;
		}
		else if (!contentType || utils::stristr(contentType, "form")) { // both multipart/form-data and application/x-www-form-urlencoded
			if (i->second.type == RESTParam::IMAGE || i->second.type == RESTParam::VIDEO || i->second.type == RESTParam::BINARY) {
				if (!(data = msg->getData(paddedParamName.c_str(), size)) || !size) {
					if (!i->second.optional) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s not found in form", paddedParamName.c_str()).c_str());
						return false;
					}
					else {
						i++;
						continue;
					}
				}
				// and now get the content-type
				if (!(val = msg->getString((paddedParamName + "_CONTENT_TYPE_").c_str(), size2)) || !size2) {
					restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Binary parameter %s content type not found in form", i->second.name.c_str()).c_str());
					return false;
				}
				restReq->setBinary(paddedParamName.c_str(), data, size, val);
				i++;
				continue;
			}
			else {
				if (!(val = msg->getString(paddedParamName.c_str(), size)) || !size) {
					if (!i->second.optional) {
						restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: Parameter %s not found in form", i->second.name.c_str()).c_str());
						return false;
					}
					else {
						i++;
						continue;
					}
				}
				value = val;
			}
		}
		else {
			restReq->addToErrorString(utils::StringFormat("Array parameter extraction error: No form, JSON or XML found in request", i->second.name.c_str()).c_str());
			return false;
		}

		if (i->second.type == RESTParam::ARRAY) {
			// For now we do not support nested arrays
		}
		else if (value.length()) {
			if (!setParameterValue(msg, i->second, value, restReq, entryNum))
				return false;
		}
		i++;
	}
	return true;
}

































bool RESTParser::setParameterValue(HTTPRequest* req, RESTParam &param, std::string value, RESTRequest* restReq, uint32 entryNum) {

	// if entryName = 0 this is a normal parameter, if not, it is an array parameter

	uint32 size;
	const char* data, *val;
	utils::StringSingleReplace(value, "\"", "", false);

	std::string paddedParamName;
	if (entryNum)
		paddedParamName = utils::StringFormat("%s_%u", param.name.c_str(), entryNum);
	else
		paddedParamName = param.name;

	switch(param.type) {
		case RESTParam::INTEGER:
			if (!utils::IsTextNumeric(value.c_str())) {
				restReq->addToErrorString(utils::StringFormat("Parameter error: %s = %s is not numeric", paddedParamName.c_str(), value.c_str()).c_str());
				return false;
			}
			restReq->setInt(paddedParamName.c_str(), utils::Ascii2Int64(value.c_str()));
			break;
		case RESTParam::FLOAT:
			if (!utils::IsTextNumeric(value.c_str())) {
				restReq->addToErrorString(utils::StringFormat("Parameter error: %s = %s is not numeric", paddedParamName.c_str(), value.c_str()).c_str());
				return false;
			}
			restReq->setDouble(paddedParamName.c_str(), (double)utils::Ascii2Float64(value.c_str()));
			break;
		case RESTParam::TEXT:
			restReq->setString(paddedParamName.c_str(), value.c_str());
			break;
		case RESTParam::IMAGE:
		case RESTParam::VIDEO:
		case RESTParam::BINARY:
			// Check that post contains an entry of value
			if (!(data = req->getPostData(value.c_str(), size))) {
				restReq->addToErrorString(utils::StringFormat("Parameter error: cannot find post entry called %s for parameter %s", value.c_str(), paddedParamName.c_str()).c_str());
				return false;
			}
			if (!(val = req->getPostDataType(value.c_str()))) {
				restReq->addToErrorString(utils::StringFormat("Parameter error: cannot find post type for entry called %s for parameter %s", value.c_str(), paddedParamName.c_str()).c_str());
				return false;
			}
			restReq->setBinary(paddedParamName.c_str(), data, size, val);
			break;
		//case RESTParam::ARRAY:
			// ###################
			// break;
			//return false;
		default:
			restReq->addToErrorString(utils::StringFormat("Parameter error: %s type is not supported", paddedParamName.c_str(), value.c_str()).c_str());
			return false;
	}
	return true;
}





bool RESTParser::setParameterValue(DataMessage* msg, RESTParam &param, std::string value, RESTRequest* restReq, uint32 entryNum) {

	// if entryName = 0 this is a normal parameter, if not, it is an array parameter

	uint32 size, size2;
	const char* data, *val;
	utils::StringSingleReplace(value, "\"", "", false);

	std::string paddedParamName;
	if (entryNum)
		paddedParamName = utils::StringFormat("%s_%u", param.name.c_str(), entryNum);
	else
		paddedParamName = param.name;

	switch(param.type) {
		case RESTParam::INTEGER:
			if (!utils::IsTextNumeric(value.c_str())) {
				restReq->addToErrorString(utils::StringFormat("Parameter error: %s = %s is not numeric", paddedParamName.c_str(), value.c_str()).c_str());
				return false;
			}
			restReq->setInt(paddedParamName.c_str(), utils::Ascii2Int64(value.c_str()));
			break;
		case RESTParam::FLOAT:
			if (!utils::IsTextNumeric(value.c_str())) {
				restReq->addToErrorString(utils::StringFormat("Parameter error: %s = %s is not numeric", paddedParamName.c_str(), value.c_str()).c_str());
				return false;
			}
			restReq->setDouble(paddedParamName.c_str(), (double)utils::Ascii2Float64(value.c_str()));
			break;
		case RESTParam::TEXT:
			restReq->setString(paddedParamName.c_str(), value.c_str());
			break;
		case RESTParam::IMAGE:
		case RESTParam::VIDEO:
		case RESTParam::BINARY:
			// Check that post contains an entry of value
			if (!(data = msg->getData(value.c_str(), size))) {
				restReq->addToErrorString(utils::StringFormat("Parameter error: cannot find post entry called %s for parameter %s", value.c_str(), paddedParamName.c_str()).c_str());
				return false;
			}
			// and now get the content-type
			if (!(val = msg->getString((value + "_CONTENT_TYPE_").c_str(), size2)) || !size2) {
				restReq->addToErrorString(utils::StringFormat("Parameter error: cannot find post type for entry called %s for parameter %s", value.c_str(), paddedParamName.c_str()).c_str());
				return false;
			}
			restReq->setBinary(paddedParamName.c_str(), data, size, val);
			break;
		//case RESTParam::ARRAY:
			// ###################
			// break;
			//return false;
		default:
			restReq->addToErrorString(utils::StringFormat("Parameter error: %s type is not supported", paddedParamName.c_str(), value.c_str()).c_str());
			return false;
	}
	return true;
}




















XMLNode RESTParser::findTopNodeForRequest(const char* topNodeString) {
	const char* name;
	XMLNode node;
	uint32 i, count = mainNode.nChildNode("api");
	for(i=0; i<count; i++) {
		if (!(node = mainNode.getChildNode("api", i)).isEmpty()) {
			name = node.getAttribute("name");
			if (strcmp(topNodeString, name) == 0)
				return node;
		}
	}
	return XMLNode::emptyXMLNode;
}














RESTRequest::RESTRequest(DataMessage* msg) {
	bool ok;
	createTime = msg->getTime("BUILTIN_CREATETIME", ok);
	status = (Status)msg->getInt("BUILTIN_STATUS", ok);
	signature = getString("BUILTIN_REQUEST_SIGNATURE", ok);
	requireAuthentication = (msg->getInt("BUILTIN_REQUIRE_AUTHENTICATION", ok) != 0);
	std::map<std::string, std::string> filterMapTemp = utils::TextMapSplit(msg->getString("BUILTIN_FILTERMAP"), ";;;", "=");
	std::map<std::string, std::string>::iterator i = filterMapTemp.begin(), e = filterMapTemp.end();
	while (i != e) {
		filterMap[html::DecodeHTML(i->first)] = html::DecodeHTML(i->second);
		i++;
	}
	storage = msg;
}

DataMessage* RESTRequest::getStorageMessageCopy() {
	DataMessage* msg = new DataMessage(*storage);
	msg->setTime("BUILTIN_CREATETIME", createTime);
	msg->setInt("BUILTIN_STATUS", status);
	if (requireAuthentication)
		msg->setInt("BUILTIN_REQUIRE_AUTHENTICATION", 1);
	std::string filterString;
	std::multimap<std::string, std::string>::iterator i = filterMap.begin(), e = filterMap.end();
	while (i != e) {
		if (filterString.length())
			filterString = utils::StringFormat("%s;;;%s=%s", filterString.c_str(), html::EncodeHTML(i->first).c_str(), html::EncodeHTML(i->second).c_str());
		else
			filterString = utils::StringFormat("%s=%s", html::EncodeHTML(i->first).c_str(), html::EncodeHTML(i->second).c_str());
		i++;
	}
	msg->setString("BUILTIN_FILTERMAP", filterString.c_str());
	msg->setString("BUILTIN_REQUEST_SIGNATURE", signature.c_str());
	return msg;
}








bool RESTParser::UnitTest() {

	std::string restDesc = utils::ReadAFileString("d:/restapi.xml");
	if (!restDesc.length())
		return false;

	RESTParser parser;
	if (!parser.init(restDesc.c_str()))
		return false;

	uint32 size;
	char* postData = utils::ReadAFile("d:/restimp.post", size, true);
	if (!postData)
		return false;

	bool isInvalid;
	HTTPRequest httpReq;
	if (!httpReq.processHeader(postData, size, isInvalid))
		return false;

	if (!httpReq.processContent(postData + httpReq.headerLength, httpReq.contentLength))
		return false;

	RESTRequest* restReq = parser.parseRequest(&httpReq);
	delete(restReq);

	DataMessage* restMsg = httpReq.convertToMessage();

	restReq = parser.parseRequest(restMsg);
	delete(restMsg);
	delete(restReq);

	return true;
}


} // namespace cmlabs

