From 300b0d6897c31b9250dd6a60746646b036986983 Mon Sep 17 00:00:00 2001
From: George Hazan <george.hazan@gmail.com>
Date: Mon, 5 Aug 2013 22:23:17 +0000
Subject: fix for massive memory leak in json parser

git-svn-id: http://svn.miranda-ng.org/main/trunk@5593 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
---
 include/m_json.h                        | 18 ++++++-
 protocols/FacebookRM/src/connection.cpp |  5 +-
 protocols/FacebookRM/src/json.cpp       | 90 ++++++++++++++++-----------------
 src/mir_core/json/JSONNode.h            |  4 +-
 src/mir_core/json/internalJSONNode.h    |  6 +--
 src/mir_core/json/libJSON.cpp           |  6 +--
 6 files changed, 73 insertions(+), 56 deletions(-)

diff --git a/include/m_json.h b/include/m_json.h
index 8c4b25c6aa..3d82fe276b 100644
--- a/include/m_json.h
+++ b/include/m_json.h
@@ -69,7 +69,7 @@ MIR_CORE_DLL(void) json_set_n(JSONNODE * node, const JSONNODE * orig);
 MIR_CORE_DLL(char) json_type(const JSONNODE * node);
 MIR_CORE_DLL(size_t) json_size(const JSONNODE * node);
 MIR_CORE_DLL(int) json_empty(const JSONNODE * node);
-MIR_CORE_DLL(char*) json_name(const JSONNODE * node);
+MIR_CORE_DLL(const char*) json_name(const JSONNODE * node);
 #ifdef JSON_COMMENTS
 	MIR_CORE_DLL(char * json_get_comment(const JSONNODE * node);
 #endif
@@ -117,6 +117,22 @@ MIR_CORE_DLL(int) json_equal(JSONNODE * node, JSONNODE * node2);
 
 #ifdef __cplusplus
 }
+
+class json_ptr
+{
+	char *data;
+
+public:
+	__inline json_ptr() : data(NULL) {}
+	__inline json_ptr(char *_p) : data(_p) {}
+	__inline ~json_ptr() { json_free(data); }
+	__inline char* operator = (char* _p) { if (data) json_free(data); data = _p; return data; }
+	__inline char* operator->() const { return data; }
+	__inline operator char*() const { return data; }
+};
+
+#define JSON_AS_STRING(A) ((char*)json_ptr(json_as_string(A)))
+
 #endif
 
 #endif // MIM_LIBJSON_H
diff --git a/protocols/FacebookRM/src/connection.cpp b/protocols/FacebookRM/src/connection.cpp
index 131e00bb9d..afe0fbcf45 100644
--- a/protocols/FacebookRM/src/connection.cpp
+++ b/protocols/FacebookRM/src/connection.cpp
@@ -185,8 +185,9 @@ bool FacebookProto::NegotiateConnection()
 	facy.https_ = getByte(FACEBOOK_KEY_FORCE_HTTPS, DEFAULT_FORCE_HTTPS) != 0;
 
 	// Create default group for new contacts
-	if (!getTString(FACEBOOK_KEY_DEF_GROUP, &dbv) && lstrlen(dbv.ptszVal) > 0)
-		CallService(MS_CLIST_GROUPCREATE, 0, (LPARAM)dbv.ptszVal);
+	ptrT groupName( getTStringA(FACEBOOK_KEY_DEF_GROUP));
+	if (groupName != NULL)
+		CallService(MS_CLIST_GROUPCREATE, 0, (LPARAM)groupName);
 	
 	return facy.login(user, pass);
 }
diff --git a/protocols/FacebookRM/src/json.cpp b/protocols/FacebookRM/src/json.cpp
index 6e62356070..98a4867464 100644
--- a/protocols/FacebookRM/src/json.cpp
+++ b/protocols/FacebookRM/src/json.cpp
@@ -53,7 +53,7 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user
 	if (lastActive != NULL) {
 		for (unsigned int i = 0; i < json_size(lastActive); i++) {
 			JSONNODE *it = json_at(lastActive, i);
-			char *id = json_name(it);
+			const char *id = json_name(it);
 			
 			current = buddy_list->find(id);
 			if (current == NULL) {
@@ -71,7 +71,7 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user
 	if (mobileFriends != NULL) {
 		for (unsigned int i = 0; i < json_size(mobileFriends); i++) {
 			JSONNODE *it = json_at(mobileFriends, i);
-			char *id = json_as_string(it);
+			std::string id( JSON_AS_STRING(it));
 			
 			current = buddy_list->find(id);
 			if (current == NULL) {
@@ -89,7 +89,7 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user
 	if (nowAvailable != NULL) {		
 		for (unsigned int i = 0; i < json_size(nowAvailable); i++) {
 			JSONNODE *it = json_at(nowAvailable, i);
-			char *id = json_name(it);
+			const char *id = json_name(it);
 			
 			current = buddy_list->find(id);
 			if (current == NULL) {
@@ -112,7 +112,7 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user
 	if (userInfos != NULL) {		
 		for (unsigned int i = 0; i < json_size(userInfos); i++) {
 			JSONNODE *it = json_at(userInfos, i);
-			char *id = json_name(it);
+			const char *id = json_name(it);
 			
 			current = buddy_list->find(id);
 			if (current == NULL)
@@ -122,9 +122,9 @@ int facebook_json_parser::parse_buddy_list(void* data, List::List< facebook_user
 			JSONNODE *thumbSrc = json_get(it, "thumbSrc");
 
 			if (name != NULL)
-				current->real_name = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(json_as_string(name)));
+				current->real_name = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(JSON_AS_STRING(name)));
 			if (thumbSrc != NULL)			
-				current->image_url = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(json_as_string(thumbSrc)));
+				current->image_url = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(JSON_AS_STRING(thumbSrc)));
 		}
 	}
 
@@ -148,7 +148,7 @@ int facebook_json_parser::parse_friends(void* data, std::map< std::string, faceb
 
 	for (unsigned int i = 0; i < json_size(payload); i++) {
 		JSONNODE *it = json_at(payload, i);
-		char *id = json_name(it);
+		const char *id = json_name(it);
 
 		JSONNODE *name = json_get(it, "name");
 		JSONNODE *thumbSrc = json_get(it, "thumbSrc");
@@ -162,9 +162,9 @@ int facebook_json_parser::parse_friends(void* data, std::map< std::string, faceb
 
 		fbu->user_id = id;
 		if (name)
-			fbu->real_name = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(json_as_string(name)));
+			fbu->real_name = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(JSON_AS_STRING(name)));
 		if (thumbSrc)
-			fbu->image_url = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(json_as_string(thumbSrc)));
+			fbu->image_url = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(JSON_AS_STRING(thumbSrc)));
 
 		if (gender)
 			switch (json_as_int(gender)) {
@@ -206,7 +206,7 @@ int facebook_json_parser::parse_notifications(void *data, std::vector< facebook_
 
 	for (unsigned int i = 0; i < json_size(list); i++) {
 		JSONNODE *it = json_at(list, i);
-		char *id = json_name(it);
+		const char *id = json_name(it);
 
 		JSONNODE *markup = json_get(it, "markup");
 		JSONNODE *unread = json_get(it, "unread");
@@ -216,7 +216,7 @@ int facebook_json_parser::parse_notifications(void *data, std::vector< facebook_
 		if (markup == NULL || unread == NULL || json_as_int(unread) == 0)
 			continue;
 
-		std::string text = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(json_as_string(markup)));
+		std::string text = utils::text::slashu_to_utf8(utils::text::special_expressions_decode(JSON_AS_STRING(markup)));
 
 		facebook_notification* notification = new facebook_notification();
 
@@ -262,12 +262,12 @@ void parseAttachments(FacebookProto *proto, std::string *message_text, JSONNODE
 			if (attach_type != NULL) {
 				// Get attachment type - "file" has priority over other types
 				if (type.empty() || type != "file")
-					type = json_as_string(attach_type);
+					type = JSON_AS_STRING(attach_type);
 			}
 			JSONNODE *name = json_get(itAttachment, "name");
 			JSONNODE *url = json_get(itAttachment, "url");
 			if (url != NULL) {
-				std::string link = json_as_string(url);
+				std::string link = JSON_AS_STRING(url);
 							
 				if (link.find("/ajax/mercury/attachments/photo/view/") != std::string::npos)
 					// fix photo url
@@ -281,7 +281,7 @@ void parseAttachments(FacebookProto *proto, std::string *message_text, JSONNODE
 				if (!link.empty()) {
 					std::string filename;
 					if (name != NULL)
-						filename = json_as_string(name);
+						filename = JSON_AS_STRING(name);
 					if (filename == "null")
 						filename.clear();
 
@@ -298,7 +298,7 @@ void parseAttachments(FacebookProto *proto, std::string *message_text, JSONNODE
 			// we can use this as offline messages doesn't have it
 			/* JSONNODE *admin_snippet = json_get(it, "admin_snippet");
 			if (admin_snippet != NULL) {
-				*message_text += json_as_string(admin_snippet);
+				*message_text += JSON_AS_STRING(admin_snippet);
 			} */
 
 			std::tstring newText;
@@ -341,7 +341,7 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 		if (type == NULL)
 			continue;
 
-		std::string t = json_as_string(type);
+		std::string t = JSON_AS_STRING(type);
 		if (t == "msg" || t == "offline_msg") {
 			// we use this only for outgoing messages
 			
@@ -361,14 +361,14 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 			if (from == NULL || from_name == NULL || text == NULL || messageId == NULL || time == NULL)
 				continue;
 
-			char *from_id = json_as_string(from);
+			std::string from_id = JSON_AS_STRING(from);
 
 			// ignore incomming messages
 			if (from_id != proto->facy.self_.user_id)
 				continue;
 
-			std::string message_id = json_as_string(messageId);
-			std::string message_text = json_as_string(text);
+			std::string message_id = JSON_AS_STRING(messageId);
+			std::string message_text = JSON_AS_STRING(text);
 
 			message_text = utils::text::trim(utils::text::special_expressions_decode(utils::text::slashu_to_utf8(message_text)), true);
 			if (message_text.empty())
@@ -394,9 +394,9 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 
 			facebook_message* message = new facebook_message();
 			message->message_text = message_text;
-			message->sender_name = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(json_as_string(to_name)));
+			message->sender_name = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(JSON_AS_STRING(to_name)));
 			message->time = utils::time::fix_timestamp(json_as_float(time));
-			message->user_id = json_as_string(to); // TODO: Check if we have contact with this ID in friendlist and otherwise do something different?
+			message->user_id = JSON_AS_STRING(to); // TODO: Check if we have contact with this ID in friendlist and otherwise do something different?
 			message->message_id = message_id;
 			message->isIncoming = false;
 
@@ -408,7 +408,7 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 			if (type == NULL)
 				continue;
 
-			std::string t = json_as_string(type);
+			std::string t = JSON_AS_STRING(type);
 
 			if (t == "read_receipt") {
 				// user read message
@@ -419,7 +419,7 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 					continue;
 
 				// TODO: add check for chat contacts
-				HANDLE hContact = proto->ContactIDToHContact(json_as_string(reader));
+				HANDLE hContact = proto->ContactIDToHContact(JSON_AS_STRING(reader));
 				if (hContact) {
 					TCHAR ttime[64], tstr[100];
 					_tcsftime(ttime, SIZEOF(ttime), _T("%X"), utils::conversion::fbtime_to_timeinfo(json_as_float(time)));
@@ -442,9 +442,9 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 				if (sender_fbid == NULL || sender_name == NULL || body == NULL || mid == NULL || timestamp == NULL)
 					continue;
 
-				std::string id = json_as_string(sender_fbid);
-				std::string message_id = json_as_string(mid);
-				std::string message_text = json_as_string(body);
+				std::string id = JSON_AS_STRING(sender_fbid);
+				std::string message_id = JSON_AS_STRING(mid);
+				std::string message_text = JSON_AS_STRING(body);
 
 				// Process attachements and stickers
 				parseAttachments(proto, &message_text, msg);
@@ -463,7 +463,7 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 
 				facebook_message* message = new facebook_message();
 				message->message_text = message_text;
-				message->sender_name = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(json_as_string(sender_name)));
+				message->sender_name = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(JSON_AS_STRING(sender_name)));
 				message->time = utils::time::fix_timestamp(json_as_float(timestamp));
 				message->user_id = id; // TODO: Check if we have contact with this ID in friendlist and otherwise do something different?
 				message->message_id = message_id;
@@ -488,9 +488,9 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 			if (from_id == NULL || text == NULL || messageId == NULL)
 				continue;
 
-			std::string id = json_as_string(from_id);
-			std::string message_id = json_as_string(messageId);
-			std::string message_text = json_as_string(text);
+			std::string id = JSON_AS_STRING(from_id);
+			std::string message_id = JSON_AS_STRING(messageId);
+			std::string message_text = JSON_AS_STRING(text);
 
 			// Ignore messages from myself
 			if (id == proto->facy.self_.user_id)
@@ -504,9 +504,9 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 			if (message_text.empty())
 				continue;
 
-			std::string title = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(json_as_string(to_name)));
+			std::string title = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(JSON_AS_STRING(to_name)));
 			std::string url = "/?action=read&sk=inbox&page&query&tid=" + id;
-			std::string popup_text = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(json_as_string(from_name)));
+			std::string popup_text = utils::text::special_expressions_decode(utils::text::slashu_to_utf8(JSON_AS_STRING(from_name)));
 			popup_text += ": " + message_text;
 
 			proto->Log("      Got multichat message");
@@ -547,9 +547,9 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 					proto->facy.last_notification_time_ = timestamp;
 
 					facebook_notification* notification = new facebook_notification();
-					notification->text = utils::text::slashu_to_utf8(json_as_string(text));
-  					notification->link = utils::text::special_expressions_decode(json_as_string(url));					
-					notification->id = json_as_string(alert_id);
+					notification->text = utils::text::slashu_to_utf8(JSON_AS_STRING(text));
+  					notification->link = utils::text::special_expressions_decode(JSON_AS_STRING(url));					
+					notification->id = JSON_AS_STRING(alert_id);
 
 					std::string::size_type pos = notification->id.find(":");
 					if (pos != std::string::npos)
@@ -566,7 +566,7 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 				continue;
 
 			facebook_user fbu;
-			fbu.user_id = json_as_string(from);
+			fbu.user_id = JSON_AS_STRING(from);
 
 			HANDLE hContact = proto->AddToContactList(&fbu, CONTACT_FRIEND);
 				
@@ -587,7 +587,7 @@ int facebook_json_parser::parse_messages(void* data, std::vector< facebook_messa
 			if (event_type == NULL || event_data == NULL)
 				continue;
 
-			std::string t = json_as_string(event_type);
+			std::string t = JSON_AS_STRING(event_type);
 			if (t == "visibility_update") {
 				// change of chat status
 				JSONNODE *visibility = json_get(event_data, "visibility");
@@ -641,7 +641,7 @@ int facebook_json_parser::parse_unread_threads(void* data, std::vector< std::str
 
 		for (unsigned int j = 0; j < json_size(thread_ids); j++) {
 			JSONNODE *id = json_at(thread_ids, j);
-			threads->push_back(json_as_string(id));
+			threads->push_back(JSON_AS_STRING(id));
 		}
 	}
 
@@ -680,11 +680,11 @@ int facebook_json_parser::parse_thread_messages(void* data, std::vector< faceboo
 		if (canonical == NULL || thread_id == NULL)
 			continue;
 
-		std::string id = json_as_string(canonical);
+		std::string id = JSON_AS_STRING(canonical);
 		if (id == "null")
 			continue;
 		
-		std::string tid = json_as_string(thread_id);
+		std::string tid = JSON_AS_STRING(thread_id);
 		thread_ids.insert(std::make_pair(tid, id));
 	}
 
@@ -707,14 +707,14 @@ int facebook_json_parser::parse_thread_messages(void* data, std::vector< faceboo
 			continue;
 
 		// Try to get user id from "threads" array (and simultaneously ignore multi user threads)		
-		std::map<std::string, std::string>::iterator iter = thread_ids.find(json_as_string(tid));
+		std::map<std::string, std::string>::iterator iter = thread_ids.find(JSON_AS_STRING(tid));
 		if (iter == thread_ids.end())
 			continue; // not found or ignored multi user thread
 
 		std::string user_id = iter->second;
-		std::string message_id = json_as_string(mid);
-		std::string message_text = json_as_string(body);
-		std::string author_id = json_as_string(author);
+		std::string message_id = JSON_AS_STRING(mid);
+		std::string message_text = JSON_AS_STRING(body);
+		std::string author_id = JSON_AS_STRING(author);
 		std::string::size_type pos = author_id.find(":");
 		if (pos != std::string::npos)
 			author_id = author_id.substr(pos+1);
@@ -729,7 +729,7 @@ int facebook_json_parser::parse_thread_messages(void* data, std::vector< faceboo
 		facebook_message* message = new facebook_message();
 		message->message_text = message_text;
 		if (author_email != NULL)
-			message->sender_name = json_as_string(author_email);
+			message->sender_name = JSON_AS_STRING(author_email);
 		message->time = utils::time::fix_timestamp(json_as_float(timestamp));
 		message->user_id = user_id; // TODO: Check if we have contact with this ID in friendlist and otherwise do something different?
 		message->message_id = message_id;
diff --git a/src/mir_core/json/JSONNode.h b/src/mir_core/json/JSONNode.h
index 487925b04f..27dbef2aee 100644
--- a/src/mir_core/json/JSONNode.h
+++ b/src/mir_core/json/JSONNode.h
@@ -93,7 +93,7 @@ public:
 	void clear(void);
 	unsigned char type(void) const;
 
-	json_string name(void) const;
+	const json_char* name(void) const;
 	void set_name(const json_string & newname);
 	#ifdef JSON_COMMENTS
 		void set_comment(const json_string & comment);
@@ -505,7 +505,7 @@ inline unsigned char JSONNode::type(void) const {
 	JSON_CHECK_INTERNAL();
 	return internal -> type();
 }
-inline json_string JSONNode::name(void) const {
+inline const json_char* JSONNode::name(void) const {
 	JSON_CHECK_INTERNAL();
 	return internal -> name();
 }
diff --git a/src/mir_core/json/internalJSONNode.h b/src/mir_core/json/internalJSONNode.h
index 8832bbd828..9ed7680a45 100644
--- a/src/mir_core/json/internalJSONNode.h
+++ b/src/mir_core/json/internalJSONNode.h
@@ -81,7 +81,7 @@ public:
 	bool empty(void) const;
 	unsigned char type(void) const;
 
-	json_string name(void) const;
+	const json_char* name(void) const;
 	void setname(const json_string & newname);
 	#ifdef JSON_COMMENTS
 		void setcomment(const json_string & comment);
@@ -260,8 +260,8 @@ inline unsigned char internalJSONNode::type(void) const {
 	#endif
 }
 
-inline json_string internalJSONNode::name(void) const {
-	return _name;
+inline const json_char* internalJSONNode::name(void) const {
+	return _name.c_str();
 }
 
 inline void internalJSONNode::setname(const json_string & newname){
diff --git a/src/mir_core/json/libJSON.cpp b/src/mir_core/json/libJSON.cpp
index 077af1b961..e3599e8936 100644
--- a/src/mir_core/json/libJSON.cpp
+++ b/src/mir_core/json/libJSON.cpp
@@ -266,9 +266,9 @@ MIR_CORE_DLL(int) json_empty(const JSONNODE * node){
 	return (int)(((JSONNode*)node) -> empty());
 }
 
-MIR_CORE_DLL(json_char*) json_name(const JSONNODE * node){
-	JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_name"), return toCString(EMPTY_CSTRING););
-	return toCString(((JSONNode*)node) -> name());
+MIR_CORE_DLL(const json_char*) json_name(const JSONNODE * node){
+	JSON_ASSERT_SAFE(node, JSON_TEXT("null node to json_name"), return EMPTY_CSTRING;);
+	return ((JSONNode*)node) -> name();
 }
 
 #ifdef JSON_COMMENTS
-- 
cgit v1.2.3