From 94667140aeb3886d22e4c1301423fe99aaf3fba4 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Thu, 4 Jan 2024 13:38:02 +0300 Subject: Netlib: pascal code is completely isolated from C++ core using helpers --- include/delphi/m_netlib.inc | 8 +- include/m_netlib.h | 4 +- libs/win32/mir_app.lib | Bin 277222 -> 278210 bytes libs/win64/mir_app.lib | Bin 276512 -> 277538 bytes plugins/Utils.pas/mirutils.pas | 55 ++-- plugins/Watrack/myshows/i_cookies.inc | 51 +-- plugins/Watrack/myshows/i_myshows_api.inc | 1 - protocols/Discord/src/gateway.cpp | 7 +- protocols/Discord/src/server.cpp | 13 +- protocols/ICQ-WIM/src/mra.cpp | 13 +- protocols/Tox/src/http_request.h | 1 - src/mir_app/src/mir_app.def | 7 + src/mir_app/src/mir_app64.def | 7 + src/mir_app/src/netlib.h | 1 - src/mir_app/src/netlib_http.cpp | 525 ++++++++++++++++-------------- 15 files changed, 349 insertions(+), 344 deletions(-) diff --git a/include/delphi/m_netlib.inc b/include/delphi/m_netlib.inc index 253c836a4d..d5c553817e 100644 --- a/include/delphi/m_netlib.inc +++ b/include/delphi/m_netlib.inc @@ -200,7 +200,7 @@ function Netlib_CloseHandle(pHandle:THANDLE) : int; stdcall; external AppDll; } -function Netlib_FreeHttpRequest(param:PNETLIBHTTPREQUEST) : bytebool; stdcall; external AppDll; +function Netlib_FreeHttpRequest(param:THANDLE) : bytebool; stdcall; external AppDll; { wParam : HANDLE @@ -239,7 +239,11 @@ function Netlib_FreeHttpRequest(param:PNETLIBHTTPREQUEST) : bytebool; stdcall; e Errors returned by the aforementioned internally used functions } -function Netlib_HttpTransaction(nlu:THANDLE; param:PNETLIBHTTPREQUEST) : PNETLIBHTTPREQUEST; stdcall; external AppDll; +function Netlib_HttpTransaction(nlu:THANDLE; param:PNETLIBHTTPREQUEST) : THANDLE; stdcall; external AppDll; + +function Netlib_HttpResult(http:THANDLE) : int; stdcall; external AppDll; +function Netlib_HttpBuffer(http:THANDLE; var cbLen:int) : PAnsiChar; stdcall; external AppDll; +function Netlib_HttpCookies(http:THANDLE) : PAnsiChar; stdcall; external AppDll; { Affect : Send data over an open connection see notes diff --git a/include/m_netlib.h b/include/m_netlib.h index 149b025f8e..0f8cdc2015 100644 --- a/include/m_netlib.h +++ b/include/m_netlib.h @@ -434,7 +434,7 @@ EXTERN_C MIR_APP_DLL(char*) Netlib_GetHeader(const NETLIBHTTPREQUEST *pRec, cons #define NLHRF_DUMPASTEXT 0x00080000 // dump posted and reply data as text. Headers are always dumped as text. #define NLHRF_NODUMPSEND 0x00100000 // do not dump sent message. -struct NETLIBHTTPREQUEST +struct MIR_APP_EXPORT NETLIBHTTPREQUEST { int requestType; // a REQUEST_ uint32_t flags; @@ -448,6 +448,8 @@ struct NETLIBHTTPREQUEST HNETLIBCONN nlc; int timeout; + CMStringA GetCookies() const; + __forceinline const char *operator[](const char *pszName) { return Netlib_GetHeader(this, pszName); } diff --git a/libs/win32/mir_app.lib b/libs/win32/mir_app.lib index 90c5625b72..0adef99d45 100644 Binary files a/libs/win32/mir_app.lib and b/libs/win32/mir_app.lib differ diff --git a/libs/win64/mir_app.lib b/libs/win64/mir_app.lib index d8752d5f06..5ed62c15f7 100644 Binary files a/libs/win64/mir_app.lib and b/libs/win64/mir_app.lib differ diff --git a/plugins/Utils.pas/mirutils.pas b/plugins/Utils.pas/mirutils.pas index c6066c8ee9..fb07550f08 100644 --- a/plugins/Utils.pas/mirutils.pas +++ b/plugins/Utils.pas/mirutils.pas @@ -404,7 +404,9 @@ function SendRequest(url:PAnsiChar;rtype:int;args:PAnsiChar=nil;hNetLib:THANDLE= var nlu:TNETLIBUSER; req :TNETLIBHTTPREQUEST; - resp:PNETLIBHTTPREQUEST; + resp:THANDLE; + bufLen:int; + pBuf:PAnsiChar; hTmpNetLib:THANDLE; nlh:array [0..1] of TNETLIBHTTPHEADER; buf:array [0..31] of AnsiChar; @@ -438,15 +440,16 @@ begin hTmpNetLib:=hNetLib; resp:=Netlib_HttpTransaction(hTmpNetLib,@req); - if resp<>nil then + if resp<>0 then begin - if resp^.resultCode=200 then + if Netlib_HttpResult(resp)=200 then begin - StrDup(result,resp.pData,resp.dataLength); + pBuf := Netlib_HttpBuffer(resp,bufLen); + StrDup(result,pBuf,bufLen); end else begin - result:=PAnsiChar(int_ptr(resp^.resultCode and $0FFF)); + result:=PAnsiChar(int_ptr(Netlib_HttpResult(resp) and $0FFF)); end; Netlib_FreeHttpRequest(resp); end; @@ -464,9 +467,10 @@ function GetFile(url:PAnsiChar; save_file:PAnsiChar; hNetLib:THANDLE=0; recurse_ var nlu:TNETLIBUSER; req :TNETLIBHTTPREQUEST; - resp:PNETLIBHTTPREQUEST; + resp:THANDLE; hSaveFile:THANDLE; - i:integer; + retCode, bufLen:integer; + pBuf:PAnsiChar; begin result:=false; if recurse_count>MAX_REDIRECT_RECURSE then @@ -477,8 +481,7 @@ begin FillChar(req,SizeOf(req),0); req.requestType:=REQUEST_GET; req.szUrl :=url; - req.flags :=NLHRF_NODUMP; - + req.flags :=NLHRF_NODUMP or NLHRF_REDIRECT; FillChar(nlu,SizeOf(nlu),0); if hNetLib=0 then @@ -489,29 +492,19 @@ begin end; resp:=Netlib_HttpTransaction(hNetLib,@req); - if resp<>nil then + if resp<>0 then begin - if resp^.resultCode=200 then + retCode:=Netlib_HttpResult(resp); + if retCode=200 then begin hSaveFile:=Rewrite(save_file); if hSaveFile<>THANDLE(INVALID_HANDLE_VALUE) then begin - BlockWrite(hSaveFile,resp^.pData^,resp^.dataLength); + pBuf := Netlib_HttpBuffer(resp,bufLen); + BlockWrite(hSaveFile,pBuf^,bufLen); CloseHandle(hSaveFile); result:=true; end - end - else if (resp.resultCode>=300) and (resp.resultCode<400) then - begin - // get new location - for i:=0 to resp^.headersCount-1 do - begin - if StrCmp(resp^.headers^[i].szName,'Location')=0 then - begin - result:=GetFile(resp^.headers^[i].szValue,save_file,hNetLib,recurse_count+1); - break; - end - end; end; Netlib_FreeHttpRequest(resp); @@ -583,8 +576,9 @@ function LoadImageURL(url:PAnsiChar;size:integer=0):HBITMAP; var nlu:TNETLIBUSER; req :TNETLIBHTTPREQUEST; - resp:PNETLIBHTTPREQUEST; - hNetLib:THANDLE; + resp,hNetLib:THANDLE; + bufLen:integer; + pBuf:PAnsiChar; begin result:=0; if (url=nil) or (url^=#0) then @@ -601,10 +595,13 @@ begin hNetLib:=Netlib_RegisterUser(@nlu); resp:=Netlib_HttpTransaction(hNetLib,@req); - if resp<>nil then + if resp<>0 then begin - if resp^.resultCode=200 then - result := Image_LoadFromMem(resp.pData, resp.dataLength, FIF_JPEG); + if Netlib_HttpResult(resp)=200 then + begin + pBuf := Netlib_HttpBuffer(resp,bufLen); + result := Image_LoadFromMem(pBuf, bufLen, FIF_JPEG); + end; Netlib_FreeHttpRequest(resp); end; diff --git a/plugins/Watrack/myshows/i_cookies.inc b/plugins/Watrack/myshows/i_cookies.inc index cd01ef6621..82c12f3b3d 100644 --- a/plugins/Watrack/myshows/i_cookies.inc +++ b/plugins/Watrack/myshows/i_cookies.inc @@ -2,46 +2,14 @@ const cookies:pAnsiChar=nil; -function ExtractCookies(resp:PNETLIBHTTPREQUEST):integer; -var - cnt,len:integer; - p,pc:pAnsiChar; -begin - result:=0; - - mFreeMem(cookies); - mGetMem(cookies,1024); - - pc:=cookies; - for cnt:=0 to resp^.headersCount-1 do - begin - with resp^.headers[cnt] do - if StrCmp(szName,'Set-Cookie')=0 then - begin - len:=0; - p:=szValue; - while (p^<>#0) and (p^<>';') do - begin - inc(p); - inc(len); - end; - if pc<>cookies then - begin - pc^:=';'; inc(pc); - pc^:=' '; inc(pc); - end; - pc:=StrCopyE(pc,szValue,len); - inc(result); - end; - end; -end; - function SendRequestCookies(url:PAnsiChar;useCookies:boolean):pAnsiChar; var nlu:TNETLIBUSER; req :TNETLIBHTTPREQUEST; - resp:PNETLIBHTTPREQUEST; + resp:THANDLE; hTmpNetLib:THANDLE; + bufLen:int; + pBuf:PAnsiChar; nlh:array [0..10] of TNETLIBHTTPHEADER; begin result:=nil; @@ -66,20 +34,21 @@ begin hTmpNetLib:=Netlib_RegisterUser(@nlu); resp:=Netlib_HttpTransaction(hTmpNetLib,@req); - if resp<>nil then + if resp<>0 then begin - if resp^.resultCode=200 then + if Netlib_HttpResult(resp)=200 then begin - if resp.pData<>nil then - StrDup(result,resp.pData,resp.dataLength) + pBuf := Netlib_HttpBuffer(resp,bufLen); + if pBuf<>nil then + StrDup(result,pBuf,bufLen) else result:=PAnsiChar(200); if not useCookies then - ExtractCookies(resp); + cookies := Netlib_HttpCookies(resp); end else begin - result:=pAnsiChar(int_ptr(resp^.resultCode and $0FFF)); + result:=pAnsiChar(int_ptr(Netlib_HttpResult(resp) and $0FFF)); end; Netlib_FreeHttpRequest(resp); end; diff --git a/plugins/Watrack/myshows/i_myshows_api.inc b/plugins/Watrack/myshows/i_myshows_api.inc index f92df86b50..828fb5c650 100644 --- a/plugins/Watrack/myshows/i_myshows_api.inc +++ b/plugins/Watrack/myshows/i_myshows_api.inc @@ -88,7 +88,6 @@ begin StrReplace(request,'',buf); res:=SendRequestCookies(request,false); -// res:=SendRequest(request,REQUEST_GET); if res<>nil then begin if uint_ptr(res)<$0FFF then diff --git a/protocols/Discord/src/gateway.cpp b/protocols/Discord/src/gateway.cpp index 543020f105..e76de2848a 100644 --- a/protocols/Discord/src/gateway.cpp +++ b/protocols/Discord/src/gateway.cpp @@ -61,12 +61,7 @@ bool CDiscordProto::GatewayThreadWorker() return false; } - if (auto *pszNewCookie = Netlib_GetHeader(pReply, "Set-Cookie")) { - char *p = strchr(pszNewCookie, ';'); - if (p) *p = 0; - - m_szWSCookie = pszNewCookie; - } + m_szWSCookie = pReply->GetCookies(); if (pReply->resultCode != 101) { // if there's no cookie & Miranda is bounced with error 404, simply apply the cookie and try again diff --git a/protocols/Discord/src/server.cpp b/protocols/Discord/src/server.cpp index 243e80d2b2..1ef3517822 100644 --- a/protocols/Discord/src/server.cpp +++ b/protocols/Discord/src/server.cpp @@ -165,18 +165,7 @@ void CDiscordProto::OnReceiveMyInfo(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest* m_wszEmail = data["email"].as_mstring(); m_ownId = id; - - m_szCookie.Empty(); - for (int i=0; i < pReply->headersCount; i++) { - if (!mir_strcmpi(pReply->headers[i].szName, "Set-Cookie")) { - char *p = strchr(pReply->headers[i].szValue, ';'); - if (p) *p = 0; - if (!m_szCookie.IsEmpty()) - m_szCookie.Append("; "); - - m_szCookie.Append(pReply->headers[i].szValue); - } - } + m_szCookie = pReply->GetCookies(); // launch gateway thread if (m_szGateway.IsEmpty()) diff --git a/protocols/ICQ-WIM/src/mra.cpp b/protocols/ICQ-WIM/src/mra.cpp index cfe427a136..b65ae238dc 100644 --- a/protocols/ICQ-WIM/src/mra.cpp +++ b/protocols/ICQ-WIM/src/mra.cpp @@ -19,18 +19,7 @@ along with this program. If not, see . void CIcqProto::SendMrimLogin(NETLIBHTTPREQUEST *pReply) { - if (pReply) { - for (int i=0; i < pReply->headersCount; i++) { - if (!mir_strcmpi(pReply->headers[i].szName, "Set-Cookie")) { - char *p = strchr(pReply->headers[i].szValue, ';'); - if (p) *p = 0; - if (!m_szMraCookie.IsEmpty()) - m_szMraCookie.Append("; "); - - m_szMraCookie.Append(pReply->headers[i].szValue); - } - } - } + m_szMraCookie = pReply->GetCookies(); auto *pReq = new AsyncHttpRequest(CONN_NONE, REQUEST_POST, "https://icqapilogin.mail.ru/auth/mrimLogin", &CIcqProto::OnCheckMrimLogin); pReq->AddHeader("User-Agent", NETLIB_USER_AGENT); diff --git a/protocols/Tox/src/http_request.h b/protocols/Tox/src/http_request.h index d51aab289f..bba9f0591c 100644 --- a/protocols/Tox/src/http_request.h +++ b/protocols/Tox/src/http_request.h @@ -24,7 +24,6 @@ private: void Init(int type) { - cbSize = sizeof(NETLIBHTTPREQUEST); requestType = type; flags = NLHRF_HTTP11 | NLHRF_SSL | NLHRF_NODUMPSEND | NLHRF_DUMPASTEXT; szUrl = nullptr; diff --git a/src/mir_app/src/mir_app.def b/src/mir_app/src/mir_app.def index 68914f96b2..bb3a1dec8e 100644 --- a/src/mir_app/src/mir_app.def +++ b/src/mir_app/src/mir_app.def @@ -919,3 +919,10 @@ _CallContactService@20 @1048 NONAME ??0MDatabaseExport@@QAE@XZ @1049 NONAME ??1MDatabaseExport@@UAE@XZ @1050 NONAME ??_7MDatabaseExport@@6B@ @1051 NONAME +??4NETLIBHTTPREQUEST@@QAEAAU0@$$QAU0@@Z @1052 NONAME +??4NETLIBHTTPREQUEST@@QAEAAU0@ABU0@@Z @1053 NONAME +??ANETLIBHTTPREQUEST@@QAEPBDPBD@Z @1054 NONAME +?GetCookies@NETLIBHTTPREQUEST@@QBE?AV?$CMStringT@DV?$ChTraitsCRT@D@@@@XZ @1055 NONAME +Netlib_HttpBuffer @1056 +Netlib_HttpCookies @1057 +Netlib_HttpResult @1058 diff --git a/src/mir_app/src/mir_app64.def b/src/mir_app/src/mir_app64.def index b3aa1095b9..26f860b889 100644 --- a/src/mir_app/src/mir_app64.def +++ b/src/mir_app/src/mir_app64.def @@ -919,3 +919,10 @@ CallContactService @1042 NONAME ??0MDatabaseExport@@QEAA@XZ @1043 NONAME ??1MDatabaseExport@@UEAA@XZ @1044 NONAME ??_7MDatabaseExport@@6B@ @1045 NONAME +??4NETLIBHTTPREQUEST@@QEAAAEAU0@$$QEAU0@@Z @1046 NONAME +??4NETLIBHTTPREQUEST@@QEAAAEAU0@AEBU0@@Z @1047 NONAME +??ANETLIBHTTPREQUEST@@QEAAPEBDPEBD@Z @1048 NONAME +?GetCookies@NETLIBHTTPREQUEST@@QEBA?AV?$CMStringT@DV?$ChTraitsCRT@D@@@@XZ @1049 NONAME +Netlib_HttpBuffer @1050 +Netlib_HttpCookies @1051 +Netlib_HttpResult @1052 diff --git a/src/mir_app/src/netlib.h b/src/mir_app/src/netlib.h index d5ddb6687b..1ce7394cfb 100644 --- a/src/mir_app/src/netlib.h +++ b/src/mir_app/src/netlib.h @@ -166,7 +166,6 @@ bool BindSocketToPort(const char *szPorts, SOCKET s, SOCKET s6, int* portn); void NetlibHttpSetLastErrorUsingHttpResult(int result); int Netlib_SendHttpRequest(HNETLIBCONN hConnection, NETLIBHTTPREQUEST *pRec); -NETLIBHTTPREQUEST* Netlib_RecvHttpHeaders(NetlibConnection *hConnection, int flags = 0); NETLIBHTTPREQUEST* NetlibHttpRecv(NetlibConnection *nlc, uint32_t hflags, uint32_t dflags, bool isConnect = false); // netliblog.cpp diff --git a/src/mir_app/src/netlib_http.cpp b/src/mir_app/src/netlib_http.cpp index 6845435d34..9e8e6759cb 100644 --- a/src/mir_app/src/netlib_http.cpp +++ b/src/mir_app/src/netlib_http.cpp @@ -79,6 +79,66 @@ struct ProxyAuthList : OBJLIST ProxyAuthList proxyAuthList; +///////////////////////////////////////////////////////////////////////////////////////// +// Module exports + +MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr) +{ + if (nlhr == nullptr || 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; +} + +MIR_APP_DLL(char*) Netlib_GetHeader(const NETLIBHTTPREQUEST *nlhr, const char *hdr) +{ + if (nlhr == nullptr || hdr == nullptr) + return nullptr; + + for (int i = 0; i < nlhr->headersCount; i++) { + NETLIBHTTPHEADER &p = nlhr->headers[i]; + if (_stricmp(p.szName, hdr) == 0) + return p.szValue; + } + + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +CMStringA NETLIBHTTPREQUEST::GetCookies() const +{ + CMStringA ret; + + for (int i = 0; i < headersCount; i++) { + if (!mir_strcmpi(headers[i].szName, "Set-Cookie")) { + char *p = strchr(headers[i].szValue, ';'); + if (p) *p = 0; + if (!ret.IsEmpty()) + ret.Append("; "); + + ret.Append(headers[i].szValue); + } + } + return ret; +} + +///////////////////////////////////////////////////////////////////////////////////////// + static int RecvWithTimeoutTime(NetlibConnection *nlc, int dwTimeoutTime, char *buf, int len, int flags) { int dwTimeNow; @@ -105,20 +165,6 @@ static int RecvWithTimeoutTime(NetlibConnection *nlc, int dwTimeoutTime, char *b return Netlib_Recv(nlc, buf, len, flags); } -MIR_APP_DLL(char *) Netlib_GetHeader(const NETLIBHTTPREQUEST *nlhr, const char *hdr) -{ - if (nlhr == nullptr || hdr == nullptr) - return nullptr; - - for (int i=0; i < nlhr->headersCount; i++) { - NETLIBHTTPHEADER &p = nlhr->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; @@ -145,7 +191,7 @@ static char* NetlibHttpFindAuthHeader(NETLIBHTTPREQUEST *nlhrReply, const char * ///////////////////////////////////////////////////////////////////////////////////////// -void NetlibConnFromUrl(const char *szUrl, bool secur, NetlibUrl &url) +static void NetlibConnFromUrl(const char *szUrl, bool secur, NetlibUrl &url) { secur = secur || _strnicmp(szUrl, "https", 5) == 0; @@ -368,6 +414,120 @@ static int SendHttpRequestAndData(NetlibConnection *nlc, CMStringA &httpRequest, return bytesSent; } +///////////////////////////////////////////////////////////////////////////////////////// +// Receives HTTP headers +// +// Returns a pointer to a NETLIBHTTPREQUEST structure on success, NULL on failure. +// Call Netlib_FreeHttpRequest() to free this. +// hConnection must have been returned by MS_NETLIB_OPENCONNECTION +// nlhr->pData = NULL and nlhr->dataLength = 0 always. The requested data should +// be retrieved using MS_NETLIB_RECV once the header has been parsed. +// If the headers haven't finished within 60 seconds the function returns NULL +// and ERROR_TIMEOUT. +// Errors: ERROR_INVALID_PARAMETER, any from MS_NETLIB_RECV or select() +// ERROR_HANDLE_EOF (connection closed before headers complete) +// ERROR_TIMEOUT (headers still not complete after 60 seconds) +// ERROR_BAD_FORMAT (invalid character or line ending in headers, or first line is blank) +// ERROR_BUFFER_OVERFLOW (each header line must be less than 4096 chars long) +// ERROR_INVALID_DATA (first header line is malformed ("http/[01].[0-9] [0-9]+ .*", or no colon in subsequent line) + +#define NHRV_BUF_SIZE 8192 + +static NETLIBHTTPREQUEST* Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags) +{ + NetlibConnection *nlc = (NetlibConnection *)hConnection; + if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) + return nullptr; + + uint32_t dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT; + NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST *)mir_calloc(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 = (char *)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; +} + ///////////////////////////////////////////////////////////////////////////////////////// // Sends a HTTP request over a connection // @@ -699,228 +859,6 @@ int Netlib_SendHttpRequest(HNETLIBCONN nlc, NETLIBHTTPREQUEST *nlhr) return bytesSent; } -MIR_APP_DLL(bool) Netlib_FreeHttpRequest(NETLIBHTTPREQUEST *nlhr) -{ - if (nlhr == nullptr || 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; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Receives HTTP headers -// -// Returns a pointer to a NETLIBHTTPREQUEST structure on success, NULL on failure. -// Call Netlib_FreeHttpRequest() to free this. -// hConnection must have been returned by MS_NETLIB_OPENCONNECTION -// nlhr->pData = NULL and nlhr->dataLength = 0 always. The requested data should -// be retrieved using MS_NETLIB_RECV once the header has been parsed. -// If the headers haven't finished within 60 seconds the function returns NULL -// and ERROR_TIMEOUT. -// Errors: ERROR_INVALID_PARAMETER, any from MS_NETLIB_RECV or select() -// ERROR_HANDLE_EOF (connection closed before headers complete) -// ERROR_TIMEOUT (headers still not complete after 60 seconds) -// ERROR_BAD_FORMAT (invalid character or line ending in headers, or first line is blank) -// ERROR_BUFFER_OVERFLOW (each header line must be less than 4096 chars long) -// ERROR_INVALID_DATA (first header line is malformed ("http/[01].[0-9] [0-9]+ .*", or no colon in subsequent line) - -#define NHRV_BUF_SIZE 8192 - -NETLIBHTTPREQUEST* Netlib_RecvHttpHeaders(HNETLIBCONN hConnection, int flags) -{ - NetlibConnection *nlc = (NetlibConnection*)hConnection; - if (!NetlibEnterNestedCS(nlc, NLNCS_RECV)) - return nullptr; - - uint32_t dwRequestTimeoutTime = GetTickCount() + HTTPRECVDATATIMEOUT; - NETLIBHTTPREQUEST *nlhr = (NETLIBHTTPREQUEST*)mir_calloc(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 = (char*)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 || !nlhr->szUrl || 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 = Netlib_GetHeader(nlhr, "User-Agent") != nullptr; - bool doneAcceptEncoding = Netlib_GetHeader(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); - - uint32_t 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); - - uint32_t 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) { @@ -941,7 +879,7 @@ void NetlibHttpSetLastErrorUsingHttpResult(int result) } } -char* gzip_decode(char *gzip_data, int *len_ptr, int window) +static char* gzip_decode(char *gzip_data, int *len_ptr, int window) { if (*len_ptr == 0) return nullptr; @@ -1176,3 +1114,114 @@ next: return nlhrReply; } + +///////////////////////////////////////////////////////////////////////////////////////// +// Module entry point + +MIR_APP_DLL(NETLIBHTTPREQUEST *) Netlib_HttpTransaction(HNETLIBUSER nlu, NETLIBHTTPREQUEST *nlhr) +{ + if (GetNetlibHandleType(nlu) != NLH_USER || !(nlu->user.flags & NUF_OUTGOING) || !nlhr || !nlhr->szUrl || 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 = Netlib_GetHeader(nlhr, "User-Agent") != nullptr; + bool doneAcceptEncoding = Netlib_GetHeader(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); + + uint32_t 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); + + uint32_t 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, 0); + 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; +} + +EXTERN_C MIR_APP_DLL(int) Netlib_HttpResult(NETLIBHTTPREQUEST *nlhr) +{ + return (nlhr) ? nlhr->resultCode : 500; +} + +EXTERN_C MIR_APP_DLL(char *) Netlib_HttpBuffer(NETLIBHTTPREQUEST *nlhr, int &cbLen) +{ + if (!nlhr) + return nullptr; + + cbLen = nlhr->dataLength; + return nlhr->pData; +} + +EXTERN_C MIR_APP_DLL(char *) Netlib_HttpCookies(NETLIBHTTPREQUEST *nlhr) +{ + if (!nlhr) + return nullptr; + + return nlhr->GetCookies().Detach(); +} -- cgit v1.2.3