From de1841e8600420cf3cf35cb73d6e0226a9519687 Mon Sep 17 00:00:00 2001
From: Piotr Piastucki <leech.miranda@gmail.com>
Date: Thu, 4 Jun 2015 12:10:46 +0000
Subject: Fixed a memleak in groupchat Added support for contact list fetching
 for plain Skype user finally enabling usage of Skype accounts via
 skylogin.dll (login only working once in a session though, currently cannot
 find out why). Prevent launching of duplicate keepAliveThread

git-svn-id: http://svn.miranda-ng.org/main/trunk@13995 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
---
 protocols/MSN/msn_10.vcxproj       |   1 +
 protocols/MSN/src/msn_auth.cpp     |  41 ++--
 protocols/MSN/src/msn_chat.cpp     |   2 +-
 protocols/MSN/src/msn_commands.cpp |   3 +-
 protocols/MSN/src/msn_contact.cpp  | 165 ++++++++------
 protocols/MSN/src/msn_lists.cpp    |   2 +-
 protocols/MSN/src/msn_opts.cpp     |   4 +-
 protocols/MSN/src/msn_proto.cpp    |  54 +++--
 protocols/MSN/src/msn_proto.h      |  14 ++
 protocols/MSN/src/msn_skypeab.cpp  | 448 +++++++++++++++++++++++++++++++++++++
 protocols/MSN/src/msn_svcs.cpp     |   2 +-
 11 files changed, 624 insertions(+), 112 deletions(-)
 create mode 100644 protocols/MSN/src/msn_skypeab.cpp

(limited to 'protocols/MSN')

diff --git a/protocols/MSN/msn_10.vcxproj b/protocols/MSN/msn_10.vcxproj
index 2cf0ee8aef..f00b7fdd97 100644
--- a/protocols/MSN/msn_10.vcxproj
+++ b/protocols/MSN/msn_10.vcxproj
@@ -207,6 +207,7 @@
       <PrecompiledHeader>NotUsing</PrecompiledHeader>
     </ClCompile>
     <ClCompile Include="src\msn_avatar.cpp" />
+    <ClCompile Include="src\msn_skypeab.cpp" />
     <ClCompile Include="src\stdafx.cpp">
       <PrecompiledHeader>Create</PrecompiledHeader>
     </ClCompile>
diff --git a/protocols/MSN/src/msn_auth.cpp b/protocols/MSN/src/msn_auth.cpp
index 47192116cd..ac3640694f 100644
--- a/protocols/MSN/src/msn_auth.cpp
+++ b/protocols/MSN/src/msn_auth.cpp
@@ -941,18 +941,10 @@ const char *CMsnProto::GetSkypeToken(bool bAsAuthHeader)
 	char szToken[1024];
 
 	// Ensure that token isn't expired
-	MSN_AuthOAuth();
+	if (MyOptions.netId == NETID_MSN) MSN_AuthOAuth();
 
 	// No token available, fetch it
 	if (!authSkypeToken) {
-		// Get skype.com OAuth token needed to acquire skype_token
-		if (!authSkypeComToken) {
-			if (RefreshOAuth(authRefreshToken, "service::skype.com::MBI_SSL", szToken))
-				replaceStr(authSkypeComToken, szToken);
-			else return NULL;
-		}
-
-		// Get skype_token
 		NETLIBHTTPREQUEST nlhr = { 0 };
 		NETLIBHTTPREQUEST *nlhrReply;
 		NETLIBHTTPHEADER headers[1];
@@ -966,12 +958,33 @@ const char *CMsnProto::GetSkypeToken(bool bAsAuthHeader)
 		nlhr.headers = headers;
 		nlhr.headers[0].szName = "User-Agent";
 		nlhr.headers[0].szValue = (char*)MSN_USER_AGENT;
-		nlhr.szUrl = "https://api.skype.com/rps/skypetoken";
-		mir_snprintf(szPOST, sizeof(szPOST), "scopes=client&clientVersion=%s&access_token=%s&partner=999", 
-			msnProductVer, authSkypeComToken);
-		nlhr.dataLength = (int)strlen(szPOST);
-		nlhr.pData = (char*)(const char*)szPOST;
+		nlhr.pData = szPOST;
+
+		if (MyOptions.netId == NETID_SKYPE) {
+			BYTE digest[16];
+			int cbPasswd;
+			char szPassword[100]={0};
+
+			cbPasswd=mir_snprintf(szPassword, sizeof(szPassword), "%s\nskyper\n", MyOptions.szEmail);
+			db_get_static(NULL, m_szModuleName, "Password", szPassword+cbPasswd, sizeof(szPassword)-cbPasswd-1);
+			mir_md5_hash((BYTE*)szPassword, mir_strlen(szPassword), digest);
+			mir_base64_encodebuf(digest, sizeof(digest), szPassword, sizeof(szPassword));
+			nlhr.szUrl = "https://api.skype.com/login/skypetoken";
+			nlhr.dataLength = mir_snprintf(szPOST, sizeof(szPOST), "scopes=client&clientVersion=%s&username=%s&passwordHash=%s", 
+				msnProductVer, MyOptions.szEmail, szPassword);
+		} else {
+			// Get skype.com OAuth token needed to acquire skype_token
+			if (!authSkypeComToken) {
+				if (RefreshOAuth(authRefreshToken, "service::skype.com::MBI_SSL", szToken))
+					replaceStr(authSkypeComToken, szToken);
+				else return NULL;
+			}
 
+			// Get skype_token
+			nlhr.szUrl = "https://api.skype.com/rps/skypetoken";
+			nlhr.dataLength = mir_snprintf(szPOST, sizeof(szPOST), "scopes=client&clientVersion=%s&access_token=%s&partner=999", 
+				msnProductVer, authSkypeComToken);
+		}
 
 		mHttpsTS = clock();
 		nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
diff --git a/protocols/MSN/src/msn_chat.cpp b/protocols/MSN/src/msn_chat.cpp
index 4e9b439eb7..4078b1bc65 100644
--- a/protocols/MSN/src/msn_chat.cpp
+++ b/protocols/MSN/src/msn_chat.cpp
@@ -253,13 +253,13 @@ void CMsnProto::MSN_GCProcessThreadActivity(ezxml_t xmli, const TCHAR *mChatID)
 				MCONTACT hContTarget = MSN_HContactFromEmail(pszTarget);
 				gce.ptszNick =GetContactNameT(hContTarget);
 				CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce);
+				mir_free((TCHAR*)gce.ptszUID);
 				if ((gcd.iType == GC_EVENT_PART || gcd.iType == GC_EVENT_KICK) && gce.bIsMe) {
 					GCDEST gcd = { m_szModuleName, mChatID, GC_EVENT_CONTROL };
 					GCEVENT gce = { sizeof(gce), &gcd };
 					CallServiceSync(MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gce);
 					break;
 				}
-				mir_free((TCHAR*)gce.ptszUID);
 				target = ezxml_next(target);
 			}
 		}
diff --git a/protocols/MSN/src/msn_commands.cpp b/protocols/MSN/src/msn_commands.cpp
index 938c3b6ea7..35af58d3aa 100644
--- a/protocols/MSN/src/msn_commands.cpp
+++ b/protocols/MSN/src/msn_commands.cpp
@@ -1019,7 +1019,7 @@ LBL_InvalidCommand:
 			}
 
 			bSentBND = false;
-			ForkThread(&CMsnProto::msn_keepAliveThread, NULL);
+			if (!hKeepAliveThreadEvt) ForkThread(&CMsnProto::msn_keepAliveThread, NULL);
 #ifdef OBSOLETE
 			/* FIXME: Currently disabled, as P2P maybe not working anymore in MSNP24? */
 			ForkThread(&CMsnProto::MSNConnDetectThread, NULL);
@@ -1122,6 +1122,7 @@ LBL_InvalidCommand:
 										MSN_ProcessURIObject(hContact, xmlact);
 										ezxml_free(xmlact);
 									}
+									continue;
 								} else if (mir_strcmp(msgtype->txt, "Text")) continue;								/* TODO: Implement i.e. RichText/Files for announcement of file transfers */
 							}
 
diff --git a/protocols/MSN/src/msn_contact.cpp b/protocols/MSN/src/msn_contact.cpp
index ea27761a17..0683a6a628 100644
--- a/protocols/MSN/src/msn_contact.cpp
+++ b/protocols/MSN/src/msn_contact.cpp
@@ -39,7 +39,7 @@ MCONTACT CMsnProto::MSN_HContactFromEmail(const char* wlid, const char* msnNick,
 		hContact = (MCONTACT)CallService(MS_DB_CONTACT_ADD, 0, 0);
 		CallService(MS_PROTO_ADDTOCONTACT, hContact, (LPARAM)m_szModuleName);
 		if (netId != NETID_SKYPE) setString(hContact, "e-mail", szEmail);
-		setStringUtf(hContact, "Nick", msnNick ? msnNick : wlid);
+		setStringUtf(hContact, "Nick", msnNick ? msnNick : szEmail);
 		setWord(hContact, "netId", netId);
 		setString(hContact, "wlid", szEmail);
 		if (temporary)
@@ -148,88 +148,102 @@ bool CMsnProto::MSN_AddUser(MCONTACT hContact, const char* email, int netId, int
 		return true;
 
 	bool res = false;
-	if (flags == LIST_FL) {
-		if (needRemove) {
-			if (hContact == NULL) {
-				hContact = MSN_HContactFromEmail(email);
-				if (hContact == NULL)
-					return false;
-			}
-
-			char id[MSN_GUID_LEN];
-			if (!db_get_static(hContact, m_szModuleName, "ID", id, sizeof(id))) {
-				int netId = Lists_GetNetId(email);
-				if (leaveHotmail)
-					res = MSN_ABAddRemoveContact(id, netId, false);
-				else
-					res = MSN_ABAddDelContactGroup(id, NULL, "ABContactDelete");
-				if (res) AddDelUserContList(email, flags, netId, true);
-
-				delSetting(hContact, "GroupID");
-				delSetting(hContact, "ID");
-				MSN_RemoveEmptyGroups();
-			}
+	if (MyOptions.netId == NETID_SKYPE) {
+		if (flags & LIST_BL) {
+			if (needRemove) return MSN_SKYABBlockContact(email, "unblock");
+			return MSN_SKYABBlockContact(email, "block");
 		}
-		else {
-			DBVARIANT dbv = { 0 };
-			if (!mir_strcmp(email, MyOptions.szEmail))
-				getStringUtf("Nick", &dbv);
-
-			unsigned res1 = MSN_ABContactAdd(email, dbv.pszVal, netId, msg, false);
-			if (netId == NETID_MSN && res1 == 2) {
-				netId = NETID_LCS;
-				res = MSN_ABContactAdd(email, dbv.pszVal, netId, msg, false) == 0;
-			}
-			else if (netId == NETID_MSN && res1 == 3) {
-				char szContactID[100];
-				hContact = MSN_HContactFromEmail(email);
-				if (db_get_static(hContact, m_szModuleName, "ID", szContactID, sizeof(szContactID)) == 0) {
-					MSN_ABAddRemoveContact(szContactID, netId, true);
-					res = true;
+		else if ((flags & LIST_FL)) {
+			if (!needRemove) return MSN_SKYABAuthRq(email, msg);
+			if (!leaveHotmail) return MSN_SKYABDeleteContact(email);
+			return true;
+		}
+		else if ((flags & LIST_AL) && !needRemove) return MSN_SKYABAuthRsp(email, "accept");
+		else if ((flags & LIST_RL) && !needRemove) return MSN_SKYABAuthRsp(email, "decline");
+	} else {
+		if (flags == LIST_FL) {
+			if (needRemove) {
+				if (hContact == NULL) {
+					hContact = MSN_HContactFromEmail(email);
+					if (hContact == NULL)
+						return false;
 				}
-			}
-			else res = (res1 == 0);
 
-			if (res) {
-				DBVARIANT dbv;
-				if (!db_get_utf(hContact, "CList", "Group", &dbv)) {
-					MSN_MoveContactToGroup(hContact, dbv.pszVal);
-					db_free(&dbv);
+				char id[MSN_GUID_LEN];
+				if (!db_get_static(hContact, m_szModuleName, "ID", id, sizeof(id))) {
+					int netId = Lists_GetNetId(email);
+					if (leaveHotmail)
+						res = MSN_ABAddRemoveContact(id, netId, false);
+					else
+						res = MSN_ABAddDelContactGroup(id, NULL, "ABContactDelete");
+					if (res) AddDelUserContList(email, flags, netId, true);
+
+					delSetting(hContact, "GroupID");
+					delSetting(hContact, "ID");
+					MSN_RemoveEmptyGroups();
+				}
+			}
+			else {
+				DBVARIANT dbv = { 0 };
+				if (!mir_strcmp(email, MyOptions.szEmail))
+					getStringUtf("Nick", &dbv);
+
+				unsigned res1 = MSN_ABContactAdd(email, dbv.pszVal, netId, msg, false);
+				if (netId == NETID_MSN && res1 == 2) {
+					netId = NETID_LCS;
+					res = MSN_ABContactAdd(email, dbv.pszVal, netId, msg, false) == 0;
+				}
+				else if (netId == NETID_MSN && res1 == 3) {
+					char szContactID[100];
+					hContact = MSN_HContactFromEmail(email);
+					if (db_get_static(hContact, m_szModuleName, "ID", szContactID, sizeof(szContactID)) == 0) {
+						MSN_ABAddRemoveContact(szContactID, netId, true);
+						res = true;
+					}
 				}
+				else res = (res1 == 0);
 
-				char szContactID[100];
-				if (db_get_static(hContact, m_szModuleName, "ID", szContactID, sizeof(szContactID)) == 0)
-					MSN_ABFind("ABFindByContacts", szContactID);
+				if (res) {
+					DBVARIANT dbv;
+					if (!db_get_utf(hContact, "CList", "Group", &dbv)) {
+						MSN_MoveContactToGroup(hContact, dbv.pszVal);
+						db_free(&dbv);
+					}
 
-				MSN_SharingFindMembership(true);
-				AddDelUserContList(email, flags, netId, false);
-			}
-#ifdef OBSOLETE
-			else if (netId == 1 && strstr(email, "@yahoo.com") != 0)
-				MSN_FindYahooUser(email);
-#endif
+					char szContactID[100];
+					if (db_get_static(hContact, m_szModuleName, "ID", szContactID, sizeof(szContactID)) == 0)
+						MSN_ABFind("ABFindByContacts", szContactID);
+
+					MSN_SharingFindMembership(true);
+					AddDelUserContList(email, flags, netId, false);
+				}
+	#ifdef OBSOLETE
+				else if (netId == 1 && strstr(email, "@yahoo.com") != 0)
+					MSN_FindYahooUser(email);
+	#endif
 
-			db_free(&dbv);
+				db_free(&dbv);
+			}
 		}
-	}
-	else if (flags == LIST_LL) {
-		if (needRemove)
-			Lists_Remove(LIST_LL, email);
-		else
-			Lists_Add(LIST_LL, NETID_MSN, email);
-	}
-	else {
-		if (netId == 0)
-			netId = Lists_GetNetId(email);
-		res = MSN_SharingAddDelMember(email, flags, netId, needRemove ? "DeleteMember" : "AddMember");
-		AddDelUserContList(email, flags, netId, needRemove);
-		if ((flags & LIST_BL) && !needRemove) {
-			ThreadData* thread = MSN_GetThreadByContact(email, SERVER_SWITCHBOARD);
-			if (thread) thread->sendTerminate();
+		else if (flags == LIST_LL) {
+			if (needRemove)
+				Lists_Remove(LIST_LL, email);
+			else
+				Lists_Add(LIST_LL, NETID_MSN, email);
 		}
+		else {
+			if (netId == 0)
+				netId = Lists_GetNetId(email);
+			res = MSN_SharingAddDelMember(email, flags, netId, needRemove ? "DeleteMember" : "AddMember");
+			AddDelUserContList(email, flags, netId, needRemove);
+			if ((flags & LIST_BL) && !needRemove) {
+				ThreadData* thread = MSN_GetThreadByContact(email, SERVER_SWITCHBOARD);
+				if (thread) thread->sendTerminate();
+			}
 
-		if ((flags & LIST_PL) && needRemove)
-			MSN_AddUser(hContact, email, netId, LIST_RL);
+			if ((flags & LIST_PL) && needRemove)
+				MSN_AddUser(hContact, email, netId, LIST_RL);
+		}
 	}
 	return res;
 }
@@ -285,7 +299,10 @@ bool CMsnProto::MSN_RefreshContactList(void)
 	}
 	else
 	{
-		/* TODO: Add pulling Skype contacts from event server or skypeweb or other unknown method.. */
+		// Refresh Skype contact list
+		MSN_SKYABRefreshClist();
+		
+		// Populate Contact list on MSN network to get status updates of contacts
 		MSN_CreateContList();
 	}
 
diff --git a/protocols/MSN/src/msn_lists.cpp b/protocols/MSN/src/msn_lists.cpp
index d1b578b36d..c9504c8011 100644
--- a/protocols/MSN/src/msn_lists.cpp
+++ b/protocols/MSN/src/msn_lists.cpp
@@ -280,7 +280,7 @@ void CMsnProto::MSN_CreateContList(void)
 
 	CMStringA cxml;
 
-	cxml.Append("<ml l=\"1\">");
+	cxml.AppendFormat("<ml l=\"%d\">", MyOptions.netId == NETID_MSN?1:0);
 	{
 		mir_cslock lck(m_csLists);
 
diff --git a/protocols/MSN/src/msn_opts.cpp b/protocols/MSN/src/msn_opts.cpp
index 0758b6aed5..83fbd42411 100644
--- a/protocols/MSN/src/msn_opts.cpp
+++ b/protocols/MSN/src/msn_opts.cpp
@@ -231,7 +231,7 @@ LBL_Continue:
 				mir_strcpy(proto->MyOptions.szEmail, szEmail);
 				proto->setString("e-mail", szEmail);
 				proto->setString("wlid", szEmail);
-				proto->setDword("netId", proto->GetMyNetID());
+				proto->setDword("netId", (proto->MyOptions.netId = proto->GetMyNetID()));
 			}
 
 			GetDlgItemTextA(hwndDlg, IDC_PASSWORD, password, SIZEOF(password));
@@ -558,7 +558,7 @@ static INT_PTR CALLBACK DlgProcAccMgrUI(HWND hwndDlg, UINT msg, WPARAM wParam, L
 				mir_strcpy(proto->MyOptions.szEmail, szEmail);
 				proto->setString("e-mail", szEmail);
 				proto->setString("wlid", szEmail);
-				proto->setDword("netId", proto->GetMyNetID());
+				proto->setDword("netId", (proto->MyOptions.netId = proto->GetMyNetID()));
 			}
 
 			GetDlgItemTextA(hwndDlg, IDC_PASSWORD, password, SIZEOF(password));
diff --git a/protocols/MSN/src/msn_proto.cpp b/protocols/MSN/src/msn_proto.cpp
index dce9ae50ff..9e8603def2 100644
--- a/protocols/MSN/src/msn_proto.cpp
+++ b/protocols/MSN/src/msn_proto.cpp
@@ -406,11 +406,13 @@ void __cdecl CMsnProto::MsnSearchAckThread(void* arg)
 		return;
 	}
 
-	unsigned res = MSN_ABContactAdd(email, NULL, NETID_MSN, NULL, 1, true);
-	switch (res) {
-	case 0:
-	case 2:
-	case 3:
+	if (MyOptions.netId == NETID_SKYPE) MSN_SKYABSearch(email, arg);
+	else {
+		unsigned res = MSN_ABContactAdd(email, NULL, NETID_MSN, NULL, 1, true);
+		switch (res) {
+		case 0:
+		case 2:
+		case 3:
 		{
 			PROTOSEARCHRESULT psr = { 0 };
 			psr.cbSize = sizeof(psr);
@@ -418,25 +420,27 @@ void __cdecl CMsnProto::MsnSearchAckThread(void* arg)
 			psr.id.t = (TCHAR*)emailT;
 			psr.nick.t = (TCHAR*)emailT;
 			psr.email.t = (TCHAR*)emailT;
+
 			ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, arg, (LPARAM)&psr);
+			ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, arg, 0);
 		}
-		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, arg, 0);
 		break;
 
-	case 1:
-		if (strstr(email, "@yahoo.com") == NULL)
+		case 1:
+			if (strstr(email, "@yahoo.com") == NULL)
+				ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, arg, 0);
+	#ifdef OBSOLETE
+			else {
+				msnSearchId = arg;
+				MSN_FindYahooUser(email);
+			}
+	#endif
+			break;
+
+		default:
 			ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, arg, 0);
-#ifdef OBSOLETE
-		else {
-			msnSearchId = arg;
-			MSN_FindYahooUser(email);
+			break;
 		}
-#endif
-		break;
-
-	default:
-		ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, arg, 0);
-		break;
 	}
 	mir_free(arg);
 }
@@ -814,6 +818,20 @@ int __cdecl CMsnProto::RecvMsg(MCONTACT hContact, PROTORECVEVENT* pre)
 	return Proto_RecvMessage(hContact, pre);
 }
 
+int CMsnProto::GetInfo(MCONTACT hContact, int infoType)
+{
+	if (MyOptions.netId == NETID_SKYPE) {
+		char tEmail[MSN_MAX_EMAIL_LEN];
+		if (db_get_static(hContact, m_szModuleName, "wlid", tEmail, sizeof(tEmail)))
+			db_get_static(hContact, m_szModuleName, "e-mail", tEmail, sizeof(tEmail));
+
+		MSN_SKYABGetProfile(tEmail);
+		return 1;
+	}
+	return 0;
+}
+
+
 /////////////////////////////////////////////////////////////////////////////////////////
 // MsnRecvContacts - creates a database event from the contacts received
 
diff --git a/protocols/MSN/src/msn_proto.h b/protocols/MSN/src/msn_proto.h
index 08654d0020..abef0e03c2 100644
--- a/protocols/MSN/src/msn_proto.h
+++ b/protocols/MSN/src/msn_proto.h
@@ -46,6 +46,7 @@ struct CMsnProto : public PROTO<CMsnProto>
 	virtual	int       __cdecl FileResume(HANDLE hTransfer, int* action, const TCHAR** szFilename);
 
 	virtual	DWORD_PTR __cdecl GetCaps(int type, MCONTACT hContact = NULL);
+	virtual	int       __cdecl GetInfo(MCONTACT hContact, int infoType);
 
 	virtual	HANDLE    __cdecl SearchBasic(const TCHAR* id);
 	virtual	HANDLE    __cdecl SearchByEmail(const TCHAR* email);
@@ -529,6 +530,19 @@ struct CMsnProto : public PROTO<CMsnProto>
 	void sttNotificationMessage(char* msgBody, bool isInitial);
 	void displayEmailCount(MCONTACT hContact);
 
+	/////////////////////////////////////////////////////////////////////////////////////////
+	//	SKYPE JSON Address Book
+	bool MSN_SKYABRefreshClist(void);
+	bool MSN_SKYABBlockContact(const char *wlid, const char *pszAction);
+	bool MSN_SKYABAuthRq(const char *wlid, const char *pszGreeting);
+	bool MSN_SKYABAuthRsp(const char *wlid, const char *pszAction);
+	bool MSN_SKYABDeleteContact(const char *wlid);
+	bool MSN_SKYABSearch(const char *keyWord, HANDLE hSearch);
+	bool MSN_SKYABGetProfiles(const char *pszPOST);
+	bool MSN_SKYABGetProfile(const char *wlid);
+
+	bool APISkypeComRequest(NETLIBHTTPREQUEST *nlhr, NETLIBHTTPHEADER *headers);
+
 	/////////////////////////////////////////////////////////////////////////////////////////
 	//	MSN SOAP Address Book
 
diff --git a/protocols/MSN/src/msn_skypeab.cpp b/protocols/MSN/src/msn_skypeab.cpp
new file mode 100644
index 0000000000..a30f0fe357
--- /dev/null
+++ b/protocols/MSN/src/msn_skypeab.cpp
@@ -0,0 +1,448 @@
+/*
+Plugin of Miranda IM for communicating with users of the MSN Messenger protocol.
+
+Copyright (c) 2012-2014 Miranda NG Team
+Copyright (c) 2007-2012 Boris Krasnovskiy.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "msn_global.h"
+#include "msn_proto.h"
+#include <m_json.h>
+
+bool CMsnProto::APISkypeComRequest(NETLIBHTTPREQUEST *nlhr, NETLIBHTTPHEADER *headers)
+{
+	const char *pszSkypeToken = GetSkypeToken(false);
+
+	if (!pszSkypeToken) return false;
+	nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
+	nlhr->flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_PERSISTENT | NLHRF_REDIRECT;
+	nlhr->nlc = hHttpsConnection;
+	nlhr->headersCount = 3;
+	nlhr->headers = headers;
+	nlhr->headers[0].szName = "User-Agent";
+	nlhr->headers[0].szValue = (char*)MSN_USER_AGENT;
+	nlhr->headers[1].szName = "Accept";
+	nlhr->headers[1].szValue = "application/json";
+	nlhr->headers[2].szName = "X-Skypetoken";
+	nlhr->headers[2].szValue = (char*)pszSkypeToken;
+	return true;
+}
+
+static TCHAR *get_json_str(JSONNODE *item, const char *pszValue)
+{
+	JSONNODE *node;
+	TCHAR *ret;
+
+	if (node=json_get(item, pszValue)) {
+		ret = json_as_string(node);
+		if (!mir_tstrcmp(ret, _T("null"))) {
+			mir_free(ret);
+			return NULL;
+		}
+		return ret;
+	}
+	return NULL;
+}
+
+bool CMsnProto::MSN_SKYABRefreshClist(void)
+{
+	NETLIBHTTPREQUEST nlhr = { 0 };
+	NETLIBHTTPREQUEST *nlhrReply;
+	NETLIBHTTPHEADER headers[3];
+	CMStringA post;
+	bool bRet = false;
+
+	// initialize the netlib request
+	if (!APISkypeComRequest(&nlhr, headers)) return false;
+	nlhr.requestType = REQUEST_GET;
+	nlhr.szUrl = "https://api.skype.com/users/self/contacts";
+
+	// Query addressbook
+	mHttpsTS = clock();
+	nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
+	mHttpsTS = clock();
+	if (nlhrReply)  {
+		hHttpsConnection = nlhrReply->nlc;
+		if (nlhrReply->resultCode == 200 && nlhrReply->pData) {
+			JSONROOT root(nlhrReply->pData);
+			if (root == NULL) return false;
+
+			JSONNODE *items = json_as_array(root), *item;
+			for (size_t i = 0; i < json_size(items); i++)
+			{
+				int lstId = LIST_FL;
+				ptrT nick;
+
+				item = json_at(items, i);
+				if (item == NULL)
+					break;
+
+				ptrA skypename(mir_t2a(ptrT(json_as_string(json_get(item, "skypename")))));
+				ptrA pszNick(mir_t2a(ptrT(get_json_str(item, "fullname"))));
+				char szWLId[128];
+				mir_snprintf(szWLId, sizeof(szWLId), "%d:%s", NETID_SKYPE, skypename);
+				MCONTACT hContact = MSN_HContactFromEmail(szWLId, pszNick, true, false);
+				if (hContact)
+				{
+					if (!json_as_bool(json_get(item, "authorized"))) lstId = LIST_PL;
+					if (!json_as_bool(json_get(item, "blocked"))) lstId = LIST_BL;
+					Lists_Add(lstId, NETID_SKYPE, skypename, NULL, pszNick, NULL);
+					post.AppendFormat("contacts[]=%s&", skypename);
+				}
+			}
+			bRet = true;
+			json_delete(items);
+			MSN_SKYABGetProfiles((const char*)post);
+		}
+		CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+	} else hHttpsConnection = NULL;
+	return bRet;
+}
+
+// pszPOST = contacts[]={skypename1}&contacts[]={skypename2}&...
+bool CMsnProto::MSN_SKYABGetProfiles(const char *pszPOST)
+{
+	NETLIBHTTPREQUEST nlhr = { 0 };
+	NETLIBHTTPREQUEST *nlhrReply;
+	NETLIBHTTPHEADER headers[4];
+	bool bRet = false;
+
+	// initialize the netlib request
+	if (!APISkypeComRequest(&nlhr, headers)) return false;
+	nlhr.requestType = REQUEST_POST;
+	nlhr.szUrl = "https://api.skype.com/users/self/contacts/profiles";
+	nlhr.dataLength = mir_strlen(pszPOST);
+	nlhr.pData = (char*)pszPOST;
+
+	mHttpsTS = clock();
+	nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
+	mHttpsTS = clock();
+	if (nlhrReply)  {
+		hHttpsConnection = nlhrReply->nlc;
+		if (nlhrReply->resultCode == 200 && nlhrReply->pData) {
+			JSONROOT root(nlhrReply->pData);
+			if (root == NULL) return false;
+
+			JSONNODE *items = json_as_array(root), *item, *node;
+			for (size_t i = 0; i < json_size(items); i++)
+			{
+				item = json_at(items, i);
+				if (item == NULL)
+					break;
+
+				node = json_get(item, "username");
+				ptrA skypename(mir_t2a(ptrT(json_as_string(node))));
+				ptrT value;
+				char szWLId[128];
+				mir_snprintf(szWLId, sizeof(szWLId), "%d:%s", NETID_SKYPE, skypename);
+				MCONTACT hContact = MSN_HContactFromEmail(szWLId, skypename, false, false);
+
+				if (hContact)
+				{
+					if (value=get_json_str(item, "firstname")) setTString(hContact, "FirstName", value);
+					if (value=get_json_str(item, "lastname")) setTString(hContact, "LastName", value);
+					if (value=get_json_str(item, "displayname")) setTString(hContact, "Nick", value);
+					if (value=get_json_str(item, "country")) setString(hContact, "Country", (char*)CallService(MS_UTILS_GETCOUNTRYBYISOCODE, (WPARAM)(char*)_T2A(value), 0));
+					if (value=get_json_str(item, "city")) setTString(hContact, "City", value);
+					if (value=get_json_str(item, "mood")) db_set_ts(hContact, "CList", "StatusMsg", value);
+				}
+			}
+			json_delete(items);
+			bRet = true;
+		}
+		CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+	} else hHttpsConnection = NULL;
+	return bRet;
+}
+
+bool CMsnProto::MSN_SKYABGetProfile(const char *wlid)
+{
+	NETLIBHTTPREQUEST nlhr = { 0 };
+	NETLIBHTTPREQUEST *nlhrReply;
+	NETLIBHTTPHEADER headers[4];
+	bool bRet = false;
+	char szURL[256];
+
+	// initialize the netlib request
+	if (!APISkypeComRequest(&nlhr, headers)) return false;
+	nlhr.requestType = REQUEST_GET;
+	mir_snprintf(szURL, sizeof(szURL), "https://api.skype.com/users/%s/profile", wlid);
+	nlhr.szUrl = szURL;
+
+	mHttpsTS = clock();
+	nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
+	mHttpsTS = clock();
+	if (nlhrReply)  {
+		hHttpsConnection = nlhrReply->nlc;
+		if (nlhrReply->resultCode == 200 && nlhrReply->pData) {
+			JSONROOT item(nlhrReply->pData);
+			JSONNODE *node;
+			if (item == NULL) return false;
+
+			ptrA skypename(mir_t2a(ptrT(json_as_string(json_get(item, "username")))));
+			ptrT value;
+			char szWLId[128];
+			mir_snprintf(szWLId, sizeof(szWLId), "%d:%s", NETID_SKYPE, skypename);
+			MCONTACT hContact = MSN_HContactFromEmail(szWLId, skypename, false, false);
+
+			if (hContact)
+			{
+				if (value=get_json_str(item, "firstname")) setTString(hContact, "FirstName", value);
+				if (value=get_json_str(item, "lastname")) setTString(hContact, "LastName", value);
+				if (value=get_json_str(item, "displayname")) setTString(hContact, "Nick", value);
+				if (value=get_json_str(item, "gender")) setByte(hContact, "Gender", (BYTE)(_ttoi(value) == 1 ? 'M' : 'F'));
+				if (value=get_json_str(item, "birthday")) {
+					int d, m, y;
+					_stscanf(value, _T("%d-%d-%d"), &y, &m, &d);
+					setWord(hContact, "BirthYear", y);
+					setByte(hContact, "BirthDay", d);
+					setByte(hContact, "BirthMonth", m);
+				}
+				if (value=get_json_str(item, "country")) setString(hContact, "Country", (char*)CallService(MS_UTILS_GETCOUNTRYBYISOCODE, (WPARAM)(char*)_T2A(value), 0));
+				if (value=get_json_str(item, "province")) setTString(hContact, "State", value);
+				if (value=get_json_str(item, "city")) setTString(hContact, "City", value);
+				if (value=get_json_str(item, "homepage")) setTString(hContact, "Homepage", value);
+				if (value=get_json_str(item, "about")) setTString(hContact, "About", value);
+				if ((node = json_get(item, "emails")) && !json_empty(node))
+				{
+					JSONNODE *items = json_as_array(node), *item;
+					char szName[16];
+					for (size_t i = 0; i < min(json_size(items), 3); i++)
+					{
+						if (!(item = json_at(items, i))) break;
+						sprintf(szName, "e-mail%d", i);
+						setTString(hContact, szName, ptrT(json_as_string(item)));
+					}
+					json_delete(items);
+				}
+				if (value=get_json_str(item, "phoneMobile")) setTString(hContact, "Cellular", value);
+				if (value=get_json_str(item, "phone")) setTString(hContact, "Phone", value);
+				if (value=get_json_str(item, "phoneOffice")) setTString(hContact, "CompanyPhone", value);
+				if (value=get_json_str(item, "mood")) db_set_ts(hContact, "CList", "StatusMsg", value);
+			}
+			bRet = true;
+		}
+		CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+	} else hHttpsConnection = NULL;
+	return bRet;
+}
+
+// pszAction: "block" or "unblock"
+bool CMsnProto::MSN_SKYABBlockContact(const char *wlid, const char *pszAction)
+{
+	NETLIBHTTPREQUEST nlhr = { 0 };
+	NETLIBHTTPREQUEST *nlhrReply;
+	NETLIBHTTPHEADER headers[4];
+	bool bRet = false;
+	char szURL[256], szPOST[128];
+
+	// initialize the netlib request
+	if (!APISkypeComRequest(&nlhr, headers)) return false;
+	nlhr.requestType = REQUEST_PUT;
+	mir_snprintf(szURL, sizeof(szURL), "https://api.skype.com/users/self/contacts/%s/%s", wlid, pszAction);
+	nlhr.szUrl = szURL;
+	nlhr.headers[3].szName = "Content-type";
+	nlhr.headers[3].szValue = "application/x-www-form-urlencoded";
+	nlhr.headersCount++;
+	nlhr.dataLength = mir_snprintf(szPOST, sizeof(szPOST), "reporterIp=123.123.123.123&uiVersion=%s", msnProductVer);
+	nlhr.pData = szPOST;
+
+	mHttpsTS = clock();
+	nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
+	mHttpsTS = clock();
+	if (nlhrReply)  {
+		hHttpsConnection = nlhrReply->nlc;
+		CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+		bRet = true;
+	} else hHttpsConnection = NULL;
+	return bRet;
+}
+
+bool CMsnProto::MSN_SKYABDeleteContact(const char *wlid)
+{
+	NETLIBHTTPREQUEST nlhr = { 0 };
+	NETLIBHTTPREQUEST *nlhrReply;
+	NETLIBHTTPHEADER headers[4];
+	bool bRet = false;
+	char szURL[256];
+
+	// initialize the netlib request
+	if (!APISkypeComRequest(&nlhr, headers)) return false;
+	nlhr.requestType = REQUEST_DELETE;
+	mir_snprintf(szURL, sizeof(szURL), "https://api.skype.com/users/self/contacts/%s", wlid);
+	nlhr.szUrl = szURL;
+	nlhr.headers[3].szName = "Content-type";
+	nlhr.headers[3].szValue = "application/x-www-form-urlencoded";
+	nlhr.headersCount++;
+
+	mHttpsTS = clock();
+	nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
+	mHttpsTS = clock();
+	if (nlhrReply)  {
+		hHttpsConnection = nlhrReply->nlc;
+		CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+		bRet = true;
+	} else hHttpsConnection = NULL;
+	return bRet;
+}
+
+// pszAction: "accept" or "decline"
+bool CMsnProto::MSN_SKYABAuthRsp(const char *wlid, const char *pszAction)
+{
+	NETLIBHTTPREQUEST nlhr = { 0 };
+	NETLIBHTTPREQUEST *nlhrReply;
+	NETLIBHTTPHEADER headers[3];
+	bool bRet = false;
+	char szURL[256];
+
+	// initialize the netlib request
+	if (!APISkypeComRequest(&nlhr, headers)) return false;
+	nlhr.requestType = REQUEST_PUT;
+	mir_snprintf(szURL, sizeof(szURL), "https://api.skype.com/users/self/contacts/auth-request/%s/%s", wlid, pszAction);
+	nlhr.szUrl = szURL;
+
+	mHttpsTS = clock();
+	nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
+	mHttpsTS = clock();
+	if (nlhrReply)  {
+		hHttpsConnection = nlhrReply->nlc;
+		CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+		bRet = true;
+	} else hHttpsConnection = NULL;
+	return bRet;
+}
+
+bool CMsnProto::MSN_SKYABAuthRq(const char *wlid, const char *pszGreeting)
+{
+	NETLIBHTTPREQUEST nlhr = { 0 };
+	NETLIBHTTPREQUEST *nlhrReply;
+	NETLIBHTTPHEADER headers[4];
+	bool bRet = false;
+	char szURL[256];
+	CMStringA post;
+
+	// initialize the netlib request
+	if (!APISkypeComRequest(&nlhr, headers)) return false;
+	nlhr.requestType = REQUEST_PUT;
+	mir_snprintf(szURL, sizeof(szURL), "https://api.skype.com/users/self/contacts/auth-request/%s", wlid);
+	nlhr.szUrl = szURL;
+	nlhr.headers[3].szName = "Content-type";
+	nlhr.headers[3].szValue = "application/x-www-form-urlencoded";
+	nlhr.headersCount++;
+	post.Format("greeting=%s", pszGreeting);
+	nlhr.dataLength = mir_strlen(post);
+	nlhr.pData = (char*)(const char*)post;
+
+	mHttpsTS = clock();
+	nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
+	mHttpsTS = clock();
+	if (nlhrReply)  {
+		hHttpsConnection = nlhrReply->nlc;
+		CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+		bRet = true;
+	} else hHttpsConnection = NULL;
+	return bRet;
+}
+
+bool CMsnProto::MSN_SKYABSearch(const char *keyWord, HANDLE hSearch)
+{
+	NETLIBHTTPREQUEST nlhr = { 0 };
+	NETLIBHTTPREQUEST *nlhrReply;
+	NETLIBHTTPHEADER headers[4];
+	bool bRet = false;
+	char szURL[256];
+
+	// initialize the netlib request
+	if (!APISkypeComRequest(&nlhr, headers)) {
+		ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, hSearch, 0);
+		return false;
+	}
+	nlhr.requestType = REQUEST_GET;
+	mir_snprintf(szURL, sizeof(szURL), "https://api.skype.com/search/users/any?keyWord=%s&contactTypes[]=skype", keyWord);
+	nlhr.szUrl = szURL;
+	nlhr.headers[3].szName = "Connection";
+	nlhr.headers[3].szValue = "keep-alive";
+	nlhr.headersCount++;
+
+	mHttpsTS = clock();
+	nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr);
+	mHttpsTS = clock();
+	if (nlhrReply)  {
+		hHttpsConnection = nlhrReply->nlc;
+		if (nlhrReply->resultCode == 200 && nlhrReply->pData) {
+			JSONROOT root(nlhrReply->pData);
+			if (root == NULL)
+			{
+				ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, hSearch, 0);
+				return false;
+			}
+
+			JSONNODE *items = json_as_array(root);
+			for (size_t i = 0; i < json_size(items); i++)
+			{
+				JSONNODE *item = json_at(items, i);
+				JSONNODE *ContactCards = json_get(item, "ContactCards");
+				JSONNODE *Skype = json_get(ContactCards, "Skype");
+
+				TCHAR *sDisplayName = json_as_string(json_get(Skype, "DisplayName"));
+				TCHAR *sSkypeName = json_as_string(json_get(Skype, "SkypeName"));
+
+				PROTOSEARCHRESULT psr = { sizeof(psr) };
+				psr.flags = PSR_TCHAR;
+				psr.id.t = sSkypeName;
+				psr.nick.t = sDisplayName;
+				ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_DATA, hSearch, (LPARAM)&psr);
+			}
+			json_free(items);
+			ProtoBroadcastAck(0, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, hSearch, 0);
+			bRet = true;
+		}
+		CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply);
+	} else hHttpsConnection = NULL;
+	return bRet;
+}
+
+/*
+class GetContactsInfoRequest : public HttpRequest
+{
+public:
+	GetContactsInfoRequest(const char *token, const LIST<char> &skypenames, const char *skypename = "self") :
+		HttpRequest(REQUEST_POST, FORMAT, "api.skype.com/users/%s/contacts/profiles", skypename)
+	{
+		Headers
+			<< CHAR_VALUE("X-Skypetoken", token)
+			<< CHAR_VALUE("Accept", "application/json");
+
+		for (int i = 0; i < skypenames.getCount(); i++)
+			Body << CHAR_VALUE("contacts[]", skypenames[i]);
+	}
+};
+
+class GetContactStatusRequest : public HttpRequest
+{
+public:
+	GetContactStatusRequest(const char *regToken, const char *skypename, const char *server = SKYPE_ENDPOINTS_HOST) :
+		HttpRequest(REQUEST_GET, FORMAT, "%s/v1/users/ME/contacts/8:%s/presenceDocs/messagingService", server, skypename)
+	{
+		Headers
+			<< CHAR_VALUE("Accept", "application/json, text/javascript")
+			<< FORMAT_VALUE("RegistrationToken", "registrationToken=%s", regToken);
+	}
+};
+
+
+
+*/
\ No newline at end of file
diff --git a/protocols/MSN/src/msn_svcs.cpp b/protocols/MSN/src/msn_svcs.cpp
index 22fa3d1b6b..3ca83db2b0 100644
--- a/protocols/MSN/src/msn_svcs.cpp
+++ b/protocols/MSN/src/msn_svcs.cpp
@@ -449,7 +449,7 @@ int CMsnProto::OnDbSettingChanged(WPARAM hContact, LPARAM lParam)
 {
 	DBCONTACTWRITESETTING* cws = (DBCONTACTWRITESETTING*)lParam;
 
-	if (!msnLoggedIn)
+	if (!msnLoggedIn || MyOptions.netId != NETID_MSN)
 		return 0;
 
 	if (hContact == NULL) {
-- 
cgit v1.2.3