summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2019-10-08 18:50:08 +0300
committerGeorge Hazan <ghazan@miranda.im>2019-10-08 18:50:08 +0300
commitbb3b89fd5d857d396050d02bc185d765760de6ff (patch)
treefc5765cbde415fedb0d3faadc9b64e32fd901a14 /src
parent8d0500df234fc5b59ea7687bd5f55f19d7b8e28c (diff)
WebSocket related code moved to the core
Diffstat (limited to 'src')
-rw-r--r--src/mir_app/src/mir_app.def3
-rw-r--r--src/mir_app/src/mir_app64.def3
-rw-r--r--src/mir_app/src/netlib_autoproxy.cpp (renamed from src/mir_app/src/netlibautoproxy.cpp)730
-rw-r--r--src/mir_app/src/netlib_bind.cpp (renamed from src/mir_app/src/netlibbind.cpp)660
-rw-r--r--src/mir_app/src/netlib_http.cpp (renamed from src/mir_app/src/netlibhttp.cpp)2292
-rw-r--r--src/mir_app/src/netlib_httpproxy.cpp (renamed from src/mir_app/src/netlibhttpproxy.cpp)170
-rw-r--r--src/mir_app/src/netlib_log.cpp (renamed from src/mir_app/src/netliblog.cpp)1092
-rw-r--r--src/mir_app/src/netlib_openconn.cpp (renamed from src/mir_app/src/netlibopenconn.cpp)0
-rw-r--r--src/mir_app/src/netlib_opts.cpp (renamed from src/mir_app/src/netlibopts.cpp)1036
-rw-r--r--src/mir_app/src/netlib_pktrecver.cpp (renamed from src/mir_app/src/netlibpktrecver.cpp)160
-rw-r--r--src/mir_app/src/netlib_security.cpp (renamed from src/mir_app/src/netlibsecurity.cpp)726
-rw-r--r--src/mir_app/src/netlib_sock.cpp (renamed from src/mir_app/src/netlibsock.cpp)580
-rw-r--r--src/mir_app/src/netlib_upnp.cpp (renamed from src/mir_app/src/netlibupnp.cpp)1626
-rw-r--r--src/mir_app/src/netlib_websocket.cpp185
14 files changed, 4727 insertions, 4536 deletions
diff --git a/src/mir_app/src/mir_app.def b/src/mir_app/src/mir_app.def
index 35f48d6460..21fcd8a36f 100644
--- a/src/mir_app/src/mir_app.def
+++ b/src/mir_app/src/mir_app.def
@@ -716,3 +716,6 @@ UnregisterHppLogger @786
_RegisterSrmmLog@12 @805 NONAME
_UnregisterSrmmLog@4 @806 NONAME
?GetType@CRtfLogWindow@@UAEHXZ @807 NONAME
+_WebSocket_Send@12 @808 NONAME
+_WebSocket_InitHeader@12 @809 NONAME
+_WebSocket_Connect@8 @810 NONAME
diff --git a/src/mir_app/src/mir_app64.def b/src/mir_app/src/mir_app64.def
index 95a66ca811..02356fb3bb 100644
--- a/src/mir_app/src/mir_app64.def
+++ b/src/mir_app/src/mir_app64.def
@@ -716,3 +716,6 @@ UnregisterHppLogger @786
RegisterSrmmLog @805 NONAME
UnregisterSrmmLog @806 NONAME
?GetType@CRtfLogWindow@@UEAAHXZ @807 NONAME
+WebSocket_Send @808 NONAME
+WebSocket_InitHeader @809 NONAME
+WebSocket_Connect @810 NONAME
diff --git a/src/mir_app/src/netlibautoproxy.cpp b/src/mir_app/src/netlib_autoproxy.cpp
index 63f823c8fd..a4753f8ecd 100644
--- a/src/mir_app/src/netlibautoproxy.cpp
+++ b/src/mir_app/src/netlib_autoproxy.cpp
@@ -1,365 +1,365 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-#include <wininet.h>
-
-/////////////////////////////////////////////////////////////////////////////////////////
-// local module data
-
-static char *szProxyHost[3];
-static LIST<char> proxyBypass(5);
-
-static HMODULE hModJS;
-
-static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll;
-static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
-static pfnInternetGetProxyInfo pInternetGetProxyInfo;
-
-static bool bEnabled, bOneProxy;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf)
-{
- NetlibUser nlu = {};
- nlu.handleType = NLH_USER;
- nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS;
- nlu.user.szSettingsModule = "(NULL)";
- nlu.toLog = 1;
-
- // initialize the netlib request
- NETLIBHTTPREQUEST nlhr = {};
- nlhr.cbSize = sizeof(nlhr);
- nlhr.requestType = REQUEST_GET;
- nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT;
- nlhr.szUrl = szUrl;
-
- // download the page
- NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr));
- if (nlhrReply) {
- if (nlhrReply->resultCode == 200) {
- buf.lpszScriptBuffer = nlhrReply->pData;
- buf.dwScriptBufferSize = nlhrReply->dataLength + 1;
-
- nlhrReply->dataLength = 0;
- nlhrReply->pData = nullptr;
- }
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps)
-{
- bool noHttp = false;
- bool usingSsl = false;
- char szUrl[1024];
-
- if ((nlc->nloc.flags & NLOCF_HTTP) && (nlc->nloc.flags & NLOCF_SSL) || nlc->nloc.wPort == 443 || forceHttps) {
- mir_snprintf(szUrl, "https://%s", nlc->nloc.szHost);
- usingSsl = true;
- }
- else if ((nlc->nloc.flags & NLOCF_HTTP))
- mir_snprintf(szUrl, "http://%s", nlc->nloc.szHost);
- else {
- strncpy_s(szUrl, nlc->nloc.szHost, _TRUNCATE);
- noHttp = true;
- }
-
- mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr;
- nlc->wProxyPort = 0;
- nlc->proxyType = 0;
-
- char *mt = NetlibGetIeProxy(szUrl);
- char *m = NEWSTR_ALLOCA(mt);
- mir_free(mt);
-
- if (m == nullptr)
- return false;
-
- // if multiple servers, use the first one
- char *c = strchr(m, ';'); if (c) *c = 0;
-
- // if 'direct' no proxy
- if (_stricmp(lrtrim(m), "direct") == 0)
- return false;
-
- // find proxy address
- char *h = strchr(m, ' ');
- if (h == nullptr)
- return false;
-
- // find proxy port
- *h = 0; ++h;
- char *p = strchr(h, ':');
- if (p) { *p = 0; ++p; }
-
- lrtrim(h); ltrim(p);
- if (_stricmp(m, "proxy") == 0 && h[0]) {
- nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP;
- nlc->wProxyPort = p ? atol(p) : 8080;
- nlc->szProxyServer = mir_strdup(h);
- }
- else if (_stricmp(m, "socks") == 0 && h[0]) {
- nlc->proxyType = PROXYTYPE_SOCKS4;
- nlc->wProxyPort = p ? atol(p) : 1080;
- nlc->szProxyServer = mir_strdup(h);
- }
- else if (_stricmp(m, "socks5") == 0 && h[0]) {
- nlc->proxyType = PROXYTYPE_SOCKS5;
- nlc->wProxyPort = p ? atol(p) : 1080;
- nlc->szProxyServer = mir_strdup(h);
- }
- else return false;
-
- return true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static char szAutoUrlStr[MAX_PATH] = "";
-static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 };
-static HANDLE hIeProxyMutex;
-static bool bAutoProxyInit;
-
-static void NetlibInitAutoProxy(void)
-{
- if (bAutoProxyInit) return;
-
- if (!hModJS) {
- if (!(hModJS = LoadLibraryA("jsproxy.dll")))
- return;
-
- pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll");
- pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll");
- pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo");
- }
-
- if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) {
- abuf.dwStructSize = sizeof(abuf);
- GetFile(szAutoUrlStr, abuf);
- }
- bAutoProxyInit = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-struct IeProxyParam
-{
- char *szUrl;
- char *szHost;
- char *szProxy;
-};
-
-static void __cdecl NetlibIeProxyThread(IeProxyParam *param)
-{
- param->szProxy = nullptr;
-
- if (!bAutoProxyInit) {
- WaitForSingleObject(hIeProxyMutex, INFINITE);
- NetlibInitAutoProxy();
- ReleaseMutex(hIeProxyMutex);
- }
-
- BOOL res;
- char *loc = strstr(szAutoUrlStr, "file://");
- if (loc || strstr(szAutoUrlStr, "://") == nullptr) {
- Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc);
- loc = loc ? loc + 7 : szAutoUrlStr;
- res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr);
- }
- else {
- Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize);
- if (abuf.dwScriptBufferSize)
- res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf);
- else
- res = false;
- }
-
- if (res) {
- char proxyBuffer[1024];
- char *proxy = proxyBuffer;
- DWORD dwProxyLen = sizeof(proxyBuffer);
-
- if (pInternetGetProxyInfo(param->szUrl, (DWORD)mir_strlen(param->szUrl),
- param->szHost, (DWORD)mir_strlen(param->szHost), &proxy, &dwProxyLen))
- param->szProxy = mir_strdup(lrtrim(proxy));
-
- Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost);
- pInternetDeInitializeAutoProxyDll(nullptr, 0);
- }
- else Netlib_Logf(nullptr, "Autoproxy init failed");
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-char* NetlibGetIeProxy(char *szUrl)
-{
- char *res = nullptr, *szHost;
- {
- char* p = strstr(szUrl, "://");
- if (p) p += 3; else p = szUrl;
-
- szHost = NEWSTR_ALLOCA(p);
- p = strchr(szHost, '/'); if (p) *p = 0;
- p = strchr(szHost, ':'); if (p) *p = 0;
- _strlwr(szHost);
- }
-
- if (bEnabled) {
- for (auto &p : proxyBypass) {
- if (mir_strcmp(p, "<local>") == 0) {
- if (strchr(szHost, '.') == nullptr)
- return nullptr;
- }
- else if (wildcmp(szHost, p))
- return nullptr;
- }
-
- int ind = -1;
- if (strstr(szUrl, "http://"))
- ind = szProxyHost[0] ? 0 : 2;
- else if (strstr(szUrl, "https://"))
- ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2);
- else
- ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2));
-
- if (ind < 0 || !szProxyHost[ind])
- return nullptr;
-
- size_t len = mir_strlen(szHost) + 20;
- res = (char*)mir_alloc(len);
- mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]);
- return res;
- }
-
- if (szAutoUrlStr[0]) {
- IeProxyParam param = { szUrl, szHost, nullptr };
- HANDLE hThread = mir_forkThread<IeProxyParam>(NetlibIeProxyThread, &param);
- WaitForSingleObject(hThread, INFINITE);
- res = param.szProxy;
- }
- return res;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void NetlibLoadIeProxy(void)
-{
- HKEY hSettings;
- if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings))
- return;
-
- DWORD tValueLen, enabled = 0;
- char szHostStr[256] = "", szProxyBypassStr[4096] = "";
-
- tValueLen = sizeof(enabled);
- int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (BYTE*)&enabled, &tValueLen);
- bEnabled = enabled && tResult == ERROR_SUCCESS;
-
- tValueLen = _countof(szHostStr);
- tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (BYTE*)szHostStr, &tValueLen);
- bEnabled = bEnabled && tResult == ERROR_SUCCESS;
-
- tValueLen = _countof(szAutoUrlStr);
- RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (BYTE*)szAutoUrlStr, &tValueLen);
-
- tValueLen = _countof(szProxyBypassStr);
- RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (BYTE*)szProxyBypassStr, &tValueLen);
-
- RegCloseKey(hSettings);
-
- if (bEnabled) {
- char *szProxy = ltrim(szHostStr);
- if (szProxy[0] == 0) {
- enabled = false;
- return;
- }
-
- while (true) {
- char *szProxyEnd = strchr(szProxy, ';');
- if (szProxyEnd)
- *szProxyEnd = 0;
-
- int ind = -1;
- if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; }
- else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; }
- else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; }
- else if (strchr(szProxy, '=')) ind = -2;
-
- if (ind != -2) {
- bOneProxy = ind < 0; if (ind < 0) ind = 0;
-
- lrtrim(szProxy);
-
- if (strchr(szProxy, ':'))
- szProxyHost[ind] = mir_strdup(szProxy);
- else {
- size_t len = mir_strlen(szProxy) + 10;
- szProxyHost[ind] = (char*)mir_alloc(len);
- mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080);
- }
- if (bOneProxy)
- break;
- }
- if (szProxyEnd == nullptr)
- break;
- szProxy = szProxyEnd + 1;
- }
-
- char *szProxyBypass = szProxyBypassStr;
- while (true) {
- char *szProxyBypassEnd = strchr(szProxyBypass, ';');
- if (szProxyBypassEnd)
- *szProxyBypassEnd = 0;
-
- lrtrim(szProxyBypass);
-
- proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass)));
- if (szProxyBypassEnd == nullptr)
- break;
-
- szProxyBypass = szProxyBypassEnd + 1;
- }
- }
-
- if (bEnabled || szAutoUrlStr[0])
- hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr);
-}
-
-void NetlibUnloadIeProxy(void)
-{
- for (int i = 0; i < 3; i++)
- mir_free(szProxyHost[i]);
-
- for (auto &p : proxyBypass)
- mir_free(p);
-
- mir_free(abuf.lpszScriptBuffer);
-
- CloseHandle(hIeProxyMutex);
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#include <wininet.h>
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// local module data
+
+static char *szProxyHost[3];
+static LIST<char> proxyBypass(5);
+
+static HMODULE hModJS;
+
+static pfnInternetInitializeAutoProxyDll pInternetInitializeAutoProxyDll;
+static pfnInternetDeInitializeAutoProxyDll pInternetDeInitializeAutoProxyDll;
+static pfnInternetGetProxyInfo pInternetGetProxyInfo;
+
+static bool bEnabled, bOneProxy;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static void GetFile(char *szUrl, AUTO_PROXY_SCRIPT_BUFFER &buf)
+{
+ NetlibUser nlu = {};
+ nlu.handleType = NLH_USER;
+ nlu.user.flags = NUF_OUTGOING | NUF_HTTPCONNS;
+ nlu.user.szSettingsModule = "(NULL)";
+ nlu.toLog = 1;
+
+ // initialize the netlib request
+ NETLIBHTTPREQUEST nlhr = {};
+ nlhr.cbSize = sizeof(nlhr);
+ nlhr.requestType = REQUEST_GET;
+ nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_REDIRECT;
+ nlhr.szUrl = szUrl;
+
+ // download the page
+ NLHR_PTR nlhrReply(Netlib_HttpTransaction(&nlu, &nlhr));
+ if (nlhrReply) {
+ if (nlhrReply->resultCode == 200) {
+ buf.lpszScriptBuffer = nlhrReply->pData;
+ buf.dwScriptBufferSize = nlhrReply->dataLength + 1;
+
+ nlhrReply->dataLength = 0;
+ nlhrReply->pData = nullptr;
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+bool NetlibGetIeProxyConn(NetlibConnection *nlc, bool forceHttps)
+{
+ bool noHttp = false;
+ bool usingSsl = false;
+ char szUrl[1024];
+
+ if ((nlc->nloc.flags & NLOCF_HTTP) && (nlc->nloc.flags & NLOCF_SSL) || nlc->nloc.wPort == 443 || forceHttps) {
+ mir_snprintf(szUrl, "https://%s", nlc->nloc.szHost);
+ usingSsl = true;
+ }
+ else if ((nlc->nloc.flags & NLOCF_HTTP))
+ mir_snprintf(szUrl, "http://%s", nlc->nloc.szHost);
+ else {
+ strncpy_s(szUrl, nlc->nloc.szHost, _TRUNCATE);
+ noHttp = true;
+ }
+
+ mir_free(nlc->szProxyServer); nlc->szProxyServer = nullptr;
+ nlc->wProxyPort = 0;
+ nlc->proxyType = 0;
+
+ char *mt = NetlibGetIeProxy(szUrl);
+ char *m = NEWSTR_ALLOCA(mt);
+ mir_free(mt);
+
+ if (m == nullptr)
+ return false;
+
+ // if multiple servers, use the first one
+ char *c = strchr(m, ';'); if (c) *c = 0;
+
+ // if 'direct' no proxy
+ if (_stricmp(lrtrim(m), "direct") == 0)
+ return false;
+
+ // find proxy address
+ char *h = strchr(m, ' ');
+ if (h == nullptr)
+ return false;
+
+ // find proxy port
+ *h = 0; ++h;
+ char *p = strchr(h, ':');
+ if (p) { *p = 0; ++p; }
+
+ lrtrim(h); ltrim(p);
+ if (_stricmp(m, "proxy") == 0 && h[0]) {
+ nlc->proxyType = (usingSsl || noHttp) ? PROXYTYPE_HTTPS : PROXYTYPE_HTTP;
+ nlc->wProxyPort = p ? atol(p) : 8080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else if (_stricmp(m, "socks") == 0 && h[0]) {
+ nlc->proxyType = PROXYTYPE_SOCKS4;
+ nlc->wProxyPort = p ? atol(p) : 1080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else if (_stricmp(m, "socks5") == 0 && h[0]) {
+ nlc->proxyType = PROXYTYPE_SOCKS5;
+ nlc->wProxyPort = p ? atol(p) : 1080;
+ nlc->szProxyServer = mir_strdup(h);
+ }
+ else return false;
+
+ return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static char szAutoUrlStr[MAX_PATH] = "";
+static AUTO_PROXY_SCRIPT_BUFFER abuf = { 0 };
+static HANDLE hIeProxyMutex;
+static bool bAutoProxyInit;
+
+static void NetlibInitAutoProxy(void)
+{
+ if (bAutoProxyInit) return;
+
+ if (!hModJS) {
+ if (!(hModJS = LoadLibraryA("jsproxy.dll")))
+ return;
+
+ pInternetInitializeAutoProxyDll = (pfnInternetInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetInitializeAutoProxyDll");
+ pInternetDeInitializeAutoProxyDll = (pfnInternetDeInitializeAutoProxyDll)GetProcAddress(hModJS, "InternetDeInitializeAutoProxyDll");
+ pInternetGetProxyInfo = (pfnInternetGetProxyInfo)GetProcAddress(hModJS, "InternetGetProxyInfo");
+ }
+
+ if (strstr(szAutoUrlStr, "file://") == nullptr && strstr(szAutoUrlStr, "://") != nullptr) {
+ abuf.dwStructSize = sizeof(abuf);
+ GetFile(szAutoUrlStr, abuf);
+ }
+ bAutoProxyInit = true;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+struct IeProxyParam
+{
+ char *szUrl;
+ char *szHost;
+ char *szProxy;
+};
+
+static void __cdecl NetlibIeProxyThread(IeProxyParam *param)
+{
+ param->szProxy = nullptr;
+
+ if (!bAutoProxyInit) {
+ WaitForSingleObject(hIeProxyMutex, INFINITE);
+ NetlibInitAutoProxy();
+ ReleaseMutex(hIeProxyMutex);
+ }
+
+ BOOL res;
+ char *loc = strstr(szAutoUrlStr, "file://");
+ if (loc || strstr(szAutoUrlStr, "://") == nullptr) {
+ Netlib_Logf(nullptr, "Autoproxy Init file: %s", loc);
+ loc = loc ? loc + 7 : szAutoUrlStr;
+ res = pInternetInitializeAutoProxyDll(0, loc, nullptr, nullptr /*&HelperFunctions*/, nullptr);
+ }
+ else {
+ Netlib_Logf(nullptr, "Autoproxy Init %d", abuf.dwScriptBufferSize);
+ if (abuf.dwScriptBufferSize)
+ res = pInternetInitializeAutoProxyDll(0, nullptr, nullptr, nullptr /*&HelperFunctions*/, &abuf);
+ else
+ res = false;
+ }
+
+ if (res) {
+ char proxyBuffer[1024];
+ char *proxy = proxyBuffer;
+ DWORD dwProxyLen = sizeof(proxyBuffer);
+
+ if (pInternetGetProxyInfo(param->szUrl, (DWORD)mir_strlen(param->szUrl),
+ param->szHost, (DWORD)mir_strlen(param->szHost), &proxy, &dwProxyLen))
+ param->szProxy = mir_strdup(lrtrim(proxy));
+
+ Netlib_Logf(nullptr, "Autoproxy got response %s, Param: %s %s", param->szProxy, param->szUrl, param->szHost);
+ pInternetDeInitializeAutoProxyDll(nullptr, 0);
+ }
+ else Netlib_Logf(nullptr, "Autoproxy init failed");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* NetlibGetIeProxy(char *szUrl)
+{
+ char *res = nullptr, *szHost;
+ {
+ char* p = strstr(szUrl, "://");
+ if (p) p += 3; else p = szUrl;
+
+ szHost = NEWSTR_ALLOCA(p);
+ p = strchr(szHost, '/'); if (p) *p = 0;
+ p = strchr(szHost, ':'); if (p) *p = 0;
+ _strlwr(szHost);
+ }
+
+ if (bEnabled) {
+ for (auto &p : proxyBypass) {
+ if (mir_strcmp(p, "<local>") == 0) {
+ if (strchr(szHost, '.') == nullptr)
+ return nullptr;
+ }
+ else if (wildcmp(szHost, p))
+ return nullptr;
+ }
+
+ int ind = -1;
+ if (strstr(szUrl, "http://"))
+ ind = szProxyHost[0] ? 0 : 2;
+ else if (strstr(szUrl, "https://"))
+ ind = bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2);
+ else
+ ind = szProxyHost[2] ? 2 : (bOneProxy ? 0 : (szProxyHost[1] ? 1 : 2));
+
+ if (ind < 0 || !szProxyHost[ind])
+ return nullptr;
+
+ size_t len = mir_strlen(szHost) + 20;
+ res = (char*)mir_alloc(len);
+ mir_snprintf(res, len, "%s %s", ind == 2 ? "SOCKS" : "PROXY", szProxyHost[ind]);
+ return res;
+ }
+
+ if (szAutoUrlStr[0]) {
+ IeProxyParam param = { szUrl, szHost, nullptr };
+ HANDLE hThread = mir_forkThread<IeProxyParam>(NetlibIeProxyThread, &param);
+ WaitForSingleObject(hThread, INFINITE);
+ res = param.szProxy;
+ }
+ return res;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NetlibLoadIeProxy(void)
+{
+ HKEY hSettings;
+ if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", 0, KEY_QUERY_VALUE, &hSettings))
+ return;
+
+ DWORD tValueLen, enabled = 0;
+ char szHostStr[256] = "", szProxyBypassStr[4096] = "";
+
+ tValueLen = sizeof(enabled);
+ int tResult = RegQueryValueExA(hSettings, "ProxyEnable", nullptr, nullptr, (BYTE*)&enabled, &tValueLen);
+ bEnabled = enabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = _countof(szHostStr);
+ tResult = RegQueryValueExA(hSettings, "ProxyServer", nullptr, nullptr, (BYTE*)szHostStr, &tValueLen);
+ bEnabled = bEnabled && tResult == ERROR_SUCCESS;
+
+ tValueLen = _countof(szAutoUrlStr);
+ RegQueryValueExA(hSettings, "AutoConfigUrl", nullptr, nullptr, (BYTE*)szAutoUrlStr, &tValueLen);
+
+ tValueLen = _countof(szProxyBypassStr);
+ RegQueryValueExA(hSettings, "ProxyOverride", nullptr, nullptr, (BYTE*)szProxyBypassStr, &tValueLen);
+
+ RegCloseKey(hSettings);
+
+ if (bEnabled) {
+ char *szProxy = ltrim(szHostStr);
+ if (szProxy[0] == 0) {
+ enabled = false;
+ return;
+ }
+
+ while (true) {
+ char *szProxyEnd = strchr(szProxy, ';');
+ if (szProxyEnd)
+ *szProxyEnd = 0;
+
+ int ind = -1;
+ if (strncmp(szProxy, "http=", 5) == 0) { ind = 0; szProxy += 5; }
+ else if (strncmp(szProxy, "https=", 6) == 0) { ind = 1; szProxy += 6; }
+ else if (strncmp(szProxy, "socks=", 6) == 0) { ind = 2; szProxy += 6; }
+ else if (strchr(szProxy, '=')) ind = -2;
+
+ if (ind != -2) {
+ bOneProxy = ind < 0; if (ind < 0) ind = 0;
+
+ lrtrim(szProxy);
+
+ if (strchr(szProxy, ':'))
+ szProxyHost[ind] = mir_strdup(szProxy);
+ else {
+ size_t len = mir_strlen(szProxy) + 10;
+ szProxyHost[ind] = (char*)mir_alloc(len);
+ mir_snprintf(szProxyHost[ind], len, "%s:%u", szProxy, ind == 2 ? 1080 : 8080);
+ }
+ if (bOneProxy)
+ break;
+ }
+ if (szProxyEnd == nullptr)
+ break;
+ szProxy = szProxyEnd + 1;
+ }
+
+ char *szProxyBypass = szProxyBypassStr;
+ while (true) {
+ char *szProxyBypassEnd = strchr(szProxyBypass, ';');
+ if (szProxyBypassEnd)
+ *szProxyBypassEnd = 0;
+
+ lrtrim(szProxyBypass);
+
+ proxyBypass.insert(_strlwr(mir_strdup(szProxyBypass)));
+ if (szProxyBypassEnd == nullptr)
+ break;
+
+ szProxyBypass = szProxyBypassEnd + 1;
+ }
+ }
+
+ if (bEnabled || szAutoUrlStr[0])
+ hIeProxyMutex = CreateMutex(nullptr, FALSE, nullptr);
+}
+
+void NetlibUnloadIeProxy(void)
+{
+ for (int i = 0; i < 3; i++)
+ mir_free(szProxyHost[i]);
+
+ for (auto &p : proxyBypass)
+ mir_free(p);
+
+ mir_free(abuf.lpszScriptBuffer);
+
+ CloseHandle(hIeProxyMutex);
+}
diff --git a/src/mir_app/src/netlibbind.cpp b/src/mir_app/src/netlib_bind.cpp
index 4f39d7d9c2..fcbdc7c943 100644
--- a/src/mir_app/src/netlibbind.cpp
+++ b/src/mir_app/src/netlib_bind.cpp
@@ -1,330 +1,330 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn)
-{
- SOCKADDR_IN sin = {0};
- sin.sin_family = AF_INET;
-
- SOCKADDR_IN6 sin6 = {0};
- sin6.sin6_family = AF_INET6;
-
- mir_cslock lck(csNetlibUser);
-
- if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) {
- BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn);
- if (*portn == 0)
- return false;
-
- WORD num;
- Utils_GetRandom(&num, sizeof(WORD));
- *portn = num % *portn;
- }
-
- bool before = false;
- while (true) {
- const char *psz;
- char *pszEnd;
- int portMin, portMax, port, portnum = 0;
-
- for (psz = szPorts;*psz;) {
- while (*psz == ' ' || *psz == ',') psz++;
- portMin = strtol(psz, &pszEnd, 0);
- if (pszEnd == psz)
- break;
- while (*pszEnd == ' ')
- pszEnd++;
- if (*pszEnd == '-') {
- psz = pszEnd + 1;
- portMax = strtol(psz, &pszEnd, 0);
- if (pszEnd == psz) portMax = 65535;
- if (portMin > portMax) {
- port = portMin;
- portMin = portMax;
- portMax = port;
- }
- }
- else portMax = portMin;
- if (portMax >= 1) {
- if (portMin <= 0) portMin = 1;
- for (port = portMin; port <= portMax; port++) {
- if (port > 65535)
- break;
-
- ++portnum;
-
- if (s == INVALID_SOCKET) continue;
- if (!before && portnum <= *portn) continue;
- if (before && portnum >= *portn)
- return false;
-
- sin.sin_port = htons((WORD)port);
- bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0;
-
- sin6.sin6_port = htons((WORD)port);
- bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0;
-
- if (bV4Mapped && bV6Mapped) {
- *portn = portnum + 1;
- return true;
- }
- }
- }
- psz = pszEnd;
- }
-
- if (*portn < 0) {
- *portn = portnum;
- return true;
- }
-
- if (*portn >= portnum)
- *portn = 0;
- else
- before = true;
- }
-}
-
-int NetlibFreeBoundPort(NetlibBoundPort *nlbp)
-{
- NETLIBCONNECTIONEVENTINFO ncei;
-
- ZeroMemory(&ncei, sizeof(ncei));
- ncei.connected = 0;
- ncei.listening = 1;
- ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule;
- int size = sizeof(SOCKADDR_IN);
- getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size);
- NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0);
-
- nlbp->close();
- if (nlbp->hThread)
- WaitForSingleObject(nlbp->hThread, INFINITE);
- Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort);
- delete nlbp;
- return 1;
-}
-
-static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp)
-{
- Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort);
-
- while (true) {
- fd_set r;
- FD_ZERO(&r);
- if (nlbp->s != INVALID_SOCKET)
- FD_SET(nlbp->s, &r);
- if (nlbp->s6 != INVALID_SOCKET)
- FD_SET(nlbp->s6, &r);
- if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) {
- Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError());
- break;
- }
-
- sockaddr_in sin;
- int sinLen = sizeof(sin);
- memset(&sin, 0, sizeof(sin));
-
- SOCKET s;
- if (FD_ISSET(nlbp->s, &r)) {
- s = accept(nlbp->s, (sockaddr*)&sin, &sinLen);
- if (s == INVALID_SOCKET) {
- Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError());
- break;
- }
- }
- else if (FD_ISSET(nlbp->s6, &r)) {
- s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen);
- if (s == INVALID_SOCKET) {
- Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError());
- break;
- }
- }
- else s = 0;
-
- Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s);
-
- NetlibConnection *nlc = new NetlibConnection();
- nlc->nlu = nlbp->nlu;
- nlc->s = s;
-
- if (nlbp->pfnNewConnection)
- nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra);
- }
-
- NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP");
- nlbp->hThread = nullptr;
-
- Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort);
-}
-
-MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb)
-{
- if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return nullptr;
- }
-
- NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb);
- if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) {
- Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError());
-LBL_Error:
- delete nlbp;
- return nullptr;
- }
-
- SOCKADDR_IN sin = { 0 };
- sin.sin_family = AF_INET;
-
- SOCKADDR_IN6 sin6 = { 0 };
- sin6.sin6_family = AF_INET6;
-
- /* if the netlib user wanted a free port given in the range, then
- they better have given wPort == 0, let's hope so */
- int foundPort = 0;
- if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) {
- if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) {
- Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified");
- SetLastError(WSAEADDRINUSE);
- }
- else foundPort = 1;
- }
- else {
- /* if ->wPort == 0 then they'll get any free port, otherwise they'll
- be asking for whatever was in nlb->wPort*/
- if (nlb->wPort != 0) {
- Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort);
- sin.sin_port = htons(nlb->wPort);
- sin6.sin6_port = htons(nlb->wPort);
- }
-
- if (nlbp->s != INVALID_SOCKET)
- if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) {
- SOCKADDR_IN sin2 = { 0 };
- int len = sizeof(sin2);
- if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len))
- sin6.sin6_port = sin2.sin_port;
- foundPort = 1;
- }
-
- if (nlbp->s6 != INVALID_SOCKET)
- if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0)
- foundPort = 1;
- }
- if (!foundPort) {
- Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError());
- goto LBL_Error;
- }
-
- if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) {
- Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError());
- goto LBL_Error;
- }
-
- if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) {
- Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError());
- goto LBL_Error;
- }
-
- SOCKADDR_INET_M sinm = { 0 };
- int len = sizeof(sinm);
- if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) {
- nlb->wPort = ntohs(sinm.Ipv4.sin_port);
- nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr);
- }
- else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len))
- nlb->wPort = ntohs(sinm.Ipv6.sin6_port);
- else {
- Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError());
- goto LBL_Error;
- }
- nlbp->wPort = nlb->wPort;
-
- if (nlb->dwInternalIP == 0) {
- char hostname[64] = "";
- gethostname(hostname, _countof(hostname));
-
- PHOSTENT he = gethostbyname(hostname);
- if (he && he->h_addr)
- nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr);
- }
-
- DWORD extIP;
- if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) {
- Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort);
- nlb->wExPort = nlbp->wExPort;
- nlb->dwExternalIP = extIP;
- }
- else {
- if (nlu->settings.enableUPnP)
- Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort);
- else
- Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort);
-
- nlbp->wExPort = 0;
- nlb->wExPort = nlb->wPort;
- nlb->dwExternalIP = nlb->dwInternalIP;
- }
-
- nlbp->hThread = mir_forkThread<NetlibBoundPort>(NetlibBindAcceptThread, nlbp);
-
- if (GetSubscribersCount((THook*)hEventConnected)) {
- NETLIBCONNECTIONEVENTINFO ncei = {};
- ncei.connected = 1;
- ncei.listening = 1;
- ncei.szSettingsModule = nlu->user.szSettingsModule;
- memcpy(&ncei.local, &sin, sizeof(sin));
- NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0);
- }
-
- return nlbp;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb)
- : handleType(NLH_BOUNDPORT),
- nlu(_nlu)
-{
- pfnNewConnection = nlb->pfnNewConnection;
- pExtra = nlb->pExtra;
-
- s = socket(PF_INET, SOCK_STREAM, 0);
- s6 = socket(PF_INET6, SOCK_STREAM, 0);
-}
-
-void NetlibBoundPort::close()
-{
- if (s != INVALID_SOCKET) {
- closesocket(s);
- s = INVALID_SOCKET;
- }
- if (s6 != INVALID_SOCKET) {
- closesocket(s6);
- s6 = INVALID_SOCKET;
- }
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn)
+{
+ SOCKADDR_IN sin = {0};
+ sin.sin_family = AF_INET;
+
+ SOCKADDR_IN6 sin6 = {0};
+ sin6.sin6_family = AF_INET6;
+
+ mir_cslock lck(csNetlibUser);
+
+ if (--*portn < 0 && (s != INVALID_SOCKET || s6 != INVALID_SOCKET)) {
+ BindSocketToPort(szPorts, INVALID_SOCKET, INVALID_SOCKET, portn);
+ if (*portn == 0)
+ return false;
+
+ WORD num;
+ Utils_GetRandom(&num, sizeof(WORD));
+ *portn = num % *portn;
+ }
+
+ bool before = false;
+ while (true) {
+ const char *psz;
+ char *pszEnd;
+ int portMin, portMax, port, portnum = 0;
+
+ for (psz = szPorts;*psz;) {
+ while (*psz == ' ' || *psz == ',') psz++;
+ portMin = strtol(psz, &pszEnd, 0);
+ if (pszEnd == psz)
+ break;
+ while (*pszEnd == ' ')
+ pszEnd++;
+ if (*pszEnd == '-') {
+ psz = pszEnd + 1;
+ portMax = strtol(psz, &pszEnd, 0);
+ if (pszEnd == psz) portMax = 65535;
+ if (portMin > portMax) {
+ port = portMin;
+ portMin = portMax;
+ portMax = port;
+ }
+ }
+ else portMax = portMin;
+ if (portMax >= 1) {
+ if (portMin <= 0) portMin = 1;
+ for (port = portMin; port <= portMax; port++) {
+ if (port > 65535)
+ break;
+
+ ++portnum;
+
+ if (s == INVALID_SOCKET) continue;
+ if (!before && portnum <= *portn) continue;
+ if (before && portnum >= *portn)
+ return false;
+
+ sin.sin_port = htons((WORD)port);
+ bool bV4Mapped = s == INVALID_SOCKET || bind(s, (SOCKADDR*)&sin, sizeof(sin)) == 0;
+
+ sin6.sin6_port = htons((WORD)port);
+ bool bV6Mapped = s6 == INVALID_SOCKET || bind(s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0;
+
+ if (bV4Mapped && bV6Mapped) {
+ *portn = portnum + 1;
+ return true;
+ }
+ }
+ }
+ psz = pszEnd;
+ }
+
+ if (*portn < 0) {
+ *portn = portnum;
+ return true;
+ }
+
+ if (*portn >= portnum)
+ *portn = 0;
+ else
+ before = true;
+ }
+}
+
+int NetlibFreeBoundPort(NetlibBoundPort *nlbp)
+{
+ NETLIBCONNECTIONEVENTINFO ncei;
+
+ ZeroMemory(&ncei, sizeof(ncei));
+ ncei.connected = 0;
+ ncei.listening = 1;
+ ncei.szSettingsModule = nlbp->nlu->user.szSettingsModule;
+ int size = sizeof(SOCKADDR_IN);
+ getsockname(nlbp->s, (SOCKADDR *)&ncei.local, &size);
+ NotifyFastHook(hEventDisconnected, (WPARAM)&ncei, 0);
+
+ nlbp->close();
+ if (nlbp->hThread)
+ WaitForSingleObject(nlbp->hThread, INFINITE);
+ Netlib_Logf(nlbp->nlu, "(%u) Port %u closed for incoming connections", nlbp->s, nlbp->wPort);
+ delete nlbp;
+ return 1;
+}
+
+static void __cdecl NetlibBindAcceptThread(NetlibBoundPort *nlbp)
+{
+ Netlib_Logf(nlbp->nlu, "(%u) Port %u opened for incoming connections", nlbp->s, nlbp->wPort);
+
+ while (true) {
+ fd_set r;
+ FD_ZERO(&r);
+ if (nlbp->s != INVALID_SOCKET)
+ FD_SET(nlbp->s, &r);
+ if (nlbp->s6 != INVALID_SOCKET)
+ FD_SET(nlbp->s6, &r);
+ if (select(0, &r, nullptr, nullptr, nullptr) == SOCKET_ERROR) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): select failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+
+ sockaddr_in sin;
+ int sinLen = sizeof(sin);
+ memset(&sin, 0, sizeof(sin));
+
+ SOCKET s;
+ if (FD_ISSET(nlbp->s, &r)) {
+ s = accept(nlbp->s, (sockaddr*)&sin, &sinLen);
+ if (s == INVALID_SOCKET) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V4 failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+ }
+ else if (FD_ISSET(nlbp->s6, &r)) {
+ s = accept(nlbp->s6, (sockaddr*)&sin, &sinLen);
+ if (s == INVALID_SOCKET) {
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread (%p): accept V6 failed (%d)", (void*)nlbp->s, GetLastError());
+ break;
+ }
+ }
+ else s = 0;
+
+ Netlib_Logf(nlbp->nlu, "New incoming connection on port %u from %s (%p)", nlbp->wPort, ptrA(Netlib_AddressToString(&sin)).get(), (void*)s);
+
+ NetlibConnection *nlc = new NetlibConnection();
+ nlc->nlu = nlbp->nlu;
+ nlc->s = s;
+
+ if (nlbp->pfnNewConnection)
+ nlbp->pfnNewConnection(nlc, ntohl(sin.sin_addr.S_un.S_addr), nlbp->pExtra);
+ }
+
+ NetlibUPnPDeletePortMapping(nlbp->wExPort, "TCP");
+ nlbp->hThread = nullptr;
+
+ Netlib_Logf(nlbp->nlu, "NetlibBindAcceptThread: (%p) thread for port %u closed", (void*)nlbp->s, nlbp->wPort);
+}
+
+MIR_APP_DLL(HNETLIBBIND) Netlib_BindPort(HNETLIBUSER nlu, NETLIBBIND *nlb)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_INCOMING) || nlb == nullptr || nlb->pfnNewConnection == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ NetlibBoundPort *nlbp = new NetlibBoundPort(nlu, nlb);
+ if (nlbp->s == INVALID_SOCKET && nlbp->s6 == INVALID_SOCKET) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "socket", WSAGetLastError());
+LBL_Error:
+ delete nlbp;
+ return nullptr;
+ }
+
+ SOCKADDR_IN sin = { 0 };
+ sin.sin_family = AF_INET;
+
+ SOCKADDR_IN6 sin6 = { 0 };
+ sin6.sin6_family = AF_INET6;
+
+ /* if the netlib user wanted a free port given in the range, then
+ they better have given wPort == 0, let's hope so */
+ int foundPort = 0;
+ if (nlu->settings.specifyIncomingPorts && nlu->settings.szIncomingPorts && nlb->wPort == 0) {
+ if (!BindSocketToPort(nlu->settings.szIncomingPorts, nlbp->s, nlbp->s6, &nlu->outportnum)) {
+ Netlib_Logf(nlu, "Netlib bind: Not enough ports for incoming connections specified");
+ SetLastError(WSAEADDRINUSE);
+ }
+ else foundPort = 1;
+ }
+ else {
+ /* if ->wPort == 0 then they'll get any free port, otherwise they'll
+ be asking for whatever was in nlb->wPort*/
+ if (nlb->wPort != 0) {
+ Netlib_Logf(nlu, "%s %d: trying to bind port %d, this 'feature' can be abused, please be sure you want to allow it.", __FILE__, __LINE__, nlb->wPort);
+ sin.sin_port = htons(nlb->wPort);
+ sin6.sin6_port = htons(nlb->wPort);
+ }
+
+ if (nlbp->s != INVALID_SOCKET)
+ if (bind(nlbp->s, (PSOCKADDR)&sin, sizeof(sin)) == 0) {
+ SOCKADDR_IN sin2 = { 0 };
+ int len = sizeof(sin2);
+ if (!getsockname(nlbp->s, (PSOCKADDR)&sin2, &len))
+ sin6.sin6_port = sin2.sin_port;
+ foundPort = 1;
+ }
+
+ if (nlbp->s6 != INVALID_SOCKET)
+ if (bind(nlbp->s6, (PSOCKADDR)&sin6, sizeof(sin6)) == 0)
+ foundPort = 1;
+ }
+ if (!foundPort) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "bind", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ if (nlbp->s != INVALID_SOCKET && listen(nlbp->s, 5)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ if (nlbp->s6 != INVALID_SOCKET && listen(nlbp->s6, 5)) {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "listen", WSAGetLastError());
+ goto LBL_Error;
+ }
+
+ SOCKADDR_INET_M sinm = { 0 };
+ int len = sizeof(sinm);
+ if (!getsockname(nlbp->s, (PSOCKADDR)&sinm, &len)) {
+ nlb->wPort = ntohs(sinm.Ipv4.sin_port);
+ nlb->dwInternalIP = ntohl(sinm.Ipv4.sin_addr.S_un.S_addr);
+ }
+ else if (!getsockname(nlbp->s6, (PSOCKADDR)&sinm, &len))
+ nlb->wPort = ntohs(sinm.Ipv6.sin6_port);
+ else {
+ Netlib_Logf(nlu, "%s %d: %s() failed (%u)", __FILE__, __LINE__, "getsockname", WSAGetLastError());
+ goto LBL_Error;
+ }
+ nlbp->wPort = nlb->wPort;
+
+ if (nlb->dwInternalIP == 0) {
+ char hostname[64] = "";
+ gethostname(hostname, _countof(hostname));
+
+ PHOSTENT he = gethostbyname(hostname);
+ if (he && he->h_addr)
+ nlb->dwInternalIP = ntohl(*(PDWORD)he->h_addr);
+ }
+
+ DWORD extIP;
+ if (nlu->settings.enableUPnP && NetlibUPnPAddPortMapping(nlb->wPort, "TCP", &nlbp->wExPort, &extIP, true)) {
+ Netlib_Logf(nullptr, "UPnP port mapping succeeded. Internal Port: %u External Port: %u\n", nlb->wPort, nlbp->wExPort);
+ nlb->wExPort = nlbp->wExPort;
+ nlb->dwExternalIP = extIP;
+ }
+ else {
+ if (nlu->settings.enableUPnP)
+ Netlib_Logf(nullptr, "UPnP port mapping failed. Internal Port: %u\n", nlb->wPort);
+ else
+ Netlib_Logf(nullptr, "UPnP disabled. Internal Port: %u\n", nlb->wPort);
+
+ nlbp->wExPort = 0;
+ nlb->wExPort = nlb->wPort;
+ nlb->dwExternalIP = nlb->dwInternalIP;
+ }
+
+ nlbp->hThread = mir_forkThread<NetlibBoundPort>(NetlibBindAcceptThread, nlbp);
+
+ if (GetSubscribersCount((THook*)hEventConnected)) {
+ NETLIBCONNECTIONEVENTINFO ncei = {};
+ ncei.connected = 1;
+ ncei.listening = 1;
+ ncei.szSettingsModule = nlu->user.szSettingsModule;
+ memcpy(&ncei.local, &sin, sizeof(sin));
+ NotifyFastHook(hEventConnected, (WPARAM)&ncei, 0);
+ }
+
+ return nlbp;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+NetlibBoundPort::NetlibBoundPort(HNETLIBUSER _nlu, NETLIBBIND *nlb)
+ : handleType(NLH_BOUNDPORT),
+ nlu(_nlu)
+{
+ pfnNewConnection = nlb->pfnNewConnection;
+ pExtra = nlb->pExtra;
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ s6 = socket(PF_INET6, SOCK_STREAM, 0);
+}
+
+void NetlibBoundPort::close()
+{
+ if (s != INVALID_SOCKET) {
+ closesocket(s);
+ s = INVALID_SOCKET;
+ }
+ if (s6 != INVALID_SOCKET) {
+ closesocket(s6);
+ s6 = INVALID_SOCKET;
+ }
+}
diff --git a/src/mir_app/src/netlibhttp.cpp b/src/mir_app/src/netlib_http.cpp
index 4936841796..7925a73c73 100644
--- a/src/mir_app/src/netlibhttp.cpp
+++ b/src/mir_app/src/netlib_http.cpp
@@ -1,1146 +1,1146 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "../libs/zlib/src/zlib.h"
-#include "netlib.h"
-
-#define HTTPRECVHEADERSTIMEOUT 30000 //in ms
-#define HTTPRECVDATATIMEOUT 20000
-
-struct ProxyAuth
-{
- char *szServer;
- char *szMethod;
-
- ProxyAuth(const char *pszServer, const char *pszMethod)
- {
- szServer = mir_strdup(pszServer);
- szMethod = mir_strdup(pszMethod);
- }
- ~ProxyAuth()
- {
- mir_free(szServer);
- mir_free(szMethod);
- }
- static int Compare(const ProxyAuth *p1, const ProxyAuth *p2)
- {
- return mir_strcmpi(p1->szServer, p2->szServer);
- }
-};
-
-struct ProxyAuthList : OBJLIST<ProxyAuth>
-{
- ProxyAuthList() : OBJLIST<ProxyAuth>(2, ProxyAuth::Compare) {}
-
- void add(const char *szServer, const char *szMethod)
- {
- if (szServer == nullptr) return;
- int i = getIndex((ProxyAuth*)&szServer);
- if (i >= 0) {
- ProxyAuth &rec = (*this)[i];
- if (szMethod == nullptr)
- remove(i);
- else if (_stricmp(rec.szMethod, szMethod)) {
- mir_free(rec.szMethod);
- rec.szMethod = mir_strdup(szMethod);
- }
- }
- else insert(new ProxyAuth(szServer, szMethod));
- }
-
- const char* find(const char *szServer)
- {
- ProxyAuth *rec = szServer ? OBJLIST<ProxyAuth>::find((ProxyAuth*)&szServer) : nullptr;
- return rec ? rec->szMethod : nullptr;
- }
-};
-
-ProxyAuthList proxyAuthList;
-
-static int RecvWithTimeoutTime(NetlibConnection *nlc, unsigned dwTimeoutTime, char *buf, int len, int flags)
-{
- DWORD dwTimeNow;
-
- if (nlc->foreBuf.isEmpty() && !sslApi.pending(nlc->hSsl)) {
- while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) {
- unsigned dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000);
- int res = WaitUntilReadable(nlc->s, dwDeltaTime);
-
- switch (res) {
- case SOCKET_ERROR:
- return SOCKET_ERROR;
-
- case 1:
- return Netlib_Recv(nlc, buf, len, flags);
- }
-
- if (nlc->termRequested || Miranda_IsTerminated())
- return 0;
- }
- SetLastError(ERROR_TIMEOUT);
- return SOCKET_ERROR;
- }
- return Netlib_Recv(nlc, buf, len, flags);
-}
-
-static char* NetlibHttpFindHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr)
-{
- for (int i=0; i < nlhrReply->headersCount; i++) {
- NETLIBHTTPHEADER &p = nlhrReply->headers[i];
- if (_stricmp(p.szName, hdr) == 0)
- return p.szValue;
- }
-
- return nullptr;
-}
-
-static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider)
-{
- char *szBasicHdr = nullptr;
- char *szNegoHdr = nullptr;
- char *szNtlmHdr = nullptr;
-
- for (int i=0; i < nlhrReply->headersCount; i++) {
- NETLIBHTTPHEADER &p = nlhrReply->headers[i];
- if (_stricmp(p.szName, hdr) == 0) {
- if (_strnicmp(p.szValue, "Negotiate", 9) == 0)
- szNegoHdr = p.szValue;
- else if (_strnicmp(p.szValue, "NTLM", 4) == 0)
- szNtlmHdr = p.szValue;
- else if (_strnicmp(p.szValue, "Basic", 5) == 0)
- szBasicHdr = p.szValue;
- }
- }
-
- if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr;
- if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr;
- if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr;
- return nullptr;
-}
-
-void NetlibConnFromUrl(const char *szUrl, bool secur, NETLIBOPENCONNECTION &nloc)
-{
- secur = secur || _strnicmp(szUrl, "https", 5) == 0;
- const char* phost = strstr(szUrl, "://");
-
- char* szHost = mir_strdup(phost ? phost + 3 : szUrl);
-
- char* ppath = strchr(szHost, '/');
- if (ppath) *ppath = '\0';
-
- memset(&nloc, 0, sizeof(nloc));
- nloc.szHost = szHost;
-
- char* pcolon = strrchr(szHost, ':');
- if (pcolon) {
- *pcolon = '\0';
- nloc.wPort = (WORD)strtol(pcolon+1, nullptr, 10);
- }
- else nloc.wPort = secur ? 443 : 80;
- nloc.flags = (secur ? NLOCF_SSL : 0);
-}
-
-static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr)
-{
- NETLIBOPENCONNECTION nloc;
-
- if (szUrl == nullptr)
- NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, nloc);
- else
- NetlibConnFromUrl(szUrl, false, nloc);
-
- nloc.flags |= NLOCF_HTTP;
- if (nloc.flags & NLOCF_SSL)
- nlhr->flags |= NLHRF_SSL;
- else
- nlhr->flags &= ~NLHRF_SSL;
-
- if (nlc != nullptr) {
- bool httpProxy = !(nloc.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP;
- bool sameHost = mir_strcmp(nlc->nloc.szHost, nloc.szHost) == 0 && nlc->nloc.wPort == nloc.wPort;
-
- if (!httpProxy && !sameHost) {
- NetlibDoCloseSocket(nlc);
-
- mir_free((char*)nlc->nloc.szHost);
- nlc->nloc = nloc;
- return NetlibDoConnect(nlc) ? nlc : nullptr;
- }
- }
- else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, &nloc);
-
- mir_free((char*)nloc.szHost);
-
- return nlc;
-}
-
-struct HttpSecurityContext
-{
- HANDLE m_hNtlmSecurity;
- char *m_szHost;
- char *m_szProvider;
-
- HttpSecurityContext()
- {
- m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr;
- }
-
- ~HttpSecurityContext() { Destroy(); }
-
- void Destroy(void)
- {
- if (!m_hNtlmSecurity) return;
-
- Netlib_DestroySecurityProvider(m_hNtlmSecurity);
- m_hNtlmSecurity = nullptr;
- mir_free(m_szHost); m_szHost = nullptr;
- mir_free(m_szProvider); m_szProvider = nullptr;
- }
-
- bool TryBasic(void)
- {
- return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic");
- }
-
- char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete)
- {
- char *szAuthHdr = nullptr;
- bool justCreated = false;
- NetlibUser *nlu = nlc->nlu;
-
- if (m_hNtlmSecurity) {
- bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider);
- newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost)));
- if (newAuth)
- Destroy();
- }
-
- if (m_hNtlmSecurity == nullptr) {
- CMStringA szSpnStr;
- if (szHost && _stricmp(szProvider, "Basic")) {
- unsigned long ip = inet_addr(szHost);
- PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET);
- szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost);
- _strlwr(szSpnStr.GetBuffer() + 5);
- Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str());
- }
- m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str()));
- if (m_hNtlmSecurity) {
- m_szProvider = mir_strdup(szProvider);
- m_szHost = mir_strdup(szHost);
- justCreated = true;
- }
- }
-
- if (m_hNtlmSecurity) {
- ptrW szLogin, szPassw;
-
- if (nlu->settings.useProxyAuth) {
- mir_cslock lck(csNetlibUser);
- szLogin = mir_a2u(nlu->settings.szProxyAuthUser);
- szPassw = mir_a2u(nlu->settings.szProxyAuthPassword);
- }
-
- szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete);
- if (!szAuthHdr)
- Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)");
- else if (justCreated)
- proxyAuthList.add(m_szHost, m_szProvider);
- }
- else complete = 1;
-
- return szAuthHdr;
- }
-};
-
-static int HttpPeekFirstResponseLine(NetlibConnection *nlc, DWORD dwTimeoutTime, DWORD recvFlags, int *resultCode, char **ppszResultDescr, int *length)
-{
- int bytesPeeked;
- char buffer[2048], *peol;
-
- while (true) {
- bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags);
- if (bytesPeeked == 0) {
- SetLastError(ERROR_HANDLE_EOF);
- return 0;
- }
- if (bytesPeeked == SOCKET_ERROR)
- return 0;
-
- buffer[bytesPeeked] = '\0';
- if ((peol = strchr(buffer, '\n')) != nullptr)
- break;
-
- if ((int)mir_strlen(buffer) < bytesPeeked) {
- SetLastError(ERROR_BAD_FORMAT);
- return 0;
- }
- if (bytesPeeked == _countof(buffer) - 1) {
- SetLastError(ERROR_BUFFER_OVERFLOW);
- return 0;
- }
- if (Miranda_IsTerminated())
- return 0;
- Sleep(10);
- }
-
- if (peol == buffer) {
- SetLastError(ERROR_BAD_FORMAT);
- return 0;
- }
-
- *peol = '\0';
-
- if (_strnicmp(buffer, "HTTP/", 5)) {
- SetLastError(ERROR_BAD_FORMAT);
- return 0;
- }
-
- size_t off = strcspn(buffer, " \t");
- if (off >= (unsigned)bytesPeeked)
- return 0;
-
- char *pResultCode = buffer + off;
- *(pResultCode++) = 0;
-
- char *pResultDescr;
- *resultCode = strtol(pResultCode, &pResultDescr, 10);
-
- if (ppszResultDescr)
- *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr));
-
- if (length)
- *length = peol - buffer + 1;
- return 1;
-}
-
-static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader)
-{
- bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH);
-
- if (sendContentLengthHeader && sendData)
- httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength);
- else
- httpRequest.AppendFormat("\r\n");
-
- DWORD hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
- (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ?
- MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
- (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
-
- int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags);
- if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) {
- DWORD sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
- (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ?
- MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
- (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
-
- int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags);
-
- bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR;
- }
-
- return bytesSent;
-}
-
-MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr)
-{
- NETLIBHTTPREQUEST *nlhrReply = nullptr;
- HttpSecurityContext httpSecurity;
-
- char *szHost = nullptr, *szNewUrl = nullptr;
- char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr;
- int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader;
- int bytesSent = 0;
- bool lastFirstLineFail = false;
-
- if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') {
- SetLastError(ERROR_INVALID_PARAMETER);
- return SOCKET_ERROR;
- }
-
- NetlibUser *nlu = nlc->nlu;
- if (GetNetlibHandleType(nlu) != NLH_USER) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return SOCKET_ERROR;
- }
-
- int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT;
-
- const char *pszRequest;
- switch (nlhr->requestType) {
- case REQUEST_GET: pszRequest = "GET"; break;
- case REQUEST_POST: pszRequest = "POST"; break;
- case REQUEST_CONNECT: pszRequest = "CONNECT"; break;
- case REQUEST_HEAD: pszRequest = "HEAD"; break;
- case REQUEST_PUT: pszRequest = "PUT"; break;
- case REQUEST_DELETE: pszRequest = "DELETE"; break;
- case REQUEST_PATCH: pszRequest = "PATCH"; break;
- default:
- SetLastError(ERROR_INVALID_PARAMETER);
- return SOCKET_ERROR;
- }
-
- if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
- return SOCKET_ERROR;
-
- const char *pszFullUrl = nlhr->szUrl;
- const char *pszUrl = nullptr;
-
- unsigned complete = false;
- int count = 11;
- while (--count) {
- if (GetNetlibHandleType(nlc) != NLH_CONNECTION) {
- nlc = nullptr;
- bytesSent = SOCKET_ERROR;
- break;
- }
-
- if (!NetlibReconnect(nlc)) {
- bytesSent = SOCKET_ERROR;
- break;
- }
-
- if (!pszUrl) {
- pszUrl = pszFullUrl;
- if (!(nlhr->flags & NLHRF_MANUALHOST)) {
- bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL);
-
- const char *ppath, *phost;
- phost = strstr(pszUrl, "://");
- if (phost == nullptr) phost = pszUrl;
- else phost += 3;
- ppath = strchr(phost, '/');
- if (ppath == phost)
- phost = nullptr;
-
- replaceStr(szHost, phost);
- if (ppath && phost)
- szHost[ppath - phost] = 0;
-
- if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy)
- pszUrl = ppath ? ppath : "/";
-
- if (usingProxy && phost && !nlc->dnsThroughProxy) {
- char *tszHost = mir_strdup(phost);
- if (ppath)
- tszHost[ppath - phost] = 0;
- char *cln = strchr(tszHost, ':'); if (cln) *cln = 0;
-
- if (inet_addr(tszHost) == INADDR_NONE) {
- in_addr ip;
- if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) {
- mir_free(szHost);
- if (cln) *cln = ':';
- szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach();
- }
- }
- mir_free(tszHost);
- }
- }
- }
-
- if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) {
- if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) {
- const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer);
- if (szAuthMethodNlu) {
- mir_free(pszProxyAuthHdr);
- pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete);
- }
- }
- }
- nlc->proxyAuthNeeded = false;
-
- CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0);
-
- // HTTP headers
- doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0;
- for (i = 0; i < nlhr->headersCount; i++) {
- NETLIBHTTPHEADER &p = nlhr->headers[i];
- if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1;
- else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1;
- else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1;
- else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1;
- else if (!mir_strcmpi(p.szName, "Connection")) continue;
- if (p.szValue == nullptr) continue;
- httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue);
- }
- if (szHost && !doneHostHeader)
- httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost);
- if (pszProxyAuthHdr && !doneProxyAuthHeader)
- httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr);
- if (pszAuthHdr && !doneAuthHeader)
- httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr);
- httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive");
- httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive");
-
- // Add Sticky Headers
- if (nlu->szStickyHeaders != nullptr)
- httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders);
-
- // send it
- bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader);
- if (bytesSent == SOCKET_ERROR)
- break;
-
- // ntlm reply
- if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD)
- break;
-
- DWORD fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0);
- DWORD dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout;
- if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) {
- DWORD err = GetLastError();
- Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count);
-
- // connection died while we were waiting
- if (GetNetlibHandleType(nlc) != NLH_CONNECTION) {
- nlc = nullptr;
- break;
- }
-
- if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) {
- bytesSent = SOCKET_ERROR;
- break;
- }
-
- lastFirstLineFail = true;
- continue;
- }
-
- int resultCode = nlhr->resultCode;
- lastFirstLineFail = false;
-
- DWORD hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ?
- MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
- (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
-
- DWORD dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) |
- (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP;
-
- if (resultCode == 100)
- nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
-
- else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect
- pszUrl = nullptr;
-
- if (nlhr->requestType == REQUEST_HEAD)
- nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
- else
- nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
-
- if (nlhrReply) {
- char* tmpUrl = NetlibHttpFindHeader(nlhrReply, "Location");
- if (tmpUrl) {
- size_t rlen = 0;
- if (tmpUrl[0] == '/') {
- const char *ppath, *phost;
- phost = strstr(pszFullUrl, "://");
- phost = phost ? phost + 3 : pszFullUrl;
- ppath = strchr(phost, '/');
- rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl);
- }
-
- nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1);
-
- strncpy(nlc->szNewUrl, pszFullUrl, rlen);
- mir_strcpy(nlc->szNewUrl + rlen, tmpUrl);
- pszFullUrl = nlc->szNewUrl;
- pszUrl = nullptr;
-
- if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) {
- bytesSent = SOCKET_ERROR;
- break;
- }
- }
- else {
- NetlibHttpSetLastErrorUsingHttpResult(resultCode);
- bytesSent = SOCKET_ERROR;
- break;
- }
- }
- else {
- NetlibHttpSetLastErrorUsingHttpResult(resultCode);
- bytesSent = SOCKET_ERROR;
- break;
- }
- }
- else if (resultCode == 401 && !doneAuthHeader) { //auth required
- if (nlhr->requestType == REQUEST_HEAD)
- nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
- else
- nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
-
- replaceStr(pszAuthHdr, nullptr);
- if (nlhrReply) {
- char *szAuthStr = nullptr;
- if (!complete) {
- szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider);
- if (szAuthStr) {
- char *szChallenge = strchr(szAuthStr, ' ');
- if (!szChallenge || !*lrtrimp(szChallenge))
- complete = true;
- }
- }
- if (complete && httpSecurity.m_hNtlmSecurity)
- szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr;
-
- if (szAuthStr) {
- char *szChallenge = strchr(szAuthStr, ' ');
- if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
-
- pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete);
- }
- }
- if (pszAuthHdr == nullptr) {
- proxyAuthList.add(szHost, nullptr);
- NetlibHttpSetLastErrorUsingHttpResult(resultCode);
- bytesSent = SOCKET_ERROR;
- break;
- }
- }
- else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required
- if (nlhr->requestType == REQUEST_HEAD)
- nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags);
- else
- nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
-
- mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr;
- if (nlhrReply) {
- char *szAuthStr = nullptr;
- if (!complete) {
- szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider);
- if (szAuthStr) {
- char *szChallenge = strchr(szAuthStr, ' ');
- if (!szChallenge || !*lrtrimp(szChallenge + 1))
- complete = true;
- }
- }
- if (complete && httpSecurity.m_hNtlmSecurity)
- szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr;
-
- if (szAuthStr) {
- char *szChallenge = strchr(szAuthStr, ' ');
- if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
-
- pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete);
- }
- }
- if (pszProxyAuthHdr == nullptr) {
- proxyAuthList.add(nlc->szProxyServer, nullptr);
- NetlibHttpSetLastErrorUsingHttpResult(resultCode);
- bytesSent = SOCKET_ERROR;
- break;
- }
- }
- else break;
-
- if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader)
- replaceStr(pszProxyAuthHdr, nullptr);
-
- if (pszAuthHdr && resultCode != 401 && !doneAuthHeader)
- replaceStr(pszAuthHdr, nullptr);
-
- if (nlhrReply) {
- Netlib_FreeHttpRequest(nlhrReply);
- nlhrReply = nullptr;
- }
- }
-
- if (count == 0) bytesSent = SOCKET_ERROR;
- if (nlhrReply)
- Netlib_FreeHttpRequest(nlhrReply);
-
- //clean up
- mir_free(pszProxyAuthHdr);
- mir_free(pszAuthHdr);
- mir_free(szHost);
- mir_free(szNewUrl);
-
- if (nlc)
- NetlibLeaveNestedCS(&nlc->ncsSend);
-
- return bytesSent;
-}
-
-MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr)
-{
- if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return false;
- }
-
- if (nlhr->headers) {
- for (int i = 0; i < nlhr->headersCount; i++) {
- NETLIBHTTPHEADER &p = nlhr->headers[i];
- mir_free(p.szName);
- mir_free(p.szValue);
- }
- mir_free(nlhr->headers);
- }
- mir_free(nlhr->pData);
- mir_free(nlhr->szResultDescr);
- mir_free(nlhr->szUrl);
- mir_free(nlhr);
- return true;
-}
-
-#define NHRV_BUF_SIZE 8192
-
-MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags)
-{
- NetlibConnection *nlc = (NetlibConnection*)hConnection;
- if (!NetlibEnterNestedCS(nlc, NLNCS_RECV))
- return nullptr;
-
- DWORD dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT;
- NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST));
- nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
- nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions
- nlhr->requestType = REQUEST_RESPONSE;
-
- int firstLineLength = 0;
- if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) {
- NetlibLeaveNestedCS(&nlc->ncsRecv);
- Netlib_FreeHttpRequest(nlhr);
- return nullptr;
- }
-
- char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1);
- int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT);
- if (bytesPeeked != firstLineLength) {
- NetlibLeaveNestedCS(&nlc->ncsRecv);
- Netlib_FreeHttpRequest(nlhr);
- if (bytesPeeked != SOCKET_ERROR)
- SetLastError(ERROR_HANDLE_EOF);
- return nullptr;
- }
-
- // Make sure all headers arrived
- MBinBuffer buf;
- int headersCount = 0;
- bytesPeeked = 0;
- for (bool headersCompleted = false; !headersCompleted;) {
- bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE);
- if (bytesPeeked == 0)
- break;
-
- if (bytesPeeked == SOCKET_ERROR) {
- bytesPeeked = 0;
- break;
- }
-
- buf.append(buffer, bytesPeeked);
-
- headersCount = 0;
- for (char *pbuffer = (char*)buf.data();; headersCount++) {
- char *peol = strchr(pbuffer, '\n');
- if (peol == nullptr) break;
- if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) {
- bytesPeeked = peol - (char*)buf.data() + 1;
- headersCompleted = true;
- break;
- }
- pbuffer = peol + 1;
- }
- }
-
- if (bytesPeeked <= 0) {
- NetlibLeaveNestedCS(&nlc->ncsRecv);
- Netlib_FreeHttpRequest(nlhr);
- return nullptr;
- }
-
- // Receive headers
- nlhr->headersCount = headersCount;
- nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount);
-
- headersCount = 0;
- for (char *pbuffer = buf.data();; headersCount++) {
- char *peol = strchr(pbuffer, '\n');
- if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r'))
- break;
- *peol = 0;
-
- char *pColon = strchr(pbuffer, ':');
- if (pColon == nullptr) {
- Netlib_FreeHttpRequest(nlhr); nlhr = nullptr;
- SetLastError(ERROR_INVALID_DATA);
- break;
- }
-
- *pColon = 0;
- nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer));
- nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1));
- pbuffer = peol + 1;
- }
-
- // remove processed data
- buf.remove(bytesPeeked);
- nlc->foreBuf.appendBefore(buf.data(), buf.length());
-
- NetlibLeaveNestedCS(&nlc->ncsRecv);
- return nlhr;
-}
-
-MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr)
-{
- if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) ||
- nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) ||
- nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0)
- {
- SetLastError(ERROR_INVALID_PARAMETER);
- return nullptr;
- }
-
- if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION)
- nlhr->nlc = nullptr;
-
- NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc);
- if (nlc == nullptr)
- return nullptr;
-
- NETLIBHTTPREQUEST nlhrSend = *nlhr;
- nlhrSend.flags |= NLHRF_SMARTREMOVEHOST;
-
- bool doneUserAgentHeader = NetlibHttpFindHeader(nlhr, "User-Agent") != nullptr;
- bool doneAcceptEncoding = NetlibHttpFindHeader(nlhr, "Accept-Encoding") != nullptr;
- if (!doneUserAgentHeader || !doneAcceptEncoding) {
- nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2));
- memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount);
- }
-
- char szUserAgent[64];
- if (!doneUserAgentHeader) {
- nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent";
- nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent;
- ++nlhrSend.headersCount;
-
- char szMirandaVer[64];
- strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE);
- #if defined(_WIN64)
- strncat_s(szMirandaVer, " x64", _TRUNCATE);
- #endif
-
- char *pspace = strchr(szMirandaVer, ' ');
- if (pspace) {
- *pspace++ = '\0';
- mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace);
- }
- else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer);
- }
- if (!doneAcceptEncoding) {
- nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding";
- nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip";
- ++nlhrSend.headersCount;
- }
- if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) {
- if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers);
- nlhr->resultCode = nlhrSend.resultCode;
- Netlib_CloseHandle(nlc);
- return nullptr;
- }
- if (!doneUserAgentHeader || !doneAcceptEncoding)
- mir_free(nlhrSend.headers);
-
- DWORD dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
- (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
- (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
-
- DWORD hflags =
- (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
- (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
-
- NETLIBHTTPREQUEST *nlhrReply;
- if (nlhr->requestType == REQUEST_HEAD)
- nlhrReply = Netlib_RecvHttpHeaders(nlc);
- else
- nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
-
- if (nlhrReply) {
- nlhrReply->szUrl = nlc->szNewUrl;
- nlc->szNewUrl = nullptr;
- }
-
- if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) {
- Netlib_CloseHandle(nlc);
- if (nlhrReply)
- nlhrReply->nlc = nullptr;
- }
- else nlhrReply->nlc = nlc;
-
- return nlhrReply;
-}
-
-void NetlibHttpSetLastErrorUsingHttpResult(int result)
-{
- if (result >= 200 && result < 300) {
- SetLastError(ERROR_SUCCESS);
- return;
- }
- switch (result) {
- case 400: SetLastError(ERROR_BAD_FORMAT); break;
- case 401:
- case 402:
- case 403:
- case 407: SetLastError(ERROR_ACCESS_DENIED); break;
- case 404: SetLastError(ERROR_FILE_NOT_FOUND); break;
- case 405:
- case 406: SetLastError(ERROR_INVALID_FUNCTION); break;
- case 408: SetLastError(ERROR_TIMEOUT); break;
- default: SetLastError(ERROR_GEN_FAILURE); break;
- }
-}
-
-char* gzip_decode(char *gzip_data, int *len_ptr, int window)
-{
- if (*len_ptr == 0) return nullptr;
-
- int gzip_len = *len_ptr * 5;
- char* output_data = nullptr;
-
- int gzip_err;
- z_stream zstr;
-
- do {
- output_data = (char*)mir_realloc(output_data, gzip_len+1);
-
- zstr.next_in = (Bytef*)gzip_data;
- zstr.avail_in = *len_ptr;
- zstr.zalloc = Z_NULL;
- zstr.zfree = Z_NULL;
- zstr.opaque = Z_NULL;
- inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream));
-
- zstr.next_out = (Bytef*)output_data;
- zstr.avail_out = gzip_len;
-
- gzip_err = inflate(&zstr, Z_FINISH);
-
- inflateEnd(&zstr);
- gzip_len *= 2;
- } while (gzip_err == Z_BUF_ERROR);
-
- gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1;
-
- if (gzip_len <= 0) {
- mir_free(output_data);
- output_data = nullptr;
- }
- else output_data[gzip_len] = 0;
-
- *len_ptr = gzip_len;
- return output_data;
-}
-
-static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, DWORD flags)
-{
- MBinBuffer buf;
-
- while (true) {
- char data[1000];
- int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags);
- if (recvResult <= 0 || recvResult >= _countof(data))
- return SOCKET_ERROR;
-
- buf.append(data, recvResult); // add chunk
-
- const char *peol1 = (const char*)memchr(buf.data(), '\n', buf.length());
- if (peol1 == nullptr)
- continue;
-
- int cbRest = int(peol1 - buf.data()) + 1;
- const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest);
- if (peol2 == nullptr)
- continue;
-
- int sz = peol2 - buf.data() + 1;
- int r = strtol(first ? buf.data() : peol1 + 1, nullptr, 16);
- if (r == 0) {
- const char *peol3 = strchr(peol2 + 1, '\n');
- if (peol3 == nullptr)
- continue;
- sz = peol3 - buf.data() + 1;
- }
- buf.remove(sz); // remove all our data from buffer
- nlc->foreBuf.appendBefore(buf.data(), buf.length());
- return r;
- }
-}
-
-NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, DWORD hflags, DWORD dflags, bool isConnect)
-{
- int dataLen = -1, i, chunkhdr = 0;
- bool chunked = false;
- int cenc = 0, cenctype = 0, close = 0;
-
-next:
- NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags);
- if (nlhrReply == nullptr)
- return nullptr;
-
- if (nlhrReply->resultCode == 100) {
- Netlib_FreeHttpRequest(nlhrReply);
- goto next;
- }
-
- if (nlhrReply->resultCode == 204)
- dataLen = 0;
-
- for (i = 0; i < nlhrReply->headersCount; i++) {
- NETLIBHTTPHEADER &p = nlhrReply->headers[i];
- if (!mir_strcmpi(p.szName, "Content-Length"))
- dataLen = atoi(p.szValue);
-
- if (!mir_strcmpi(p.szName, "Content-Encoding")) {
- cenc = i;
- if (strstr(p.szValue, "gzip"))
- cenctype = 1;
- else if (strstr(p.szValue, "deflate"))
- cenctype = 2;
- }
-
- if (!mir_strcmpi(p.szName, "Connection"))
- close = !mir_strcmpi(p.szValue, "close");
-
- if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) {
- chunked = true;
- chunkhdr = i;
- dataLen = -1;
- }
- }
-
- if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) {
- int recvResult, chunksz = -1;
- int dataBufferAlloced;
-
- if (chunked) {
- chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0));
- if (chunksz == SOCKET_ERROR) {
- Netlib_FreeHttpRequest(nlhrReply);
- return nullptr;
- }
- dataLen = chunksz;
- }
- dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1;
- nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
-
- while (chunksz != 0) {
- while (true) {
- recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT,
- nlhrReply->pData + nlhrReply->dataLength,
- dataBufferAlloced - nlhrReply->dataLength - 1,
- dflags | (cenctype ? MSG_NODUMP : 0));
-
- if (recvResult == 0) break;
- if (recvResult == SOCKET_ERROR) {
- Netlib_FreeHttpRequest(nlhrReply);
- return nullptr;
- }
- nlhrReply->dataLength += recvResult;
-
- if (dataLen >= 0) {
- if (nlhrReply->dataLength >= dataLen)
- break;
- }
- else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) {
- dataBufferAlloced += 2048;
- nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
- if (nlhrReply->pData == nullptr) {
- SetLastError(ERROR_OUTOFMEMORY);
- Netlib_FreeHttpRequest(nlhrReply);
- return nullptr;
- }
- }
- Sleep(10);
- }
-
- if (!chunked)
- break;
-
- chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP);
- if (chunksz == SOCKET_ERROR) {
- Netlib_FreeHttpRequest(nlhrReply);
- return nullptr;
- }
- dataLen += chunksz;
- dataBufferAlloced += chunksz;
-
- nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
- }
-
- nlhrReply->pData[nlhrReply->dataLength] = '\0';
- }
-
- if (chunked) {
- nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16);
- mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length");
-
- nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16);
- mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength);
- }
-
- if (cenctype) {
- int bufsz = nlhrReply->dataLength;
- char* szData = nullptr;
-
- switch (cenctype) {
- case 1:
- szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS);
- break;
-
- case 2:
- szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS);
- if (bufsz < 0) {
- bufsz = nlhrReply->dataLength;
- szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS);
- }
- break;
- }
-
- if (bufsz > 0) {
- NetlibDumpData(nlc, (PBYTE)szData, bufsz, 0, dflags | MSG_NOTITLE);
- mir_free(nlhrReply->pData);
- nlhrReply->pData = szData;
- nlhrReply->dataLength = bufsz;
-
- mir_free(nlhrReply->headers[cenc].szName);
- mir_free(nlhrReply->headers[cenc].szValue);
- memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0]));
- }
- else if (bufsz == 0) {
- mir_free(nlhrReply->pData);
- nlhrReply->pData = nullptr;
- nlhrReply->dataLength = 0;
- }
- }
-
- if (close &&
- (nlc->proxyType != PROXYTYPE_HTTP || nlc->nloc.flags & NLOCF_SSL) &&
- (!isConnect || nlhrReply->resultCode != 200))
- NetlibDoCloseSocket(nlc);
-
- return nlhrReply;
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "../libs/zlib/src/zlib.h"
+#include "netlib.h"
+
+#define HTTPRECVHEADERSTIMEOUT 30000 //in ms
+#define HTTPRECVDATATIMEOUT 20000
+
+struct ProxyAuth
+{
+ char *szServer;
+ char *szMethod;
+
+ ProxyAuth(const char *pszServer, const char *pszMethod)
+ {
+ szServer = mir_strdup(pszServer);
+ szMethod = mir_strdup(pszMethod);
+ }
+ ~ProxyAuth()
+ {
+ mir_free(szServer);
+ mir_free(szMethod);
+ }
+ static int Compare(const ProxyAuth *p1, const ProxyAuth *p2)
+ {
+ return mir_strcmpi(p1->szServer, p2->szServer);
+ }
+};
+
+struct ProxyAuthList : OBJLIST<ProxyAuth>
+{
+ ProxyAuthList() : OBJLIST<ProxyAuth>(2, ProxyAuth::Compare) {}
+
+ void add(const char *szServer, const char *szMethod)
+ {
+ if (szServer == nullptr) return;
+ int i = getIndex((ProxyAuth*)&szServer);
+ if (i >= 0) {
+ ProxyAuth &rec = (*this)[i];
+ if (szMethod == nullptr)
+ remove(i);
+ else if (_stricmp(rec.szMethod, szMethod)) {
+ mir_free(rec.szMethod);
+ rec.szMethod = mir_strdup(szMethod);
+ }
+ }
+ else insert(new ProxyAuth(szServer, szMethod));
+ }
+
+ const char* find(const char *szServer)
+ {
+ ProxyAuth *rec = szServer ? OBJLIST<ProxyAuth>::find((ProxyAuth*)&szServer) : nullptr;
+ return rec ? rec->szMethod : nullptr;
+ }
+};
+
+ProxyAuthList proxyAuthList;
+
+static int RecvWithTimeoutTime(NetlibConnection *nlc, unsigned dwTimeoutTime, char *buf, int len, int flags)
+{
+ DWORD dwTimeNow;
+
+ if (nlc->foreBuf.isEmpty() && !sslApi.pending(nlc->hSsl)) {
+ while ((dwTimeNow = GetTickCount()) < dwTimeoutTime) {
+ unsigned dwDeltaTime = min(dwTimeoutTime - dwTimeNow, 1000);
+ int res = WaitUntilReadable(nlc->s, dwDeltaTime);
+
+ switch (res) {
+ case SOCKET_ERROR:
+ return SOCKET_ERROR;
+
+ case 1:
+ return Netlib_Recv(nlc, buf, len, flags);
+ }
+
+ if (nlc->termRequested || Miranda_IsTerminated())
+ return 0;
+ }
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ return Netlib_Recv(nlc, buf, len, flags);
+}
+
+static char* NetlibHttpFindHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr)
+{
+ for (int i=0; i < nlhrReply->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhrReply->headers[i];
+ if (_stricmp(p.szName, hdr) == 0)
+ return p.szValue;
+ }
+
+ return nullptr;
+}
+
+static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char *hdr, const char *szProvider)
+{
+ char *szBasicHdr = nullptr;
+ char *szNegoHdr = nullptr;
+ char *szNtlmHdr = nullptr;
+
+ for (int i=0; i < nlhrReply->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhrReply->headers[i];
+ if (_stricmp(p.szName, hdr) == 0) {
+ if (_strnicmp(p.szValue, "Negotiate", 9) == 0)
+ szNegoHdr = p.szValue;
+ else if (_strnicmp(p.szValue, "NTLM", 4) == 0)
+ szNtlmHdr = p.szValue;
+ else if (_strnicmp(p.szValue, "Basic", 5) == 0)
+ szBasicHdr = p.szValue;
+ }
+ }
+
+ if (szNegoHdr && (!szProvider || !_stricmp(szProvider, "Negotiate"))) return szNegoHdr;
+ if (szNtlmHdr && (!szProvider || !_stricmp(szProvider, "NTLM"))) return szNtlmHdr;
+ if (!szProvider || !_stricmp(szProvider, "Basic")) return szBasicHdr;
+ return nullptr;
+}
+
+void NetlibConnFromUrl(const char *szUrl, bool secur, NETLIBOPENCONNECTION &nloc)
+{
+ secur = secur || _strnicmp(szUrl, "https", 5) == 0;
+ const char* phost = strstr(szUrl, "://");
+
+ char* szHost = mir_strdup(phost ? phost + 3 : szUrl);
+
+ char* ppath = strchr(szHost, '/');
+ if (ppath) *ppath = '\0';
+
+ memset(&nloc, 0, sizeof(nloc));
+ nloc.szHost = szHost;
+
+ char* pcolon = strrchr(szHost, ':');
+ if (pcolon) {
+ *pcolon = '\0';
+ nloc.wPort = (WORD)strtol(pcolon+1, nullptr, 10);
+ }
+ else nloc.wPort = secur ? 443 : 80;
+ nloc.flags = (secur ? NLOCF_SSL : 0);
+}
+
+static NetlibConnection* NetlibHttpProcessUrl(NETLIBHTTPREQUEST *nlhr, NetlibUser *nlu, NetlibConnection *nlc, const char *szUrl = nullptr)
+{
+ NETLIBOPENCONNECTION nloc;
+
+ if (szUrl == nullptr)
+ NetlibConnFromUrl(nlhr->szUrl, (nlhr->flags & NLHRF_SSL) != 0, nloc);
+ else
+ NetlibConnFromUrl(szUrl, false, nloc);
+
+ nloc.flags |= NLOCF_HTTP;
+ if (nloc.flags & NLOCF_SSL)
+ nlhr->flags |= NLHRF_SSL;
+ else
+ nlhr->flags &= ~NLHRF_SSL;
+
+ if (nlc != nullptr) {
+ bool httpProxy = !(nloc.flags & NLOCF_SSL) && nlc->proxyType == PROXYTYPE_HTTP;
+ bool sameHost = mir_strcmp(nlc->nloc.szHost, nloc.szHost) == 0 && nlc->nloc.wPort == nloc.wPort;
+
+ if (!httpProxy && !sameHost) {
+ NetlibDoCloseSocket(nlc);
+
+ mir_free((char*)nlc->nloc.szHost);
+ nlc->nloc = nloc;
+ return NetlibDoConnect(nlc) ? nlc : nullptr;
+ }
+ }
+ else nlc = (NetlibConnection*)Netlib_OpenConnection(nlu, &nloc);
+
+ mir_free((char*)nloc.szHost);
+
+ return nlc;
+}
+
+struct HttpSecurityContext
+{
+ HANDLE m_hNtlmSecurity;
+ char *m_szHost;
+ char *m_szProvider;
+
+ HttpSecurityContext()
+ {
+ m_hNtlmSecurity = nullptr; m_szHost = nullptr; m_szProvider = nullptr;
+ }
+
+ ~HttpSecurityContext() { Destroy(); }
+
+ void Destroy(void)
+ {
+ if (!m_hNtlmSecurity) return;
+
+ Netlib_DestroySecurityProvider(m_hNtlmSecurity);
+ m_hNtlmSecurity = nullptr;
+ mir_free(m_szHost); m_szHost = nullptr;
+ mir_free(m_szProvider); m_szProvider = nullptr;
+ }
+
+ bool TryBasic(void)
+ {
+ return m_hNtlmSecurity && m_szProvider && _stricmp(m_szProvider, "Basic");
+ }
+
+ char* Execute(NetlibConnection *nlc, char *szHost, const char *szProvider, const char *szChallenge, unsigned &complete)
+ {
+ char *szAuthHdr = nullptr;
+ bool justCreated = false;
+ NetlibUser *nlu = nlc->nlu;
+
+ if (m_hNtlmSecurity) {
+ bool newAuth = !m_szProvider || !szProvider || _stricmp(m_szProvider, szProvider);
+ newAuth = newAuth || (m_szHost != szHost && (!m_szHost || !szHost || _stricmp(m_szHost, szHost)));
+ if (newAuth)
+ Destroy();
+ }
+
+ if (m_hNtlmSecurity == nullptr) {
+ CMStringA szSpnStr;
+ if (szHost && _stricmp(szProvider, "Basic")) {
+ unsigned long ip = inet_addr(szHost);
+ PHOSTENT host = (ip == INADDR_NONE) ? gethostbyname(szHost) : gethostbyaddr((char*)&ip, 4, AF_INET);
+ szSpnStr.Format("HTTP/%s", host && host->h_name ? host->h_name : szHost);
+ _strlwr(szSpnStr.GetBuffer() + 5);
+ Netlib_Logf(nlu, "Host SPN: %s", szSpnStr.c_str());
+ }
+ m_hNtlmSecurity = Netlib_InitSecurityProvider(_A2T(szProvider), szSpnStr.IsEmpty() ? nullptr : _A2T(szSpnStr.c_str()));
+ if (m_hNtlmSecurity) {
+ m_szProvider = mir_strdup(szProvider);
+ m_szHost = mir_strdup(szHost);
+ justCreated = true;
+ }
+ }
+
+ if (m_hNtlmSecurity) {
+ ptrW szLogin, szPassw;
+
+ if (nlu->settings.useProxyAuth) {
+ mir_cslock lck(csNetlibUser);
+ szLogin = mir_a2u(nlu->settings.szProxyAuthUser);
+ szPassw = mir_a2u(nlu->settings.szProxyAuthPassword);
+ }
+
+ szAuthHdr = NtlmCreateResponseFromChallenge(m_hNtlmSecurity, szChallenge, szLogin, szPassw, true, complete);
+ if (!szAuthHdr)
+ Netlib_Logf(nullptr, "Security login %s failed, user: %S pssw: %S", szProvider, szLogin ? szLogin.get() : L"(no user)", szPassw ? L"(exist)" : L"(no psw)");
+ else if (justCreated)
+ proxyAuthList.add(m_szHost, m_szProvider);
+ }
+ else complete = 1;
+
+ return szAuthHdr;
+ }
+};
+
+static int HttpPeekFirstResponseLine(NetlibConnection *nlc, DWORD dwTimeoutTime, DWORD recvFlags, int *resultCode, char **ppszResultDescr, int *length)
+{
+ int bytesPeeked;
+ char buffer[2048], *peol;
+
+ while (true) {
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwTimeoutTime, buffer, _countof(buffer) - 1, MSG_PEEK | recvFlags);
+ if (bytesPeeked == 0) {
+ SetLastError(ERROR_HANDLE_EOF);
+ return 0;
+ }
+ if (bytesPeeked == SOCKET_ERROR)
+ return 0;
+
+ buffer[bytesPeeked] = '\0';
+ if ((peol = strchr(buffer, '\n')) != nullptr)
+ break;
+
+ if ((int)mir_strlen(buffer) < bytesPeeked) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+ if (bytesPeeked == _countof(buffer) - 1) {
+ SetLastError(ERROR_BUFFER_OVERFLOW);
+ return 0;
+ }
+ if (Miranda_IsTerminated())
+ return 0;
+ Sleep(10);
+ }
+
+ if (peol == buffer) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ *peol = '\0';
+
+ if (_strnicmp(buffer, "HTTP/", 5)) {
+ SetLastError(ERROR_BAD_FORMAT);
+ return 0;
+ }
+
+ size_t off = strcspn(buffer, " \t");
+ if (off >= (unsigned)bytesPeeked)
+ return 0;
+
+ char *pResultCode = buffer + off;
+ *(pResultCode++) = 0;
+
+ char *pResultDescr;
+ *resultCode = strtol(pResultCode, &pResultDescr, 10);
+
+ if (ppszResultDescr)
+ *ppszResultDescr = mir_strdup(lrtrimp(pResultDescr));
+
+ if (length)
+ *length = peol - buffer + 1;
+ return 1;
+}
+
+static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, NETLIBHTTPREQUEST *nlhr, int sendContentLengthHeader)
+{
+ bool sendData = (nlhr->requestType == REQUEST_POST || nlhr->requestType == REQUEST_PUT || nlhr->requestType == REQUEST_PATCH);
+
+ if (sendContentLengthHeader && sendData)
+ httpRequest.AppendFormat("Content-Length: %d\r\n\r\n", nlhr->dataLength);
+ else
+ httpRequest.AppendFormat("\r\n");
+
+ DWORD hflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND | NLHRF_NODUMPHEADERS) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ int bytesSent = Netlib_Send(nlc, httpRequest, httpRequest.GetLength(), hflags);
+ if (bytesSent != SOCKET_ERROR && sendData && nlhr->dataLength) {
+ DWORD sflags = MSG_NOTITLE | (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ int sendResult = Netlib_Send(nlc, nlhr->pData, nlhr->dataLength, sflags);
+
+ bytesSent = sendResult != SOCKET_ERROR ? bytesSent + sendResult : SOCKET_ERROR;
+ }
+
+ return bytesSent;
+}
+
+MIR_APP_DLL(int) Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr)
+{
+ NETLIBHTTPREQUEST *nlhrReply = nullptr;
+ HttpSecurityContext httpSecurity;
+
+ char *szHost = nullptr, *szNewUrl = nullptr;
+ char *pszProxyAuthHdr = nullptr, *pszAuthHdr = nullptr;
+ int i, doneHostHeader, doneContentLengthHeader, doneProxyAuthHeader, doneAuthHeader;
+ int bytesSent = 0;
+ bool lastFirstLineFail = false;
+
+ if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->szUrl == nullptr || nlhr->szUrl[0] == '\0') {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ NetlibUser *nlu = nlc->nlu;
+ if (GetNetlibHandleType(nlu) != NLH_USER) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int hdrTimeout = (nlhr->timeout) ? nlhr->timeout : HTTPRECVHEADERSTIMEOUT;
+
+ const char *pszRequest;
+ switch (nlhr->requestType) {
+ case REQUEST_GET: pszRequest = "GET"; break;
+ case REQUEST_POST: pszRequest = "POST"; break;
+ case REQUEST_CONNECT: pszRequest = "CONNECT"; break;
+ case REQUEST_HEAD: pszRequest = "HEAD"; break;
+ case REQUEST_PUT: pszRequest = "PUT"; break;
+ case REQUEST_DELETE: pszRequest = "DELETE"; break;
+ case REQUEST_PATCH: pszRequest = "PATCH"; break;
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
+ return SOCKET_ERROR;
+
+ const char *pszFullUrl = nlhr->szUrl;
+ const char *pszUrl = nullptr;
+
+ unsigned complete = false;
+ int count = 11;
+ while (--count) {
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION) {
+ nlc = nullptr;
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ if (!NetlibReconnect(nlc)) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ if (!pszUrl) {
+ pszUrl = pszFullUrl;
+ if (!(nlhr->flags & NLHRF_MANUALHOST)) {
+ bool usingProxy = nlc->proxyType == PROXYTYPE_HTTP && !(nlhr->flags & NLHRF_SSL);
+
+ const char *ppath, *phost;
+ phost = strstr(pszUrl, "://");
+ if (phost == nullptr) phost = pszUrl;
+ else phost += 3;
+ ppath = strchr(phost, '/');
+ if (ppath == phost)
+ phost = nullptr;
+
+ replaceStr(szHost, phost);
+ if (ppath && phost)
+ szHost[ppath - phost] = 0;
+
+ if ((nlhr->flags & NLHRF_SMARTREMOVEHOST) && !usingProxy)
+ pszUrl = ppath ? ppath : "/";
+
+ if (usingProxy && phost && !nlc->dnsThroughProxy) {
+ char *tszHost = mir_strdup(phost);
+ if (ppath)
+ tszHost[ppath - phost] = 0;
+ char *cln = strchr(tszHost, ':'); if (cln) *cln = 0;
+
+ if (inet_addr(tszHost) == INADDR_NONE) {
+ in_addr ip;
+ if (ip.S_un.S_addr = DnsLookup(nlu, tszHost)) {
+ mir_free(szHost);
+ if (cln) *cln = ':';
+ szHost = CMStringA(FORMAT, "%s%s", inet_ntoa(ip), cln ? cln : "").Detach();
+ }
+ }
+ mir_free(tszHost);
+ }
+ }
+ }
+
+ if (nlc->proxyAuthNeeded && proxyAuthList.getCount()) {
+ if (httpSecurity.m_szProvider == nullptr && nlc->szProxyServer) {
+ const char *szAuthMethodNlu = proxyAuthList.find(nlc->szProxyServer);
+ if (szAuthMethodNlu) {
+ mir_free(pszProxyAuthHdr);
+ pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthMethodNlu, "", complete);
+ }
+ }
+ }
+ nlc->proxyAuthNeeded = false;
+
+ CMStringA httpRequest(FORMAT, "%s %s HTTP/1.%d\r\n", pszRequest, pszUrl, (nlhr->flags & NLHRF_HTTP11) != 0);
+
+ // HTTP headers
+ doneHostHeader = doneContentLengthHeader = doneProxyAuthHeader = doneAuthHeader = 0;
+ for (i = 0; i < nlhr->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhr->headers[i];
+ if (!mir_strcmpi(p.szName, "Host")) doneHostHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Content-Length")) doneContentLengthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Proxy-Authorization")) doneProxyAuthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Authorization")) doneAuthHeader = 1;
+ else if (!mir_strcmpi(p.szName, "Connection")) continue;
+ if (p.szValue == nullptr) continue;
+ httpRequest.AppendFormat("%s: %s\r\n", p.szName, p.szValue);
+ }
+ if (szHost && !doneHostHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Host", szHost);
+ if (pszProxyAuthHdr && !doneProxyAuthHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Authorization", pszProxyAuthHdr);
+ if (pszAuthHdr && !doneAuthHeader)
+ httpRequest.AppendFormat("%s: %s\r\n", "Authorization", pszAuthHdr);
+ httpRequest.AppendFormat("%s: %s\r\n", "Connection", "Keep-Alive");
+ httpRequest.AppendFormat("%s: %s\r\n", "Proxy-Connection", "Keep-Alive");
+
+ // Add Sticky Headers
+ if (nlu->szStickyHeaders != nullptr)
+ httpRequest.AppendFormat("%s\r\n", nlu->szStickyHeaders);
+
+ // send it
+ bytesSent = SendHttpRequestAndData(nlc, httpRequest, nlhr, !doneContentLengthHeader);
+ if (bytesSent == SOCKET_ERROR)
+ break;
+
+ // ntlm reply
+ if (doneContentLengthHeader && nlhr->requestType != REQUEST_HEAD)
+ break;
+
+ DWORD fflags = MSG_PEEK | MSG_NODUMP | ((nlhr->flags & NLHRF_NOPROXY) ? MSG_RAW : 0);
+ DWORD dwTimeOutTime = hdrTimeout < 0 ? -1 : GetTickCount() + hdrTimeout;
+ if (!HttpPeekFirstResponseLine(nlc, dwTimeOutTime, fflags, &nlhr->resultCode, nullptr, nullptr)) {
+ DWORD err = GetLastError();
+ Netlib_Logf(nlu, "%s %d: %s Failed (%u %u)", __FILE__, __LINE__, "HttpPeekFirstResponseLine", err, count);
+
+ // connection died while we were waiting
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION) {
+ nlc = nullptr;
+ break;
+ }
+
+ if (err == ERROR_TIMEOUT || err == ERROR_BAD_FORMAT || err == ERROR_BUFFER_OVERFLOW || lastFirstLineFail || nlc->termRequested || nlhr->requestType == REQUEST_CONNECT) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+
+ lastFirstLineFail = true;
+ continue;
+ }
+
+ int resultCode = nlhr->resultCode;
+ lastFirstLineFail = false;
+
+ DWORD hflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPHEADERS | NLHRF_NODUMPSEND) ?
+ MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ DWORD dflags = (nlhr->flags & (NLHRF_NODUMP | NLHRF_NODUMPSEND) ? MSG_NODUMP : MSG_DUMPASTEXT | MSG_DUMPPROXY) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0) | MSG_NODUMP;
+
+ if (resultCode == 100)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+
+ else if (resultCode == 307 || ((resultCode == 301 || resultCode == 302) && (nlhr->flags & NLHRF_REDIRECT))) { // redirect
+ pszUrl = nullptr;
+
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply) {
+ char* tmpUrl = NetlibHttpFindHeader(nlhrReply, "Location");
+ if (tmpUrl) {
+ size_t rlen = 0;
+ if (tmpUrl[0] == '/') {
+ const char *ppath, *phost;
+ phost = strstr(pszFullUrl, "://");
+ phost = phost ? phost + 3 : pszFullUrl;
+ ppath = strchr(phost, '/');
+ rlen = ppath ? ppath - pszFullUrl : mir_strlen(pszFullUrl);
+ }
+
+ nlc->szNewUrl = (char*)mir_realloc(nlc->szNewUrl, rlen + mir_strlen(tmpUrl) * 3 + 1);
+
+ strncpy(nlc->szNewUrl, pszFullUrl, rlen);
+ mir_strcpy(nlc->szNewUrl + rlen, tmpUrl);
+ pszFullUrl = nlc->szNewUrl;
+ pszUrl = nullptr;
+
+ if (NetlibHttpProcessUrl(nlhr, nlu, nlc, pszFullUrl) == nullptr) {
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else {
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else {
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 401 && !doneAuthHeader) { //auth required
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = (NETLIBHTTPREQUEST*)Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ replaceStr(pszAuthHdr, nullptr);
+ if (nlhrReply) {
+ char *szAuthStr = nullptr;
+ if (!complete) {
+ szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", httpSecurity.m_szProvider);
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (!szChallenge || !*lrtrimp(szChallenge))
+ complete = true;
+ }
+ }
+ if (complete && httpSecurity.m_hNtlmSecurity)
+ szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "WWW-Authenticate", "Basic") : nullptr;
+
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszAuthHdr = httpSecurity.Execute(nlc, szHost, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszAuthHdr == nullptr) {
+ proxyAuthList.add(szHost, nullptr);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else if (resultCode == 407 && !doneProxyAuthHeader) { //proxy auth required
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ mir_free(pszProxyAuthHdr); pszProxyAuthHdr = nullptr;
+ if (nlhrReply) {
+ char *szAuthStr = nullptr;
+ if (!complete) {
+ szAuthStr = NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", httpSecurity.m_szProvider);
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (!szChallenge || !*lrtrimp(szChallenge + 1))
+ complete = true;
+ }
+ }
+ if (complete && httpSecurity.m_hNtlmSecurity)
+ szAuthStr = httpSecurity.TryBasic() ? NetlibHttpFindAuthHeader(nlhrReply, "Proxy-Authenticate", "Basic") : nullptr;
+
+ if (szAuthStr) {
+ char *szChallenge = strchr(szAuthStr, ' ');
+ if (szChallenge) { *szChallenge = 0; szChallenge = lrtrimp(szChallenge + 1); }
+
+ pszProxyAuthHdr = httpSecurity.Execute(nlc, nlc->szProxyServer, szAuthStr, szChallenge, complete);
+ }
+ }
+ if (pszProxyAuthHdr == nullptr) {
+ proxyAuthList.add(nlc->szProxyServer, nullptr);
+ NetlibHttpSetLastErrorUsingHttpResult(resultCode);
+ bytesSent = SOCKET_ERROR;
+ break;
+ }
+ }
+ else break;
+
+ if (pszProxyAuthHdr && resultCode != 407 && !doneProxyAuthHeader)
+ replaceStr(pszProxyAuthHdr, nullptr);
+
+ if (pszAuthHdr && resultCode != 401 && !doneAuthHeader)
+ replaceStr(pszAuthHdr, nullptr);
+
+ if (nlhrReply) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ nlhrReply = nullptr;
+ }
+ }
+
+ if (count == 0) bytesSent = SOCKET_ERROR;
+ if (nlhrReply)
+ Netlib_FreeHttpRequest(nlhrReply);
+
+ //clean up
+ mir_free(pszProxyAuthHdr);
+ mir_free(pszAuthHdr);
+ mir_free(szHost);
+ mir_free(szNewUrl);
+
+ if (nlc)
+ NetlibLeaveNestedCS(&nlc->ncsSend);
+
+ return bytesSent;
+}
+
+MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr)
+{
+ if (nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) || nlhr->requestType != REQUEST_RESPONSE) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+
+ if (nlhr->headers) {
+ for (int i = 0; i < nlhr->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhr->headers[i];
+ mir_free(p.szName);
+ mir_free(p.szValue);
+ }
+ mir_free(nlhr->headers);
+ }
+ mir_free(nlhr->pData);
+ mir_free(nlhr->szResultDescr);
+ mir_free(nlhr->szUrl);
+ mir_free(nlhr);
+ return true;
+}
+
+#define NHRV_BUF_SIZE 8192
+
+MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags)
+{
+ NetlibConnection *nlc = (NetlibConnection*)hConnection;
+ if (!NetlibEnterNestedCS(nlc, NLNCS_RECV))
+ return nullptr;
+
+ DWORD dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT;
+ NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(sizeof(NETLIBHTTPREQUEST));
+ nlhr->cbSize = sizeof(NETLIBHTTPREQUEST);
+ nlhr->nlc = nlc; // Needed to id connection in the protocol HTTP gateway wrapper functions
+ nlhr->requestType = REQUEST_RESPONSE;
+
+ int firstLineLength = 0;
+ if (!HttpPeekFirstResponseLine(nlc, dwRequestTimeoutTime, flags | MSG_PEEK, &nlhr->resultCode, &nlhr->szResultDescr, &firstLineLength)) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ return nullptr;
+ }
+
+ char *buffer = (char*)_alloca(NHRV_BUF_SIZE + 1);
+ int bytesPeeked = Netlib_Recv(nlc, buffer, min(firstLineLength, NHRV_BUF_SIZE), flags | MSG_DUMPASTEXT);
+ if (bytesPeeked != firstLineLength) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ if (bytesPeeked != SOCKET_ERROR)
+ SetLastError(ERROR_HANDLE_EOF);
+ return nullptr;
+ }
+
+ // Make sure all headers arrived
+ MBinBuffer buf;
+ int headersCount = 0;
+ bytesPeeked = 0;
+ for (bool headersCompleted = false; !headersCompleted;) {
+ bytesPeeked = RecvWithTimeoutTime(nlc, dwRequestTimeoutTime, buffer, NHRV_BUF_SIZE, flags | MSG_DUMPASTEXT | MSG_NOTITLE);
+ if (bytesPeeked == 0)
+ break;
+
+ if (bytesPeeked == SOCKET_ERROR) {
+ bytesPeeked = 0;
+ break;
+ }
+
+ buf.append(buffer, bytesPeeked);
+
+ headersCount = 0;
+ for (char *pbuffer = (char*)buf.data();; headersCount++) {
+ char *peol = strchr(pbuffer, '\n');
+ if (peol == nullptr) break;
+ if (peol == pbuffer || (peol == (pbuffer + 1) && *pbuffer == '\r')) {
+ bytesPeeked = peol - (char*)buf.data() + 1;
+ headersCompleted = true;
+ break;
+ }
+ pbuffer = peol + 1;
+ }
+ }
+
+ if (bytesPeeked <= 0) {
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ Netlib_FreeHttpRequest(nlhr);
+ return nullptr;
+ }
+
+ // Receive headers
+ nlhr->headersCount = headersCount;
+ nlhr->headers = (NETLIBHTTPHEADER*)mir_calloc(sizeof(NETLIBHTTPHEADER) * headersCount);
+
+ headersCount = 0;
+ for (char *pbuffer = buf.data();; headersCount++) {
+ char *peol = strchr(pbuffer, '\n');
+ if (peol == nullptr || peol == pbuffer || (peol == (pbuffer+1) && *pbuffer == '\r'))
+ break;
+ *peol = 0;
+
+ char *pColon = strchr(pbuffer, ':');
+ if (pColon == nullptr) {
+ Netlib_FreeHttpRequest(nlhr); nlhr = nullptr;
+ SetLastError(ERROR_INVALID_DATA);
+ break;
+ }
+
+ *pColon = 0;
+ nlhr->headers[headersCount].szName = mir_strdup(rtrim(pbuffer));
+ nlhr->headers[headersCount].szValue = mir_strdup(lrtrimp(pColon+1));
+ pbuffer = peol + 1;
+ }
+
+ // remove processed data
+ buf.remove(bytesPeeked);
+ nlc->foreBuf.appendBefore(buf.data(), buf.length());
+
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ return nlhr;
+}
+
+MIR_APP_DLL(NETLIBHTTPREQUEST*) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) ||
+ nlhr == nullptr || nlhr->cbSize != sizeof(NETLIBHTTPREQUEST) ||
+ nlhr->szUrl == nullptr || nlhr->szUrl[0] == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ if (nlhr->nlc != nullptr && GetNetlibHandleType(nlhr->nlc) != NLH_CONNECTION)
+ nlhr->nlc = nullptr;
+
+ NetlibConnection *nlc = NetlibHttpProcessUrl(nlhr, nlu, (NetlibConnection*)nlhr->nlc);
+ if (nlc == nullptr)
+ return nullptr;
+
+ NETLIBHTTPREQUEST nlhrSend = *nlhr;
+ nlhrSend.flags |= NLHRF_SMARTREMOVEHOST;
+
+ bool doneUserAgentHeader = NetlibHttpFindHeader(nlhr, "User-Agent") != nullptr;
+ bool doneAcceptEncoding = NetlibHttpFindHeader(nlhr, "Accept-Encoding") != nullptr;
+ if (!doneUserAgentHeader || !doneAcceptEncoding) {
+ nlhrSend.headers = (NETLIBHTTPHEADER*)mir_alloc(sizeof(NETLIBHTTPHEADER) * (nlhrSend.headersCount + 2));
+ memcpy(nlhrSend.headers, nlhr->headers, sizeof(NETLIBHTTPHEADER) * nlhr->headersCount);
+ }
+
+ char szUserAgent[64];
+ if (!doneUserAgentHeader) {
+ nlhrSend.headers[nlhrSend.headersCount].szName = "User-Agent";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = szUserAgent;
+ ++nlhrSend.headersCount;
+
+ char szMirandaVer[64];
+ strncpy_s(szMirandaVer, MIRANDA_VERSION_STRING, _TRUNCATE);
+ #if defined(_WIN64)
+ strncat_s(szMirandaVer, " x64", _TRUNCATE);
+ #endif
+
+ char *pspace = strchr(szMirandaVer, ' ');
+ if (pspace) {
+ *pspace++ = '\0';
+ mir_snprintf(szUserAgent, "Miranda/%s (%s)", szMirandaVer, pspace);
+ }
+ else mir_snprintf(szUserAgent, "Miranda/%s", szMirandaVer);
+ }
+ if (!doneAcceptEncoding) {
+ nlhrSend.headers[nlhrSend.headersCount].szName = "Accept-Encoding";
+ nlhrSend.headers[nlhrSend.headersCount].szValue = "deflate, gzip";
+ ++nlhrSend.headersCount;
+ }
+ if (Netlib_SendHttpRequest(nlc, &nlhrSend) == SOCKET_ERROR) {
+ if (!doneUserAgentHeader || !doneAcceptEncoding) mir_free(nlhrSend.headers);
+ nlhr->resultCode = nlhrSend.resultCode;
+ Netlib_CloseHandle(nlc);
+ return nullptr;
+ }
+ if (!doneUserAgentHeader || !doneAcceptEncoding)
+ mir_free(nlhrSend.headers);
+
+ DWORD dflags = (nlhr->flags & NLHRF_DUMPASTEXT ? MSG_DUMPASTEXT : 0) |
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ DWORD hflags =
+ (nlhr->flags & NLHRF_NODUMP ? MSG_NODUMP : (nlhr->flags & NLHRF_DUMPPROXY ? MSG_DUMPPROXY : 0)) |
+ (nlhr->flags & NLHRF_NOPROXY ? MSG_RAW : 0);
+
+ NETLIBHTTPREQUEST *nlhrReply;
+ if (nlhr->requestType == REQUEST_HEAD)
+ nlhrReply = Netlib_RecvHttpHeaders(nlc);
+ else
+ nlhrReply = NetlibHttpRecv(nlc, hflags, dflags);
+
+ if (nlhrReply) {
+ nlhrReply->szUrl = nlc->szNewUrl;
+ nlc->szNewUrl = nullptr;
+ }
+
+ if ((nlhr->flags & NLHRF_PERSISTENT) == 0 || nlhrReply == nullptr) {
+ Netlib_CloseHandle(nlc);
+ if (nlhrReply)
+ nlhrReply->nlc = nullptr;
+ }
+ else nlhrReply->nlc = nlc;
+
+ return nlhrReply;
+}
+
+void NetlibHttpSetLastErrorUsingHttpResult(int result)
+{
+ if (result >= 200 && result < 300) {
+ SetLastError(ERROR_SUCCESS);
+ return;
+ }
+ switch (result) {
+ case 400: SetLastError(ERROR_BAD_FORMAT); break;
+ case 401:
+ case 402:
+ case 403:
+ case 407: SetLastError(ERROR_ACCESS_DENIED); break;
+ case 404: SetLastError(ERROR_FILE_NOT_FOUND); break;
+ case 405:
+ case 406: SetLastError(ERROR_INVALID_FUNCTION); break;
+ case 408: SetLastError(ERROR_TIMEOUT); break;
+ default: SetLastError(ERROR_GEN_FAILURE); break;
+ }
+}
+
+char* gzip_decode(char *gzip_data, int *len_ptr, int window)
+{
+ if (*len_ptr == 0) return nullptr;
+
+ int gzip_len = *len_ptr * 5;
+ char* output_data = nullptr;
+
+ int gzip_err;
+ z_stream zstr;
+
+ do {
+ output_data = (char*)mir_realloc(output_data, gzip_len+1);
+
+ zstr.next_in = (Bytef*)gzip_data;
+ zstr.avail_in = *len_ptr;
+ zstr.zalloc = Z_NULL;
+ zstr.zfree = Z_NULL;
+ zstr.opaque = Z_NULL;
+ inflateInit2_(&zstr, window, ZLIB_VERSION, sizeof(z_stream));
+
+ zstr.next_out = (Bytef*)output_data;
+ zstr.avail_out = gzip_len;
+
+ gzip_err = inflate(&zstr, Z_FINISH);
+
+ inflateEnd(&zstr);
+ gzip_len *= 2;
+ } while (gzip_err == Z_BUF_ERROR);
+
+ gzip_len = gzip_err == Z_STREAM_END ? zstr.total_out : -1;
+
+ if (gzip_len <= 0) {
+ mir_free(output_data);
+ output_data = nullptr;
+ }
+ else output_data[gzip_len] = 0;
+
+ *len_ptr = gzip_len;
+ return output_data;
+}
+
+static int NetlibHttpRecvChunkHeader(NetlibConnection *nlc, bool first, DWORD flags)
+{
+ MBinBuffer buf;
+
+ while (true) {
+ char data[1000];
+ int recvResult = Netlib_Recv(nlc, data, _countof(data) - 1, MSG_RAW | flags);
+ if (recvResult <= 0 || recvResult >= _countof(data))
+ return SOCKET_ERROR;
+
+ buf.append(data, recvResult); // add chunk
+
+ const char *peol1 = (const char*)memchr(buf.data(), '\n', buf.length());
+ if (peol1 == nullptr)
+ continue;
+
+ int cbRest = int(peol1 - buf.data()) + 1;
+ const char *peol2 = first ? peol1 : (const char*)memchr(peol1 + 1, '\n', buf.length() - cbRest);
+ if (peol2 == nullptr)
+ continue;
+
+ int sz = peol2 - buf.data() + 1;
+ int r = strtol(first ? buf.data() : peol1 + 1, nullptr, 16);
+ if (r == 0) {
+ const char *peol3 = strchr(peol2 + 1, '\n');
+ if (peol3 == nullptr)
+ continue;
+ sz = peol3 - buf.data() + 1;
+ }
+ buf.remove(sz); // remove all our data from buffer
+ nlc->foreBuf.appendBefore(buf.data(), buf.length());
+ return r;
+ }
+}
+
+NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, DWORD hflags, DWORD dflags, bool isConnect)
+{
+ int dataLen = -1, i, chunkhdr = 0;
+ bool chunked = false;
+ int cenc = 0, cenctype = 0, close = 0;
+
+next:
+ NETLIBHTTPREQUEST *nlhrReply = Netlib_RecvHttpHeaders(nlc, hflags);
+ if (nlhrReply == nullptr)
+ return nullptr;
+
+ if (nlhrReply->resultCode == 100) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ goto next;
+ }
+
+ if (nlhrReply->resultCode == 204)
+ dataLen = 0;
+
+ for (i = 0; i < nlhrReply->headersCount; i++) {
+ NETLIBHTTPHEADER &p = nlhrReply->headers[i];
+ if (!mir_strcmpi(p.szName, "Content-Length"))
+ dataLen = atoi(p.szValue);
+
+ if (!mir_strcmpi(p.szName, "Content-Encoding")) {
+ cenc = i;
+ if (strstr(p.szValue, "gzip"))
+ cenctype = 1;
+ else if (strstr(p.szValue, "deflate"))
+ cenctype = 2;
+ }
+
+ if (!mir_strcmpi(p.szName, "Connection"))
+ close = !mir_strcmpi(p.szValue, "close");
+
+ if (!mir_strcmpi(p.szName, "Transfer-Encoding") && !mir_strcmpi(p.szValue, "chunked")) {
+ chunked = true;
+ chunkhdr = i;
+ dataLen = -1;
+ }
+ }
+
+ if (nlhrReply->resultCode >= 200 && (dataLen > 0 || (!isConnect && dataLen < 0))) {
+ int recvResult, chunksz = -1;
+ int dataBufferAlloced;
+
+ if (chunked) {
+ chunksz = NetlibHttpRecvChunkHeader(nlc, true, dflags | (cenctype ? MSG_NODUMP : 0));
+ if (chunksz == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ dataLen = chunksz;
+ }
+ dataBufferAlloced = dataLen < 0 ? 2048 : dataLen + 1;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+
+ while (chunksz != 0) {
+ while (true) {
+ recvResult = RecvWithTimeoutTime(nlc, GetTickCount() + HTTPRECVDATATIMEOUT,
+ nlhrReply->pData + nlhrReply->dataLength,
+ dataBufferAlloced - nlhrReply->dataLength - 1,
+ dflags | (cenctype ? MSG_NODUMP : 0));
+
+ if (recvResult == 0) break;
+ if (recvResult == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ nlhrReply->dataLength += recvResult;
+
+ if (dataLen >= 0) {
+ if (nlhrReply->dataLength >= dataLen)
+ break;
+ }
+ else if ((dataBufferAlloced - nlhrReply->dataLength) < 256) {
+ dataBufferAlloced += 2048;
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ if (nlhrReply->pData == nullptr) {
+ SetLastError(ERROR_OUTOFMEMORY);
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ }
+ Sleep(10);
+ }
+
+ if (!chunked)
+ break;
+
+ chunksz = NetlibHttpRecvChunkHeader(nlc, false, dflags | MSG_NODUMP);
+ if (chunksz == SOCKET_ERROR) {
+ Netlib_FreeHttpRequest(nlhrReply);
+ return nullptr;
+ }
+ dataLen += chunksz;
+ dataBufferAlloced += chunksz;
+
+ nlhrReply->pData = (char*)mir_realloc(nlhrReply->pData, dataBufferAlloced);
+ }
+
+ nlhrReply->pData[nlhrReply->dataLength] = '\0';
+ }
+
+ if (chunked) {
+ nlhrReply->headers[chunkhdr].szName = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szName, 16);
+ mir_strcpy(nlhrReply->headers[chunkhdr].szName, "Content-Length");
+
+ nlhrReply->headers[chunkhdr].szValue = (char*)mir_realloc(nlhrReply->headers[chunkhdr].szValue, 16);
+ mir_snprintf(nlhrReply->headers[chunkhdr].szValue, 16, "%u", nlhrReply->dataLength);
+ }
+
+ if (cenctype) {
+ int bufsz = nlhrReply->dataLength;
+ char* szData = nullptr;
+
+ switch (cenctype) {
+ case 1:
+ szData = gzip_decode(nlhrReply->pData, &bufsz, 0x10 | MAX_WBITS);
+ break;
+
+ case 2:
+ szData = gzip_decode(nlhrReply->pData, &bufsz, -MAX_WBITS);
+ if (bufsz < 0) {
+ bufsz = nlhrReply->dataLength;
+ szData = gzip_decode(nlhrReply->pData, &bufsz, MAX_WBITS);
+ }
+ break;
+ }
+
+ if (bufsz > 0) {
+ NetlibDumpData(nlc, (PBYTE)szData, bufsz, 0, dflags | MSG_NOTITLE);
+ mir_free(nlhrReply->pData);
+ nlhrReply->pData = szData;
+ nlhrReply->dataLength = bufsz;
+
+ mir_free(nlhrReply->headers[cenc].szName);
+ mir_free(nlhrReply->headers[cenc].szValue);
+ memmove(&nlhrReply->headers[cenc], &nlhrReply->headers[cenc+1], (--nlhrReply->headersCount-cenc)*sizeof(nlhrReply->headers[0]));
+ }
+ else if (bufsz == 0) {
+ mir_free(nlhrReply->pData);
+ nlhrReply->pData = nullptr;
+ nlhrReply->dataLength = 0;
+ }
+ }
+
+ if (close &&
+ (nlc->proxyType != PROXYTYPE_HTTP || nlc->nloc.flags & NLOCF_SSL) &&
+ (!isConnect || nlhrReply->resultCode != 200))
+ NetlibDoCloseSocket(nlc);
+
+ return nlhrReply;
+}
diff --git a/src/mir_app/src/netlibhttpproxy.cpp b/src/mir_app/src/netlib_httpproxy.cpp
index ccc791e702..8d702d6884 100644
--- a/src/mir_app/src/netlibhttpproxy.cpp
+++ b/src/mir_app/src/netlib_httpproxy.cpp
@@ -1,85 +1,85 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-typedef enum
-{
- reqHelloGet,
- reqOldGet,
- reqOldPost,
- reqNewPost,
-}
-RequestType;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-#define NETLIBHTTP_RETRYCOUNT 3
-#define NETLIBHTTP_RETRYTIMEOUT 2000
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi)
-{
- if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return 0;
- }
-
- mir_free(nlc->nlhpi.szHttpGetUrl);
- mir_free(nlc->nlhpi.szHttpPostUrl);
-
- nlc->nlhpi.combinePackets = 1;
- memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi));
- if (nlc->nlhpi.combinePackets == 0)
- nlc->nlhpi.combinePackets = 1;
-
- nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl);
- nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl);
- return 1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders)
-{
- if (GetNetlibHandleType(nlu) != NLH_USER)
- return ERROR_INVALID_PARAMETER;
-
- replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout)
-{
- if (GetNetlibHandleType(nlc) != NLH_CONNECTION)
- return -1;
-
- int oldTimeout = nlc->pollingTimeout;
- nlc->pollingTimeout = iTimeout;
- return oldTimeout;
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+typedef enum
+{
+ reqHelloGet,
+ reqOldGet,
+ reqOldPost,
+ reqNewPost,
+}
+RequestType;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+#define NETLIBHTTP_RETRYCOUNT 3
+#define NETLIBHTTP_RETRYTIMEOUT 2000
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetHttpProxyInfo(HNETLIBCONN nlc, const NETLIBHTTPPROXYINFO *nlhpi)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION || nlhpi == nullptr || nlhpi->szHttpPostUrl == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ mir_free(nlc->nlhpi.szHttpGetUrl);
+ mir_free(nlc->nlhpi.szHttpPostUrl);
+
+ nlc->nlhpi.combinePackets = 1;
+ memcpy(&nlc->nlhpi, nlhpi, sizeof(*nlhpi));
+ if (nlc->nlhpi.combinePackets == 0)
+ nlc->nlhpi.combinePackets = 1;
+
+ nlc->nlhpi.szHttpGetUrl = mir_strdup(nlc->nlhpi.szHttpGetUrl);
+ nlc->nlhpi.szHttpPostUrl = mir_strdup(nlc->nlhpi.szHttpPostUrl);
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetStickyHeaders(HNETLIBUSER nlu, const char *szHeaders)
+{
+ if (GetNetlibHandleType(nlu) != NLH_USER)
+ return ERROR_INVALID_PARAMETER;
+
+ replaceStr(nlu->szStickyHeaders, szHeaders); // pointer is ours
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_SetPollingTimeout(HNETLIBCONN nlc, int iTimeout)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION)
+ return -1;
+
+ int oldTimeout = nlc->pollingTimeout;
+ nlc->pollingTimeout = iTimeout;
+ return oldTimeout;
+}
diff --git a/src/mir_app/src/netliblog.cpp b/src/mir_app/src/netlib_log.cpp
index bd41723192..ca0d377dc9 100644
--- a/src/mir_app/src/netliblog.cpp
+++ b/src/mir_app/src/netlib_log.cpp
@@ -1,546 +1,546 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-#define MS_NETLIB_LOGWIN "Netlib/Log/Win"
-
-extern HANDLE hConnectionHeaderMutex;
-
-#define TIMEFORMAT_NONE 0
-#define TIMEFORMAT_HHMMSS 1
-#define TIMEFORMAT_MILLISECONDS 2
-#define TIMEFORMAT_MICROSECONDS 3
-struct {
- HWND hwndOpts;
- int toOutputDebugString;
- int toFile;
- int toLog;
- int timeFormat;
- int showUser;
- int dumpSent, dumpRecv, dumpProxy, dumpSsl;
- int textDumps, autoDetectText;
- CMStringW tszFile, tszUserFile;
-}
-static logOptions = {};
-
-struct LOGMSG
-{
- const char* pszHead;
- const char* pszMsg;
-};
-
-static __int64 mirandaStartTime, perfCounterFreq;
-static int bIsActive = TRUE;
-static HANDLE hLogEvent = nullptr;
-static HANDLE hLogger = nullptr;
-
-static void InitLog()
-{
- if (hLogger) {
- mir_closeLog(hLogger);
- hLogger = nullptr;
- }
-
- ptrW szBuf(db_get_wsa(0, "Netlib", "File"));
- if (mir_wstrlen(szBuf)) {
- logOptions.tszUserFile = szBuf.get();
-
- wchar_t path[MAX_PATH];
- PathToAbsoluteW(VARSW(szBuf), path);
- logOptions.tszFile = path;
- }
- else {
- db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt");
- logOptions.tszFile = VARSW(logOptions.tszUserFile);
- }
-
- if (logOptions.toFile)
- hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), logOptions.tszFile, 0);
-}
-
-static const wchar_t* szTimeFormats[] =
-{
- LPGENW("No times"),
- LPGENW("Standard hh:mm:ss times"),
- LPGENW("Times in milliseconds"),
- LPGENW("Times in microseconds")
-};
-
-static INT_PTR CALLBACK LogOptionsDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
-{
- wchar_t str[MAX_PATH];
-
- switch (message) {
- case WM_INITDIALOG:
- logOptions.hwndOpts = hwndDlg;
- TranslateDialogDefault(hwndDlg);
- CheckDlgButton(hwndDlg, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED);
-
- for (auto &it : szTimeFormats)
- SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_ADDSTRING, 0, (LPARAM)TranslateW(it));
-
- SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_SETCURSEL, logOptions.timeFormat, 0);
- CheckDlgButton(hwndDlg, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED);
- CheckDlgButton(hwndDlg, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED);
- SetDlgItemText(hwndDlg, IDC_FILENAME, logOptions.tszUserFile);
- SetDlgItemText(hwndDlg, IDC_PATH, logOptions.tszFile);
- CheckDlgButton(hwndDlg, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED);
- {
- ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart"));
- if (szRun)
- SetDlgItemTextA(hwndDlg, IDC_RUNATSTART, szRun);
-
- HWND hwndFilter = GetDlgItem(hwndDlg, IDC_FILTER);
- SetWindowLongPtr(hwndFilter, GWL_STYLE, GetWindowLongPtr(hwndFilter, GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
-
- TVINSERTSTRUCT tvis = {};
- tvis.hInsertAfter = TVI_SORT;
- tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
- tvis.item.stateMask = TVIS_STATEIMAGEMASK;
-
- for (auto &it : netlibUser) {
- tvis.item.pszText = it->user.szDescriptiveName.w;
- tvis.item.lParam = netlibUser.indexOf(&it);
- tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1);
- TreeView_InsertItem(hwndFilter, &tvis);
- }
- tvis.item.lParam = -1;
- tvis.item.pszText = TranslateT("(Miranda core logging)");
- tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1);
- TreeView_InsertItem(hwndFilter, &tvis);
- }
- return TRUE;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case IDC_FILENAME:
- if (HIWORD(wParam) == EN_CHANGE) {
- if ((HWND)lParam == GetFocus())
- CheckDlgButton(hwndDlg, IDC_TOFILE, BST_CHECKED);
-
- wchar_t path[MAX_PATH];
- GetWindowText((HWND)lParam, path, _countof(path));
-
- PathToAbsoluteW(VARSW(path), path);
- SetDlgItemText(hwndDlg, IDC_PATH, path);
- }
- break;
-
- case IDC_FILENAMEBROWSE:
- case IDC_RUNATSTARTBROWSE:
- GetWindowText(GetWindow((HWND)lParam, GW_HWNDPREV), str, _countof(str));
- {
- wchar_t filter[200];
- mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0);
-
- OPENFILENAME ofn = { 0 };
- ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
- ofn.hwndOwner = hwndDlg;
- ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
- if (LOWORD(wParam) == IDC_FILENAMEBROWSE)
- ofn.lpstrTitle = TranslateT("Select where log file will be created");
- else {
- ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
- ofn.lpstrTitle = TranslateT("Select program to be run");
- }
- ofn.lpstrFilter = filter;
- ofn.lpstrFile = str;
- ofn.nMaxFile = _countof(str) - 2;
- ofn.nMaxFileTitle = MAX_PATH;
- if (LOWORD(wParam) == IDC_FILENAMEBROWSE) {
- if (!GetSaveFileName(&ofn)) return 1;
- }
- else if (!GetOpenFileName(&ofn))
- return 1;
-
- if (LOWORD(wParam) == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) {
- memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t)));
- str[0] = '"';
- mir_wstrcat(str, L"\"");
- }
- SetWindowText(GetWindow((HWND)lParam, GW_HWNDPREV), str);
- }
- break;
-
- case IDC_RUNNOW:
- GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, _countof(str));
- if (str[0]) {
- STARTUPINFO si = { sizeof(si) };
- PROCESS_INFORMATION pi;
- CreateProcess(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
- }
- break;
-
- case IDOK:
- GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, _countof(str));
- db_set_ws(0, "Netlib", "RunAtStart", str);
- db_set_b(0, "Netlib", "ShowLogOptsAtStart", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_SHOWTHISDLGATSTART));
-
- GetDlgItemText(hwndDlg, IDC_FILENAME, str, _countof(str));
- logOptions.tszUserFile = rtrimw(str);
- db_set_ws(0, "Netlib", "File", str);
-
- GetDlgItemText(hwndDlg, IDC_PATH, str, _countof(str));
- logOptions.tszFile = rtrimw(str);
-
- db_set_b(0, "Netlib", "DumpRecv", logOptions.dumpRecv = IsDlgButtonChecked(hwndDlg, IDC_DUMPRECV));
- db_set_b(0, "Netlib", "DumpSent", logOptions.dumpSent = IsDlgButtonChecked(hwndDlg, IDC_DUMPSENT));
- db_set_b(0, "Netlib", "DumpProxy", logOptions.dumpProxy = IsDlgButtonChecked(hwndDlg, IDC_DUMPPROXY));
- db_set_b(0, "Netlib", "DumpSsl", logOptions.dumpSsl = IsDlgButtonChecked(hwndDlg, IDC_DUMPSSL));
- db_set_b(0, "Netlib", "TextDumps", logOptions.textDumps = IsDlgButtonChecked(hwndDlg, IDC_TEXTDUMPS));
- db_set_b(0, "Netlib", "AutoDetectText", logOptions.autoDetectText = IsDlgButtonChecked(hwndDlg, IDC_AUTODETECTTEXT));
- db_set_b(0, "Netlib", "TimeFormat", logOptions.timeFormat = SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_GETCURSEL, 0, 0));
- db_set_b(0, "Netlib", "ShowUser", logOptions.showUser = IsDlgButtonChecked(hwndDlg, IDC_SHOWNAMES));
- db_set_b(0, "Netlib", "ToOutputDebugString", logOptions.toOutputDebugString = IsDlgButtonChecked(hwndDlg, IDC_TOOUTPUTDEBUGSTRING));
- db_set_b(0, "Netlib", "ToFile", logOptions.toFile = IsDlgButtonChecked(hwndDlg, IDC_TOFILE));
- {
- HWND hwndFilter = GetDlgItem(logOptions.hwndOpts, IDC_FILTER);
- TVITEM tvi = { 0 };
- BOOL checked;
-
- tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT;
- tvi.hItem = TreeView_GetRoot(hwndFilter);
-
- while (tvi.hItem) {
- TreeView_GetItem(hwndFilter, &tvi);
- checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2);
-
- if (tvi.lParam == -1) {
- logOptions.toLog = checked;
- db_set_dw(0, "Netlib", "NLlog", checked);
- }
- else if (tvi.lParam < netlibUser.getCount()) {
- netlibUser[tvi.lParam]->toLog = checked;
- db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked);
- }
-
- tvi.hItem = TreeView_GetNextSibling(hwndFilter, tvi.hItem);
- }
- }
- InitLog();
- __fallthrough;
-
- case IDCANCEL:
- DestroyWindow(hwndDlg);
- }
- break;
-
- case WM_CLOSE:
- DestroyWindow(hwndDlg);
- break;
-
- case WM_DESTROY:
- ImageList_Destroy(TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_FILTER), TVSIL_STATE));
- logOptions.hwndOpts = nullptr;
- break;
- }
- return FALSE;
-}
-
-void NetlibLogShowOptions(void)
-{
- if (logOptions.hwndOpts == nullptr)
- logOptions.hwndOpts = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_NETLIBLOGOPTS), nullptr, LogOptionsDlgProc);
- SetForegroundWindow(logOptions.hwndOpts);
-}
-
-static INT_PTR ShowOptions(WPARAM, LPARAM)
-{
- NetlibLogShowOptions();
- return 0;
-}
-
-int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags)
-{
- if (!bIsActive)
- return 0;
-
- DWORD dwOriginalLastError = GetLastError();
-
- if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return 0;
- }
-
- /* if the Netlib user handle is nullptr, just pretend its not */
- if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog))
- return 1;
-
- LARGE_INTEGER liTimeNow;
- char szTime[32], szHead[128];
- switch (logOptions.timeFormat) {
- case TIMEFORMAT_HHMMSS:
- GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime));
- mir_strcat(szTime, " ");
- break;
-
- case TIMEFORMAT_MILLISECONDS:
- QueryPerformanceCounter(&liTimeNow);
- liTimeNow.QuadPart -= mirandaStartTime;
- mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq,
- 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
- break;
-
- case TIMEFORMAT_MICROSECONDS:
- QueryPerformanceCounter(&liTimeNow);
- liTimeNow.QuadPart -= mirandaStartTime;
- mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq,
- 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
- break;
-
- default:
- szTime[0] = '\0';
- break;
- }
-
- if (flags & MSG_NOTITLE)
- szHead[0] = 0;
- else {
- char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr;
- if (szUser)
- mir_snprintf(szHead, "[%s%04X] [%s] ", szTime, GetCurrentThreadId(), szUser);
- else
- mir_snprintf(szHead, "[%s%04X] ", szTime, GetCurrentThreadId());
- }
-
- if (logOptions.toOutputDebugString) {
- if (szHead[0])
- OutputDebugStringA(szHead);
- OutputDebugStringA(pszMsg);
- OutputDebugStringA("\n");
- }
-
- if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) {
- size_t len = mir_strlen(pszMsg);
- mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n");
- }
-
- LOGMSG logMsg = { szHead, pszMsg };
- NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg);
-
- SetLastError(dwOriginalLastError);
- return 1;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void PROTO_INTERFACE::debugLogA(const char *szFormat, ...)
-{
- char buf[4096];
- va_list args;
- va_start(args, szFormat);
- int res = _vsnprintf(buf, _countof(buf), szFormat, args);
- NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0);
- va_end(args);
-}
-
-void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...)
-{
- WCHAR buf[4096];
- va_list args;
- va_start(args, wszFormat);
- int res = _vsnwprintf(buf, _countof(buf), wszFormat, args);
- NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0);
- va_end(args);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
- char szText[8000];
- mir_vsnprintf(szText, _countof(szText), fmt, va);
- va_end(va);
- return NetlibLog_Worker(hUser, szText, 0);
-}
-
-MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
- wchar_t szText[8000];
- mir_vsnwprintf(szText, _countof(szText), fmt, va);
- va_end(va);
- return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr)
-{
- return NetlibLog_Worker(hUser, pszStr, 0);
-}
-
-MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr)
-{
- return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void NetlibDumpData(NetlibConnection *nlc, PBYTE buf, int len, int sent, int flags)
-{
- // This section checks a number of conditions and aborts
- // the dump if the data should not be written to the log
-
- // Check packet flags
- if (flags & (MSG_PEEK | MSG_NODUMP))
- return;
-
- // Check user's log settings
- if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty())))
- return;
- if ((sent && !logOptions.dumpSent) || (!sent && !logOptions.dumpRecv))
- return;
- if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy)
- return;
- if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl)
- return;
-
- CMStringA str;
-
- WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
- NetlibUser *nlu = nlc ? nlc->nlu : nullptr;
-
- if (!(flags & MSG_NOTITLE))
- str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, sent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : "");
- ReleaseMutex(hConnectionHeaderMutex);
-
- // check filter settings
- if (nlu == nullptr) {
- if (!logOptions.toLog)
- return;
- }
- else if (!nlu->toLog)
- return;
-
- bool isText = true;
- if (!logOptions.textDumps)
- isText = false;
- else if (!(flags & MSG_DUMPASTEXT)) {
- if (logOptions.autoDetectText) {
- for (int i = 0; i < len; i++) {
- if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) {
- isText = false;
- break;
- }
- }
- }
- else isText = false;
- }
-
- // Text data
- if (isText) {
- str.Append((const char*)buf, len);
- }
- // Binary data
- else {
- for (int line = 0;; line += 16) {
- PBYTE p = buf + line;
- int colsInLine = min(16, len - line);
- if (colsInLine == 16)
- str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ",
- line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
- else {
- str.AppendFormat("%08X: ", line);
-
- // Dump data as hex
- int col;
- for (col = 0; col < colsInLine; col++)
- str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' ');
-
- // Fill out last line with blanks
- for (; col < 16; col++)
- str.Append(" ");
-
- str.AppendChar(' ');
- }
-
- for (int col = 0; col < colsInLine; col++)
- str.AppendChar((p[col] < ' ') ? '.' : p[col]);
-
- if (len - line <= 16)
- break;
-
- str.AppendChar('\r'); // End each line with a break
- str.AppendChar('\n'); // End each line with a break
- }
- }
-
- NetlibLog_Worker(nlu, str, flags);
-}
-
-void NetlibLogInit(void)
-{
- LARGE_INTEGER li;
- QueryPerformanceFrequency(&li);
- perfCounterFreq = li.QuadPart;
- QueryPerformanceCounter(&li);
- mirandaStartTime = li.QuadPart;
-
- CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions);
- hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP);
-
- logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", 1);
- logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", 1);
- logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", 1);
- logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", 0);
- logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", 1);
- logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", 1);
- logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS);
- logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", 1);
- logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", 0);
- logOptions.toFile = db_get_b(0, "Netlib", "ToFile", 0);
- logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", 1);
-
- InitLog();
-
- if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0))
- NetlibLogShowOptions();
-
- ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart"));
- if (szBuf) {
- STARTUPINFO si = { sizeof(si) };
- PROCESS_INFORMATION pi;
- CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
- }
-}
-
-void NetlibLogShutdown(void)
-{
- bIsActive = FALSE;
- DestroyHookableEvent(hLogEvent); hLogEvent = nullptr;
- if (IsWindow(logOptions.hwndOpts))
- DestroyWindow(logOptions.hwndOpts);
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#define MS_NETLIB_LOGWIN "Netlib/Log/Win"
+
+extern HANDLE hConnectionHeaderMutex;
+
+#define TIMEFORMAT_NONE 0
+#define TIMEFORMAT_HHMMSS 1
+#define TIMEFORMAT_MILLISECONDS 2
+#define TIMEFORMAT_MICROSECONDS 3
+struct {
+ HWND hwndOpts;
+ int toOutputDebugString;
+ int toFile;
+ int toLog;
+ int timeFormat;
+ int showUser;
+ int dumpSent, dumpRecv, dumpProxy, dumpSsl;
+ int textDumps, autoDetectText;
+ CMStringW tszFile, tszUserFile;
+}
+static logOptions = {};
+
+struct LOGMSG
+{
+ const char* pszHead;
+ const char* pszMsg;
+};
+
+static __int64 mirandaStartTime, perfCounterFreq;
+static int bIsActive = TRUE;
+static HANDLE hLogEvent = nullptr;
+static HANDLE hLogger = nullptr;
+
+static void InitLog()
+{
+ if (hLogger) {
+ mir_closeLog(hLogger);
+ hLogger = nullptr;
+ }
+
+ ptrW szBuf(db_get_wsa(0, "Netlib", "File"));
+ if (mir_wstrlen(szBuf)) {
+ logOptions.tszUserFile = szBuf.get();
+
+ wchar_t path[MAX_PATH];
+ PathToAbsoluteW(VARSW(szBuf), path);
+ logOptions.tszFile = path;
+ }
+ else {
+ db_set_ws(0, "Netlib", "File", logOptions.tszUserFile = L"%miranda_logpath%\\netlog.txt");
+ logOptions.tszFile = VARSW(logOptions.tszUserFile);
+ }
+
+ if (logOptions.toFile)
+ hLogger = mir_createLog("Netlib", LPGENW("Standard Netlib log"), logOptions.tszFile, 0);
+}
+
+static const wchar_t* szTimeFormats[] =
+{
+ LPGENW("No times"),
+ LPGENW("Standard hh:mm:ss times"),
+ LPGENW("Times in milliseconds"),
+ LPGENW("Times in microseconds")
+};
+
+static INT_PTR CALLBACK LogOptionsDlgProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ wchar_t str[MAX_PATH];
+
+ switch (message) {
+ case WM_INITDIALOG:
+ logOptions.hwndOpts = hwndDlg;
+ TranslateDialogDefault(hwndDlg);
+ CheckDlgButton(hwndDlg, IDC_DUMPRECV, logOptions.dumpRecv ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DUMPSENT, logOptions.dumpSent ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DUMPPROXY, logOptions.dumpProxy ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DUMPSSL, logOptions.dumpSsl ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TEXTDUMPS, logOptions.textDumps ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AUTODETECTTEXT, logOptions.autoDetectText ? BST_CHECKED : BST_UNCHECKED);
+
+ for (auto &it : szTimeFormats)
+ SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_ADDSTRING, 0, (LPARAM)TranslateW(it));
+
+ SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_SETCURSEL, logOptions.timeFormat, 0);
+ CheckDlgButton(hwndDlg, IDC_SHOWNAMES, logOptions.showUser ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TOOUTPUTDEBUGSTRING, logOptions.toOutputDebugString ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TOFILE, logOptions.toFile ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemText(hwndDlg, IDC_FILENAME, logOptions.tszUserFile);
+ SetDlgItemText(hwndDlg, IDC_PATH, logOptions.tszFile);
+ CheckDlgButton(hwndDlg, IDC_SHOWTHISDLGATSTART, db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0) ? BST_CHECKED : BST_UNCHECKED);
+ {
+ ptrA szRun(db_get_sa(0, "Netlib", "RunAtStart"));
+ if (szRun)
+ SetDlgItemTextA(hwndDlg, IDC_RUNATSTART, szRun);
+
+ HWND hwndFilter = GetDlgItem(hwndDlg, IDC_FILTER);
+ SetWindowLongPtr(hwndFilter, GWL_STYLE, GetWindowLongPtr(hwndFilter, GWL_STYLE) | (TVS_NOHSCROLL | TVS_CHECKBOXES));
+
+ TVINSERTSTRUCT tvis = {};
+ tvis.hInsertAfter = TVI_SORT;
+ tvis.item.mask = TVIF_PARAM | TVIF_TEXT | TVIF_STATE;
+ tvis.item.stateMask = TVIS_STATEIMAGEMASK;
+
+ for (auto &it : netlibUser) {
+ tvis.item.pszText = it->user.szDescriptiveName.w;
+ tvis.item.lParam = netlibUser.indexOf(&it);
+ tvis.item.state = INDEXTOSTATEIMAGEMASK(it->toLog ? 2 : 1);
+ TreeView_InsertItem(hwndFilter, &tvis);
+ }
+ tvis.item.lParam = -1;
+ tvis.item.pszText = TranslateT("(Miranda core logging)");
+ tvis.item.state = INDEXTOSTATEIMAGEMASK((logOptions.toLog) ? 2 : 1);
+ TreeView_InsertItem(hwndFilter, &tvis);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_FILENAME:
+ if (HIWORD(wParam) == EN_CHANGE) {
+ if ((HWND)lParam == GetFocus())
+ CheckDlgButton(hwndDlg, IDC_TOFILE, BST_CHECKED);
+
+ wchar_t path[MAX_PATH];
+ GetWindowText((HWND)lParam, path, _countof(path));
+
+ PathToAbsoluteW(VARSW(path), path);
+ SetDlgItemText(hwndDlg, IDC_PATH, path);
+ }
+ break;
+
+ case IDC_FILENAMEBROWSE:
+ case IDC_RUNATSTARTBROWSE:
+ GetWindowText(GetWindow((HWND)lParam, GW_HWNDPREV), str, _countof(str));
+ {
+ wchar_t filter[200];
+ mir_snwprintf(filter, L"%s (*)%c*%c", TranslateT("All files"), 0, 0);
+
+ OPENFILENAME ofn = { 0 };
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_DONTADDTORECENT;
+ if (LOWORD(wParam) == IDC_FILENAMEBROWSE)
+ ofn.lpstrTitle = TranslateT("Select where log file will be created");
+ else {
+ ofn.Flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
+ ofn.lpstrTitle = TranslateT("Select program to be run");
+ }
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = _countof(str) - 2;
+ ofn.nMaxFileTitle = MAX_PATH;
+ if (LOWORD(wParam) == IDC_FILENAMEBROWSE) {
+ if (!GetSaveFileName(&ofn)) return 1;
+ }
+ else if (!GetOpenFileName(&ofn))
+ return 1;
+
+ if (LOWORD(wParam) == IDC_RUNATSTARTBROWSE && wcschr(str, ' ') != nullptr) {
+ memmove(str + 1, str, ((_countof(str) - 2) * sizeof(wchar_t)));
+ str[0] = '"';
+ mir_wstrcat(str, L"\"");
+ }
+ SetWindowText(GetWindow((HWND)lParam, GW_HWNDPREV), str);
+ }
+ break;
+
+ case IDC_RUNNOW:
+ GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, _countof(str));
+ if (str[0]) {
+ STARTUPINFO si = { sizeof(si) };
+ PROCESS_INFORMATION pi;
+ CreateProcess(nullptr, str, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
+ }
+ break;
+
+ case IDOK:
+ GetDlgItemText(hwndDlg, IDC_RUNATSTART, str, _countof(str));
+ db_set_ws(0, "Netlib", "RunAtStart", str);
+ db_set_b(0, "Netlib", "ShowLogOptsAtStart", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_SHOWTHISDLGATSTART));
+
+ GetDlgItemText(hwndDlg, IDC_FILENAME, str, _countof(str));
+ logOptions.tszUserFile = rtrimw(str);
+ db_set_ws(0, "Netlib", "File", str);
+
+ GetDlgItemText(hwndDlg, IDC_PATH, str, _countof(str));
+ logOptions.tszFile = rtrimw(str);
+
+ db_set_b(0, "Netlib", "DumpRecv", logOptions.dumpRecv = IsDlgButtonChecked(hwndDlg, IDC_DUMPRECV));
+ db_set_b(0, "Netlib", "DumpSent", logOptions.dumpSent = IsDlgButtonChecked(hwndDlg, IDC_DUMPSENT));
+ db_set_b(0, "Netlib", "DumpProxy", logOptions.dumpProxy = IsDlgButtonChecked(hwndDlg, IDC_DUMPPROXY));
+ db_set_b(0, "Netlib", "DumpSsl", logOptions.dumpSsl = IsDlgButtonChecked(hwndDlg, IDC_DUMPSSL));
+ db_set_b(0, "Netlib", "TextDumps", logOptions.textDumps = IsDlgButtonChecked(hwndDlg, IDC_TEXTDUMPS));
+ db_set_b(0, "Netlib", "AutoDetectText", logOptions.autoDetectText = IsDlgButtonChecked(hwndDlg, IDC_AUTODETECTTEXT));
+ db_set_b(0, "Netlib", "TimeFormat", logOptions.timeFormat = SendDlgItemMessage(hwndDlg, IDC_TIMEFORMAT, CB_GETCURSEL, 0, 0));
+ db_set_b(0, "Netlib", "ShowUser", logOptions.showUser = IsDlgButtonChecked(hwndDlg, IDC_SHOWNAMES));
+ db_set_b(0, "Netlib", "ToOutputDebugString", logOptions.toOutputDebugString = IsDlgButtonChecked(hwndDlg, IDC_TOOUTPUTDEBUGSTRING));
+ db_set_b(0, "Netlib", "ToFile", logOptions.toFile = IsDlgButtonChecked(hwndDlg, IDC_TOFILE));
+ {
+ HWND hwndFilter = GetDlgItem(logOptions.hwndOpts, IDC_FILTER);
+ TVITEM tvi = { 0 };
+ BOOL checked;
+
+ tvi.mask = TVIF_HANDLE | TVIF_PARAM | TVIF_STATE | TVIF_TEXT;
+ tvi.hItem = TreeView_GetRoot(hwndFilter);
+
+ while (tvi.hItem) {
+ TreeView_GetItem(hwndFilter, &tvi);
+ checked = ((tvi.state & TVIS_STATEIMAGEMASK) >> 12 == 2);
+
+ if (tvi.lParam == -1) {
+ logOptions.toLog = checked;
+ db_set_dw(0, "Netlib", "NLlog", checked);
+ }
+ else if (tvi.lParam < netlibUser.getCount()) {
+ netlibUser[tvi.lParam]->toLog = checked;
+ db_set_dw(0, netlibUser[tvi.lParam]->user.szSettingsModule, "NLlog", checked);
+ }
+
+ tvi.hItem = TreeView_GetNextSibling(hwndFilter, tvi.hItem);
+ }
+ }
+ InitLog();
+ __fallthrough;
+
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ ImageList_Destroy(TreeView_GetImageList(GetDlgItem(hwndDlg, IDC_FILTER), TVSIL_STATE));
+ logOptions.hwndOpts = nullptr;
+ break;
+ }
+ return FALSE;
+}
+
+void NetlibLogShowOptions(void)
+{
+ if (logOptions.hwndOpts == nullptr)
+ logOptions.hwndOpts = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_NETLIBLOGOPTS), nullptr, LogOptionsDlgProc);
+ SetForegroundWindow(logOptions.hwndOpts);
+}
+
+static INT_PTR ShowOptions(WPARAM, LPARAM)
+{
+ NetlibLogShowOptions();
+ return 0;
+}
+
+int NetlibLog_Worker(NetlibUser *nlu, const char *pszMsg, int flags)
+{
+ if (!bIsActive)
+ return 0;
+
+ DWORD dwOriginalLastError = GetLastError();
+
+ if ((nlu != nullptr && GetNetlibHandleType(nlu) != NLH_USER) || pszMsg == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return 0;
+ }
+
+ /* if the Netlib user handle is nullptr, just pretend its not */
+ if (!(nlu != nullptr ? nlu->toLog : logOptions.toLog))
+ return 1;
+
+ LARGE_INTEGER liTimeNow;
+ char szTime[32], szHead[128];
+ switch (logOptions.timeFormat) {
+ case TIMEFORMAT_HHMMSS:
+ GetTimeFormatA(LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT | TIME_NOTIMEMARKER, nullptr, nullptr, szTime, _countof(szTime));
+ mir_strcat(szTime, " ");
+ break;
+
+ case TIMEFORMAT_MILLISECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, "%I64u.%03I64u ", liTimeNow.QuadPart / perfCounterFreq,
+ 1000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ case TIMEFORMAT_MICROSECONDS:
+ QueryPerformanceCounter(&liTimeNow);
+ liTimeNow.QuadPart -= mirandaStartTime;
+ mir_snprintf(szTime, "%I64u.%06I64u ", liTimeNow.QuadPart / perfCounterFreq,
+ 1000000 * (liTimeNow.QuadPart % perfCounterFreq) / perfCounterFreq);
+ break;
+
+ default:
+ szTime[0] = '\0';
+ break;
+ }
+
+ if (flags & MSG_NOTITLE)
+ szHead[0] = 0;
+ else {
+ char *szUser = (logOptions.showUser) ? (nlu == nullptr ? nullptr : nlu->user.szSettingsModule) : nullptr;
+ if (szUser)
+ mir_snprintf(szHead, "[%s%04X] [%s] ", szTime, GetCurrentThreadId(), szUser);
+ else
+ mir_snprintf(szHead, "[%s%04X] ", szTime, GetCurrentThreadId());
+ }
+
+ if (logOptions.toOutputDebugString) {
+ if (szHead[0])
+ OutputDebugStringA(szHead);
+ OutputDebugStringA(pszMsg);
+ OutputDebugStringA("\n");
+ }
+
+ if (logOptions.toFile && !logOptions.tszFile.IsEmpty()) {
+ size_t len = mir_strlen(pszMsg);
+ mir_writeLogA(hLogger, "%s%s%s", szHead, pszMsg, pszMsg[len-1] == '\n' ? "" : "\r\n");
+ }
+
+ LOGMSG logMsg = { szHead, pszMsg };
+ NotifyFastHook(hLogEvent, (WPARAM)nlu, (LPARAM)&logMsg);
+
+ SetLastError(dwOriginalLastError);
+ return 1;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void PROTO_INTERFACE::debugLogA(const char *szFormat, ...)
+{
+ char buf[4096];
+ va_list args;
+ va_start(args, szFormat);
+ int res = _vsnprintf(buf, _countof(buf), szFormat, args);
+ NetlibLog_Worker(m_hNetlibUser, (res != -1) ? buf : CMStringA().FormatV(szFormat, args), 0);
+ va_end(args);
+}
+
+void PROTO_INTERFACE::debugLogW(const wchar_t *wszFormat, ...)
+{
+ WCHAR buf[4096];
+ va_list args;
+ va_start(args, wszFormat);
+ int res = _vsnwprintf(buf, _countof(buf), wszFormat, args);
+ NetlibLog_Worker(m_hNetlibUser, ptrA(mir_utf8encodeW((res != -1) ? buf : CMStringW().FormatV(wszFormat, args))), 0);
+ va_end(args);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Logf(HNETLIBUSER hUser, _Printf_format_string_ const char *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ char szText[8000];
+ mir_vsnprintf(szText, _countof(szText), fmt, va);
+ va_end(va);
+ return NetlibLog_Worker(hUser, szText, 0);
+}
+
+MIR_APP_DLL(int) Netlib_LogfW(HNETLIBUSER hUser, _Printf_format_string_ const wchar_t *fmt, ...)
+{
+ va_list va;
+ va_start(va, fmt);
+ wchar_t szText[8000];
+ mir_vsnwprintf(szText, _countof(szText), fmt, va);
+ va_end(va);
+ return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(szText)), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Log(HNETLIBUSER hUser, const char *pszStr)
+{
+ return NetlibLog_Worker(hUser, pszStr, 0);
+}
+
+MIR_APP_DLL(int) Netlib_LogW(HNETLIBUSER hUser, const wchar_t *pwszStr)
+{
+ return NetlibLog_Worker(hUser, ptrA(mir_utf8encodeW(pwszStr)), 0);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void NetlibDumpData(NetlibConnection *nlc, PBYTE buf, int len, int sent, int flags)
+{
+ // This section checks a number of conditions and aborts
+ // the dump if the data should not be written to the log
+
+ // Check packet flags
+ if (flags & (MSG_PEEK | MSG_NODUMP))
+ return;
+
+ // Check user's log settings
+ if (!(logOptions.toOutputDebugString || GetSubscribersCount((THook*)hLogEvent) != 0 || (logOptions.toFile && !logOptions.tszFile.IsEmpty())))
+ return;
+ if ((sent && !logOptions.dumpSent) || (!sent && !logOptions.dumpRecv))
+ return;
+ if ((flags & MSG_DUMPPROXY) && !logOptions.dumpProxy)
+ return;
+ if ((flags & MSG_DUMPSSL) && !logOptions.dumpSsl)
+ return;
+
+ CMStringA str;
+
+ WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
+ NetlibUser *nlu = nlc ? nlc->nlu : nullptr;
+
+ if (!(flags & MSG_NOTITLE))
+ str.Format("(%p:%u) Data %s%s\r\n", nlc, nlc ? (int)nlc->s : 0, sent ? "sent" : "received", flags & MSG_DUMPPROXY ? " (proxy)" : "");
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ // check filter settings
+ if (nlu == nullptr) {
+ if (!logOptions.toLog)
+ return;
+ }
+ else if (!nlu->toLog)
+ return;
+
+ bool isText = true;
+ if (!logOptions.textDumps)
+ isText = false;
+ else if (!(flags & MSG_DUMPASTEXT)) {
+ if (logOptions.autoDetectText) {
+ for (int i = 0; i < len; i++) {
+ if ((buf[i] < ' ' && buf[i] != '\t' && buf[i] != '\r' && buf[i] != '\n') || buf[i] >= 0x80) {
+ isText = false;
+ break;
+ }
+ }
+ }
+ else isText = false;
+ }
+
+ // Text data
+ if (isText) {
+ str.Append((const char*)buf, len);
+ }
+ // Binary data
+ else {
+ for (int line = 0;; line += 16) {
+ PBYTE p = buf + line;
+ int colsInLine = min(16, len - line);
+ if (colsInLine == 16)
+ str.AppendFormat("%08X: %02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X-%02X %02X %02X %02X ",
+ line, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ else {
+ str.AppendFormat("%08X: ", line);
+
+ // Dump data as hex
+ int col;
+ for (col = 0; col < colsInLine; col++)
+ str.AppendFormat("%02X%c", p[col], ((col & 3) == 3) ? '-' : ' ');
+
+ // Fill out last line with blanks
+ for (; col < 16; col++)
+ str.Append(" ");
+
+ str.AppendChar(' ');
+ }
+
+ for (int col = 0; col < colsInLine; col++)
+ str.AppendChar((p[col] < ' ') ? '.' : p[col]);
+
+ if (len - line <= 16)
+ break;
+
+ str.AppendChar('\r'); // End each line with a break
+ str.AppendChar('\n'); // End each line with a break
+ }
+ }
+
+ NetlibLog_Worker(nlu, str, flags);
+}
+
+void NetlibLogInit(void)
+{
+ LARGE_INTEGER li;
+ QueryPerformanceFrequency(&li);
+ perfCounterFreq = li.QuadPart;
+ QueryPerformanceCounter(&li);
+ mirandaStartTime = li.QuadPart;
+
+ CreateServiceFunction(MS_NETLIB_LOGWIN, ShowOptions);
+ hLogEvent = CreateHookableEvent(ME_NETLIB_FASTDUMP);
+
+ logOptions.dumpRecv = db_get_b(0, "Netlib", "DumpRecv", 1);
+ logOptions.dumpSent = db_get_b(0, "Netlib", "DumpSent", 1);
+ logOptions.dumpProxy = db_get_b(0, "Netlib", "DumpProxy", 1);
+ logOptions.dumpSsl = db_get_b(0, "Netlib", "DumpSsl", 0);
+ logOptions.textDumps = db_get_b(0, "Netlib", "TextDumps", 1);
+ logOptions.autoDetectText = db_get_b(0, "Netlib", "AutoDetectText", 1);
+ logOptions.timeFormat = db_get_b(0, "Netlib", "TimeFormat", TIMEFORMAT_HHMMSS);
+ logOptions.showUser = db_get_b(0, "Netlib", "ShowUser", 1);
+ logOptions.toOutputDebugString = db_get_b(0, "Netlib", "ToOutputDebugString", 0);
+ logOptions.toFile = db_get_b(0, "Netlib", "ToFile", 0);
+ logOptions.toLog = db_get_dw(0, "Netlib", "NLlog", 1);
+
+ InitLog();
+
+ if (db_get_b(0, "Netlib", "ShowLogOptsAtStart", 0))
+ NetlibLogShowOptions();
+
+ ptrW szBuf(db_get_wsa(0, "Netlib", "RunAtStart"));
+ if (szBuf) {
+ STARTUPINFO si = { sizeof(si) };
+ PROCESS_INFORMATION pi;
+ CreateProcess(nullptr, szBuf, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
+ }
+}
+
+void NetlibLogShutdown(void)
+{
+ bIsActive = FALSE;
+ DestroyHookableEvent(hLogEvent); hLogEvent = nullptr;
+ if (IsWindow(logOptions.hwndOpts))
+ DestroyWindow(logOptions.hwndOpts);
+}
diff --git a/src/mir_app/src/netlibopenconn.cpp b/src/mir_app/src/netlib_openconn.cpp
index ee4bb5d996..ee4bb5d996 100644
--- a/src/mir_app/src/netlibopenconn.cpp
+++ b/src/mir_app/src/netlib_openconn.cpp
diff --git a/src/mir_app/src/netlibopts.cpp b/src/mir_app/src/netlib_opts.cpp
index 9ffeab1cce..46317d0708 100644
--- a/src/mir_app/src/netlibopts.cpp
+++ b/src/mir_app/src/netlib_opts.cpp
@@ -1,518 +1,518 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-struct NetlibTempSettings
-{
- DWORD flags;
- char *szSettingsModule;
- NETLIBUSERSETTINGS settings;
-};
-
-static LIST <NetlibTempSettings> tempSettings(5);
-
-static const UINT outgoingConnectionsControls[] =
-{
- IDC_STATIC12,
- IDC_USEPROXY,
- IDC_STATIC21, IDC_PROXYTYPE,
- IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT,
- IDC_PROXYAUTH,
- IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS,
- IDC_PROXYDNS,
- IDC_SPECIFYPORTSO,
- IDC_PORTSRANGEO,
- IDC_STATIC54,
- IDC_VALIDATESSL};
-static const UINT useProxyControls[] = {
- IDC_STATIC21, IDC_PROXYTYPE,
- IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT,
- IDC_PROXYAUTH,
- IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS,
- IDC_PROXYDNS};
-static const UINT specifyOPortsControls[] = {
- IDC_PORTSRANGEO,
- IDC_STATIC54
-};
-static const UINT incomingConnectionsControls[] = {
- IDC_STATIC43,
- IDC_SPECIFYPORTS,
- IDC_PORTSRANGE,
- IDC_STATIC52,
- IDC_ENABLEUPNP};
-static const UINT specifyPortsControls[] = {
- IDC_PORTSRANGE,
- IDC_STATIC52};
-
-static const wchar_t* szProxyTypes[] = {LPGENW("<mixed>"), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"};
-static const WORD oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080};
-
-#define M_REFRESHALL (WM_USER+100)
-#define M_REFRESHENABLING (WM_USER+101)
-
-static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state)
-{
- for (int i = 0; i < cControls; i++)
- ShowWindow(GetDlgItem(hwndDlg, controls[i]), state);
-}
-
-static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state)
-{
- for (int i = 0; i < cControls; i++)
- EnableWindow(GetDlgItem(hwndDlg, controls[i]), state);
-}
-
-static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType)
-{
- int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type]));
- SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type);
- if (type == selectType)
- SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0);
-}
-
-static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source)
-{
- *dest = *source;
- if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts);
- if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts);
- if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword);
- if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser);
- if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer);
-}
-
-static void CombineSettingsStrings(char **dest, char **source)
-{
- if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; }
-}
-
-static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, DWORD *destFlags, NETLIBUSERSETTINGS *source, DWORD sourceFlags)
-{
- if (sourceFlags & NUF_OUTGOING) {
- if (*destFlags & NUF_OUTGOING) {
- if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2;
- if (dest->useProxy != source->useProxy) dest->useProxy = 2;
- if (dest->proxyType != source->proxyType) dest->proxyType = 0;
- CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer);
- if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0;
- if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2;
- CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser);
- CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword);
- if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2;
- if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2;
- CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts);
- }
- else {
- dest->validateSSL = source->validateSSL;
- dest->useProxy = source->useProxy;
- dest->proxyType = source->proxyType;
- dest->szProxyServer = source->szProxyServer;
- if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer);
- dest->wProxyPort = source->wProxyPort;
- dest->useProxyAuth = source->useProxyAuth;
- dest->szProxyAuthUser = source->szProxyAuthUser;
- if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser);
- dest->szProxyAuthPassword = source->szProxyAuthPassword;
- if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword);
- dest->dnsThroughProxy = source->dnsThroughProxy;
- dest->specifyOutgoingPorts = source->specifyOutgoingPorts;
- dest->szOutgoingPorts = source->szOutgoingPorts;
- if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts);
- }
- }
- if (sourceFlags & NUF_INCOMING) {
- if (*destFlags & NUF_INCOMING) {
- if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2;
- if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2;
- CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts);
- }
- else {
- dest->enableUPnP = source->enableUPnP;
- dest->specifyIncomingPorts = source->specifyIncomingPorts;
- dest->szIncomingPorts = source->szIncomingPorts;
- if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts);
- }
- }
- if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION))
- *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION;
- else *destFlags |= sourceFlags;
-}
-
-static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset)
-{
- int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED;
- CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED);
- if (iUser == -1) {
- for (auto &p : tempSettings)
- if (!(p->flags & NUF_NOOPTIONS))
- *(int*)(((PBYTE)&p->settings) + memberOffset) = newValue;
- }
- else *(int*)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset) = newValue;
- SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
-}
-
-static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset)
-{
- int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId));
- char *szNewValue = (char*)mir_alloc(newValueLen+1);
- GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1);
- if (iUser == -1) {
- for (auto &p : tempSettings) {
- if (!(p->flags & NUF_NOOPTIONS)) {
- char **ppszNew = (char**)(((PBYTE)&p->settings) + memberOffset);
- mir_free(*ppszNew);
- *ppszNew = mir_strdup(szNewValue);
- }
- }
- mir_free(szNewValue);
- }
- else {
- char **ppszNew = (char**)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset);
- mir_free(*ppszNew);
- *ppszNew = szNewValue;
- }
-}
-
-static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, DWORD flags)
-{
- if (flags & NUF_OUTGOING) {
- db_set_b(0, szSettingsModule, "NLValidateSSL", (BYTE)settings->validateSSL);
- db_set_b(0, szSettingsModule, "NLUseProxy", (BYTE)settings->useProxy);
- db_set_b(0, szSettingsModule, "NLProxyType", (BYTE)settings->proxyType);
- db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : "");
- db_set_w(0, szSettingsModule, "NLProxyPort", (WORD)settings->wProxyPort);
- db_set_b(0, szSettingsModule, "NLUseProxyAuth", (BYTE)settings->useProxyAuth);
- db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : "");
- db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : "");
- db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (BYTE)settings->dnsThroughProxy);
- db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (BYTE)settings->specifyOutgoingPorts);
- db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : "");
- }
- if (flags & NUF_INCOMING) {
- db_set_b(0, szSettingsModule, "NLEnableUPnP", (BYTE)settings->enableUPnP);
- db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (BYTE)settings->specifyIncomingPorts);
- db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : "");
- }
-}
-
-void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings)
-{
- mir_cslock lck(csNetlibUser);
-
- NetlibUser tUser;
- tUser.user.szSettingsModule = (char*)szSettingsModule;
- NetlibUser *thisUser = netlibUser.find(&tUser);
- if (thisUser == nullptr)
- return;
-
- NetlibFreeUserSettingsStruct(&thisUser->settings);
- CopySettingsStruct(&thisUser->settings, settings);
- WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags);
-
- NETLIBUSERSETTINGS combinedSettings = { 0 };
- combinedSettings.cbSize = sizeof(combinedSettings);
-
- DWORD flags = 0;
- for (auto &p : netlibUser) {
- if (p->user.flags & NUF_NOOPTIONS)
- continue;
- CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags);
- }
- if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0;
- if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0;
- if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5;
- if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0;
- if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1;
- if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1;
- if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0;
- WriteSettingsStructToDb("Netlib", &combinedSettings, flags);
- NetlibFreeUserSettingsStruct(&combinedSettings);
-}
-
-static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- int iUser;
-
- switch (msg) {
- case WM_INITDIALOG:
- TranslateDialogDefault(hwndDlg);
- {
- int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("<All connections>"));
- SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1);
- SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0);
-
- mir_cslock lck(csNetlibUser);
- for (auto &it : netlibUser) {
- NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings));
- thisSettings->flags = it->user.flags;
- thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule);
- CopySettingsStruct(&thisSettings->settings, &it->settings);
- tempSettings.insert(thisSettings);
-
- if (it->user.flags & NUF_NOOPTIONS)
- continue;
- iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w);
- SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it));
- }
- }
-
- SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
- return TRUE;
-
- case M_REFRESHALL:
- iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0);
- {
- NETLIBUSERSETTINGS settings = { 0 };
- DWORD flags = 0;
-
- if (iUser == -1) {
- settings.cbSize = sizeof(settings);
- for (auto &p : tempSettings)
- if (!(p->flags & NUF_NOOPTIONS))
- CombineSettingsStructs(&settings, &flags, &p->settings, p->flags);
- }
- else {
- NetlibFreeUserSettingsStruct(&settings);
- CopySettingsStruct(&settings, &tempSettings[iUser]->settings);
- flags = tempSettings[iUser]->flags;
- }
- ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE);
- CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy);
- SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0);
- if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType);
- AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType);
- AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType);
- if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType);
- if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType);
- if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION))
- AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType);
- SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : "");
- if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE);
- else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, "");
- CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth);
- SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : "");
- SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : "");
- CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy);
- CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL);
-
- ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE);
- CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts);
- SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : "");
-
- CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts);
- SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : "");
-
- CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP);
-
- NetlibFreeUserSettingsStruct(&settings);
- SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
- }
- break;
-
- case M_REFRESHENABLING:
- wchar_t str[80];
- {
- int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0);
- mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]);
- SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str);
- if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) {
- int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1;
- EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE);
- if (selectedProxyType == 0) {
- for (auto &p : tempSettings) {
- if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING))
- continue;
-
- if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1;
- else {
- enableAuth = 1;
- if (p->settings.useProxyAuth)
- enableUser = enablePass = 1;
- }
- }
- }
- else {
- if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1;
- else {
- if (selectedProxyType == PROXYTYPE_IE) enableServer = 0;
- enableAuth = 1;
- if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED)
- enableUser = enablePass = 1;
- }
- }
- EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth);
- EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser);
- EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser);
- EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass);
- EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass);
- EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer);
- EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer);
- }
- else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE);
- EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED);
- EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED);
- }
- break;
-
- case WM_COMMAND:
- iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0);
- switch (LOWORD(wParam)) {
- case IDC_NETLIBUSERS:
- if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
- return 0;
-
- case IDC_LOGOPTIONS:
- NetlibLogShowOptions();
- return 0;
-
- case IDC_PROXYTYPE:
- if (HIWORD(wParam) == CBN_SELCHANGE) {
- int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0);
- if (iUser == -1) {
- if (newValue == 0)
- return 0;
-
- for (auto &p : tempSettings) {
- if (p->flags & NUF_NOOPTIONS)
- continue;
- if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS))
- p->settings.proxyType = PROXYTYPE_HTTPS;
- else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION)
- p->settings.proxyType = PROXYTYPE_HTTP;
- else p->settings.proxyType = newValue;
- }
- SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
- }
- else {
- tempSettings[iUser]->settings.proxyType = newValue;
- SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
- }
- }
- break;
- case IDC_USEPROXY:
- ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy));
- break;
- case IDC_PROXYAUTH:
- ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth));
- break;
- case IDC_PROXYDNS:
- ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy));
- break;
- case IDC_SPECIFYPORTS:
- ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts));
- break;
- case IDC_SPECIFYPORTSO:
- ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts));
- break;
- case IDC_ENABLEUPNP:
- ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP));
- break;
- case IDC_VALIDATESSL:
- ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL));
- break;
- case IDC_PROXYHOST:
- if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
- ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer));
- break;
- case IDC_PROXYPORT:
- if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
- {
- int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE);
- if (iUser == -1) {
- for (auto &p : tempSettings)
- if (!(p->flags & NUF_NOOPTIONS))
- p->settings.wProxyPort = newValue;
- }
- else tempSettings[iUser]->settings.wProxyPort = newValue;
- }
- break;
- case IDC_PROXYUSER:
- if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
- ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser));
- break;
- case IDC_PROXYPASS:
- if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
- ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword));
- break;
- case IDC_PORTSRANGE:
- if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
- ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts));
- break;
- case IDC_PORTSRANGEO:
- if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
- ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts));
- break;
- }
- ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW);
- SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
- break;
-
- case WM_NOTIFY:
- switch (((LPNMHDR)lParam)->idFrom) {
- case 0:
- switch (((LPNMHDR)lParam)->code) {
- case PSN_APPLY:
- for (auto &p : tempSettings)
- NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings);
- return TRUE;
- }
- break;
- }
- break;
-
- case WM_DESTROY:
- for (auto &p : tempSettings) {
- mir_free(p->szSettingsModule);
- NetlibFreeUserSettingsStruct(&p->settings);
- mir_free(p);
- }
- tempSettings.destroy();
- break;
- }
- return FALSE;
-}
-
-int NetlibOptInitialise(WPARAM wParam, LPARAM)
-{
- int optionsCount = 0;
- {
- mir_cslock lck(csNetlibUser);
- for (auto &p : netlibUser)
- if (!(p->user.flags & NUF_NOOPTIONS))
- ++optionsCount;
- }
-
- if (optionsCount == 0)
- return 0;
-
- OPTIONSDIALOGPAGE odp = {};
- odp.position = 900000000;
- odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB);
- odp.szTitle.a = LPGEN("Network");
- odp.pfnDlgProc = DlgProcNetlibOpts;
- odp.flags = ODPF_BOLDGROUPS;
- g_plugin.addOptions(wParam, &odp);
- return 0;
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+struct NetlibTempSettings
+{
+ DWORD flags;
+ char *szSettingsModule;
+ NETLIBUSERSETTINGS settings;
+};
+
+static LIST <NetlibTempSettings> tempSettings(5);
+
+static const UINT outgoingConnectionsControls[] =
+{
+ IDC_STATIC12,
+ IDC_USEPROXY,
+ IDC_STATIC21, IDC_PROXYTYPE,
+ IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT,
+ IDC_PROXYAUTH,
+ IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS,
+ IDC_PROXYDNS,
+ IDC_SPECIFYPORTSO,
+ IDC_PORTSRANGEO,
+ IDC_STATIC54,
+ IDC_VALIDATESSL};
+static const UINT useProxyControls[] = {
+ IDC_STATIC21, IDC_PROXYTYPE,
+ IDC_STATIC22, IDC_PROXYHOST, IDC_STATIC23, IDC_PROXYPORT, IDC_STOFTENPORT,
+ IDC_PROXYAUTH,
+ IDC_STATIC31, IDC_PROXYUSER, IDC_STATIC32, IDC_PROXYPASS,
+ IDC_PROXYDNS};
+static const UINT specifyOPortsControls[] = {
+ IDC_PORTSRANGEO,
+ IDC_STATIC54
+};
+static const UINT incomingConnectionsControls[] = {
+ IDC_STATIC43,
+ IDC_SPECIFYPORTS,
+ IDC_PORTSRANGE,
+ IDC_STATIC52,
+ IDC_ENABLEUPNP};
+static const UINT specifyPortsControls[] = {
+ IDC_PORTSRANGE,
+ IDC_STATIC52};
+
+static const wchar_t* szProxyTypes[] = {LPGENW("<mixed>"), L"SOCKS4", L"SOCKS5", L"HTTP", L"HTTPS", L"Internet Explorer"};
+static const WORD oftenProxyPorts[] = {1080, 1080, 1080, 8080, 8080, 8080};
+
+#define M_REFRESHALL (WM_USER+100)
+#define M_REFRESHENABLING (WM_USER+101)
+
+static void ShowMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state)
+{
+ for (int i = 0; i < cControls; i++)
+ ShowWindow(GetDlgItem(hwndDlg, controls[i]), state);
+}
+
+static void EnableMultipleControls(HWND hwndDlg, const UINT *controls, int cControls, int state)
+{
+ for (int i = 0; i < cControls; i++)
+ EnableWindow(GetDlgItem(hwndDlg, controls[i]), state);
+}
+
+static void AddProxyTypeItem(HWND hwndDlg, int type, int selectType)
+{
+ int i = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_ADDSTRING, 0, (LPARAM)(type == 0 ? TranslateW(szProxyTypes[type]) : szProxyTypes[type]));
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETITEMDATA, i, type);
+ if (type == selectType)
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_SETCURSEL, i, 0);
+}
+
+static void CopySettingsStruct(NETLIBUSERSETTINGS *dest, const NETLIBUSERSETTINGS *source)
+{
+ *dest = *source;
+ if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts);
+ if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts);
+ if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword);
+ if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser);
+ if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer);
+}
+
+static void CombineSettingsStrings(char **dest, char **source)
+{
+ if (*dest != nullptr && (*source == nullptr || mir_strcmpi(*dest, *source))) { mir_free(*dest); *dest = nullptr; }
+}
+
+static void CombineSettingsStructs(NETLIBUSERSETTINGS *dest, DWORD *destFlags, NETLIBUSERSETTINGS *source, DWORD sourceFlags)
+{
+ if (sourceFlags & NUF_OUTGOING) {
+ if (*destFlags & NUF_OUTGOING) {
+ if (dest->validateSSL != source->validateSSL) dest->validateSSL = 2;
+ if (dest->useProxy != source->useProxy) dest->useProxy = 2;
+ if (dest->proxyType != source->proxyType) dest->proxyType = 0;
+ CombineSettingsStrings(&dest->szProxyServer, &source->szProxyServer);
+ if (dest->wProxyPort != source->wProxyPort) dest->wProxyPort = 0;
+ if (dest->useProxyAuth != source->useProxyAuth) dest->useProxyAuth = 2;
+ CombineSettingsStrings(&dest->szProxyAuthUser, &source->szProxyAuthUser);
+ CombineSettingsStrings(&dest->szProxyAuthPassword, &source->szProxyAuthPassword);
+ if (dest->dnsThroughProxy != source->dnsThroughProxy) dest->dnsThroughProxy = 2;
+ if (dest->specifyOutgoingPorts != source->specifyOutgoingPorts) dest->specifyOutgoingPorts = 2;
+ CombineSettingsStrings(&dest->szOutgoingPorts, &source->szOutgoingPorts);
+ }
+ else {
+ dest->validateSSL = source->validateSSL;
+ dest->useProxy = source->useProxy;
+ dest->proxyType = source->proxyType;
+ dest->szProxyServer = source->szProxyServer;
+ if (dest->szProxyServer) dest->szProxyServer = mir_strdup(dest->szProxyServer);
+ dest->wProxyPort = source->wProxyPort;
+ dest->useProxyAuth = source->useProxyAuth;
+ dest->szProxyAuthUser = source->szProxyAuthUser;
+ if (dest->szProxyAuthUser) dest->szProxyAuthUser = mir_strdup(dest->szProxyAuthUser);
+ dest->szProxyAuthPassword = source->szProxyAuthPassword;
+ if (dest->szProxyAuthPassword) dest->szProxyAuthPassword = mir_strdup(dest->szProxyAuthPassword);
+ dest->dnsThroughProxy = source->dnsThroughProxy;
+ dest->specifyOutgoingPorts = source->specifyOutgoingPorts;
+ dest->szOutgoingPorts = source->szOutgoingPorts;
+ if (dest->szOutgoingPorts) dest->szOutgoingPorts = mir_strdup(dest->szOutgoingPorts);
+ }
+ }
+ if (sourceFlags & NUF_INCOMING) {
+ if (*destFlags & NUF_INCOMING) {
+ if (dest->enableUPnP != source->enableUPnP) dest->enableUPnP = 2;
+ if (dest->specifyIncomingPorts != source->specifyIncomingPorts) dest->specifyIncomingPorts = 2;
+ CombineSettingsStrings(&dest->szIncomingPorts, &source->szIncomingPorts);
+ }
+ else {
+ dest->enableUPnP = source->enableUPnP;
+ dest->specifyIncomingPorts = source->specifyIncomingPorts;
+ dest->szIncomingPorts = source->szIncomingPorts;
+ if (dest->szIncomingPorts) dest->szIncomingPorts = mir_strdup(dest->szIncomingPorts);
+ }
+ }
+ if ((*destFlags & NUF_NOHTTPSOPTION) != (sourceFlags & NUF_NOHTTPSOPTION))
+ *destFlags = (*destFlags | sourceFlags) & ~NUF_NOHTTPSOPTION;
+ else *destFlags |= sourceFlags;
+}
+
+static void ChangeSettingIntByCheckbox(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset)
+{
+ int newValue = IsDlgButtonChecked(hwndDlg, ctrlId) != BST_CHECKED;
+ CheckDlgButton(hwndDlg, ctrlId, newValue ? BST_CHECKED : BST_UNCHECKED);
+ if (iUser == -1) {
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ *(int*)(((PBYTE)&p->settings) + memberOffset) = newValue;
+ }
+ else *(int*)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset) = newValue;
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+}
+
+static void ChangeSettingStringByEdit(HWND hwndDlg, UINT ctrlId, int iUser, int memberOffset)
+{
+ int newValueLen = GetWindowTextLength(GetDlgItem(hwndDlg, ctrlId));
+ char *szNewValue = (char*)mir_alloc(newValueLen+1);
+ GetDlgItemTextA(hwndDlg, ctrlId, szNewValue, newValueLen+1);
+ if (iUser == -1) {
+ for (auto &p : tempSettings) {
+ if (!(p->flags & NUF_NOOPTIONS)) {
+ char **ppszNew = (char**)(((PBYTE)&p->settings) + memberOffset);
+ mir_free(*ppszNew);
+ *ppszNew = mir_strdup(szNewValue);
+ }
+ }
+ mir_free(szNewValue);
+ }
+ else {
+ char **ppszNew = (char**)(((PBYTE)&tempSettings[iUser]->settings) + memberOffset);
+ mir_free(*ppszNew);
+ *ppszNew = szNewValue;
+ }
+}
+
+static void WriteSettingsStructToDb(const char *szSettingsModule, NETLIBUSERSETTINGS *settings, DWORD flags)
+{
+ if (flags & NUF_OUTGOING) {
+ db_set_b(0, szSettingsModule, "NLValidateSSL", (BYTE)settings->validateSSL);
+ db_set_b(0, szSettingsModule, "NLUseProxy", (BYTE)settings->useProxy);
+ db_set_b(0, szSettingsModule, "NLProxyType", (BYTE)settings->proxyType);
+ db_set_s(0, szSettingsModule, "NLProxyServer", settings->szProxyServer ? settings->szProxyServer : "");
+ db_set_w(0, szSettingsModule, "NLProxyPort", (WORD)settings->wProxyPort);
+ db_set_b(0, szSettingsModule, "NLUseProxyAuth", (BYTE)settings->useProxyAuth);
+ db_set_s(0, szSettingsModule, "NLProxyAuthUser", settings->szProxyAuthUser ? settings->szProxyAuthUser : "");
+ db_set_s(0, szSettingsModule, "NLProxyAuthPassword", settings->szProxyAuthPassword ? settings->szProxyAuthPassword : "");
+ db_set_b(0, szSettingsModule, "NLDnsThroughProxy", (BYTE)settings->dnsThroughProxy);
+ db_set_b(0, szSettingsModule, "NLSpecifyOutgoingPorts", (BYTE)settings->specifyOutgoingPorts);
+ db_set_s(0, szSettingsModule, "NLOutgoingPorts", settings->szOutgoingPorts ? settings->szOutgoingPorts : "");
+ }
+ if (flags & NUF_INCOMING) {
+ db_set_b(0, szSettingsModule, "NLEnableUPnP", (BYTE)settings->enableUPnP);
+ db_set_b(0, szSettingsModule, "NLSpecifyIncomingPorts", (BYTE)settings->specifyIncomingPorts);
+ db_set_s(0, szSettingsModule, "NLIncomingPorts", settings->szIncomingPorts ? settings->szIncomingPorts : "");
+ }
+}
+
+void NetlibSaveUserSettingsStruct(const char *szSettingsModule, const NETLIBUSERSETTINGS *settings)
+{
+ mir_cslock lck(csNetlibUser);
+
+ NetlibUser tUser;
+ tUser.user.szSettingsModule = (char*)szSettingsModule;
+ NetlibUser *thisUser = netlibUser.find(&tUser);
+ if (thisUser == nullptr)
+ return;
+
+ NetlibFreeUserSettingsStruct(&thisUser->settings);
+ CopySettingsStruct(&thisUser->settings, settings);
+ WriteSettingsStructToDb(thisUser->user.szSettingsModule, &thisUser->settings, thisUser->user.flags);
+
+ NETLIBUSERSETTINGS combinedSettings = { 0 };
+ combinedSettings.cbSize = sizeof(combinedSettings);
+
+ DWORD flags = 0;
+ for (auto &p : netlibUser) {
+ if (p->user.flags & NUF_NOOPTIONS)
+ continue;
+ CombineSettingsStructs(&combinedSettings, &flags, &p->settings, p->user.flags);
+ }
+ if (combinedSettings.validateSSL == 2) combinedSettings.validateSSL = 0;
+ if (combinedSettings.useProxy == 2) combinedSettings.useProxy = 0;
+ if (combinedSettings.proxyType == 0) combinedSettings.proxyType = PROXYTYPE_SOCKS5;
+ if (combinedSettings.useProxyAuth == 2) combinedSettings.useProxyAuth = 0;
+ if (combinedSettings.dnsThroughProxy == 2) combinedSettings.dnsThroughProxy = 1;
+ if (combinedSettings.enableUPnP == 2) combinedSettings.enableUPnP = 1;
+ if (combinedSettings.specifyIncomingPorts == 2) combinedSettings.specifyIncomingPorts = 0;
+ WriteSettingsStructToDb("Netlib", &combinedSettings, flags);
+ NetlibFreeUserSettingsStruct(&combinedSettings);
+}
+
+static INT_PTR CALLBACK DlgProcNetlibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int iUser;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ int iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)TranslateT("<All connections>"));
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, (LPARAM)-1);
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETCURSEL, iItem, 0);
+
+ mir_cslock lck(csNetlibUser);
+ for (auto &it : netlibUser) {
+ NetlibTempSettings *thisSettings = (NetlibTempSettings*)mir_calloc(sizeof(NetlibTempSettings));
+ thisSettings->flags = it->user.flags;
+ thisSettings->szSettingsModule = mir_strdup(it->user.szSettingsModule);
+ CopySettingsStruct(&thisSettings->settings, &it->settings);
+ tempSettings.insert(thisSettings);
+
+ if (it->user.flags & NUF_NOOPTIONS)
+ continue;
+ iItem = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_ADDSTRING, 0, (LPARAM)it->user.szDescriptiveName.w);
+ SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_SETITEMDATA, iItem, netlibUser.indexOf(&it));
+ }
+ }
+
+ SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ return TRUE;
+
+ case M_REFRESHALL:
+ iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0);
+ {
+ NETLIBUSERSETTINGS settings = { 0 };
+ DWORD flags = 0;
+
+ if (iUser == -1) {
+ settings.cbSize = sizeof(settings);
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ CombineSettingsStructs(&settings, &flags, &p->settings, p->flags);
+ }
+ else {
+ NetlibFreeUserSettingsStruct(&settings);
+ CopySettingsStruct(&settings, &tempSettings[iUser]->settings);
+ flags = tempSettings[iUser]->flags;
+ }
+ ShowMultipleControls(hwndDlg, outgoingConnectionsControls, _countof(outgoingConnectionsControls), flags & NUF_OUTGOING ? SW_SHOW : SW_HIDE);
+ CheckDlgButton(hwndDlg, IDC_USEPROXY, settings.useProxy);
+ SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_RESETCONTENT, 0, 0);
+ if (settings.proxyType == 0) AddProxyTypeItem(hwndDlg, 0, settings.proxyType);
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS4, settings.proxyType);
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_SOCKS5, settings.proxyType);
+ if (flags & NUF_HTTPCONNS) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTP, settings.proxyType);
+ if (!(flags & NUF_NOHTTPSOPTION)) AddProxyTypeItem(hwndDlg, PROXYTYPE_HTTPS, settings.proxyType);
+ if ((flags & NUF_HTTPCONNS) || !(flags & NUF_NOHTTPSOPTION))
+ AddProxyTypeItem(hwndDlg, PROXYTYPE_IE, settings.proxyType);
+ SetDlgItemTextA(hwndDlg, IDC_PROXYHOST, settings.szProxyServer ? settings.szProxyServer : "");
+ if (settings.wProxyPort) SetDlgItemInt(hwndDlg, IDC_PROXYPORT, settings.wProxyPort, FALSE);
+ else SetDlgItemTextA(hwndDlg, IDC_PROXYPORT, "");
+ CheckDlgButton(hwndDlg, IDC_PROXYAUTH, settings.useProxyAuth);
+ SetDlgItemTextA(hwndDlg, IDC_PROXYUSER, settings.szProxyAuthUser ? settings.szProxyAuthUser : "");
+ SetDlgItemTextA(hwndDlg, IDC_PROXYPASS, settings.szProxyAuthPassword ? settings.szProxyAuthPassword : "");
+ CheckDlgButton(hwndDlg, IDC_PROXYDNS, settings.dnsThroughProxy);
+ CheckDlgButton(hwndDlg, IDC_VALIDATESSL, settings.validateSSL);
+
+ ShowMultipleControls(hwndDlg, incomingConnectionsControls, _countof(incomingConnectionsControls), flags & NUF_INCOMING ? SW_SHOW : SW_HIDE);
+ CheckDlgButton(hwndDlg, IDC_SPECIFYPORTS, settings.specifyIncomingPorts);
+ SetDlgItemTextA(hwndDlg, IDC_PORTSRANGE, settings.szIncomingPorts ? settings.szIncomingPorts : "");
+
+ CheckDlgButton(hwndDlg, IDC_SPECIFYPORTSO, settings.specifyOutgoingPorts);
+ SetDlgItemTextA(hwndDlg, IDC_PORTSRANGEO, settings.szOutgoingPorts ? settings.szOutgoingPorts : "");
+
+ CheckDlgButton(hwndDlg, IDC_ENABLEUPNP, settings.enableUPnP);
+
+ NetlibFreeUserSettingsStruct(&settings);
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+ }
+ break;
+
+ case M_REFRESHENABLING:
+ wchar_t str[80];
+ {
+ int selectedProxyType = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0);
+ mir_snwprintf(str, TranslateT("(often %d)"), oftenProxyPorts[selectedProxyType]);
+ SetDlgItemText(hwndDlg, IDC_STOFTENPORT, str);
+ if (IsDlgButtonChecked(hwndDlg, IDC_USEPROXY) != BST_UNCHECKED) {
+ int enableAuth = 0, enableUser = 0, enablePass = 0, enableServer = 1;
+ EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), TRUE);
+ if (selectedProxyType == 0) {
+ for (auto &p : tempSettings) {
+ if (!p->settings.useProxy || p->flags & NUF_NOOPTIONS || !(p->flags & NUF_OUTGOING))
+ continue;
+
+ if (p->settings.proxyType == PROXYTYPE_SOCKS4) enableUser = 1;
+ else {
+ enableAuth = 1;
+ if (p->settings.useProxyAuth)
+ enableUser = enablePass = 1;
+ }
+ }
+ }
+ else {
+ if (selectedProxyType == PROXYTYPE_SOCKS4) enableUser = 1;
+ else {
+ if (selectedProxyType == PROXYTYPE_IE) enableServer = 0;
+ enableAuth = 1;
+ if (IsDlgButtonChecked(hwndDlg, IDC_PROXYAUTH) != BST_UNCHECKED)
+ enableUser = enablePass = 1;
+ }
+ }
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYAUTH), enableAuth);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC31), enableUser);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYUSER), enableUser);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC32), enablePass);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPASS), enablePass);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYHOST), enableServer);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PROXYPORT), enableServer);
+ }
+ else EnableMultipleControls(hwndDlg, useProxyControls, _countof(useProxyControls), FALSE);
+ EnableMultipleControls(hwndDlg, specifyPortsControls, _countof(specifyPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTS) != BST_UNCHECKED);
+ EnableMultipleControls(hwndDlg, specifyOPortsControls, _countof(specifyOPortsControls), IsDlgButtonChecked(hwndDlg, IDC_SPECIFYPORTSO) != BST_UNCHECKED);
+ }
+ break;
+
+ case WM_COMMAND:
+ iUser = SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_NETLIBUSERS, CB_GETCURSEL, 0, 0), 0);
+ switch (LOWORD(wParam)) {
+ case IDC_NETLIBUSERS:
+ if (HIWORD(wParam) == CBN_SELCHANGE) SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ return 0;
+
+ case IDC_LOGOPTIONS:
+ NetlibLogShowOptions();
+ return 0;
+
+ case IDC_PROXYTYPE:
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ int newValue = SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_PROXYTYPE, CB_GETCURSEL, 0, 0), 0);
+ if (iUser == -1) {
+ if (newValue == 0)
+ return 0;
+
+ for (auto &p : tempSettings) {
+ if (p->flags & NUF_NOOPTIONS)
+ continue;
+ if (newValue == PROXYTYPE_HTTP && !(p->flags & NUF_HTTPCONNS))
+ p->settings.proxyType = PROXYTYPE_HTTPS;
+ else if (newValue == PROXYTYPE_HTTPS && p->flags & NUF_NOHTTPSOPTION)
+ p->settings.proxyType = PROXYTYPE_HTTP;
+ else p->settings.proxyType = newValue;
+ }
+ SendMessage(hwndDlg, M_REFRESHALL, 0, 0);
+ }
+ else {
+ tempSettings[iUser]->settings.proxyType = newValue;
+ SendMessage(hwndDlg, M_REFRESHENABLING, 0, 0);
+ }
+ }
+ break;
+ case IDC_USEPROXY:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxy));
+ break;
+ case IDC_PROXYAUTH:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, useProxyAuth));
+ break;
+ case IDC_PROXYDNS:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, dnsThroughProxy));
+ break;
+ case IDC_SPECIFYPORTS:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyIncomingPorts));
+ break;
+ case IDC_SPECIFYPORTSO:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, specifyOutgoingPorts));
+ break;
+ case IDC_ENABLEUPNP:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, enableUPnP));
+ break;
+ case IDC_VALIDATESSL:
+ ChangeSettingIntByCheckbox(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, validateSSL));
+ break;
+ case IDC_PROXYHOST:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyServer));
+ break;
+ case IDC_PROXYPORT:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ {
+ int newValue = GetDlgItemInt(hwndDlg, LOWORD(wParam), nullptr, FALSE);
+ if (iUser == -1) {
+ for (auto &p : tempSettings)
+ if (!(p->flags & NUF_NOOPTIONS))
+ p->settings.wProxyPort = newValue;
+ }
+ else tempSettings[iUser]->settings.wProxyPort = newValue;
+ }
+ break;
+ case IDC_PROXYUSER:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthUser));
+ break;
+ case IDC_PROXYPASS:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szProxyAuthPassword));
+ break;
+ case IDC_PORTSRANGE:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szIncomingPorts));
+ break;
+ case IDC_PORTSRANGEO:
+ if (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus()) return 0;
+ ChangeSettingStringByEdit(hwndDlg, LOWORD(wParam), iUser, offsetof(NETLIBUSERSETTINGS, szOutgoingPorts));
+ break;
+ }
+ ShowWindow(GetDlgItem(hwndDlg, IDC_RECONNECTREQD), SW_SHOW);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ for (auto &p : tempSettings)
+ NetlibSaveUserSettingsStruct(p->szSettingsModule, &p->settings);
+ return TRUE;
+ }
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ for (auto &p : tempSettings) {
+ mir_free(p->szSettingsModule);
+ NetlibFreeUserSettingsStruct(&p->settings);
+ mir_free(p);
+ }
+ tempSettings.destroy();
+ break;
+ }
+ return FALSE;
+}
+
+int NetlibOptInitialise(WPARAM wParam, LPARAM)
+{
+ int optionsCount = 0;
+ {
+ mir_cslock lck(csNetlibUser);
+ for (auto &p : netlibUser)
+ if (!(p->user.flags & NUF_NOOPTIONS))
+ ++optionsCount;
+ }
+
+ if (optionsCount == 0)
+ return 0;
+
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 900000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NETLIB);
+ odp.szTitle.a = LPGEN("Network");
+ odp.pfnDlgProc = DlgProcNetlibOpts;
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wParam, &odp);
+ return 0;
+}
diff --git a/src/mir_app/src/netlibpktrecver.cpp b/src/mir_app/src/netlib_pktrecver.cpp
index 8214910dfc..8b1902de7c 100644
--- a/src/mir_app/src/netlibpktrecver.cpp
+++ b/src/mir_app/src/netlib_pktrecver.cpp
@@ -1,80 +1,80 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize)
-{
- if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return nullptr;
- }
-
- NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver));
- nlpr->handleType = NLH_PACKETRECVER;
- nlpr->nlc = nlc;
- nlpr->packetRecver.bufferSize = iMaxSize;
- nlpr->packetRecver.buffer = (PBYTE)mir_alloc(nlpr->packetRecver.bufferSize);
- nlpr->packetRecver.bytesUsed = 0;
- nlpr->packetRecver.bytesAvailable = 0;
- return nlpr;
-}
-
-MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam)
-{
- NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver;
- if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return SOCKET_ERROR;
- }
- if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */
- SetLastError(ERROR_TIMEOUT);
- return SOCKET_ERROR;
- }
- nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout;
- if (nlprParam->bytesUsed == 0) {
- if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) {
- nlpr->packetRecver.bytesAvailable = 0;
- Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching");
- }
- }
- else {
- memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed);
- nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed;
- }
-
- if (nlprParam->dwTimeout != INFINITE) {
- if (!sslApi.pending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) {
- *nlprParam = nlpr->packetRecver;
- return SOCKET_ERROR;
- }
- }
-
- INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0);
- if (recvResult > 0)
- nlpr->packetRecver.bytesAvailable += recvResult;
- *nlprParam = nlpr->packetRecver;
- return recvResult;
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+MIR_APP_DLL(HANDLE) Netlib_CreatePacketReceiver(HNETLIBCONN nlc, int iMaxSize)
+{
+ if (GetNetlibHandleType(nlc) != NLH_CONNECTION || iMaxSize == 0) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return nullptr;
+ }
+
+ NetlibPacketRecver *nlpr = (struct NetlibPacketRecver*)mir_calloc(sizeof(struct NetlibPacketRecver));
+ nlpr->handleType = NLH_PACKETRECVER;
+ nlpr->nlc = nlc;
+ nlpr->packetRecver.bufferSize = iMaxSize;
+ nlpr->packetRecver.buffer = (PBYTE)mir_alloc(nlpr->packetRecver.bufferSize);
+ nlpr->packetRecver.bytesUsed = 0;
+ nlpr->packetRecver.bytesAvailable = 0;
+ return nlpr;
+}
+
+MIR_APP_DLL(int) Netlib_GetMorePackets(HANDLE hReceiver, NETLIBPACKETRECVER *nlprParam)
+{
+ NetlibPacketRecver *nlpr = (NetlibPacketRecver*)hReceiver;
+ if (GetNetlibHandleType(nlpr) != NLH_PACKETRECVER || nlprParam == nullptr || nlprParam->bytesUsed > nlpr->packetRecver.bytesAvailable) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+ if (Miranda_IsTerminated()) { /* HACK: Lame, break while loops of protocols that can't kill their while loops, (cough, ICQ, cough) */
+ SetLastError(ERROR_TIMEOUT);
+ return SOCKET_ERROR;
+ }
+ nlpr->packetRecver.dwTimeout = nlprParam->dwTimeout;
+ if (nlprParam->bytesUsed == 0) {
+ if (nlpr->packetRecver.bytesAvailable == nlpr->packetRecver.bufferSize) {
+ nlpr->packetRecver.bytesAvailable = 0;
+ Netlib_Logf(nlpr->nlc->nlu, "Packet recver: packet overflowed buffer, ditching");
+ }
+ }
+ else {
+ memmove(nlpr->packetRecver.buffer, nlpr->packetRecver.buffer + nlprParam->bytesUsed, nlpr->packetRecver.bytesAvailable - nlprParam->bytesUsed);
+ nlpr->packetRecver.bytesAvailable -= nlprParam->bytesUsed;
+ }
+
+ if (nlprParam->dwTimeout != INFINITE) {
+ if (!sslApi.pending(nlpr->nlc->hSsl) && WaitUntilReadable(nlpr->nlc->s, nlprParam->dwTimeout) <= 0) {
+ *nlprParam = nlpr->packetRecver;
+ return SOCKET_ERROR;
+ }
+ }
+
+ INT_PTR recvResult = Netlib_Recv(nlpr->nlc, (char*)nlpr->packetRecver.buffer + nlpr->packetRecver.bytesAvailable, nlpr->packetRecver.bufferSize - nlpr->packetRecver.bytesAvailable, 0);
+ if (recvResult > 0)
+ nlpr->packetRecver.bytesAvailable += recvResult;
+ *nlprParam = nlpr->packetRecver;
+ return recvResult;
+}
diff --git a/src/mir_app/src/netlibsecurity.cpp b/src/mir_app/src/netlib_security.cpp
index c48becd6c9..c3b6e11ffa 100644
--- a/src/mir_app/src/netlibsecurity.cpp
+++ b/src/mir_app/src/netlib_security.cpp
@@ -1,363 +1,363 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-#define SECURITY_WIN32
-#include <security.h>
-#include <rpcdce.h>
-
-#pragma comment(lib, "secur32.lib")
-
-struct NtlmHandleType
-{
- CtxtHandle hClientContext;
- CredHandle hClientCredential;
- wchar_t* szProvider;
- wchar_t* szPrincipal;
- unsigned cbMaxToken;
- bool hasDomain;
-};
-
-struct NTLM_String
-{
- WORD len;
- WORD allocedSpace;
- DWORD offset;
-};
-
-struct NtlmType2packet
-{
- char sign[8];
- DWORD type; // == 2
- NTLM_String targetName;
- DWORD flags;
- BYTE challenge[8];
- BYTE context[8];
- NTLM_String targetInfo;
-};
-
-static unsigned ntlmCnt = 0;
-static mir_cs csSec;
-
-static void ReportSecError(SECURITY_STATUS scRet, int line)
-{
- wchar_t szMsgBuf[256];
- FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr);
-
- wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0;
-
- Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal)
-{
- HANDLE hSecurity = nullptr;
-
- if (mir_wstrcmpi(szProvider, L"Basic") == 0) {
- NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
- hNtlm->szProvider = mir_wstrdup(szProvider);
- SecInvalidateHandle(&hNtlm->hClientContext);
- SecInvalidateHandle(&hNtlm->hClientCredential);
- ntlmCnt++;
-
- return hNtlm;
- }
-
- mir_cslock lck(csSec);
-
- PSecPkgInfo ntlmSecurityPackageInfo;
- SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo);
- if (sc == SEC_E_OK) {
- NtlmHandleType* hNtlm;
-
- hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
- hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken;
- FreeContextBuffer(ntlmSecurityPackageInfo);
-
- hNtlm->szProvider = mir_wstrdup(szProvider);
- hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L"");
- SecInvalidateHandle(&hNtlm->hClientContext);
- SecInvalidateHandle(&hNtlm->hClientCredential);
- ntlmCnt++;
- }
- return hSecurity;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity)
-{
- if (hSecurity == nullptr)
- return;
-
- mir_cslock lck(csSec);
-
- if (ntlmCnt != 0) {
- NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
- if (hNtlm != nullptr) {
- if (SecIsValidHandle(&hNtlm->hClientContext))
- DeleteSecurityContext(&hNtlm->hClientContext);
- if (SecIsValidHandle(&hNtlm->hClientCredential))
- FreeCredentialsHandle(&hNtlm->hClientCredential);
- mir_free(hNtlm->szProvider);
- mir_free(hNtlm->szPrincipal);
- mir_free(hNtlm);
- }
-
- --ntlmCnt;
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz)
-{
- if (!szChallenge || !szChallenge[0]) return nullptr;
-
- NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
- unsigned char inDataBuffer[1024];
-
- SecBuffer inBuffers[2] =
- {
- { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer },
- { chlsz, SECBUFFER_STREAM, szChallenge }
- };
-
- SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers };
-
- unsigned long qop = 0;
- SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop);
- if (sc != SEC_E_OK) {
- ReportSecError(sc, __LINE__);
- return nullptr;
- }
-
- // unsigned char LayerMask = inDataBuffer[0];
- // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]);
-
- SecPkgContext_Sizes sizes;
- sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes);
- if (sc != SEC_E_OK) {
- ReportSecError(sc, __LINE__);
- return nullptr;
- }
-
- unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer);
- unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize);
-
- unsigned char outDataBuffer[4] = { 1, 0, 16, 0 };
-
- SecBuffer outBuffers[3] =
- {
- { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer },
- { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer },
- { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer }
- };
- SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers };
-
- sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0);
- if (sc != SEC_E_OK) {
- ReportSecError(sc, __LINE__);
- return nullptr;
- }
-
- unsigned i, ressz = 0;
- for (i = 0; i < outBuffersDesc.cBuffers; i++)
- ressz += outBuffersDesc.pBuffers[i].cbBuffer;
-
- unsigned char *response = (unsigned char*)alloca(ressz), *p = response;
- for (i = 0; i < outBuffersDesc.cBuffers; i++) {
- memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer);
- p += outBuffersDesc.pBuffers[i].cbBuffer;
- }
-
- return mir_base64_encode(response, ressz);
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete)
-{
- if (hSecurity == nullptr || ntlmCnt == 0)
- return nullptr;
-
- SecBufferDesc outputBufferDescriptor, inputBufferDescriptor;
- SecBuffer outputSecurityToken, inputSecurityToken;
- char *szOutputToken;
-
- NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity;
-
- Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete);
-
- if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) {
- bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0;
- bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0';
- if (hasChallenge) {
- size_t tokenLen;
- BYTE *token = (BYTE*)mir_base64_decode(szChallenge, &tokenLen);
- if (token == nullptr)
- return nullptr;
-
- if (isGSSAPI && complete)
- return CompleteGssapi(hSecurity, token, (unsigned)tokenLen);
-
- inputBufferDescriptor.cBuffers = 1;
- inputBufferDescriptor.pBuffers = &inputSecurityToken;
- inputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
- inputSecurityToken.BufferType = SECBUFFER_TOKEN;
- inputSecurityToken.cbBuffer = (unsigned)tokenLen;
- inputSecurityToken.pvBuffer = token;
-
- // try to decode the domain name from the NTLM challenge
- if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) {
- NtlmType2packet* pkt = (NtlmType2packet*)token;
- if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) {
-
- wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset];
- int domainLen = pkt->targetName.len;
-
- // Negotiate ANSI? if yes, convert the ANSI name to unicode
- if ((pkt->flags & 1) == 0) {
- int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0);
- wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t));
- domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1;
- buf[domainLen] = 0;
- domainName = buf;
- }
- else domainLen /= sizeof(wchar_t);
-
- if (domainLen) {
- CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login);
- char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete);
- mir_free(szChl);
- }
- }
- }
- }
- else {
- if (SecIsValidHandle(&hNtlm->hClientContext))
- DeleteSecurityContext(&hNtlm->hClientContext);
- if (SecIsValidHandle(&hNtlm->hClientCredential))
- FreeCredentialsHandle(&hNtlm->hClientCredential);
-
- SEC_WINNT_AUTH_IDENTITY auth;
-
- if (login != nullptr && login[0] != '\0') {
- memset(&auth, 0, sizeof(auth));
-
- Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)");
-
- const wchar_t* loginName = login;
- const wchar_t* domainName = wcschr(login, '\\');
- size_t domainLen = 0;
- size_t loginLen = mir_wstrlen(loginName);
- if (domainName != nullptr) {
- loginName = domainName + 1;
- loginLen = mir_wstrlen(loginName);
- domainLen = domainName - login;
- domainName = login;
- }
- else if ((domainName = wcschr(login, '@')) != nullptr) {
- loginName = login;
- loginLen = domainName - login;
- domainLen = mir_wstrlen(++domainName);
- }
-
- auth.User = (PWORD)loginName;
- auth.UserLength = (ULONG)loginLen;
- auth.Password = (PWORD)psw;
- auth.PasswordLength = (ULONG)mir_wstrlen(psw);
- auth.Domain = (PWORD)domainName;
- auth.DomainLength = (ULONG)domainLen;
- auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
-
- hNtlm->hasDomain = domainLen != 0;
- }
-
- TimeStamp tokenExpiration;
- SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration);
- if (sc != SEC_E_OK) {
- ReportSecError(sc, __LINE__);
- return nullptr;
- }
- }
-
- outputBufferDescriptor.cBuffers = 1;
- outputBufferDescriptor.pBuffers = &outputSecurityToken;
- outputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
- outputSecurityToken.BufferType = SECBUFFER_TOKEN;
- outputSecurityToken.cbBuffer = hNtlm->cbMaxToken;
- outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer);
-
- ULONG contextAttributes;
- TimeStamp tokenExpiration;
- SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential,
- hasChallenge ? &hNtlm->hClientContext : nullptr,
- hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP,
- hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext,
- &outputBufferDescriptor, &contextAttributes, &tokenExpiration);
- Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc);
-
- complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED);
- if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) {
- sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor);
- Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc);
- }
-
- if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) {
- ReportSecError(sc, __LINE__);
- return nullptr;
- }
-
- szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer);
- }
- else {
- if (!login || !psw)
- return nullptr;
-
- T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw));
- szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth));
- complete = true;
- }
-
- if (szOutputToken == nullptr)
- return nullptr;
-
- if (!http)
- return szOutputToken;
-
- CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken);
- mir_free(szOutputToken);
- return szResult.Detach();
-}
-
-MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete)
-{
- return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete);
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#define SECURITY_WIN32
+#include <security.h>
+#include <rpcdce.h>
+
+#pragma comment(lib, "secur32.lib")
+
+struct NtlmHandleType
+{
+ CtxtHandle hClientContext;
+ CredHandle hClientCredential;
+ wchar_t* szProvider;
+ wchar_t* szPrincipal;
+ unsigned cbMaxToken;
+ bool hasDomain;
+};
+
+struct NTLM_String
+{
+ WORD len;
+ WORD allocedSpace;
+ DWORD offset;
+};
+
+struct NtlmType2packet
+{
+ char sign[8];
+ DWORD type; // == 2
+ NTLM_String targetName;
+ DWORD flags;
+ BYTE challenge[8];
+ BYTE context[8];
+ NTLM_String targetInfo;
+};
+
+static unsigned ntlmCnt = 0;
+static mir_cs csSec;
+
+static void ReportSecError(SECURITY_STATUS scRet, int line)
+{
+ wchar_t szMsgBuf[256];
+ FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr, scRet, LANG_USER_DEFAULT, szMsgBuf, _countof(szMsgBuf), nullptr);
+
+ wchar_t *p = wcschr(szMsgBuf, 13); if (p) *p = 0;
+
+ Netlib_LogfW(nullptr, L"Security error 0x%x on line %u (%s)", scRet, line, szMsgBuf);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(HANDLE) Netlib_InitSecurityProvider(const wchar_t *szProvider, const wchar_t *szPrincipal)
+{
+ HANDLE hSecurity = nullptr;
+
+ if (mir_wstrcmpi(szProvider, L"Basic") == 0) {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->szProvider = mir_wstrdup(szProvider);
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+
+ return hNtlm;
+ }
+
+ mir_cslock lck(csSec);
+
+ PSecPkgInfo ntlmSecurityPackageInfo;
+ SECURITY_STATUS sc = QuerySecurityPackageInfo((LPTSTR)szProvider, &ntlmSecurityPackageInfo);
+ if (sc == SEC_E_OK) {
+ NtlmHandleType* hNtlm;
+
+ hSecurity = hNtlm = (NtlmHandleType*)mir_calloc(sizeof(NtlmHandleType));
+ hNtlm->cbMaxToken = ntlmSecurityPackageInfo->cbMaxToken;
+ FreeContextBuffer(ntlmSecurityPackageInfo);
+
+ hNtlm->szProvider = mir_wstrdup(szProvider);
+ hNtlm->szPrincipal = mir_wstrdup(szPrincipal ? szPrincipal : L"");
+ SecInvalidateHandle(&hNtlm->hClientContext);
+ SecInvalidateHandle(&hNtlm->hClientCredential);
+ ntlmCnt++;
+ }
+ return hSecurity;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(void) Netlib_DestroySecurityProvider(HANDLE hSecurity)
+{
+ if (hSecurity == nullptr)
+ return;
+
+ mir_cslock lck(csSec);
+
+ if (ntlmCnt != 0) {
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ if (hNtlm != nullptr) {
+ if (SecIsValidHandle(&hNtlm->hClientContext))
+ DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential))
+ FreeCredentialsHandle(&hNtlm->hClientCredential);
+ mir_free(hNtlm->szProvider);
+ mir_free(hNtlm->szPrincipal);
+ mir_free(hNtlm);
+ }
+
+ --ntlmCnt;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* CompleteGssapi(HANDLE hSecurity, unsigned char *szChallenge, unsigned chlsz)
+{
+ if (!szChallenge || !szChallenge[0]) return nullptr;
+
+ NtlmHandleType* hNtlm = (NtlmHandleType*)hSecurity;
+ unsigned char inDataBuffer[1024];
+
+ SecBuffer inBuffers[2] =
+ {
+ { sizeof(inDataBuffer), SECBUFFER_DATA, inDataBuffer },
+ { chlsz, SECBUFFER_STREAM, szChallenge }
+ };
+
+ SecBufferDesc inBuffersDesc = { SECBUFFER_VERSION, 2, inBuffers };
+
+ unsigned long qop = 0;
+ SECURITY_STATUS sc = DecryptMessage(&hNtlm->hClientContext, &inBuffersDesc, 0, &qop);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ // unsigned char LayerMask = inDataBuffer[0];
+ // unsigned int MaxMessageSize = htonl(*(unsigned*)&inDataBuffer[1]);
+
+ SecPkgContext_Sizes sizes;
+ sc = QueryContextAttributes(&hNtlm->hClientContext, SECPKG_ATTR_SIZES, &sizes);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ unsigned char *tokenBuffer = (unsigned char*)alloca(sizes.cbSecurityTrailer);
+ unsigned char *paddingBuffer = (unsigned char*)alloca(sizes.cbBlockSize);
+
+ unsigned char outDataBuffer[4] = { 1, 0, 16, 0 };
+
+ SecBuffer outBuffers[3] =
+ {
+ { sizes.cbSecurityTrailer, SECBUFFER_TOKEN, tokenBuffer },
+ { sizeof(outDataBuffer), SECBUFFER_DATA, outDataBuffer },
+ { sizes.cbBlockSize, SECBUFFER_PADDING, paddingBuffer }
+ };
+ SecBufferDesc outBuffersDesc = { SECBUFFER_VERSION, 3, outBuffers };
+
+ sc = EncryptMessage(&hNtlm->hClientContext, SECQOP_WRAP_NO_ENCRYPT, &outBuffersDesc, 0);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ unsigned i, ressz = 0;
+ for (i = 0; i < outBuffersDesc.cBuffers; i++)
+ ressz += outBuffersDesc.pBuffers[i].cbBuffer;
+
+ unsigned char *response = (unsigned char*)alloca(ressz), *p = response;
+ for (i = 0; i < outBuffersDesc.cBuffers; i++) {
+ memcpy(p, outBuffersDesc.pBuffers[i].pvBuffer, outBuffersDesc.pBuffers[i].cbBuffer);
+ p += outBuffersDesc.pBuffers[i].cbBuffer;
+ }
+
+ return mir_base64_encode(response, ressz);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+char* NtlmCreateResponseFromChallenge(HANDLE hSecurity, const char *szChallenge, const wchar_t *login, const wchar_t *psw, bool http, unsigned &complete)
+{
+ if (hSecurity == nullptr || ntlmCnt == 0)
+ return nullptr;
+
+ SecBufferDesc outputBufferDescriptor, inputBufferDescriptor;
+ SecBuffer outputSecurityToken, inputSecurityToken;
+ char *szOutputToken;
+
+ NtlmHandleType *hNtlm = (NtlmHandleType*)hSecurity;
+
+ Netlib_Logf(nullptr, "NtlmCreateResponseFromChallenge (%s): chl=%s {%S:%S} => %d", hNtlm->szProvider, szChallenge, login, psw, complete);
+
+ if (mir_wstrcmpi(hNtlm->szProvider, L"Basic")) {
+ bool isGSSAPI = mir_wstrcmpi(hNtlm->szProvider, L"Kerberos") == 0;
+ bool hasChallenge = szChallenge != nullptr && szChallenge[0] != '\0';
+ if (hasChallenge) {
+ size_t tokenLen;
+ BYTE *token = (BYTE*)mir_base64_decode(szChallenge, &tokenLen);
+ if (token == nullptr)
+ return nullptr;
+
+ if (isGSSAPI && complete)
+ return CompleteGssapi(hSecurity, token, (unsigned)tokenLen);
+
+ inputBufferDescriptor.cBuffers = 1;
+ inputBufferDescriptor.pBuffers = &inputSecurityToken;
+ inputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ inputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ inputSecurityToken.cbBuffer = (unsigned)tokenLen;
+ inputSecurityToken.pvBuffer = token;
+
+ // try to decode the domain name from the NTLM challenge
+ if (login != nullptr && login[0] != '\0' && !hNtlm->hasDomain) {
+ NtlmType2packet* pkt = (NtlmType2packet*)token;
+ if (!strncmp(pkt->sign, "NTLMSSP", 8) && pkt->type == 2) {
+
+ wchar_t* domainName = (wchar_t*)&token[pkt->targetName.offset];
+ int domainLen = pkt->targetName.len;
+
+ // Negotiate ANSI? if yes, convert the ANSI name to unicode
+ if ((pkt->flags & 1) == 0) {
+ int bufsz = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, nullptr, 0);
+ wchar_t* buf = (wchar_t*)alloca((bufsz+1) * sizeof(wchar_t));
+ domainLen = MultiByteToWideChar(CP_ACP, 0, (char*)domainName, domainLen, buf, bufsz) - 1;
+ buf[domainLen] = 0;
+ domainName = buf;
+ }
+ else domainLen /= sizeof(wchar_t);
+
+ if (domainLen) {
+ CMStringW wszNewLogin(FORMAT, L"%s\\%s", domainName, login);
+ char* szChl = NtlmCreateResponseFromChallenge(hSecurity, nullptr, wszNewLogin, psw, http, complete);
+ mir_free(szChl);
+ }
+ }
+ }
+ }
+ else {
+ if (SecIsValidHandle(&hNtlm->hClientContext))
+ DeleteSecurityContext(&hNtlm->hClientContext);
+ if (SecIsValidHandle(&hNtlm->hClientCredential))
+ FreeCredentialsHandle(&hNtlm->hClientCredential);
+
+ SEC_WINNT_AUTH_IDENTITY auth;
+
+ if (login != nullptr && login[0] != '\0') {
+ memset(&auth, 0, sizeof(auth));
+
+ Netlib_Logf(nullptr, "Security login requested, user: %S pssw: %s", login, psw ? "(exist)" : "(no psw)");
+
+ const wchar_t* loginName = login;
+ const wchar_t* domainName = wcschr(login, '\\');
+ size_t domainLen = 0;
+ size_t loginLen = mir_wstrlen(loginName);
+ if (domainName != nullptr) {
+ loginName = domainName + 1;
+ loginLen = mir_wstrlen(loginName);
+ domainLen = domainName - login;
+ domainName = login;
+ }
+ else if ((domainName = wcschr(login, '@')) != nullptr) {
+ loginName = login;
+ loginLen = domainName - login;
+ domainLen = mir_wstrlen(++domainName);
+ }
+
+ auth.User = (PWORD)loginName;
+ auth.UserLength = (ULONG)loginLen;
+ auth.Password = (PWORD)psw;
+ auth.PasswordLength = (ULONG)mir_wstrlen(psw);
+ auth.Domain = (PWORD)domainName;
+ auth.DomainLength = (ULONG)domainLen;
+ auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+
+ hNtlm->hasDomain = domainLen != 0;
+ }
+
+ TimeStamp tokenExpiration;
+ SECURITY_STATUS sc = AcquireCredentialsHandle(nullptr, hNtlm->szProvider, SECPKG_CRED_OUTBOUND, nullptr, hNtlm->hasDomain ? &auth : nullptr, nullptr, nullptr, &hNtlm->hClientCredential, &tokenExpiration);
+ if (sc != SEC_E_OK) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+ }
+
+ outputBufferDescriptor.cBuffers = 1;
+ outputBufferDescriptor.pBuffers = &outputSecurityToken;
+ outputBufferDescriptor.ulVersion = SECBUFFER_VERSION;
+ outputSecurityToken.BufferType = SECBUFFER_TOKEN;
+ outputSecurityToken.cbBuffer = hNtlm->cbMaxToken;
+ outputSecurityToken.pvBuffer = alloca(outputSecurityToken.cbBuffer);
+
+ ULONG contextAttributes;
+ TimeStamp tokenExpiration;
+ SECURITY_STATUS sc = InitializeSecurityContext(&hNtlm->hClientCredential,
+ hasChallenge ? &hNtlm->hClientContext : nullptr,
+ hNtlm->szPrincipal, isGSSAPI ? ISC_REQ_MUTUAL_AUTH | ISC_REQ_STREAM : 0, 0, SECURITY_NATIVE_DREP,
+ hasChallenge ? &inputBufferDescriptor : nullptr, 0, &hNtlm->hClientContext,
+ &outputBufferDescriptor, &contextAttributes, &tokenExpiration);
+ Netlib_Logf(nullptr, "InitializeSecurityContext(%S): 0x%x", hNtlm->szProvider, sc);
+
+ complete = (sc != SEC_I_COMPLETE_AND_CONTINUE && sc != SEC_I_CONTINUE_NEEDED);
+ if (sc == SEC_I_COMPLETE_NEEDED || sc == SEC_I_COMPLETE_AND_CONTINUE) {
+ sc = CompleteAuthToken(&hNtlm->hClientContext, &outputBufferDescriptor);
+ Netlib_Logf(nullptr, "CompleteAuthToken: 0x%x", sc);
+ }
+
+ if (sc != SEC_E_OK && sc != SEC_I_CONTINUE_NEEDED) {
+ ReportSecError(sc, __LINE__);
+ return nullptr;
+ }
+
+ szOutputToken = mir_base64_encode(outputSecurityToken.pvBuffer, outputSecurityToken.cbBuffer);
+ }
+ else {
+ if (!login || !psw)
+ return nullptr;
+
+ T2Utf szAuth(CMStringW(FORMAT, L"%s:%s", login, psw));
+ szOutputToken = mir_base64_encode(szAuth.get(), mir_strlen(szAuth));
+ complete = true;
+ }
+
+ if (szOutputToken == nullptr)
+ return nullptr;
+
+ if (!http)
+ return szOutputToken;
+
+ CMStringA szResult(FORMAT, "%S %s", hNtlm->szProvider, szOutputToken);
+ mir_free(szOutputToken);
+ return szResult.Detach();
+}
+
+MIR_APP_DLL(char*) Netlib_NtlmCreateResponse(HANDLE hProvider, const char *szChallenge, wchar_t *pwszLogin, wchar_t *pwszPassword, unsigned &complete)
+{
+ return NtlmCreateResponseFromChallenge(hProvider, szChallenge, pwszLogin, pwszPassword, false, complete);
+}
diff --git a/src/mir_app/src/netlibsock.cpp b/src/mir_app/src/netlib_sock.cpp
index afe705d98d..9ccb53258e 100644
--- a/src/mir_app/src/netlibsock.cpp
+++ b/src/mir_app/src/netlib_sock.cpp
@@ -1,290 +1,290 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-extern HANDLE hConnectionHeaderMutex, hSendEvent, hRecvEvent;
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags)
-{
- if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
- return SOCKET_ERROR;
-
- int result;
- NetlibDumpData(nlc, (PBYTE)buf, len, 1, flags);
- if (nlc->hSsl)
- result = sslApi.write(nlc->hSsl, buf, len);
- else
- result = send(nlc->s, buf, len, flags & 0xFFFF);
-
- NetlibLeaveNestedCS(&nlc->ncsSend);
-
- NETLIBNOTIFY nln = { buf, len, flags, result };
- NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
-
- return result;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags)
-{
- if (!NetlibEnterNestedCS(nlc, NLNCS_RECV))
- return SOCKET_ERROR;
-
- int recvResult;
- if (!nlc->foreBuf.isEmpty()) {
- recvResult = min(len, (int)nlc->foreBuf.length());
- memcpy(buf, nlc->foreBuf.data(), recvResult);
- nlc->foreBuf.remove(recvResult);
- }
- else if (nlc->hSsl)
- recvResult = sslApi.read(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0);
- else
- recvResult = recv(nlc->s, buf, len, flags & 0xFFFF);
-
- NetlibLeaveNestedCS(&nlc->ncsRecv);
- if (recvResult <= 0)
- return recvResult;
-
- NetlibDumpData(nlc, (PBYTE)buf, recvResult, 0, flags);
-
- if ((flags & MSG_PEEK) == 0) {
- NETLIBNOTIFY nln = { buf, len, flags, recvResult };
- NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
- }
- return recvResult;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending)
-{
- FD_ZERO(fd);
- for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) {
- NetlibConnection *nlcCheck = hConns[i];
- if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) {
- SetLastError(ERROR_INVALID_DATA);
- return 0;
- }
- FD_SET(nlcCheck->s, fd);
- if (!nlcCheck->foreBuf.isEmpty() || sslApi.pending(nlcCheck->hSsl))
- pending++;
- }
- return 1;
-}
-
-MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls)
-{
- if (nls == nullptr) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return SOCKET_ERROR;
- }
-
- int pending = 0;
- fd_set readfd, writefd, exceptfd;
- WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
- if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending)
- || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending)
- || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending))
- {
- ReleaseMutex(hConnectionHeaderMutex);
- return SOCKET_ERROR;
- }
- ReleaseMutex(hConnectionHeaderMutex);
- if (pending)
- return 1;
-
- TIMEVAL tv;
- tv.tv_sec = nls->dwTimeout / 1000;
- tv.tv_usec = (nls->dwTimeout % 1000) * 1000;
- return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv);
-}
-
-MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls)
-{
- if (nls == nullptr) {
- SetLastError(ERROR_INVALID_PARAMETER);
- return SOCKET_ERROR;
- }
-
- TIMEVAL tv;
- tv.tv_sec = nls->dwTimeout / 1000;
- tv.tv_usec = (nls->dwTimeout % 1000) * 1000;
- WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
-
- int pending = 0;
- fd_set readfd, writefd, exceptfd;
- if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending)
- || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending)
- || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending))
- {
- ReleaseMutex(hConnectionHeaderMutex);
- return SOCKET_ERROR;
- }
- ReleaseMutex(hConnectionHeaderMutex);
-
- int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv);
-
- WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
- /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET()
- to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not)
- This happens for read/write/except */
- NetlibConnection *conn = nullptr;
- int j;
- for (j = 0; j < FD_SETSIZE; j++) {
- conn = (NetlibConnection*)nls->hReadConns[j];
- if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
-
- if (sslApi.pending(conn->hSsl))
- nls->hReadStatus[j] = TRUE;
- nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd);
- }
-
- for (j = 0; j < FD_SETSIZE; j++) {
- conn = (NetlibConnection*)nls->hWriteConns[j];
- if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
- nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd);
- }
-
- for (j = 0; j < FD_SETSIZE; j++) {
- conn = (NetlibConnection*)nls->hExceptConns[j];
- if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
- nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd);
- }
- ReleaseMutex(hConnectionHeaderMutex);
- return rc;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr)
-{
- if (!str) return false;
-
- int len = sizeof(SOCKADDR_INET_M);
- return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len);
-}
-
-MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr)
-{
- char saddr[128];
- DWORD len = sizeof(saddr);
- if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len))
- return mir_strdup(saddr);
-
- if (addr->sin_family == AF_INET) {
- char *szIp = inet_ntoa(addr->sin_addr);
- if (addr->sin_port != 0) {
- mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port);
- return mir_strdup(saddr);
- }
- return mir_strdup(szIp);
- }
- return nullptr;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN hConnection, NETLIBCONNINFO *connInfo)
-{
- NetlibConnection *nlc = (NetlibConnection*)hConnection;
- if (!nlc || !connInfo)
- return 1;
-
- sockaddr_in sin = { 0 };
- int len = sizeof(sin);
- if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) {
- connInfo->wPort = ntohs(sin.sin_port);
- connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0;
- strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE);
- }
- return 0;
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-inline bool IsAddrGlobal(const IN6_ADDR *a)
-{
- unsigned char High = a->s6_bytes[0] & 0xf0;
- return High != 0 && High != 0xf0;
-}
-
-MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly)
-{
- addrinfo *air = nullptr, *ai, hints = { 0 };
- const char *szMyHost = "";
-
- hints.ai_family = AF_UNSPEC;
- hints.ai_flags = AI_PASSIVE;
-
- if (GetAddrInfoA(szMyHost, nullptr, &hints, &air))
- return nullptr;
-
- unsigned n = 0;
- for (ai = air; ai; ai = ai->ai_next) {
- SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr;
- if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr))))
- ++n;
- }
-
- NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4);
- addr->cbNum = n;
-
- unsigned i = 0;
- for (ai = air; ai; ai = ai->ai_next) {
- sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr;
- if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) {
- char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr);
- if (szIp)
- strncpy_s(addr->szIp[i++], szIp, _TRUNCATE);
- mir_free(szIp);
- }
- }
- FreeAddrInfoA(air);
- return addr;
-}
-
-static NETLIBIPLIST* GetMyIpv4(void)
-{
- char hostname[256] = "";
-
- gethostname(hostname, sizeof(hostname));
- PHOSTENT he = gethostbyname(hostname);
-
- unsigned n;
- for (n = 0; he->h_addr_list[n]; ++n)
- ;
-
- NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4);
- addr->cbNum = n;
-
- for (unsigned i = 0; i < n; i++)
- strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE);
-
- return addr;
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+extern HANDLE hConnectionHeaderMutex, hSendEvent, hRecvEvent;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Send(HNETLIBCONN nlc, const char *buf, int len, int flags)
+{
+ if (!NetlibEnterNestedCS(nlc, NLNCS_SEND))
+ return SOCKET_ERROR;
+
+ int result;
+ NetlibDumpData(nlc, (PBYTE)buf, len, 1, flags);
+ if (nlc->hSsl)
+ result = sslApi.write(nlc->hSsl, buf, len);
+ else
+ result = send(nlc->s, buf, len, flags & 0xFFFF);
+
+ NetlibLeaveNestedCS(&nlc->ncsSend);
+
+ NETLIBNOTIFY nln = { buf, len, flags, result };
+ NotifyFastHook(hSendEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
+
+ return result;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_Recv(HNETLIBCONN nlc, char *buf, int len, int flags)
+{
+ if (!NetlibEnterNestedCS(nlc, NLNCS_RECV))
+ return SOCKET_ERROR;
+
+ int recvResult;
+ if (!nlc->foreBuf.isEmpty()) {
+ recvResult = min(len, (int)nlc->foreBuf.length());
+ memcpy(buf, nlc->foreBuf.data(), recvResult);
+ nlc->foreBuf.remove(recvResult);
+ }
+ else if (nlc->hSsl)
+ recvResult = sslApi.read(nlc->hSsl, buf, len, (flags & MSG_PEEK) != 0);
+ else
+ recvResult = recv(nlc->s, buf, len, flags & 0xFFFF);
+
+ NetlibLeaveNestedCS(&nlc->ncsRecv);
+ if (recvResult <= 0)
+ return recvResult;
+
+ NetlibDumpData(nlc, (PBYTE)buf, recvResult, 0, flags);
+
+ if ((flags & MSG_PEEK) == 0) {
+ NETLIBNOTIFY nln = { buf, len, flags, recvResult };
+ NotifyFastHook(hRecvEvent, (WPARAM)&nln, (LPARAM)&nlc->nlu->user);
+ }
+ return recvResult;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+static int ConnectionListToSocketList(const HNETLIBCONN *hConns, fd_set *fd, int& pending)
+{
+ FD_ZERO(fd);
+ for (int i = 0; hConns[i] && hConns[i] != INVALID_HANDLE_VALUE && i < FD_SETSIZE; i++) {
+ NetlibConnection *nlcCheck = hConns[i];
+ if (nlcCheck->handleType != NLH_CONNECTION && nlcCheck->handleType != NLH_BOUNDPORT) {
+ SetLastError(ERROR_INVALID_DATA);
+ return 0;
+ }
+ FD_SET(nlcCheck->s, fd);
+ if (!nlcCheck->foreBuf.isEmpty() || sslApi.pending(nlcCheck->hSsl))
+ pending++;
+ }
+ return 1;
+}
+
+MIR_APP_DLL(int) Netlib_Select(NETLIBSELECT *nls)
+{
+ if (nls == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ int pending = 0;
+ fd_set readfd, writefd, exceptfd;
+ WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
+ if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending)
+ || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending)
+ || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending))
+ {
+ ReleaseMutex(hConnectionHeaderMutex);
+ return SOCKET_ERROR;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+ if (pending)
+ return 1;
+
+ TIMEVAL tv;
+ tv.tv_sec = nls->dwTimeout / 1000;
+ tv.tv_usec = (nls->dwTimeout % 1000) * 1000;
+ return select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv);
+}
+
+MIR_APP_DLL(int) Netlib_SelectEx(NETLIBSELECTEX *nls)
+{
+ if (nls == nullptr) {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return SOCKET_ERROR;
+ }
+
+ TIMEVAL tv;
+ tv.tv_sec = nls->dwTimeout / 1000;
+ tv.tv_usec = (nls->dwTimeout % 1000) * 1000;
+ WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
+
+ int pending = 0;
+ fd_set readfd, writefd, exceptfd;
+ if (!ConnectionListToSocketList(nls->hReadConns, &readfd, pending)
+ || !ConnectionListToSocketList(nls->hWriteConns, &writefd, pending)
+ || !ConnectionListToSocketList(nls->hExceptConns, &exceptfd, pending))
+ {
+ ReleaseMutex(hConnectionHeaderMutex);
+ return SOCKET_ERROR;
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+
+ int rc = (pending) ? pending : select(0, &readfd, &writefd, &exceptfd, nls->dwTimeout == INFINITE ? nullptr : &tv);
+
+ WaitForSingleObject(hConnectionHeaderMutex, INFINITE);
+ /* go thru each passed HCONN array and grab its socket handle, then give it to FD_ISSET()
+ to see if an event happened for that socket, if it has it will be returned as TRUE (otherwise not)
+ This happens for read/write/except */
+ NetlibConnection *conn = nullptr;
+ int j;
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hReadConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+
+ if (sslApi.pending(conn->hSsl))
+ nls->hReadStatus[j] = TRUE;
+ nls->hReadStatus[j] = FD_ISSET(conn->s, &readfd);
+ }
+
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hWriteConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+ nls->hWriteStatus[j] = FD_ISSET(conn->s, &writefd);
+ }
+
+ for (j = 0; j < FD_SETSIZE; j++) {
+ conn = (NetlibConnection*)nls->hExceptConns[j];
+ if (conn == nullptr || conn == INVALID_HANDLE_VALUE) break;
+ nls->hExceptStatus[j] = FD_ISSET(conn->s, &exceptfd);
+ }
+ ReleaseMutex(hConnectionHeaderMutex);
+ return rc;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(bool) Netlib_StringToAddress(const char *str, SOCKADDR_INET_M *addr)
+{
+ if (!str) return false;
+
+ int len = sizeof(SOCKADDR_INET_M);
+ return !WSAStringToAddressA((char*)str, AF_INET6, nullptr, (PSOCKADDR)addr, &len);
+}
+
+MIR_APP_DLL(char*) Netlib_AddressToString(sockaddr_in *addr)
+{
+ char saddr[128];
+ DWORD len = sizeof(saddr);
+ if (!WSAAddressToStringA((PSOCKADDR)addr, sizeof(*addr), nullptr, saddr, &len))
+ return mir_strdup(saddr);
+
+ if (addr->sin_family == AF_INET) {
+ char *szIp = inet_ntoa(addr->sin_addr);
+ if (addr->sin_port != 0) {
+ mir_snprintf(saddr, "%s:%d", szIp, addr->sin_port);
+ return mir_strdup(saddr);
+ }
+ return mir_strdup(szIp);
+ }
+ return nullptr;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+MIR_APP_DLL(int) Netlib_GetConnectionInfo(HNETLIBCONN hConnection, NETLIBCONNINFO *connInfo)
+{
+ NetlibConnection *nlc = (NetlibConnection*)hConnection;
+ if (!nlc || !connInfo)
+ return 1;
+
+ sockaddr_in sin = { 0 };
+ int len = sizeof(sin);
+ if (!getsockname(nlc->s, (PSOCKADDR)&sin, &len)) {
+ connInfo->wPort = ntohs(sin.sin_port);
+ connInfo->dwIpv4 = sin.sin_family == AF_INET ? htonl(sin.sin_addr.s_addr) : 0;
+ strncpy_s(connInfo->szIpPort, ptrA(Netlib_AddressToString(&sin)), _TRUNCATE);
+ }
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+inline bool IsAddrGlobal(const IN6_ADDR *a)
+{
+ unsigned char High = a->s6_bytes[0] & 0xf0;
+ return High != 0 && High != 0xf0;
+}
+
+MIR_APP_DLL(NETLIBIPLIST*) Netlib_GetMyIp(bool bGlobalOnly)
+{
+ addrinfo *air = nullptr, *ai, hints = { 0 };
+ const char *szMyHost = "";
+
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_PASSIVE;
+
+ if (GetAddrInfoA(szMyHost, nullptr, &hints, &air))
+ return nullptr;
+
+ unsigned n = 0;
+ for (ai = air; ai; ai = ai->ai_next) {
+ SOCKADDR_INET_M *iaddr = (SOCKADDR_INET_M*)ai->ai_addr;
+ if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->Ipv6.sin6_addr))))
+ ++n;
+ }
+
+ NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4);
+ addr->cbNum = n;
+
+ unsigned i = 0;
+ for (ai = air; ai; ai = ai->ai_next) {
+ sockaddr_in6 *iaddr = (sockaddr_in6*)ai->ai_addr;
+ if (ai->ai_family == AF_INET || (ai->ai_family == AF_INET6 && (!bGlobalOnly || IsAddrGlobal(&iaddr->sin6_addr)))) {
+ char *szIp = Netlib_AddressToString((sockaddr_in*)iaddr);
+ if (szIp)
+ strncpy_s(addr->szIp[i++], szIp, _TRUNCATE);
+ mir_free(szIp);
+ }
+ }
+ FreeAddrInfoA(air);
+ return addr;
+}
+
+static NETLIBIPLIST* GetMyIpv4(void)
+{
+ char hostname[256] = "";
+
+ gethostname(hostname, sizeof(hostname));
+ PHOSTENT he = gethostbyname(hostname);
+
+ unsigned n;
+ for (n = 0; he->h_addr_list[n]; ++n)
+ ;
+
+ NETLIBIPLIST *addr = (NETLIBIPLIST*)mir_calloc(n * 64 + 4);
+ addr->cbNum = n;
+
+ for (unsigned i = 0; i < n; i++)
+ strncpy_s(addr->szIp[i], inet_ntoa(*(PIN_ADDR)he->h_addr_list[i]), _TRUNCATE);
+
+ return addr;
+}
diff --git a/src/mir_app/src/netlibupnp.cpp b/src/mir_app/src/netlib_upnp.cpp
index 4b18050603..dfd409ee1f 100644
--- a/src/mir_app/src/netlibupnp.cpp
+++ b/src/mir_app/src/netlib_upnp.cpp
@@ -1,813 +1,813 @@
-/*
-
-Miranda NG: the free IM client for Microsoft* Windows*
-
-Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
-Copyright (c) 2000-12 Miranda IM project,
-all portions of this codebase are copyrighted to the people
-listed in contributors.txt.
-
-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, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-*/
-
-#include "stdafx.h"
-#include "netlib.h"
-
-static const char search_request_msg[] =
- "M-SEARCH * HTTP/1.1\r\n"
- "HOST: 239.255.255.250:1900\r\n"
- "MAN: \"ssdp:discover\"\r\n"
- "MX: 1\r\n"
- "ST: urn:schemas-upnp-org:service:%s\r\n"
- "\r\n";
-
-static const char xml_get_hdr[] =
- "GET %s HTTP/1.1\r\n"
- "HOST: %s:%u\r\n"
- "ACCEPT-LANGUAGE: *\r\n\r\n";
-
-static const char soap_post_hdr[] =
- "POST %s HTTP/1.1\r\n"
- "HOST: %s:%u\r\n"
- "CONTENT-LENGTH: %u\r\n"
- "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n"
- "SOAPACTION: \"%s#%s\"\r\n\r\n"
- "%s";
-
-static const char soap_post_hdr_m[] =
- "M-POST %s URL HTTP/1.1\r\n"
- "HOST: %s:%u\r\n"
- "CONTENT-LENGTH: %u\r\n"
- "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n"
- "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n"
- "01-SOAPACTION: \"%s#%s\"\r\n\r\n"
- "%s";
-
-static const char search_device[] =
- "<serviceType>%s</serviceType>";
-
-static const char soap_action[] =
- "<?xml version = \"1.0\"?>\r\n"
- "<s:Envelope\r\n"
- " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
- " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
- " <s:Body>\r\n"
- " <u:%s xmlns:u = \"%s\">\r\n"
- "%s"
- " </u:%s>\r\n"
- " </s:Body>\r\n"
- "</s:Envelope>\r\n";
-
-static const char soap_query[] =
- "<s:Envelope\r\n"
- " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
- " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
- " <s:Body>\r\n"
- " <u:QueryStateVariable xmlns:u = \"urn:schemas-upnp-org:control-1-0\">\r\n"
- " <u:varName>%s</u:varName>\r\n"
- " </u:QueryStateVariable>\r\n"
- " </s:Body>\r\n"
- "</s:Envelope>\r\n";
-
-static const char add_port_mapping[] =
- " <NewRemoteHost></NewRemoteHost>\r\n"
- " <NewExternalPort>%i</NewExternalPort>\r\n"
- " <NewProtocol>%s</NewProtocol>\r\n"
- " <NewInternalPort>%i</NewInternalPort>\r\n"
- " <NewInternalClient>%s</NewInternalClient>\r\n"
- " <NewEnabled>1</NewEnabled>\r\n"
- " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n"
- " <NewLeaseDuration>0</NewLeaseDuration>\r\n";
-
-static const char delete_port_mapping[] =
- " <NewRemoteHost></NewRemoteHost>\r\n"
- " <NewExternalPort>%i</NewExternalPort>\r\n"
- " <NewProtocol>%s</NewProtocol>\r\n";
-
-static const char get_port_mapping[] =
- " <NewPortMappingIndex>%i</NewPortMappingIndex>\r\n";
-
-static bool gatewayFound;
-static SOCKADDR_IN locIP;
-static time_t lastDiscTime;
-static int expireTime = 120;
-
-static int retryCount;
-static SOCKET sock = INVALID_SOCKET;
-static char szConnHost[256];
-static unsigned short sConnPort;
-
-static WORD *portList;
-static unsigned numports, numportsAlloc;
-static HANDLE portListMutex;
-
-static char szCtlUrl[256], szDev[256];
-
-typedef enum
-{
- DeviceGetReq,
- ControlAction,
- ControlQuery
-} ReqType;
-
-static bool txtParseParam(char* szData, char* presearch,
- char* start, char* finish, char* param, size_t size)
-{
- char *cp, *cp1;
- size_t len;
-
- *param = 0;
-
- if (presearch != nullptr) {
- cp1 = strstr(szData, presearch);
- if (cp1 == nullptr) return false;
- }
- else
- cp1 = szData;
-
- cp = strstr(cp1, start);
- if (cp == nullptr) return false;
- cp += mir_strlen(start);
- while (*cp == ' ') ++cp;
-
- cp1 = strstr(cp, finish);
- if (cp1 == nullptr) return false;
- while (*(cp1-1) == ' ' && cp1 > cp) --cp1;
-
- len = min((size_t)(cp1 - cp), size-1);
- strncpy(param, cp, len);
- param[len] = 0;
-
- return true;
-}
-
-void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath)
-{
- char *ppath, *phost, *pport;
- int sz;
-
- phost = strstr(szUrl, "://");
- if (phost == nullptr) phost = szUrl;
- else phost += 3;
-
- ppath = strchr(phost, '/');
- if (ppath == nullptr) ppath = phost + mir_strlen(phost);
-
- pport = strchr(phost, ':');
- if (pport == nullptr) pport = ppath;
-
- if (szHost != nullptr) {
- sz = pport - phost + 1;
- if (sz > 256) sz = 256;
- strncpy(szHost, phost, sz);
- szHost[sz - 1] = 0;
- }
-
- if (sPort != nullptr) {
- if (pport < ppath) {
- long prt = atol(pport + 1);
- *sPort = prt != 0 ? (unsigned short)prt : 80;
- }
- else
- *sPort = 80;
- }
-
- if (szPath != nullptr) {
- strncpy(szPath, ppath, 256);
- szPath[255] = 0;
- }
-}
-
-static void LongLog(char* szData)
-{
- Netlib_Logf(nullptr, szData);
-}
-
-static void closeRouterConnection(void)
-{
- if (sock != INVALID_SOCKET) {
- closesocket(sock);
- sock = INVALID_SOCKET;
- }
-}
-
-static void validateSocket(void)
-{
- static const TIMEVAL tv = { 0, 0 };
- fd_set rfd;
- char buf[4];
-
- if (sock == INVALID_SOCKET)
- return;
-
- FD_ZERO(&rfd);
- FD_SET(sock, &rfd);
-
- bool opened = false;
- switch (select(1, &rfd, nullptr, nullptr, &tv)) {
- case 0:
- opened = true;
- break;
-
- case 1:
- opened = recv(sock, buf, 1, MSG_PEEK) > 0;
- break;
- }
-
- if (!opened)
- closeRouterConnection();
-}
-
-static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype)
-{
- // Parse URL
- char szHost[256], szPath[256], szRes[16];
- int sz = 0, res = 0;
- unsigned short sPort;
- bool needClose = false;
-
- const char* szPostHdr = soap_post_hdr;
- char* szData = (char*)mir_alloc(4096);
- char* szReq = nullptr;
-
- parseURL(szUrl, szHost, &sPort, szPath);
-
- if (sPort != sConnPort || _stricmp(szHost, szConnHost))
- closeRouterConnection();
- else
- validateSocket();
-
- while (true) {
- retryCount = 0;
- switch (reqtype) {
- case DeviceGetReq:
- sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort);
- break;
-
- case ControlAction:
- {
- char szData1[1024];
-
- szReq = mir_strdup(szResult);
- sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName);
- sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1);
- }
- break;
-
- case ControlQuery:
- {
- char szData1[1024];
- sz = mir_snprintf(szData1, soap_query, szActionName);
- sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1);
- }
- break;
- }
- szResult[0] = 0;
- {
- static const TIMEVAL tv = { 6, 0 };
- static unsigned ttl = 4;
- static u_long mode = 1;
- fd_set rfd, wfd, efd;
- SOCKADDR_IN enetaddr;
-
-retrycon:
- if (sock == INVALID_SOCKET) {
- sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-
- enetaddr.sin_family = AF_INET;
- enetaddr.sin_port = htons(sPort);
- enetaddr.sin_addr.s_addr = inet_addr(szHost);
-
- // Resolve host name if needed
- if (enetaddr.sin_addr.s_addr == INADDR_NONE) {
- PHOSTENT he = gethostbyname(szHost);
- if (he)
- enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0];
- }
-
- Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort);
-
- FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd);
- FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd);
-
- // Limit the scope of the connection (does not work for
- setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned));
-
- // Put socket into non-blocking mode for timeout on connect
- ioctlsocket(sock, FIONBIO, &mode);
-
- // Connect to the remote host
- if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) {
- int err = WSAGetLastError();
-
- // Socket connection failed
- if (err != WSAEWOULDBLOCK) {
- closeRouterConnection();
- Netlib_Logf(nullptr, "UPnP connect failed %d", err);
- break;
- }
- // Wait for socket to connect
- else if (select(1, &rfd, &wfd, &efd, &tv) != 1) {
- closeRouterConnection();
- Netlib_Logf(nullptr, "UPnP connect timeout");
- break;
- }
- else if (!FD_ISSET(sock, &wfd)) {
- closeRouterConnection();
- Netlib_Logf(nullptr, "UPnP connect failed");
- break;
- }
- }
- strncpy_s(szConnHost, szHost, _TRUNCATE);
- sConnPort = sPort;
- }
-
- if (send(sock, szData, sz, 0) != SOCKET_ERROR) {
- char *hdrend = nullptr;
- int acksz = 0, pktsz = 0;
-
- if (szActionName == nullptr) {
- int len = sizeof(locIP);
- getsockname(sock, (SOCKADDR*)&locIP, &len);
- if (locIP.sin_addr.S_un.S_addr == 0x0100007f) {
- struct hostent *he;
-
- gethostname(szPath, sizeof(szPath));
- he = gethostbyname(szPath);
- if (he != nullptr)
- locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0];
- }
- }
-
- LongLog(szData);
- sz = 0;
- while (true) {
- int bytesRecv;
-
- FD_ZERO(&rfd);
- FD_SET(sock, &rfd);
-
- // Wait for the next packet
- if (select(1, &rfd, nullptr, nullptr, &tv) != 1) {
- closeRouterConnection();
- Netlib_Logf(nullptr, "UPnP recieve timeout");
- break;
- }
-
- //
- bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0);
-
- // Connection closed or aborted, all data received
- if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) {
- closeRouterConnection();
- if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) {
- ++retryCount;
- goto retrycon;
- }
- break;
- }
-
- sz += bytesRecv;
-
- // Insert null terminator to use string functions
- if (sz >= (resSize - 1)) {
- szResult[resSize - 1] = 0;
- break;
- }
- else
- szResult[sz] = 0;
-
- // HTTP header found?
- if (hdrend == nullptr) {
- // Find HTTP header end
- hdrend = strstr(szResult, "\r\n\r\n");
- if (hdrend == nullptr) {
- hdrend = strstr(szResult, "\n\n");
- if (hdrend) hdrend += 2;
- }
-
- else
- hdrend += 4;
-
- if (hdrend != nullptr) {
- // Get packet size if provided
- if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) ||
- txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) {
- // Add size of HTTP header to the packet size to compute full transmission size
- pktsz = atol(ltrimp(szRes)) + (hdrend - szResult);
- }
- // Get encoding type if provided
- else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) {
- if (_stricmp(lrtrimp(szRes), "Chunked") == 0)
- acksz = hdrend - szResult;
- }
- if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) {
- needClose = (_stricmp(lrtrimp(szRes), "close") == 0);
- }
- }
- }
-
- // Content-Length bytes reached, all data received
- if (sz >= pktsz && pktsz != 0) {
- szResult[pktsz] = 0;
- break;
- }
-
- // Chunked encoding processing
- if (sz > acksz && acksz != 0) {
-retry:
- // Parse out chunk size
- char* data = szResult + acksz;
- char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n');
- if (peol1 != nullptr) {
- char *peol2 = strchr(++peol1, '\n');
- if (peol2 != nullptr) {
- // Get chunk size
- int chunkBytes = strtol(peol1, nullptr, 16);
- acksz += chunkBytes;
- peol2++;
-
- memmove(data, peol2, mir_strlen(peol2) + 1);
- sz -= peol2 - data;
-
- // Last chunk, all data received
- if (chunkBytes == 0) break;
- if (sz > acksz) goto retry;
- }
- }
- }
- }
- LongLog(szResult);
- }
- else {
- if (retryCount < 2) {
- closeRouterConnection();
- ++retryCount;
- goto retrycon;
- }
- else
- Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError());
- }
- }
- txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes));
- res = atol(szRes);
- if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr)
- szPostHdr = soap_post_hdr_m;
- else
- break;
- }
-
- if (needClose)
- closeRouterConnection();
-
- mir_free(szData);
- mir_free(szReq);
- return res;
-}
-
-static unsigned getExtIP(void)
-{
- char szExtIP[30];
- char* szData = (char*)mir_alloc(4096); szData[0] = 0;
-
- unsigned extip = 0;
- int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction);
- if (res == 200 && txtParseParam(szData, "<NewExternalIPAddress", ">", "<", szExtIP, sizeof(szExtIP)))
- extip = ntohl(inet_addr(szExtIP));
-
- mir_free(szData);
- return extip;
-}
-
-static bool getUPnPURLs(char* szUrl, size_t sizeUrl)
-{
- char* szData = (char*)mir_alloc(8192);
-
- gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200;
- if (gatewayFound) {
- char szTemp[256], *rpth;
- size_t ctlLen;
-
- txtParseParam(szData, nullptr, "<URLBase>", "</URLBase>", szTemp, sizeof(szTemp));
- strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl));
- szCtlUrl[sizeof(szCtlUrl) - 1] = 0;
-
- mir_snprintf(szTemp, search_device, szDev);
- txtParseParam(szData, szTemp, "<controlURL>", "</controlURL>", szUrl, sizeUrl);
-
- // URL combining per RFC 2396
- if (szUrl[0] != 0) {
- if (strstr(szUrl, "://") != nullptr) // absolute URI
- rpth = szCtlUrl;
- else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path
- {
- rpth = strstr(szCtlUrl, "//");
- if (rpth == nullptr) rpth = szCtlUrl;
- }
- else if (szUrl[0] == '/') // relative URI abs_path
- {
- rpth = strstr(szCtlUrl, "//");
- rpth = rpth ? rpth + 2 : szCtlUrl;
-
- rpth = strchr(rpth, '/');
- if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl);
- }
- else { // relative URI rel_path
- size_t ctlCLen = mir_strlen(szCtlUrl);
- rpth = szCtlUrl + ctlCLen;
- if (ctlCLen != 0 && *(rpth - 1) != '/')
- strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen);
- }
-
- ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl);
- strncpy(rpth, szUrl, ctlLen);
- szCtlUrl[sizeof(szCtlUrl) - 1] = 0;
- }
- else {
- szCtlUrl[0] = 0;
- gatewayFound = false;
- }
- }
- mir_free(szData);
-
- return gatewayFound;
-}
-
-static void discoverUPnP(void)
-{
- char* buf;
- int buflen;
- unsigned i, j, nip = 0;
- unsigned* ips = nullptr;
-
- static const unsigned any = INADDR_ANY;
- static const TIMEVAL tv = { 1, 600000 };
-
- char szUrl[256] = "";
- char hostname[256];
- PHOSTENT he;
- fd_set readfd;
-
- SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-
- SOCKADDR_IN enetaddr;
- enetaddr.sin_family = AF_INET;
- enetaddr.sin_port = htons(1900);
- enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250");
-
- gethostname(hostname, sizeof(hostname));
- he = gethostbyname(hostname);
-
- if (he) {
- while (he->h_addr_list[nip]) ++nip;
-
- ips = (unsigned*)mir_alloc(nip * sizeof(unsigned));
-
- for (j = 0; j < nip; j++)
- ips[j] = *(unsigned*)he->h_addr_list[j];
- }
-
- buf = (char*)mir_alloc(1500);
-
- for (i = 3; --i && szUrl[0] == 0;) {
- for (j = 0; j < nip; j++) {
- if (ips)
- setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned));
-
- buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1");
- sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
- LongLog(buf);
-
- buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1");
- sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
- LongLog(buf);
- }
-
- if (Miranda_IsTerminated()) break;
-
- FD_ZERO(&readfd);
- FD_SET(s, &readfd);
-
- while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) {
- buflen = recv(s, buf, 1500, 0);
- if (buflen != SOCKET_ERROR) {
- buf[buflen] = 0;
- LongLog(buf);
-
- if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) ||
- txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) {
- char age[30];
- char szHostNew[256], szHostExist[256];
-
- lrtrim(szUrl);
-
- parseURL(szUrl, szHostNew, nullptr, nullptr);
- parseURL(szCtlUrl, szHostExist, nullptr, nullptr);
- if (mir_strcmp(szHostNew, szHostExist) == 0) {
- gatewayFound = true;
- break;
- }
-
- txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev));
- txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age));
- expireTime = atoi(lrtrimp(age));
- lrtrim(szDev);
-
- if (getUPnPURLs(szUrl, sizeof(szUrl))) {
- gatewayFound = getExtIP() != 0;
- if (gatewayFound) break;
- }
- }
- }
- FD_ZERO(&readfd);
- FD_SET(s, &readfd);
- }
- }
-
- mir_free(buf);
- mir_free(ips);
- setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned));
- closesocket(s);
-}
-
-static bool findUPnPGateway(void)
-{
- if ((time(0) - lastDiscTime) >= expireTime) {
- WaitForSingleObject(portListMutex, INFINITE);
-
- time_t curTime = time(0);
-
- if ((curTime - lastDiscTime) >= expireTime) {
- gatewayFound = false;
-
- discoverUPnP();
- lastDiscTime = curTime;
-
- Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl);
- }
-
- ReleaseMutex(portListMutex);
- }
-
- return gatewayFound;
-}
-
-bool NetlibUPnPAddPortMapping(WORD intport, char *proto, WORD *extport, DWORD *extip, bool search)
-{
- int res = 0, i = 5;
-
- if (findUPnPGateway()) {
- char* szData = (char*)mir_alloc(4096);
- char szExtIP[30];
-
- *extport = intport - 1;
- *extip = ntohl(locIP.sin_addr.S_un.S_addr);
-
- WaitForSingleObject(portListMutex, INFINITE);
-
- do {
- ++*extport;
- mir_snprintf(szData, 4096, add_port_mapping,
- *extport, proto, intport, inet_ntoa(locIP.sin_addr));
- res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction);
- txtParseParam(szData, nullptr, "<errorCode>", "</errorCode>", szExtIP, sizeof(szExtIP));
-
- } while (search && res == 500 && atol(szExtIP) == 718 && --i);
-
- mir_free(szData);
-
- if (res == 200) {
- unsigned ip = getExtIP();
- if (ip) *extip = ip;
-
- if (numports >= numportsAlloc)
- mir_realloc(portList, sizeof(WORD)*(numportsAlloc += 10));
- portList[numports++] = *extport;
- }
-
- ReleaseMutex(portListMutex);
- }
-
- return res == 200;
-}
-
-void NetlibUPnPDeletePortMapping(WORD extport, char* proto)
-{
- if (extport == 0)
- return;
-
- // findUPnPGateway();
-
- if (gatewayFound) {
- unsigned i;
- char* szData = (char*)mir_alloc(4096);
-
- WaitForSingleObject(portListMutex, INFINITE);
- mir_snprintf(szData, 4096, delete_port_mapping, extport, proto);
- httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction);
-
- for (i = 0; i < numports; i++)
- if (portList[i] == extport && --numports > 0)
- memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(WORD));
-
- mir_free(szData);
- ReleaseMutex(portListMutex);
- }
-}
-
-void NetlibUPnPCleanup(void*)
-{
- // upnp is disabled globally, no need for a cleanup
- if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0)
- return;
-
- {
- int incoming = 0;
- mir_cslock lck(csNetlibUser);
- for (auto &p : netlibUser)
- if (p->user.flags & NUF_INCOMING) {
- incoming = 1;
- break;
- }
-
- if (!incoming)
- return;
- }
-
- if (findUPnPGateway()) {
- char *szData = (char*)alloca(4096);
- char buf[50], lip[50];
- unsigned j = 0, k, num = 100;
-
- strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE);
-
- WaitForSingleObject(portListMutex, INFINITE);
-
- if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 &&
- txtParseParam(szData, "QueryStateVariableResponse", "<return>", "<", buf, sizeof(buf)))
- num = atol(buf);
-
- WORD ports[30];
- for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) {
- mir_snprintf(szData, 4096, get_port_mapping, i);
-
- ReleaseMutex(portListMutex);
- WaitForSingleObject(portListMutex, INFINITE);
-
- if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200)
- break;
-
- if (!txtParseParam(szData, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0)
- continue;
-
- if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0)
- continue;
-
- if (txtParseParam(szData, "<NewExternalPort", ">", "<", buf, sizeof(buf))) {
- WORD mport = (WORD)atol(buf);
-
- if (j >= _countof(ports))
- break;
-
- for (k = 0; k < numports; ++k)
- if (portList[k] == mport)
- break;
-
- if (k >= numports)
- ports[j++] = mport;
- }
- }
-
- ReleaseMutex(portListMutex);
-
- for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++)
- NetlibUPnPDeletePortMapping(ports[i], "TCP");
- }
-}
-
-void NetlibUPnPInit(void)
-{
- numports = 0;
- numportsAlloc = 10;
- portList = (WORD*)mir_alloc(sizeof(WORD)*numportsAlloc);
-
- portListMutex = CreateMutex(nullptr, FALSE, nullptr);
-}
-
-void NetlibUPnPDestroy(void)
-{
- mir_free(portList);
- CloseHandle(portListMutex);
-}
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+static const char search_request_msg[] =
+ "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: 239.255.255.250:1900\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: 1\r\n"
+ "ST: urn:schemas-upnp-org:service:%s\r\n"
+ "\r\n";
+
+static const char xml_get_hdr[] =
+ "GET %s HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "ACCEPT-LANGUAGE: *\r\n\r\n";
+
+static const char soap_post_hdr[] =
+ "POST %s HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "CONTENT-LENGTH: %u\r\n"
+ "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n"
+ "SOAPACTION: \"%s#%s\"\r\n\r\n"
+ "%s";
+
+static const char soap_post_hdr_m[] =
+ "M-POST %s URL HTTP/1.1\r\n"
+ "HOST: %s:%u\r\n"
+ "CONTENT-LENGTH: %u\r\n"
+ "CONTENT-TYPE: text/xml; charset = \"utf-8\"\r\n"
+ "MAN: \"http://schemas.xmlsoap.org/soap/envelope/\"; ns = 01\r\n"
+ "01-SOAPACTION: \"%s#%s\"\r\n\r\n"
+ "%s";
+
+static const char search_device[] =
+ "<serviceType>%s</serviceType>";
+
+static const char soap_action[] =
+ "<?xml version = \"1.0\"?>\r\n"
+ "<s:Envelope\r\n"
+ " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:%s xmlns:u = \"%s\">\r\n"
+ "%s"
+ " </u:%s>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char soap_query[] =
+ "<s:Envelope\r\n"
+ " xmlns:s = \"http://schemas.xmlsoap.org/soap/envelope/\"\r\n"
+ " s:encodingStyle = \"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
+ " <s:Body>\r\n"
+ " <u:QueryStateVariable xmlns:u = \"urn:schemas-upnp-org:control-1-0\">\r\n"
+ " <u:varName>%s</u:varName>\r\n"
+ " </u:QueryStateVariable>\r\n"
+ " </s:Body>\r\n"
+ "</s:Envelope>\r\n";
+
+static const char add_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n"
+ " <NewInternalPort>%i</NewInternalPort>\r\n"
+ " <NewInternalClient>%s</NewInternalClient>\r\n"
+ " <NewEnabled>1</NewEnabled>\r\n"
+ " <NewPortMappingDescription>Miranda</NewPortMappingDescription>\r\n"
+ " <NewLeaseDuration>0</NewLeaseDuration>\r\n";
+
+static const char delete_port_mapping[] =
+ " <NewRemoteHost></NewRemoteHost>\r\n"
+ " <NewExternalPort>%i</NewExternalPort>\r\n"
+ " <NewProtocol>%s</NewProtocol>\r\n";
+
+static const char get_port_mapping[] =
+ " <NewPortMappingIndex>%i</NewPortMappingIndex>\r\n";
+
+static bool gatewayFound;
+static SOCKADDR_IN locIP;
+static time_t lastDiscTime;
+static int expireTime = 120;
+
+static int retryCount;
+static SOCKET sock = INVALID_SOCKET;
+static char szConnHost[256];
+static unsigned short sConnPort;
+
+static WORD *portList;
+static unsigned numports, numportsAlloc;
+static HANDLE portListMutex;
+
+static char szCtlUrl[256], szDev[256];
+
+typedef enum
+{
+ DeviceGetReq,
+ ControlAction,
+ ControlQuery
+} ReqType;
+
+static bool txtParseParam(char* szData, char* presearch,
+ char* start, char* finish, char* param, size_t size)
+{
+ char *cp, *cp1;
+ size_t len;
+
+ *param = 0;
+
+ if (presearch != nullptr) {
+ cp1 = strstr(szData, presearch);
+ if (cp1 == nullptr) return false;
+ }
+ else
+ cp1 = szData;
+
+ cp = strstr(cp1, start);
+ if (cp == nullptr) return false;
+ cp += mir_strlen(start);
+ while (*cp == ' ') ++cp;
+
+ cp1 = strstr(cp, finish);
+ if (cp1 == nullptr) return false;
+ while (*(cp1-1) == ' ' && cp1 > cp) --cp1;
+
+ len = min((size_t)(cp1 - cp), size-1);
+ strncpy(param, cp, len);
+ param[len] = 0;
+
+ return true;
+}
+
+void parseURL(char* szUrl, char* szHost, unsigned short* sPort, char* szPath)
+{
+ char *ppath, *phost, *pport;
+ int sz;
+
+ phost = strstr(szUrl, "://");
+ if (phost == nullptr) phost = szUrl;
+ else phost += 3;
+
+ ppath = strchr(phost, '/');
+ if (ppath == nullptr) ppath = phost + mir_strlen(phost);
+
+ pport = strchr(phost, ':');
+ if (pport == nullptr) pport = ppath;
+
+ if (szHost != nullptr) {
+ sz = pport - phost + 1;
+ if (sz > 256) sz = 256;
+ strncpy(szHost, phost, sz);
+ szHost[sz - 1] = 0;
+ }
+
+ if (sPort != nullptr) {
+ if (pport < ppath) {
+ long prt = atol(pport + 1);
+ *sPort = prt != 0 ? (unsigned short)prt : 80;
+ }
+ else
+ *sPort = 80;
+ }
+
+ if (szPath != nullptr) {
+ strncpy(szPath, ppath, 256);
+ szPath[255] = 0;
+ }
+}
+
+static void LongLog(char* szData)
+{
+ Netlib_Logf(nullptr, szData);
+}
+
+static void closeRouterConnection(void)
+{
+ if (sock != INVALID_SOCKET) {
+ closesocket(sock);
+ sock = INVALID_SOCKET;
+ }
+}
+
+static void validateSocket(void)
+{
+ static const TIMEVAL tv = { 0, 0 };
+ fd_set rfd;
+ char buf[4];
+
+ if (sock == INVALID_SOCKET)
+ return;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ bool opened = false;
+ switch (select(1, &rfd, nullptr, nullptr, &tv)) {
+ case 0:
+ opened = true;
+ break;
+
+ case 1:
+ opened = recv(sock, buf, 1, MSG_PEEK) > 0;
+ break;
+ }
+
+ if (!opened)
+ closeRouterConnection();
+}
+
+static int httpTransact(char* szUrl, char* szResult, int resSize, char* szActionName, ReqType reqtype)
+{
+ // Parse URL
+ char szHost[256], szPath[256], szRes[16];
+ int sz = 0, res = 0;
+ unsigned short sPort;
+ bool needClose = false;
+
+ const char* szPostHdr = soap_post_hdr;
+ char* szData = (char*)mir_alloc(4096);
+ char* szReq = nullptr;
+
+ parseURL(szUrl, szHost, &sPort, szPath);
+
+ if (sPort != sConnPort || _stricmp(szHost, szConnHost))
+ closeRouterConnection();
+ else
+ validateSocket();
+
+ while (true) {
+ retryCount = 0;
+ switch (reqtype) {
+ case DeviceGetReq:
+ sz = mir_snprintf(szData, 4096, xml_get_hdr, szPath, szHost, sPort);
+ break;
+
+ case ControlAction:
+ {
+ char szData1[1024];
+
+ szReq = mir_strdup(szResult);
+ sz = mir_snprintf(szData1, soap_action, szActionName, szDev, szReq, szActionName);
+ sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, szDev, szActionName, szData1);
+ }
+ break;
+
+ case ControlQuery:
+ {
+ char szData1[1024];
+ sz = mir_snprintf(szData1, soap_query, szActionName);
+ sz = mir_snprintf(szData, 4096, szPostHdr, szPath, szHost, sPort, sz, "urn:schemas-upnp-org:control-1-0", "QueryStateVariable", szData1);
+ }
+ break;
+ }
+ szResult[0] = 0;
+ {
+ static const TIMEVAL tv = { 6, 0 };
+ static unsigned ttl = 4;
+ static u_long mode = 1;
+ fd_set rfd, wfd, efd;
+ SOCKADDR_IN enetaddr;
+
+retrycon:
+ if (sock == INVALID_SOCKET) {
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ enetaddr.sin_family = AF_INET;
+ enetaddr.sin_port = htons(sPort);
+ enetaddr.sin_addr.s_addr = inet_addr(szHost);
+
+ // Resolve host name if needed
+ if (enetaddr.sin_addr.s_addr == INADDR_NONE) {
+ PHOSTENT he = gethostbyname(szHost);
+ if (he)
+ enetaddr.sin_addr.s_addr = *(unsigned*)he->h_addr_list[0];
+ }
+
+ Netlib_Logf(nullptr, "UPnP HTTP connection Host: %s Port: %u", szHost, sPort);
+
+ FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd);
+ FD_SET(sock, &rfd); FD_SET(sock, &wfd); FD_SET(sock, &efd);
+
+ // Limit the scope of the connection (does not work for
+ setsockopt(sock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(unsigned));
+
+ // Put socket into non-blocking mode for timeout on connect
+ ioctlsocket(sock, FIONBIO, &mode);
+
+ // Connect to the remote host
+ if (connect(sock, (SOCKADDR*)&enetaddr, sizeof(enetaddr)) == SOCKET_ERROR) {
+ int err = WSAGetLastError();
+
+ // Socket connection failed
+ if (err != WSAEWOULDBLOCK) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect failed %d", err);
+ break;
+ }
+ // Wait for socket to connect
+ else if (select(1, &rfd, &wfd, &efd, &tv) != 1) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect timeout");
+ break;
+ }
+ else if (!FD_ISSET(sock, &wfd)) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP connect failed");
+ break;
+ }
+ }
+ strncpy_s(szConnHost, szHost, _TRUNCATE);
+ sConnPort = sPort;
+ }
+
+ if (send(sock, szData, sz, 0) != SOCKET_ERROR) {
+ char *hdrend = nullptr;
+ int acksz = 0, pktsz = 0;
+
+ if (szActionName == nullptr) {
+ int len = sizeof(locIP);
+ getsockname(sock, (SOCKADDR*)&locIP, &len);
+ if (locIP.sin_addr.S_un.S_addr == 0x0100007f) {
+ struct hostent *he;
+
+ gethostname(szPath, sizeof(szPath));
+ he = gethostbyname(szPath);
+ if (he != nullptr)
+ locIP.sin_addr.S_un.S_addr = *(PDWORD)he->h_addr_list[0];
+ }
+ }
+
+ LongLog(szData);
+ sz = 0;
+ while (true) {
+ int bytesRecv;
+
+ FD_ZERO(&rfd);
+ FD_SET(sock, &rfd);
+
+ // Wait for the next packet
+ if (select(1, &rfd, nullptr, nullptr, &tv) != 1) {
+ closeRouterConnection();
+ Netlib_Logf(nullptr, "UPnP recieve timeout");
+ break;
+ }
+
+ //
+ bytesRecv = recv(sock, &szResult[sz], resSize - sz, 0);
+
+ // Connection closed or aborted, all data received
+ if (bytesRecv == 0 || bytesRecv == SOCKET_ERROR) {
+ closeRouterConnection();
+ if ((bytesRecv == SOCKET_ERROR || sz == 0) && retryCount < 2) {
+ ++retryCount;
+ goto retrycon;
+ }
+ break;
+ }
+
+ sz += bytesRecv;
+
+ // Insert null terminator to use string functions
+ if (sz >= (resSize - 1)) {
+ szResult[resSize - 1] = 0;
+ break;
+ }
+ else
+ szResult[sz] = 0;
+
+ // HTTP header found?
+ if (hdrend == nullptr) {
+ // Find HTTP header end
+ hdrend = strstr(szResult, "\r\n\r\n");
+ if (hdrend == nullptr) {
+ hdrend = strstr(szResult, "\n\n");
+ if (hdrend) hdrend += 2;
+ }
+
+ else
+ hdrend += 4;
+
+ if (hdrend != nullptr) {
+ // Get packet size if provided
+ if (txtParseParam(szResult, nullptr, "Content-Length:", "\n", szRes, sizeof(szRes)) ||
+ txtParseParam(szResult, nullptr, "CONTENT-LENGTH:", "\n", szRes, sizeof(szRes))) {
+ // Add size of HTTP header to the packet size to compute full transmission size
+ pktsz = atol(ltrimp(szRes)) + (hdrend - szResult);
+ }
+ // Get encoding type if provided
+ else if (txtParseParam(szResult, nullptr, "Transfer-Encoding:", "\n", szRes, sizeof(szRes))) {
+ if (_stricmp(lrtrimp(szRes), "Chunked") == 0)
+ acksz = hdrend - szResult;
+ }
+ if (txtParseParam(szResult, nullptr, "Connection:", "\n", szRes, sizeof(szRes))) {
+ needClose = (_stricmp(lrtrimp(szRes), "close") == 0);
+ }
+ }
+ }
+
+ // Content-Length bytes reached, all data received
+ if (sz >= pktsz && pktsz != 0) {
+ szResult[pktsz] = 0;
+ break;
+ }
+
+ // Chunked encoding processing
+ if (sz > acksz && acksz != 0) {
+retry:
+ // Parse out chunk size
+ char* data = szResult + acksz;
+ char* peol1 = data == hdrend ? data - 1 : strchr(data, '\n');
+ if (peol1 != nullptr) {
+ char *peol2 = strchr(++peol1, '\n');
+ if (peol2 != nullptr) {
+ // Get chunk size
+ int chunkBytes = strtol(peol1, nullptr, 16);
+ acksz += chunkBytes;
+ peol2++;
+
+ memmove(data, peol2, mir_strlen(peol2) + 1);
+ sz -= peol2 - data;
+
+ // Last chunk, all data received
+ if (chunkBytes == 0) break;
+ if (sz > acksz) goto retry;
+ }
+ }
+ }
+ }
+ LongLog(szResult);
+ }
+ else {
+ if (retryCount < 2) {
+ closeRouterConnection();
+ ++retryCount;
+ goto retrycon;
+ }
+ else
+ Netlib_Logf(nullptr, "UPnP send failed %d", WSAGetLastError());
+ }
+ }
+ txtParseParam(szResult, "HTTP", " ", " ", szRes, sizeof(szRes));
+ res = atol(szRes);
+ if (szActionName != nullptr && res == 405 && szPostHdr == soap_post_hdr)
+ szPostHdr = soap_post_hdr_m;
+ else
+ break;
+ }
+
+ if (needClose)
+ closeRouterConnection();
+
+ mir_free(szData);
+ mir_free(szReq);
+ return res;
+}
+
+static unsigned getExtIP(void)
+{
+ char szExtIP[30];
+ char* szData = (char*)mir_alloc(4096); szData[0] = 0;
+
+ unsigned extip = 0;
+ int res = httpTransact(szCtlUrl, szData, 4096, "GetExternalIPAddress", ControlAction);
+ if (res == 200 && txtParseParam(szData, "<NewExternalIPAddress", ">", "<", szExtIP, sizeof(szExtIP)))
+ extip = ntohl(inet_addr(szExtIP));
+
+ mir_free(szData);
+ return extip;
+}
+
+static bool getUPnPURLs(char* szUrl, size_t sizeUrl)
+{
+ char* szData = (char*)mir_alloc(8192);
+
+ gatewayFound = httpTransact(szUrl, szData, 8192, nullptr, DeviceGetReq) == 200;
+ if (gatewayFound) {
+ char szTemp[256], *rpth;
+ size_t ctlLen;
+
+ txtParseParam(szData, nullptr, "<URLBase>", "</URLBase>", szTemp, sizeof(szTemp));
+ strncpy(szCtlUrl, szTemp[0] ? szTemp : szUrl, sizeof(szCtlUrl));
+ szCtlUrl[sizeof(szCtlUrl) - 1] = 0;
+
+ mir_snprintf(szTemp, search_device, szDev);
+ txtParseParam(szData, szTemp, "<controlURL>", "</controlURL>", szUrl, sizeUrl);
+
+ // URL combining per RFC 2396
+ if (szUrl[0] != 0) {
+ if (strstr(szUrl, "://") != nullptr) // absolute URI
+ rpth = szCtlUrl;
+ else if (strncmp(szUrl, "//", 2) == 0) // relative URI net_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ if (rpth == nullptr) rpth = szCtlUrl;
+ }
+ else if (szUrl[0] == '/') // relative URI abs_path
+ {
+ rpth = strstr(szCtlUrl, "//");
+ rpth = rpth ? rpth + 2 : szCtlUrl;
+
+ rpth = strchr(rpth, '/');
+ if (rpth == nullptr) rpth = szCtlUrl + mir_strlen(szCtlUrl);
+ }
+ else { // relative URI rel_path
+ size_t ctlCLen = mir_strlen(szCtlUrl);
+ rpth = szCtlUrl + ctlCLen;
+ if (ctlCLen != 0 && *(rpth - 1) != '/')
+ strncpy(rpth++, "/", sizeof(szCtlUrl) - ctlCLen);
+ }
+
+ ctlLen = sizeof(szCtlUrl) - (rpth - szCtlUrl);
+ strncpy(rpth, szUrl, ctlLen);
+ szCtlUrl[sizeof(szCtlUrl) - 1] = 0;
+ }
+ else {
+ szCtlUrl[0] = 0;
+ gatewayFound = false;
+ }
+ }
+ mir_free(szData);
+
+ return gatewayFound;
+}
+
+static void discoverUPnP(void)
+{
+ char* buf;
+ int buflen;
+ unsigned i, j, nip = 0;
+ unsigned* ips = nullptr;
+
+ static const unsigned any = INADDR_ANY;
+ static const TIMEVAL tv = { 1, 600000 };
+
+ char szUrl[256] = "";
+ char hostname[256];
+ PHOSTENT he;
+ fd_set readfd;
+
+ SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ SOCKADDR_IN enetaddr;
+ enetaddr.sin_family = AF_INET;
+ enetaddr.sin_port = htons(1900);
+ enetaddr.sin_addr.s_addr = inet_addr("239.255.255.250");
+
+ gethostname(hostname, sizeof(hostname));
+ he = gethostbyname(hostname);
+
+ if (he) {
+ while (he->h_addr_list[nip]) ++nip;
+
+ ips = (unsigned*)mir_alloc(nip * sizeof(unsigned));
+
+ for (j = 0; j < nip; j++)
+ ips[j] = *(unsigned*)he->h_addr_list[j];
+ }
+
+ buf = (char*)mir_alloc(1500);
+
+ for (i = 3; --i && szUrl[0] == 0;) {
+ for (j = 0; j < nip; j++) {
+ if (ips)
+ setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&ips[j], sizeof(unsigned));
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANIPConnection:1");
+ sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+
+ buflen = mir_snprintf(buf, 1500, search_request_msg, "WANPPPConnection:1");
+ sendto(s, buf, buflen, 0, (SOCKADDR*)&enetaddr, sizeof(enetaddr));
+ LongLog(buf);
+ }
+
+ if (Miranda_IsTerminated()) break;
+
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+
+ while (select(1, &readfd, nullptr, nullptr, &tv) >= 1) {
+ buflen = recv(s, buf, 1500, 0);
+ if (buflen != SOCKET_ERROR) {
+ buf[buflen] = 0;
+ LongLog(buf);
+
+ if (txtParseParam(buf, nullptr, "LOCATION:", "\n", szUrl, sizeof(szUrl)) ||
+ txtParseParam(buf, nullptr, "Location:", "\n", szUrl, sizeof(szUrl))) {
+ char age[30];
+ char szHostNew[256], szHostExist[256];
+
+ lrtrim(szUrl);
+
+ parseURL(szUrl, szHostNew, nullptr, nullptr);
+ parseURL(szCtlUrl, szHostExist, nullptr, nullptr);
+ if (mir_strcmp(szHostNew, szHostExist) == 0) {
+ gatewayFound = true;
+ break;
+ }
+
+ txtParseParam(buf, nullptr, "ST:", "\n", szDev, sizeof(szDev));
+ txtParseParam(buf, "max-age", " = ", "\n", age, sizeof(age));
+ expireTime = atoi(lrtrimp(age));
+ lrtrim(szDev);
+
+ if (getUPnPURLs(szUrl, sizeof(szUrl))) {
+ gatewayFound = getExtIP() != 0;
+ if (gatewayFound) break;
+ }
+ }
+ }
+ FD_ZERO(&readfd);
+ FD_SET(s, &readfd);
+ }
+ }
+
+ mir_free(buf);
+ mir_free(ips);
+ setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *)&any, sizeof(unsigned));
+ closesocket(s);
+}
+
+static bool findUPnPGateway(void)
+{
+ if ((time(0) - lastDiscTime) >= expireTime) {
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ time_t curTime = time(0);
+
+ if ((curTime - lastDiscTime) >= expireTime) {
+ gatewayFound = false;
+
+ discoverUPnP();
+ lastDiscTime = curTime;
+
+ Netlib_Logf(nullptr, "UPnP Gateway detected %d, Control URL: %s", gatewayFound, szCtlUrl);
+ }
+
+ ReleaseMutex(portListMutex);
+ }
+
+ return gatewayFound;
+}
+
+bool NetlibUPnPAddPortMapping(WORD intport, char *proto, WORD *extport, DWORD *extip, bool search)
+{
+ int res = 0, i = 5;
+
+ if (findUPnPGateway()) {
+ char* szData = (char*)mir_alloc(4096);
+ char szExtIP[30];
+
+ *extport = intport - 1;
+ *extip = ntohl(locIP.sin_addr.S_un.S_addr);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ do {
+ ++*extport;
+ mir_snprintf(szData, 4096, add_port_mapping,
+ *extport, proto, intport, inet_ntoa(locIP.sin_addr));
+ res = httpTransact(szCtlUrl, szData, 4096, "AddPortMapping", ControlAction);
+ txtParseParam(szData, nullptr, "<errorCode>", "</errorCode>", szExtIP, sizeof(szExtIP));
+
+ } while (search && res == 500 && atol(szExtIP) == 718 && --i);
+
+ mir_free(szData);
+
+ if (res == 200) {
+ unsigned ip = getExtIP();
+ if (ip) *extip = ip;
+
+ if (numports >= numportsAlloc)
+ mir_realloc(portList, sizeof(WORD)*(numportsAlloc += 10));
+ portList[numports++] = *extport;
+ }
+
+ ReleaseMutex(portListMutex);
+ }
+
+ return res == 200;
+}
+
+void NetlibUPnPDeletePortMapping(WORD extport, char* proto)
+{
+ if (extport == 0)
+ return;
+
+ // findUPnPGateway();
+
+ if (gatewayFound) {
+ unsigned i;
+ char* szData = (char*)mir_alloc(4096);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+ mir_snprintf(szData, 4096, delete_port_mapping, extport, proto);
+ httpTransact(szCtlUrl, szData, 4096, "DeletePortMapping", ControlAction);
+
+ for (i = 0; i < numports; i++)
+ if (portList[i] == extport && --numports > 0)
+ memmove(&portList[i], &portList[i + 1], (numports - i) * sizeof(WORD));
+
+ mir_free(szData);
+ ReleaseMutex(portListMutex);
+ }
+}
+
+void NetlibUPnPCleanup(void*)
+{
+ // upnp is disabled globally, no need for a cleanup
+ if (db_get_b(0, "Netlib", "NLEnableUPnP", 1) == 0)
+ return;
+
+ {
+ int incoming = 0;
+ mir_cslock lck(csNetlibUser);
+ for (auto &p : netlibUser)
+ if (p->user.flags & NUF_INCOMING) {
+ incoming = 1;
+ break;
+ }
+
+ if (!incoming)
+ return;
+ }
+
+ if (findUPnPGateway()) {
+ char *szData = (char*)alloca(4096);
+ char buf[50], lip[50];
+ unsigned j = 0, k, num = 100;
+
+ strncpy_s(lip, inet_ntoa(locIP.sin_addr), _TRUNCATE);
+
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "PortMappingNumberOfEntries", ControlQuery) == 200 &&
+ txtParseParam(szData, "QueryStateVariableResponse", "<return>", "<", buf, sizeof(buf)))
+ num = atol(buf);
+
+ WORD ports[30];
+ for (unsigned i = 0; i < num && !Miranda_IsTerminated(); i++) {
+ mir_snprintf(szData, 4096, get_port_mapping, i);
+
+ ReleaseMutex(portListMutex);
+ WaitForSingleObject(portListMutex, INFINITE);
+
+ if (httpTransact(szCtlUrl, szData, 4096, "GetGenericPortMappingEntry", ControlAction) != 200)
+ break;
+
+ if (!txtParseParam(szData, "<NewPortMappingDescription", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, "Miranda") != 0)
+ continue;
+
+ if (!txtParseParam(szData, "<NewInternalClient", ">", "<", buf, sizeof(buf)) || mir_strcmp(buf, lip) != 0)
+ continue;
+
+ if (txtParseParam(szData, "<NewExternalPort", ">", "<", buf, sizeof(buf))) {
+ WORD mport = (WORD)atol(buf);
+
+ if (j >= _countof(ports))
+ break;
+
+ for (k = 0; k < numports; ++k)
+ if (portList[k] == mport)
+ break;
+
+ if (k >= numports)
+ ports[j++] = mport;
+ }
+ }
+
+ ReleaseMutex(portListMutex);
+
+ for (unsigned i = 0; i < j && !Miranda_IsTerminated(); i++)
+ NetlibUPnPDeletePortMapping(ports[i], "TCP");
+ }
+}
+
+void NetlibUPnPInit(void)
+{
+ numports = 0;
+ numportsAlloc = 10;
+ portList = (WORD*)mir_alloc(sizeof(WORD)*numportsAlloc);
+
+ portListMutex = CreateMutex(nullptr, FALSE, nullptr);
+}
+
+void NetlibUPnPDestroy(void)
+{
+ mir_free(portList);
+ CloseHandle(portListMutex);
+}
diff --git a/src/mir_app/src/netlib_websocket.cpp b/src/mir_app/src/netlib_websocket.cpp
new file mode 100644
index 0000000000..64b5fda069
--- /dev/null
+++ b/src/mir_app/src/netlib_websocket.cpp
@@ -0,0 +1,185 @@
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org),
+Copyright (c) 2000-12 Miranda IM project,
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+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, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+#include "netlib.h"
+
+#include "../../libs/zlib/src/zlib.h"
+
+MIR_APP_DLL(HNETLIBCONN) WebSocket_Connect(HNETLIBUSER nlu, const char *szHost)
+{
+ CMStringA tmpHost(szHost);
+
+ // connect to the gateway server
+ if (!mir_strncmp(tmpHost, "wss://", 6))
+ tmpHost.Delete(0, 6);
+
+ NETLIBOPENCONNECTION conn = {};
+ conn.flags = NLOCF_V2 | NLOCF_SSL;
+ conn.timeout = 5;
+
+ int pos = tmpHost.Find(':');
+ if (pos != -1) {
+ conn.wPort = atoi(tmpHost.GetBuffer() + pos + 1);
+ tmpHost.Truncate(pos);
+ }
+ else conn.wPort = 443;
+
+ CMStringA args;
+ if ((pos = tmpHost.Find('/')) != -1) {
+ args = tmpHost.Mid(pos);
+ tmpHost.Truncate(pos);
+ }
+
+ conn.szHost = tmpHost;
+
+ HNETLIBCONN res = Netlib_OpenConnection(nlu, &conn);
+ if (res == nullptr) {
+ Netlib_Logf(nlu, "WebSocket connection failed to connect to %s:%d, exiting", tmpHost.c_str(), conn.wPort);
+ return false;
+ }
+
+ CMStringA szBuf;
+ szBuf.AppendFormat("GET https://%s%s HTTP/1.1\r\n", tmpHost.c_str(), args.c_str());
+ szBuf.AppendFormat("Host: %s\r\n", tmpHost.c_str());
+ szBuf.AppendFormat("Upgrade: websocket\r\n");
+ szBuf.AppendFormat("Pragma: no-cache\r\n");
+ szBuf.AppendFormat("Cache-Control: no-cache\r\n");
+ szBuf.AppendFormat("Connection: Upgrade\r\n");
+ szBuf.AppendFormat("Sec-WebSocket-Key: KFShSwLlp4E6C7JZc5h4sg==\r\n");
+ szBuf.AppendFormat("Sec-WebSocket-Version: 13\r\n");
+ szBuf.AppendFormat("Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n");
+ szBuf.AppendFormat("\r\n");
+ if (Netlib_Send(res, szBuf, szBuf.GetLength(), MSG_DUMPASTEXT) == SOCKET_ERROR) {
+ Netlib_Logf(nlu, "Error establishing WebSocket connection to %s:%d, send failed", tmpHost.c_str(), conn.wPort);
+ Netlib_CloseHandle(res);
+ return nullptr;
+ }
+
+ char buf[1024];
+ int bufSize = Netlib_Recv(res, buf, _countof(buf), MSG_DUMPASTEXT);
+ if (bufSize <= 0) {
+ Netlib_Logf(nlu, "Error establishing WebSocket connection to %s:%d, read failed", tmpHost.c_str(), conn.wPort);
+ Netlib_CloseHandle(res);
+ return nullptr;
+ }
+
+ int status = 0;
+ if (sscanf(buf, "HTTP/1.1 %d", &status) != 1 || status != 101) {
+ Netlib_Logf(nlu, "Error establishing WebSocket connection to %s:%d, status %d", tmpHost.c_str(), conn.wPort, status);
+ Netlib_CloseHandle(res);
+ return nullptr;
+ }
+
+ return res;
+}
+
+MIR_APP_DLL(bool) WebSocket_InitHeader(WSHeader &hdr, const void *pData, size_t bufSize)
+{
+ if (bufSize < 2)
+ return false;
+
+ auto *buf = (const BYTE *)pData;
+ hdr.bIsFinal = (buf[0] & 0x80) != 0;
+ hdr.bIsMasked = (buf[1] & 0x80) != 0;
+ hdr.opCode = buf[0] & 0x0F;
+ hdr.firstByte = buf[1] & 0x7F;
+ hdr.headerSize = 2 + (hdr.firstByte == 0x7E ? 2 : 0) + (hdr.firstByte == 0x7F ? 8 : 0) + (hdr.bIsMasked ? 4 : 0);
+ if (bufSize < hdr.headerSize)
+ return false;
+
+ uint64_t tmpSize = 0;
+ switch (hdr.firstByte) {
+ case 0x7F:
+ tmpSize += ((uint64_t)buf[2]) << 56;
+ tmpSize += ((uint64_t)buf[3]) << 48;
+ tmpSize += ((uint64_t)buf[4]) << 40;
+ tmpSize += ((uint64_t)buf[5]) << 32;
+ tmpSize += ((uint64_t)buf[6]) << 24;
+ tmpSize += ((uint64_t)buf[7]) << 16;
+ tmpSize += ((uint64_t)buf[8]) << 8;
+ tmpSize += ((uint64_t)buf[9]);
+ break;
+
+ case 0x7E:
+ tmpSize += ((uint64_t)buf[2]) << 8;
+ tmpSize += ((uint64_t)buf[3]);
+ break;
+
+ default:
+ tmpSize = hdr.firstByte;
+ }
+ hdr.payloadSize = tmpSize;
+ return true;
+}
+
+MIR_APP_DLL(void) WebSocket_Send(HNETLIBCONN nlc, const void *pData, size_t strLen)
+{
+ BYTE header[20];
+ int opCode = 1;
+ size_t datalen;
+
+ header[0] = 0x80 + (opCode & 0x7F);
+ if (strLen < 126) {
+ header[1] = (strLen & 0xFF);
+ datalen = 2;
+ }
+ else if (strLen < 65536) {
+ header[1] = 0x7E;
+ header[2] = (strLen >> 8) & 0xFF;
+ header[3] = strLen & 0xFF;
+ datalen = 4;
+ }
+ else {
+ header[1] = 0x7F;
+ header[2] = (strLen >> 56) & 0xff;
+ header[3] = (strLen >> 48) & 0xff;
+ header[4] = (strLen >> 40) & 0xff;
+ header[5] = (strLen >> 32) & 0xff;
+ header[6] = (strLen >> 24) & 0xff;
+ header[7] = (strLen >> 16) & 0xff;
+ header[8] = (strLen >> 8) & 0xff;
+ header[9] = strLen & 0xff;
+ datalen = 10;
+ }
+
+ union {
+ uint32_t dwMask;
+ uint8_t arMask[4];
+ };
+
+ dwMask = crc32(rand(), (uint8_t*)pData, (unsigned)strLen);
+ memcpy(header + datalen, arMask, _countof(arMask));
+ datalen += _countof(arMask);
+ header[1] |= 0x80;
+
+ ptrA sendBuf((char*)mir_alloc(strLen + datalen));
+ memcpy(sendBuf, header, datalen);
+ if (strLen) {
+ memcpy(sendBuf.get() + datalen, pData, strLen);
+ for (size_t i = 0; i < strLen; i++)
+ sendBuf[i + datalen] ^= arMask[i & 3];
+ }
+ Netlib_Send(nlc, sendBuf, int(strLen + datalen), 0);
+}