diff options
author | George Hazan <ghazan@miranda.im> | 2019-10-08 18:50:08 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-10-08 18:50:08 +0300 |
commit | bb3b89fd5d857d396050d02bc185d765760de6ff (patch) | |
tree | fc5765cbde415fedb0d3faadc9b64e32fd901a14 /src | |
parent | 8d0500df234fc5b59ea7687bd5f55f19d7b8e28c (diff) |
WebSocket related code moved to the core
Diffstat (limited to 'src')
-rw-r--r-- | src/mir_app/src/mir_app.def | 3 | ||||
-rw-r--r-- | src/mir_app/src/mir_app64.def | 3 | ||||
-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.cpp | 185 |
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, ¶m);
- 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, ¶m); + 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); +} |