From 21e7268a503be780b35e367564d711e79a61dffc Mon Sep 17 00:00:00 2001
From: Alexander Lantsev <aunsane@gmail.com>
Date: Sat, 28 Feb 2015 06:48:33 +0000
Subject: Tox: - added support of tox1 dns resolving - added request/grant
 items in contact menu - added copy id item in statum menu - code reordering -
 fixed minor bugs

git-svn-id: http://svn.miranda-ng.org/main/trunk@12281 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
---
 protocols/Tox/Tox_12.vcxproj         |   3 +
 protocols/Tox/Tox_12.vcxproj.filters |   9 +++
 protocols/Tox/src/common.h           |   6 +-
 protocols/Tox/src/main.cpp           |  17 ++++-
 protocols/Tox/src/tox_accounts.cpp   |  19 ++++--
 protocols/Tox/src/tox_address.h      |  34 ++++++----
 protocols/Tox/src/tox_contacts.cpp   |  69 ++++++++++++++++++--
 protocols/Tox/src/tox_core.cpp       |   6 +-
 protocols/Tox/src/tox_dns.h          |   6 +-
 protocols/Tox/src/tox_icons.cpp      |  63 ++++++++++++++++++
 protocols/Tox/src/tox_icons.h        |  12 ++++
 protocols/Tox/src/tox_menus.cpp      |  96 +++++++++++++++++++++++----
 protocols/Tox/src/tox_menus.h        |  13 ++++
 protocols/Tox/src/tox_messages.cpp   |   3 -
 protocols/Tox/src/tox_profile.cpp    |  16 +++++
 protocols/Tox/src/tox_proto.cpp      |  83 +++++++-----------------
 protocols/Tox/src/tox_proto.h        |  48 +++++++++++---
 protocols/Tox/src/tox_search.cpp     | 122 +++++++++++++++++++++++++++--------
 protocols/Tox/src/tox_transfer.cpp   |  18 +++---
 19 files changed, 490 insertions(+), 153 deletions(-)
 create mode 100644 protocols/Tox/src/tox_icons.cpp
 create mode 100644 protocols/Tox/src/tox_icons.h
 create mode 100644 protocols/Tox/src/tox_menus.h

(limited to 'protocols/Tox')

diff --git a/protocols/Tox/Tox_12.vcxproj b/protocols/Tox/Tox_12.vcxproj
index e1503116bd..ca8ff06d6c 100644
--- a/protocols/Tox/Tox_12.vcxproj
+++ b/protocols/Tox/Tox_12.vcxproj
@@ -205,6 +205,8 @@
     <ClInclude Include="src\tox_address.h" />
     <ClInclude Include="src\tox_chatrooms.h" />
     <ClInclude Include="src\tox_dns.h" />
+    <ClInclude Include="src\tox_icons.h" />
+    <ClInclude Include="src\tox_menus.h" />
     <ClInclude Include="src\tox_options.h" />
     <ClInclude Include="src\tox_proto.h" />
     <ClInclude Include="src\tox_transfer.h" />
@@ -227,6 +229,7 @@
     <ClCompile Include="src\tox_avatars.cpp" />
     <ClCompile Include="src\tox_events.cpp" />
     <ClCompile Include="src\tox_chatrooms.cpp" />
+    <ClCompile Include="src\tox_icons.cpp" />
     <ClCompile Include="src\tox_menus.cpp" />
     <ClCompile Include="src\tox_messages.cpp" />
     <ClCompile Include="src\tox_netlib.cpp" />
diff --git a/protocols/Tox/Tox_12.vcxproj.filters b/protocols/Tox/Tox_12.vcxproj.filters
index 4a3b598362..dd4311df49 100644
--- a/protocols/Tox/Tox_12.vcxproj.filters
+++ b/protocols/Tox/Tox_12.vcxproj.filters
@@ -57,6 +57,12 @@
     <ClInclude Include="src\tox_chatrooms.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="src\tox_icons.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="src\tox_menus.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="src\tox_proto.cpp">
@@ -137,6 +143,9 @@
     <ClCompile Include="src\tox_menus.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\tox_icons.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="res\resource.rc">
diff --git a/protocols/Tox/src/common.h b/protocols/Tox/src/common.h
index d450211fe8..d6bdc6e1c4 100644
--- a/protocols/Tox/src/common.h
+++ b/protocols/Tox/src/common.h
@@ -43,6 +43,8 @@ struct CToxProto;
 
 #include "version.h"
 #include "resource.h"
+#include "tox_icons.h"
+#include "tox_menus.h"
 #include "tox_address.h"
 #include "tox_options.h"
 #include "tox_transfer.h"
@@ -82,10 +84,6 @@ extern HMODULE g_hToxLibrary;
 template<typename T>
 T CreateFunction(LPCSTR functionName)
 {
-	if (g_hToxLibrary == NULL)
-	{
-		g_hToxLibrary = LoadLibrary(L"libtox.dll");
-	}
 	return reinterpret_cast<T>(GetProcAddress(g_hToxLibrary, functionName));
 }
 
diff --git a/protocols/Tox/src/main.cpp b/protocols/Tox/src/main.cpp
index fd0f48aab0..6d9711e12d 100644
--- a/protocols/Tox/src/main.cpp
+++ b/protocols/Tox/src/main.cpp
@@ -35,6 +35,12 @@ extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCO
 
 extern "C" int __declspec(dllexport) Load(void)
 {
+	g_hToxLibrary = LoadLibrary(_T("libtox.dll"));
+	if (g_hToxLibrary == NULL)
+	{
+		return 0;
+	}
+
 	mir_getLP(&pluginInfo);
 
 	PROTOCOLDESCRIPTOR pd = { sizeof(pd) };
@@ -42,14 +48,23 @@ extern "C" int __declspec(dllexport) Load(void)
 	pd.type = PROTOTYPE_PROTOCOL;
 	pd.fnInit = (pfnInitProto)CToxProto::InitAccount;
 	pd.fnUninit = (pfnUninitProto)CToxProto::UninitAccount;
-	return CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM)&pd);
+	CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM)&pd);
+
+	CToxProto::InitIcons();
+	CToxProto::InitMenus();
+
+	return 0;
 }
 
 extern "C" int __declspec(dllexport) Unload(void)
 {
+	CToxProto::UninitIcons();
+	CToxProto::UninitMenus();
+
 	if (g_hToxLibrary)
 	{
 		FreeLibrary(g_hToxLibrary);
 	}
+
 	return 0;
 }
\ No newline at end of file
diff --git a/protocols/Tox/src/tox_accounts.cpp b/protocols/Tox/src/tox_accounts.cpp
index ce435043d7..618105dba7 100644
--- a/protocols/Tox/src/tox_accounts.cpp
+++ b/protocols/Tox/src/tox_accounts.cpp
@@ -1,6 +1,6 @@
 #include "common.h"
 
-LIST<CToxProto> CToxProto::accounts(1, CToxProto::CompareAccounts);
+LIST<CToxProto> CToxProto::Accounts(1, CToxProto::CompareAccounts);
 
 int CToxProto::CompareAccounts(const CToxProto *p1, const CToxProto *p2)
 {
@@ -10,18 +10,29 @@ int CToxProto::CompareAccounts(const CToxProto *p1, const CToxProto *p2)
 CToxProto* CToxProto::InitAccount(const char *protoName, const wchar_t *userName)
 {
 	CToxProto *proto = new CToxProto(protoName, userName);
-	accounts.insert(proto);
+	Accounts.insert(proto);
 	return proto;
 }
 
 int CToxProto::UninitAccount(CToxProto *proto)
 {
-	accounts.remove(proto);
+	Accounts.remove(proto);
 	delete proto;
-
 	return 0;
 }
 
+CToxProto* CToxProto::GetContactAccount(MCONTACT hContact)
+{
+	for (int i = 0; i < Accounts.getCount(); i++)
+	{
+		if (mir_strcmpi(GetContactProto(hContact), Accounts[i]->m_szModuleName) == 0)
+		{
+			return Accounts[i];
+		}
+	}
+	return NULL;
+}
+
 int CToxProto::OnAccountLoaded(WPARAM, LPARAM)
 {
 	HookProtoEvent(ME_OPT_INITIALISE, &CToxProto::OnOptionsInit);
diff --git a/protocols/Tox/src/tox_address.h b/protocols/Tox/src/tox_address.h
index 6eada63300..4270e23ee2 100644
--- a/protocols/Tox/src/tox_address.h
+++ b/protocols/Tox/src/tox_address.h
@@ -9,12 +9,11 @@ class ToxHexAddress
 private:
 	std::string hexData;
 public:
-	ToxHexAddress(const char *hex) : hexData(hex) { }
-	ToxHexAddress(const std::string &hex) : hexData(hex) { }
 	ToxHexAddress(const ToxHexAddress &address) : hexData(address.hexData) { }
-	ToxHexAddress(const std::vector<uint8_t> &bin)
+	ToxHexAddress(const char *hex, size_t size = TOX_FRIEND_ADDRESS_SIZE * 2) : hexData(hex, hex + size) { }
+	ToxHexAddress(const std::string &hex)
 	{
-		this->ToxHexAddress::ToxHexAddress(bin.data(), bin.size());
+		this->ToxHexAddress::ToxHexAddress(hex.c_str(), hex.size());
 	}
 	ToxHexAddress(const uint8_t *bin, size_t size = TOX_FRIEND_ADDRESS_SIZE)
 	{
@@ -23,10 +22,18 @@ public:
 		std::transform(hexData.begin(), hexData.end(), hexData.begin(), ::toupper);
 		mir_free(hex);
 	}
+	ToxHexAddress(const std::vector<uint8_t> &bin)
+	{
+		this->ToxHexAddress::ToxHexAddress(bin.data(), bin.size());
+	}
 	const size_t GetLength() const
 	{
 		return hexData.length();
 	}
+	const bool IsEmpty() const
+	{
+		return hexData.length() == 0;
+	}
 	const ToxHexAddress GetPubKey() const
 	{
 		ToxHexAddress pubKey = hexData.substr(0, TOX_PUBLIC_KEY_SIZE * 2).c_str();
@@ -36,6 +43,10 @@ public:
 	{
 		return hexData.c_str();
 	}
+	static ToxHexAddress Empty()
+	{
+		return ToxHexAddress("");
+	}
 	ToxBinAddress ToBin() const;
 };
 
@@ -45,24 +56,23 @@ private:
 	std::vector<uint8_t> binData;
 public:
 	ToxBinAddress(const ToxBinAddress &address) : binData(address.binData) { }
-	ToxBinAddress(const std::vector<uint8_t> &bin) : binData(bin) { }
 	ToxBinAddress(const uint8_t *bin, size_t size = TOX_FRIEND_ADDRESS_SIZE) : binData(bin, bin + size) { }
-	ToxBinAddress(const std::string &hex)
-	{
-		this->ToxBinAddress::ToxBinAddress(hex.c_str());
-	}
-	ToxBinAddress(const char *hex)
+	ToxBinAddress(const std::vector<uint8_t> &bin, size_t size = TOX_FRIEND_ADDRESS_SIZE) : binData(bin.begin(), bin.begin() + size) { }
+	ToxBinAddress(const char *hex, size_t size = TOX_FRIEND_ADDRESS_SIZE * 2)
 	{
 		char *endptr;
 		const char *pos = hex;
-		size_t size = mir_strlen(hex) / 2;
-		for (size_t i = 0; i < size; i++)
+		for (size_t i = 0; i < size / 2; i++)
 		{
 			char buf[5] = { '0', 'x', pos[0], pos[1], 0 };
 			binData.push_back((uint8_t)strtol(buf, &endptr, 0));
 			pos += 2;
 		}
 	}
+	ToxBinAddress(const std::string &hex)
+	{
+		this->ToxBinAddress::ToxBinAddress(hex.c_str(), hex.size());
+	}
 	const ToxBinAddress GetPubKey() const
 	{
 		ToxBinAddress pubKey(binData.data(), TOX_PUBLIC_KEY_SIZE);
diff --git a/protocols/Tox/src/tox_contacts.cpp b/protocols/Tox/src/tox_contacts.cpp
index d4173a041a..fdcc378691 100644
--- a/protocols/Tox/src/tox_contacts.cpp
+++ b/protocols/Tox/src/tox_contacts.cpp
@@ -26,7 +26,7 @@ MCONTACT CToxProto::GetContactFromAuthEvent(MEVENT hEvent)
 {
 	DWORD body[3];
 	DBEVENTINFO dbei = { sizeof(DBEVENTINFO) };
-	dbei.cbBlob = sizeof(DWORD)* 2;
+	dbei.cbBlob = sizeof(DWORD) * 2;
 	dbei.pBlob = (PBYTE)&body;
 
 	if (db_event_get(hEvent, &dbei))
@@ -137,7 +137,7 @@ void CToxProto::LoadFriendList(void*)
 				}
 
 				uint64_t timestamp = tox_get_last_online(tox, friendNumber);
-				if (timestamp)
+				if (timestamp > getDword(hContact, "LastEventDateTS", 0))
 				{
 					setDword(hContact, "LastEventDateTS", timestamp);
 				}
@@ -152,6 +152,58 @@ void CToxProto::LoadFriendList(void*)
 	}
 }
 
+int CToxProto::OnRequestAuth(MCONTACT hContact, LPARAM lParam)
+{
+	if (!IsOnline())
+	{
+		return 1;
+	}
+
+	char *reason = lParam ? (char*)lParam : " ";
+	size_t length = mir_strlen(reason);
+	ToxBinAddress address(ptrA(getStringA(hContact, TOX_SETTINGS_ID)));
+
+	int32_t friendNumber = tox_add_friend(tox, address, (uint8_t*)reason, length);
+	if (friendNumber <= TOX_ERROR)
+	{
+		debugLogA("CToxProto::OnRequestAuth: failed to request auth");
+		return 2;
+	}
+
+	// trim address to public key
+	setString(hContact, TOX_SETTINGS_ID, address.ToHex().GetPubKey());
+	db_unset(hContact, "CList", "NotOnList");
+	delSetting(hContact, "Grant");
+
+	std::string nick("", TOX_MAX_NAME_LENGTH);
+	tox_get_name(tox, friendNumber, (uint8_t*)&nick[0]);
+	setString(hContact, "Nick", nick.c_str());
+
+	return 0;
+}
+
+int CToxProto::OnGrantAuth(MCONTACT hContact, LPARAM)
+{
+	if (!IsOnline())
+	{
+		return 1;
+	}
+
+	ToxBinAddress pubKey(ptrA(getStringA(hContact, TOX_SETTINGS_ID)), TOX_CLIENT_ID_SIZE * 2);
+	if (tox_add_friend_norequest(tox, pubKey) == TOX_ERROR)
+	{
+		debugLogA("CToxProto::OnGrantAuth: failed to grant auth");
+		return 2;
+	}
+
+	// trim address to public key
+	setString(hContact, TOX_SETTINGS_ID, pubKey.ToHex());
+	db_unset(hContact, "CList", "NotOnList");
+	delSetting(hContact, "Grant");
+
+	return 0;
+}
+
 void CToxProto::OnFriendRequest(Tox *, const uint8_t *data, const uint8_t *message, const uint16_t messageSize, void *arg)
 {
 	CToxProto *proto = (CToxProto*)arg;
@@ -236,11 +288,12 @@ void CToxProto::OnConnectionStatusChanged(Tox *tox, const int friendNumber, cons
 		{
 			tox_send_avatar_info(proto->tox, friendNumber);
 			proto->delSetting(hContact, "Auth");
+			proto->delSetting(hContact, "Grant");
 
-			for (size_t i = 0; i < proto->transfers->Count(); i++)
+			for (size_t i = 0; i < proto->transfers.Count(); i++)
 			{
 				// only for receiving
-				FileTransferParam *transfer = proto->transfers->GetAt(i);
+				FileTransferParam *transfer = proto->transfers.GetAt(i);
 				if (transfer->friendNumber == friendNumber && transfer->GetDirection() == 1)
 				{
 					proto->debugLogA("CToxProto::OnConnectionStatusChanged: sending ask to resume the transfer of file (%d)", transfer->fileNumber);
@@ -254,9 +307,11 @@ void CToxProto::OnConnectionStatusChanged(Tox *tox, const int friendNumber, cons
 		}
 		else
 		{
-			for (size_t i = 0; i < proto->transfers->Count(); i++)
+			proto->setDword(hContact, "LastEventDateTS", time(NULL));
+
+			for (size_t i = 0; i < proto->transfers.Count(); i++)
 			{
-				FileTransferParam *transfer = proto->transfers->GetAt(i);
+				FileTransferParam *transfer = proto->transfers.GetAt(i);
 				if (transfer->friendNumber == friendNumber)
 				{
 					transfer->status = BROKEN;
@@ -323,7 +378,7 @@ INT_PTR CToxProto::UserInfoProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPar
 
 				SetDlgItemText(hwnd, IDC_DNS_ID, ptrT(proto->getTStringA(hContact, TOX_SETTINGS_DNS)));
 			}
-				break;
+			break;
 
 			case PSN_PARAMCHANGED:
 				SetWindowLongPtr(hwnd, GWLP_USERDATA, ((PSHNOTIFY*)lParam)->lParam);
diff --git a/protocols/Tox/src/tox_core.cpp b/protocols/Tox/src/tox_core.cpp
index ae1d8eec51..337ac9b2f7 100644
--- a/protocols/Tox/src/tox_core.cpp
+++ b/protocols/Tox/src/tox_core.cpp
@@ -106,13 +106,13 @@ bool CToxProto::InitToxCore()
 
 void CToxProto::UninitToxCore()
 {
-	for (size_t i = 0; i < transfers->Count(); i++)
+	for (size_t i = 0; i < transfers.Count(); i++)
 	{
-		FileTransferParam *transfer = transfers->GetAt(i);
+		FileTransferParam *transfer = transfers.GetAt(i);
 		transfer->status = CANCELED;
 		tox_file_send_control(tox, transfer->friendNumber, transfer->GetDirection(), transfer->fileNumber, TOX_FILECONTROL_KILL, NULL, 0);
 		ProtoBroadcastAck(transfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DENIED, (HANDLE)transfer, 0);
-		transfers->Remove(transfer);
+		transfers.Remove(transfer);
 	}
 
 	ptrA nickname(mir_utf8encodeW(ptrT(getTStringA("Nick"))));
diff --git a/protocols/Tox/src/tox_dns.h b/protocols/Tox/src/tox_dns.h
index 1629141680..394ea9f61f 100644
--- a/protocols/Tox/src/tox_dns.h
+++ b/protocols/Tox/src/tox_dns.h
@@ -1,10 +1,12 @@
 #ifndef _TOX_DNS_H_
 #define _TOX_DNS_H_
 
-struct dns_server {
+struct dns_server
+{
 	char *domain;
 	uint8_t key[32];
-} dns_servers[] = {
+} dns_servers[] =
+{
 	{
 		"toxme.se",
 		{
diff --git a/protocols/Tox/src/tox_icons.cpp b/protocols/Tox/src/tox_icons.cpp
new file mode 100644
index 0000000000..505dbec2e3
--- /dev/null
+++ b/protocols/Tox/src/tox_icons.cpp
@@ -0,0 +1,63 @@
+#include "common.h"
+
+IconInfo CToxProto::Icons[] =
+{
+	{ LPGENT("Protocol icon"),			"main",				IDI_TOX },
+};
+
+void CToxProto::InitIcons()
+{
+	TCHAR szFile[MAX_PATH];
+	GetModuleFileName(g_hInstance, szFile, MAX_PATH);
+
+	char szSettingName[100];
+	TCHAR szSectionName[100];
+
+	SKINICONDESC sid = { sizeof(SKINICONDESC) };
+	sid.flags = SIDF_ALL_TCHAR;
+	sid.ptszDefaultFile = szFile;
+	sid.pszName = szSettingName;
+	sid.ptszSection = szSectionName;
+
+	mir_sntprintf(szSectionName, SIZEOF(szSectionName), _T("%s/%s"), LPGENT("Protocols"), LPGENT(MODULE));
+	for (int i = 0; i < SIZEOF(Icons); i++)
+	{
+		mir_snprintf(szSettingName, SIZEOF(szSettingName), "%s_%s", MODULE, Icons[i].Name);
+
+		sid.ptszDescription = Icons[i].Description;
+		sid.iDefaultIndex = -Icons[i].IconId;
+		Icons[i].Handle = Skin_AddIcon(&sid);
+	}	
+}
+
+HANDLE CToxProto::GetIconHandle(const char *name)
+{
+	for (size_t i = 0; i < SIZEOF(Icons); i++)
+	{
+		if (mir_strcmpi(Icons[i].Name, name) == 0)
+		{
+			return Icons[i].Handle;
+		}
+	}
+	return 0;
+}
+
+HANDLE CToxProto::GetSkinIconHandle(const char *name)
+{
+	char iconName[100];
+	mir_snprintf(iconName, SIZEOF(iconName), "%s_%s", MODULE, name);
+	HANDLE hIcon = Skin_GetIconHandle(iconName);
+	if (hIcon == NULL)
+	{
+		hIcon = GetIconHandle(name);
+	}
+	return hIcon;
+}
+
+void CToxProto::UninitIcons()
+{
+	for (size_t i = 0; i < SIZEOF(Icons); i++)
+	{
+		Skin_RemoveIcon(Icons[i].Name);
+	}
+}
\ No newline at end of file
diff --git a/protocols/Tox/src/tox_icons.h b/protocols/Tox/src/tox_icons.h
new file mode 100644
index 0000000000..919050c631
--- /dev/null
+++ b/protocols/Tox/src/tox_icons.h
@@ -0,0 +1,12 @@
+#ifndef _TOX_ICONS_H_
+#define _TOX_ICONS_H_
+
+struct IconInfo
+{
+	TCHAR *Description;
+	char *Name;
+	int IconId;
+	HANDLE Handle;
+};
+
+#endif //_TOX_ICONS_H_
\ No newline at end of file
diff --git a/protocols/Tox/src/tox_menus.cpp b/protocols/Tox/src/tox_menus.cpp
index 2afa6fd218..60daad3090 100644
--- a/protocols/Tox/src/tox_menus.cpp
+++ b/protocols/Tox/src/tox_menus.cpp
@@ -1,5 +1,72 @@
 #include "common.h"
 
+HGENMENU CToxProto::ContactMenuItems[CMI_MAX];
+
+int CToxProto::OnPrebuildContactMenu(MCONTACT hContact, LPARAM)
+{
+	if (!hContact)
+		return 0;
+
+	if (!this->IsOnline())
+		return 0;
+
+	if (this->isChatRoom(hContact))
+		return 0;
+
+	bool isCtrlPressed = (GetKeyState(VK_CONTROL) & 0x8000) != 0;
+	bool isAuthNeed = getByte(hContact, "Auth", 0) > 0;
+	bool isGrantNeed = getByte(hContact, "Grant", 0) > 0;
+	//bool isOffline = GetContactStatus(hContact) == ID_STATUS_OFFLINE;
+	//bool isLongOffline = ((time(NULL) - getDword(hContact, "LastEventDateTS", 0)) / (1000 * 60 * 60 * 24 * 7)) > 0;
+	//bool hasDnsID = mir_strlen(ptrA(getStringA(hContact, TOX_SETTINGS_DNS))) > 0;
+
+	Menu_ShowItem(ContactMenuItems[CMI_AUTH_REQUEST], isCtrlPressed || isAuthNeed);
+	Menu_ShowItem(ContactMenuItems[CMI_AUTH_GRANT], isCtrlPressed || isGrantNeed);
+
+	return 0;
+}
+
+int CToxProto::PrebuildContactMenu(MCONTACT hContact, LPARAM lParam)
+{
+	for (int i = 0; i < SIZEOF(ContactMenuItems); i++)
+	{
+		Menu_ShowItem(ContactMenuItems[i], false);
+	}
+	CToxProto *proto = CToxProto::GetContactAccount(hContact);
+	return proto ? proto->OnPrebuildContactMenu(hContact, lParam) : 0;
+}
+
+void CToxProto::InitMenus()
+{
+	HookEvent(ME_CLIST_PREBUILDCONTACTMENU, &CToxProto::PrebuildContactMenu);
+
+	//hChooserMenu = MO_CreateMenuObject("SkypeAccountChooser", LPGEN("Skype menu chooser"), 0, "Skype/MenuChoose");
+
+	CLISTMENUITEM mi = { sizeof(CLISTMENUITEM) };
+	mi.flags = CMIF_TCHAR;
+
+	// Request authorization
+	mi.pszService = MODULE"/RequestAuth";
+	mi.ptszName = LPGENT("Request authorization");
+	mi.position = CMI_POSITION + CMI_AUTH_REQUEST;
+	mi.icolibItem = LoadSkinnedIconHandle(SKINICON_AUTH_REQUEST);
+	ContactMenuItems[CMI_AUTH_REQUEST] = Menu_AddContactMenuItem(&mi);
+	CreateServiceFunction(mi.pszService, GlobalService<&CToxProto::OnRequestAuth>);
+
+	// Grant authorization
+	mi.pszService = MODULE"/GrantAuth";
+	mi.ptszName = LPGENT("Grant authorization");
+	mi.position = CMI_POSITION + CMI_AUTH_GRANT;
+	mi.icolibItem = LoadSkinnedIconHandle(SKINICON_AUTH_GRANT);
+	ContactMenuItems[CMI_AUTH_GRANT] = Menu_AddContactMenuItem(&mi);
+	CreateServiceFunction(mi.pszService, GlobalService<&CToxProto::OnGrantAuth>);
+}
+
+void CToxProto::UninitMenus()
+{
+}
+
+
 int CToxProto::OnInitStatusMenu()
 {
 	char text[MAX_PATH];
@@ -16,26 +83,33 @@ int CToxProto::OnInitStatusMenu()
 		mi.position = -1999901006;
 		mi.hParentMenu = HGENMENU_ROOT;
 		mi.flags = CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
-		//mi.icolibItem = CToxProto::GetSkinIconHandle("main");
+		mi.icolibItem = GetSkinIconHandle("main");
 		hStatusMunuRoot = /*m_hMenuRoot = */Menu_AddProtoMenuItem(&mi);
 	}
-	else
+	/*else
 	{
-		//if (m_hMenuRoot)
-		//	CallService(MO_REMOVEMENUITEM, (WPARAM)m_hMenuRoot, 0);
-		//m_hMenuRoot = NULL;
-	}
+		if (m_hMenuRoot)
+			CallService(MO_REMOVEMENUITEM, (WPARAM)m_hMenuRoot, 0);
+		m_hMenuRoot = NULL;
+	}*/
 
 	mi.hParentMenu = hStatusMunuRoot;
 	mi.flags = CMIF_CHILDPOPUP | CMIF_TCHAR;
 
-	// Create chat room command
-	mir_strcpy(tDest, "/CreateChatRoom");
+	// Create copy tox id command
+	mir_strcpy(tDest, "/CopyToxID");
+	CreateProtoService(tDest, &CToxProto::OnCopyToxID);
+	mi.ptszName = LPGENT("Copy Tox ID");
+	mi.position = SMI_POSITION + SMI_TOXID_COPY;
+	Menu_AddProtoMenuItem(&mi);
+
+	// Create group chat command
+	/*mir_strcpy(tDest, "/CreateChatRoom");
 	CreateProtoService(tDest, &CToxProto::OnCreateChatRoom);
 	mi.ptszName = LPGENT("Create group chat");
-	mi.position = 200000;// +SMI_CHAT_CREATE;
-	//mi.icolibItem = CToxProto::GetSkinIconHandle("conference");
-	Menu_AddProtoMenuItem(&mi);
+	mi.position = SMI_POSITION + SMI_GROUPCHAT_CREATE;
+	mi.icolibItem = GetSkinIconHandle("conference");
+	Menu_AddProtoMenuItem(&mi);*/
 
 	return 0;
 }
\ No newline at end of file
diff --git a/protocols/Tox/src/tox_menus.h b/protocols/Tox/src/tox_menus.h
new file mode 100644
index 0000000000..e75f9c8b45
--- /dev/null
+++ b/protocols/Tox/src/tox_menus.h
@@ -0,0 +1,13 @@
+#ifndef _TOX_MENUS_H_
+#define _TOX_MENUS_H_
+
+#define CMI_POSITION -201001000
+#define CMI_AUTH_REQUEST 1
+#define CMI_AUTH_GRANT 2
+#define CMI_MAX 3 // this item shall be the last one
+
+#define SMI_POSITION 200000
+#define SMI_TOXID_COPY 1
+#define SMI_GROUPCHAT_CREATE 2
+
+#endif //_TOX_MENUS_H_
\ No newline at end of file
diff --git a/protocols/Tox/src/tox_messages.cpp b/protocols/Tox/src/tox_messages.cpp
index 2e3e0b4a71..13d0adffcd 100644
--- a/protocols/Tox/src/tox_messages.cpp
+++ b/protocols/Tox/src/tox_messages.cpp
@@ -51,9 +51,6 @@ int CToxProto::OnReceiveMessage(MCONTACT hContact, PROTORECVEVENT *pre)
 	if (pre->szMessage == NULL)
 		return NULL;
 
-	ptrA pszTemp;
-	mir_ptr<BYTE> pszBlob;
-
 	DBEVENTINFO dbei = { sizeof(dbei) };
 	dbei.szModule = GetContactProto(hContact);
 	dbei.timestamp = pre->timestamp;
diff --git a/protocols/Tox/src/tox_profile.cpp b/protocols/Tox/src/tox_profile.cpp
index a404d080ca..35014a4500 100644
--- a/protocols/Tox/src/tox_profile.cpp
+++ b/protocols/Tox/src/tox_profile.cpp
@@ -137,6 +137,22 @@ void CToxProto::SaveToxProfile()
 	mir_free(data);
 }
 
+int CToxProto::OnCopyToxID(WPARAM, LPARAM)
+{
+	ptrA address(getStringA(TOX_SETTINGS_ID));
+	size_t length = mir_strlen(address) + 1;
+	if (OpenClipboard(NULL))
+	{
+		EmptyClipboard();
+		HGLOBAL hMem = GlobalAlloc(GMEM_FIXED, length);
+		memcpy(GlobalLock(hMem), address, length);
+		GlobalUnlock(hMem);
+		SetClipboardData(CF_TEXT, hMem);
+		CloseClipboard();
+	}
+	return 0;
+}
+
 INT_PTR CToxProto::ToxProfilePasswordProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
 	CToxProto *proto = (CToxProto*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
diff --git a/protocols/Tox/src/tox_proto.cpp b/protocols/Tox/src/tox_proto.cpp
index a3dac57a20..9b6060f13c 100644
--- a/protocols/Tox/src/tox_proto.cpp
+++ b/protocols/Tox/src/tox_proto.cpp
@@ -11,33 +11,12 @@ CToxProto::CToxProto(const char* protoName, const TCHAR* userName) :
 
 	SetAllContactsStatus(ID_STATUS_OFFLINE);
 
-	// icons
-	wchar_t filePath[MAX_PATH];
-	GetModuleFileName(g_hInstance, filePath, MAX_PATH);
-
-	wchar_t sectionName[100];
-	mir_sntprintf(sectionName, SIZEOF(sectionName), _T("%s/%s"), LPGENT("Protocols"), _A2T(MODULE));
-
-	char settingName[100];
-	mir_snprintf(settingName, SIZEOF(settingName), "%s_%s", MODULE, "main");
-
-	SKINICONDESC sid = { 0 };
-	sid.cbSize = sizeof(SKINICONDESC);
-	sid.flags = SIDF_ALL_TCHAR;
-	sid.ptszDefaultFile = filePath;
-	sid.pszName = settingName;
-	sid.ptszSection = sectionName;
-	sid.ptszDescription = LPGENT("Protocol icon");
-	sid.iDefaultIndex = -IDI_TOX;
-	Skin_AddIcon(&sid);
-
 	// custom event
 	DBEVENTTYPEDESCR dbEventType = { sizeof(dbEventType) };
-	dbEventType.module = this->m_szModuleName;
+	dbEventType.module = m_szModuleName;
 	dbEventType.flags = DETF_HISTORY | DETF_MSGWINDOW;
-
 	dbEventType.eventType = TOX_DB_EVENT_TYPE_ACTION;
-	dbEventType.descr = "Tox action";
+	dbEventType.descr = Translate("Tox action");
 	CallService(MS_DB_EVENT_REGISTERTYPE, 0, (LPARAM)&dbEventType);
 
 	// avatars
@@ -46,15 +25,12 @@ CToxProto::CToxProto(const char* protoName, const TCHAR* userName) :
 	CreateProtoService(PS_GETMYAVATART, &CToxProto::GetMyAvatar);
 	CreateProtoService(PS_SETMYAVATART, &CToxProto::SetMyAvatar);
 
+	// nick
 	CreateProtoService(PS_SETMYNICKNAME, &CToxProto::SetMyNickname);
-
-	// transfers
-	transfers = new CTransferList();
 }
 
 CToxProto::~CToxProto()
 {
-	delete transfers;
 	mir_free(accountName);
 	UninitNetlib();
 }
@@ -111,21 +87,7 @@ int __cdecl CToxProto::Authorize(MEVENT hDbEvent)
 	{
 		return 1;
 	}
-
-	ToxBinAddress address = ptrA(getStringA(hContact, TOX_SETTINGS_ID));
-	ToxBinAddress pubKey = address.GetPubKey();
-	if (tox_add_friend_norequest(tox, pubKey) == TOX_ERROR)
-	{
-		return 1;
-	}
-
-	// trim address to public key
-	setString(hContact, TOX_SETTINGS_ID, pubKey.ToHex());
-
-	db_unset(hContact, "CList", "NotOnList");
-	delSetting(hContact, "Grant");
-
-	return 0;
+	return OnGrantAuth(hContact, 0);
 }
 
 int __cdecl CToxProto::AuthDeny(MEVENT, const PROTOCHAR*) { return 0; }
@@ -138,30 +100,28 @@ int __cdecl CToxProto::AuthRecv(MCONTACT, PROTORECVEVENT* pre)
 int __cdecl CToxProto::AuthRequest(MCONTACT hContact, const PROTOCHAR *szMessage)
 {
 	ptrA reason(mir_utf8encodeW(szMessage));
+	return OnRequestAuth(hContact, (LPARAM)reason);
+}
 
-	ToxBinAddress address = ptrA(getStringA(hContact, TOX_SETTINGS_ID));
-	int32_t number = tox_add_friend(tox, address, (uint8_t*)(char*)reason, mir_strlen(reason));
-	if (number > TOX_ERROR)
-	{
-		// trim address to public key
-		setString(hContact, TOX_SETTINGS_ID, address.ToHex().GetPubKey());
+HANDLE __cdecl CToxProto::ChangeInfo(int, void*) { return 0; }
 
-		db_unset(hContact, "CList", "NotOnList");
-		delSetting(hContact, "Grant");
+int __cdecl CToxProto::GetInfo(MCONTACT, int) { return 0; }
 
-		std::string nick("", TOX_MAX_NAME_LENGTH);
-		tox_get_name(tox, number, (uint8_t*)&nick[0]);
-		setString(hContact, "Nick", nick.c_str());
+HANDLE __cdecl CToxProto::SearchBasic(const PROTOCHAR*) { return 0; }
 
-		return 0;
-	}
+HANDLE __cdecl CToxProto::SearchByEmail(const PROTOCHAR*) { return 0; }
 
-	return 1;
-}
+HANDLE __cdecl CToxProto::SearchByName(const PROTOCHAR*, const PROTOCHAR*, const PROTOCHAR*) { return 0; }
 
-HANDLE __cdecl CToxProto::ChangeInfo(int, void*) { return 0; }
+HWND __cdecl CToxProto::SearchAdvanced(HWND owner)
+{
+	return OnSearchAdvanced(owner);
+}
 
-int __cdecl CToxProto::GetInfo(MCONTACT, int) { return 0; }
+HWND __cdecl CToxProto::CreateExtendedSearchUI(HWND owner)
+{
+	return OnCreateExtendedSearchUI(owner);
+}
 
 int __cdecl CToxProto::RecvContacts(MCONTACT, PROTORECVEVENT*) { return 0; }
 
@@ -262,6 +222,7 @@ int __cdecl CToxProto::SetStatus(int iNewStatus)
 }
 
 HANDLE __cdecl CToxProto::GetAwayMsg(MCONTACT) { return 0; }
+
 int __cdecl CToxProto::RecvAwayMsg(MCONTACT, int, PROTORECVEVENT*) { return 0; }
 
 int __cdecl CToxProto::SetAwayMsg(int, const PROTOCHAR *msg)
@@ -291,8 +252,8 @@ int __cdecl CToxProto::OnEvent(PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM
 	case EV_PROTO_ONCONTACTDELETED:
 		return OnContactDeleted(wParam, lParam);
 
-	//case EV_PROTO_ONMENU:
-	//	return OnInitStatusMenu();
+	case EV_PROTO_ONMENU:
+		return OnInitStatusMenu();
 	}
 
 	return 1;
diff --git a/protocols/Tox/src/tox_proto.h b/protocols/Tox/src/tox_proto.h
index 8ea108a01f..a68ec41c28 100644
--- a/protocols/Tox/src/tox_proto.h
+++ b/protocols/Tox/src/tox_proto.h
@@ -59,9 +59,17 @@ public:
 
 	virtual	int       __cdecl OnEvent(PROTOEVENTTYPE iEventType, WPARAM wParam, LPARAM lParam);
 
-	// instances
-	static CToxProto* InitAccount(const char* protoName, const wchar_t* userName);
-	static int        UninitAccount(CToxProto* ppro);
+	// accounts
+	static CToxProto* InitAccount(const char *protoName, const TCHAR *userName);
+	static int        UninitAccount(CToxProto *proto);
+
+	// icons
+	static void InitIcons();
+	static void UninitIcons();
+
+	// menus
+	static void InitMenus();
+	static void UninitMenus();
 
 private:
 	Tox *tox;
@@ -70,7 +78,7 @@ private:
 	TCHAR *accountName;
 	HANDLE hNetlib, hPollingThread;
 	bool isTerminated, isConnected;
-	CTransferList *transfers;
+	CTransferList transfers;
 
 	// tox profile
 	std::tstring GetToxProfilePath();
@@ -79,6 +87,8 @@ private:
 	bool LoadToxProfile();
 	void SaveToxProfile();
 
+	int __cdecl OnCopyToxID(WPARAM, LPARAM);
+
 	static INT_PTR CALLBACK ToxProfileImportProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 	static INT_PTR CALLBACK ToxProfilePasswordProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
@@ -101,9 +111,11 @@ private:
 	void __cdecl PollingThread(void*);
 
 	// accounts
-	static LIST<CToxProto> accounts;
+	static LIST<CToxProto> Accounts;
 	static int CompareAccounts(const CToxProto *p1, const CToxProto *p2);
 
+	static CToxProto* GetContactAccount(MCONTACT hContact);
+
 	int __cdecl OnAccountLoaded(WPARAM, LPARAM);
 	int __cdecl OnAccountRenamed(WPARAM, LPARAM);
 
@@ -113,10 +125,17 @@ private:
 	void InitNetlib();
 	void UninitNetlib();
 
+	// icons
+	static IconInfo Icons[];
+	static HANDLE GetIconHandle(const char *name);
+	static HANDLE GetSkinIconHandle(const char *name);
+
 	// menus
+	static HGENMENU ContactMenuItems[CMI_MAX];
+	int OnPrebuildContactMenu(MCONTACT hContact, LPARAM);
+	static int PrebuildContactMenu(MCONTACT hContact, LPARAM lParam);
+
 	int OnInitStatusMenu();
-	static void InitMenus();
-	static void UninitMenus();
 
 	// options
 	static INT_PTR CALLBACK MainOptionsProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
@@ -147,6 +166,9 @@ private:
 
 	void __cdecl LoadFriendList(void*);
 
+	int __cdecl OnRequestAuth(MCONTACT hContact, LPARAM lParam);
+	int __cdecl OnGrantAuth(MCONTACT hContact, LPARAM);
+
 	static void OnFriendRequest(Tox *tox, const uint8_t *pubKey, const uint8_t *message, const uint16_t messageSize, void *arg);
 	static void OnFriendNameChange(Tox *tox, const int friendNumber, const uint8_t *name, const uint16_t nameSize, void *arg);
 	static void OnStatusMessageChanged(Tox *tox, const int friendNumber, const uint8_t* message, const uint16_t messageSize, void *arg);
@@ -154,11 +176,14 @@ private:
 	static void OnConnectionStatusChanged(Tox *tox, const int friendNumber, const uint8_t status, void *arg);
 
 	// contacts search
-	void __cdecl SearchFailedAsync(void* arg);
 	void __cdecl SearchByNameAsync(void* arg);
+	void __cdecl SearchFailedAsync(void* arg);
 
 	static INT_PTR CALLBACK SearchDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
+	HWND __cdecl OnSearchAdvanced(HWND owner);
+	HWND __cdecl OnCreateExtendedSearchUI(HWND owner);
+
 	// chat rooms
 	//MCONTACT GetChatRoom(const char *pubKey);
 	MCONTACT GetChatRoom(int groupNumber);
@@ -226,6 +251,13 @@ private:
 	static void ShowNotification(const TCHAR *caption, const TCHAR *message, int flags = 0, MCONTACT hContact = NULL);
 
 	static bool IsFileExists(std::tstring path);
+
+	template<int(__cdecl CToxProto::*Service)(WPARAM, LPARAM)>
+	static INT_PTR GlobalService(WPARAM wParam, LPARAM lParam)
+	{
+		CToxProto *proto = CToxProto::GetContactAccount((MCONTACT)wParam);
+		return proto ? (proto->*Service)(wParam, lParam) : 0;
+	}
 };
 
 #endif //_TOX_PROTO_H_
\ No newline at end of file
diff --git a/protocols/Tox/src/tox_search.cpp b/protocols/Tox/src/tox_search.cpp
index b7aa7ec9df..8ec4a35250 100644
--- a/protocols/Tox/src/tox_search.cpp
+++ b/protocols/Tox/src/tox_search.cpp
@@ -1,9 +1,42 @@
 #include "common.h"
 #include "tox_dns.h"
 
-void CToxProto::SearchFailedAsync(void*)
+ToxHexAddress ResolveToxAddressFromDnsRecordV1(const std::string &dnsRecord)
 {
-	ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HWND)1, 0);
+	std::smatch match;
+	std::regex regex("^v=tox1;id=([A-Fa-f0-9]{76})(;sign=(\\S+))?$");
+	if (std::regex_search(dnsRecord, match, regex))
+	{
+		return match[1];
+	}
+	return ToxHexAddress::Empty();
+}
+
+ToxHexAddress ResolveToxAddressFromDnsRecordV2(const std::string &dnsRecord)
+{
+	// unsupported
+	/*std::smatch match;
+	std::regex regex("^v=tox2;pub=([A-Fa-f0-9]{64});check=([A-Fa-f0-9]{8});sign=(\\S+))?$");
+	if (std::regex_search(dnsRecord, match, regex))
+	{
+	}*/
+	return ToxHexAddress::Empty();
+}
+
+ToxHexAddress ResolveToxAddressFromDnsRecordV3(void *dns, uint32_t requestId, const std::string &dnsRecord)
+{
+	std::smatch match;
+	std::regex regex("^v=tox3;id=([a-z0-5.]+)$");
+	if (std::regex_search(dnsRecord, match, regex))
+	{
+		std::string id = match[1];
+		uint8_t data[TOX_FRIEND_ADDRESS_SIZE];
+		if (tox_decrypt_dns3_TXT(dns, data, (uint8_t*)id.c_str(), id.length(), requestId) != TOX_ERROR)
+		{
+			return ToxHexAddress(data, TOX_FRIEND_ADDRESS_SIZE);
+		}
+	}
+	return ToxHexAddress::Empty();
 }
 
 void CToxProto::SearchByNameAsync(void *arg)
@@ -12,38 +45,34 @@ void CToxProto::SearchByNameAsync(void *arg)
 	char *name = strtok(query, "@");
 	char *domain = strtok(NULL, "");
 
-	int i = 0;
-	static int j = 0;
-	while (i < 2)
+	int resolved = 0;
+
+	for (size_t i = 0; i < SIZEOF(dns_servers); i++)
 	{
-		struct dns_server *server = &dns_servers[j % SIZEOF(dns_servers)];
-		if (domain == NULL || strcmp(domain, server->domain) == 0)
+		struct dns_server *server = &dns_servers[i];
+		if (domain == NULL || mir_strcmpi(domain, server->domain) == 0)
 		{
 			void *dns = tox_dns3_new(server->key);
 
-			uint8_t dnsString[256];
 			uint32_t requestId = 0;
+			uint8_t dnsString[MAX_PATH];
 			int length = tox_generate_dns3_string(dns, dnsString, sizeof(dnsString), &requestId, (uint8_t*)name, mir_strlen(name));
 			if (length != TOX_ERROR)
 			{
 				dnsString[length] = 0;
-
-				char dnsQuery[512];
-				mir_snprintf(dnsQuery, 512, "_%s._tox.%s", dnsString, server->domain);
+				char dnsQuery[MAX_PATH * 2];
+				mir_snprintf(dnsQuery, SIZEOF(dnsQuery), "_%s._tox.%s", dnsString, server->domain);
 
 				DNS_RECORDA *record = NULL;
-				DnsQuery_A(dnsQuery, DNS_TYPE_TEXT, 0, NULL, (PDNS_RECORD*)&record, NULL);
-				while (record)
+				DNS_STATUS status = DnsQuery_A(dnsQuery, DNS_TYPE_TEXT, 0, NULL, (PDNS_RECORD*)&record, NULL);
+				while (status == ERROR_SUCCESS && record)
 				{
 					DNS_TXT_DATAA *txt = &record->Data.Txt;
 					if (record->wType == DNS_TYPE_TEXT && txt->dwStringCount)
 					{
-						char *recordId = &txt->pStringArray[0][10];
-						uint8_t data[TOX_FRIEND_ADDRESS_SIZE];
-						if (tox_decrypt_dns3_TXT(dns, data, (uint8_t*)recordId, mir_strlen(recordId), requestId) != TOX_ERROR)
+						ToxHexAddress address = ResolveToxAddressFromDnsRecordV3(dns, requestId, txt->pStringArray[0]);
+						if (!address.IsEmpty())
 						{
-							ToxHexAddress address(data, TOX_FRIEND_ADDRESS_SIZE);
-
 							PROTOSEARCHRESULT psr = { sizeof(PROTOSEARCHRESULT) };
 							psr.flags = PSR_TCHAR;
 							psr.id = mir_a2t(address);
@@ -54,20 +83,63 @@ void CToxProto::SearchByNameAsync(void *arg)
 							psr.email = mir_tstrdup(email);
 
 							ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)1, (LPARAM)&psr);
+
+							resolved++;
+							break;
 						}
 					}
 					record = record->pNext;
 				}
+				DnsRecordListFree((PDNS_RECORD*)record, DnsFreeRecordList);
 			}
 			tox_dns3_kill(dns);
 		}
-		i++; j++;
+	}
+
+	if (resolved == 0 && domain)
+	{
+		char dnsQuery[MAX_PATH];
+		mir_snprintf(dnsQuery, SIZEOF(dnsQuery), "%s._tox.%s", name, domain);
+
+		DNS_RECORDA *record = NULL;
+		DNS_STATUS status = DnsQuery_A(dnsQuery, DNS_TYPE_TEXT, DNS_QUERY_STANDARD, NULL, (PDNS_RECORD*)&record, NULL);
+		while (status == ERROR_SUCCESS && record)
+		{
+			DNS_TXT_DATAA *txt = &record->Data.Txt;
+			if (record->wType == DNS_TYPE_TEXT && txt->dwStringCount)
+			{
+				ToxHexAddress address = ResolveToxAddressFromDnsRecordV1(txt->pStringArray[0]);
+				if (!address.IsEmpty())
+				{
+					PROTOSEARCHRESULT psr = { sizeof(PROTOSEARCHRESULT) };
+					psr.flags = PSR_TCHAR;
+					psr.id = mir_a2t(address);
+					psr.nick = mir_utf8decodeT(name);
+
+					TCHAR email[MAX_PATH];
+					mir_sntprintf(email, SIZEOF(email), _T("%s@%s"), psr.nick, _A2T(domain));
+					psr.email = mir_tstrdup(email);
+
+					ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)1, (LPARAM)&psr);
+
+					resolved++;
+					break;
+				}
+			}
+			record = record->pNext;
+		}
+		DnsRecordListFree((PDNS_RECORD*)record, DnsFreeRecordList);
 	}
 
 	ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)1, 0);
 	mir_free(arg);
 }
 
+void CToxProto::SearchFailedAsync(void*)
+{
+	ProtoBroadcastAck(NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HWND)1, 0);
+}
+
 INT_PTR CToxProto::SearchDlgProc(HWND hwnd, UINT uMsg, WPARAM, LPARAM lParam)
 {
 	CToxProto *proto = (CToxProto*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
@@ -90,13 +162,7 @@ INT_PTR CToxProto::SearchDlgProc(HWND hwnd, UINT uMsg, WPARAM, LPARAM lParam)
 	return FALSE;
 }
 
-HANDLE __cdecl CToxProto::SearchBasic(const PROTOCHAR*) { return 0; }
-
-HANDLE __cdecl CToxProto::SearchByEmail(const PROTOCHAR*) { return 0; }
-
-HANDLE __cdecl CToxProto::SearchByName(const PROTOCHAR*, const PROTOCHAR*, const PROTOCHAR*) { return 0; }
-
-HWND __cdecl CToxProto::SearchAdvanced(HWND owner)
+HWND CToxProto::OnSearchAdvanced(HWND owner)
 {
 	if (!IsOnline())
 	{
@@ -132,7 +198,7 @@ HWND __cdecl CToxProto::SearchAdvanced(HWND owner)
 		regex = "^\\s*(([^ @/:;()\"']+)(@[A-Za-z]+.[A-Za-z]{2,6})?)\\s*$";
 		if (std::regex_search(query, match, regex))
 		{
-			ForkThread(&CToxProto::SearchByNameAsync, mir_strdup(match[1].str().c_str()));
+			ForkThread(&CToxProto::SearchByNameAsync, mir_strdup(query.c_str()));
 		}
 		else
 		{
@@ -142,7 +208,7 @@ HWND __cdecl CToxProto::SearchAdvanced(HWND owner)
 	return (HWND)1;
 }
 
-HWND __cdecl CToxProto::CreateExtendedSearchUI(HWND owner)
+HWND CToxProto::OnCreateExtendedSearchUI(HWND owner)
 {
 	return CreateDialogParam(
 		g_hInstance,
diff --git a/protocols/Tox/src/tox_transfer.cpp b/protocols/Tox/src/tox_transfer.cpp
index 2f377ab58c..7b5a011786 100644
--- a/protocols/Tox/src/tox_transfer.cpp
+++ b/protocols/Tox/src/tox_transfer.cpp
@@ -20,7 +20,7 @@ void CToxProto::OnFriendFile(Tox *, int32_t friendNumber, uint8_t fileNumber, ui
 		FileTransferParam *transfer = new FileTransferParam(friendNumber, fileNumber, name, fileSize);
 		transfer->pfts.hContact = hContact;
 		transfer->pfts.flags |= PFTS_RECEIVING;
-		proto->transfers->Add(transfer);
+		proto->transfers.Add(transfer);
 
 		PROTORECVFILET pre = { 0 };
 		pre.flags = PREF_TCHAR;
@@ -53,7 +53,7 @@ HANDLE __cdecl CToxProto::FileAllow(MCONTACT hContact, HANDLE hTransfer, const P
 			debugLogA("CToxProto::FileAllow: failed to open file (%d)", transfer->fileNumber);
 			transfer->status = FAILED;
 			tox_file_send_control(tox, transfer->friendNumber, transfer->GetDirection(), transfer->fileNumber, TOX_FILECONTROL_KILL, NULL, 0);
-			transfers->Remove(transfer);
+			transfers.Remove(transfer);
 			return NULL;
 		}
 
@@ -106,7 +106,7 @@ int __cdecl CToxProto::FileResume(HANDLE hTransfer, int *action, const PROTOCHAR
 	{
 		transfer->status = CANCELED;
 		tox_file_send_control(tox, transfer->friendNumber, transfer->GetDirection(), transfer->fileNumber, TOX_FILECONTROL_KILL, NULL, 0);
-		transfers->Remove(transfer);
+		transfers.Remove(transfer);
 	}
 	ProtoBroadcastAck(transfer->pfts.hContact, ACKTYPE_FILE, result ? ACKRESULT_CONNECTED : ACKRESULT_DENIED, (HANDLE)transfer, 0);
 
@@ -126,7 +126,7 @@ void CToxProto::OnFileData(Tox *tox, int32_t friendNumber, uint8_t fileNumber, c
 		return;
 	}
 
-	FileTransferParam *transfer = proto->transfers->Get(friendNumber, fileNumber);
+	FileTransferParam *transfer = proto->transfers.Get(friendNumber, fileNumber);
 	if (transfer == NULL)
 	{
 		proto->debugLogA("CToxProto::OnFileData: cannot find transfer by number (%d)", fileNumber);
@@ -190,7 +190,7 @@ HANDLE __cdecl CToxProto::SendFile(MCONTACT hContact, const PROTOCHAR*, PROTOCHA
 	transfer->pfts.flags |= PFTS_SENDING;
 	transfer->pfts.tszWorkingDir = fileDir;
 	transfer->hFile = hFile;
-	transfers->Add(transfer);
+	transfers.Add(transfer);
 
 	return (HANDLE)transfer;
 }
@@ -262,7 +262,7 @@ int __cdecl CToxProto::FileCancel(MCONTACT, HANDLE hTransfer)
 	FileTransferParam *transfer = (FileTransferParam*)hTransfer;
 	transfer->status = CANCELED;
 	tox_file_send_control(tox, transfer->friendNumber, transfer->GetDirection(), transfer->fileNumber, TOX_FILECONTROL_KILL, NULL, 0);
-	transfers->Remove(transfer);
+	transfers.Remove(transfer);
 
 	return 0;
 }
@@ -280,7 +280,7 @@ void CToxProto::OnFileRequest(Tox *tox, int32_t friendNumber, uint8_t receive_se
 	MCONTACT hContact = proto->GetContact(friendNumber);
 	if (hContact)
 	{
-		FileTransferParam *transfer = proto->transfers->Get(friendNumber, fileNumber);
+		FileTransferParam *transfer = proto->transfers.Get(friendNumber, fileNumber);
 		if (transfer == NULL)
 		{
 			tox_file_send_control(tox, friendNumber, receive_send, fileNumber, TOX_FILECONTROL_KILL, NULL, 0);
@@ -341,7 +341,7 @@ void CToxProto::OnFileRequest(Tox *tox, int32_t friendNumber, uint8_t receive_se
 		case TOX_FILECONTROL_KILL:
 			transfer->status = CANCELED;
 			proto->ProtoBroadcastAck(transfer->pfts.hContact, ACKTYPE_FILE, ACKRESULT_DENIED, (HANDLE)transfer, 0);
-			proto->transfers->Remove(transfer);
+			proto->transfers.Remove(transfer);
 			break;
 
 		case TOX_FILECONTROL_FINISHED:
@@ -355,7 +355,7 @@ void CToxProto::OnFileRequest(Tox *tox, int32_t friendNumber, uint8_t receive_se
 				}
 				tox_file_send_control(tox, friendNumber, transfer->GetDirection(), fileNumber, isFileFullyTransfered ? TOX_FILECONTROL_FINISHED : TOX_FILECONTROL_KILL, NULL, 0);
 				proto->ProtoBroadcastAck(transfer->pfts.hContact, ACKTYPE_FILE, isFileFullyTransfered ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)transfer, 0);
-				proto->transfers->Remove(transfer);
+				proto->transfers.Remove(transfer);
 			}
 			break;
 		}
-- 
cgit v1.2.3