summaryrefslogtreecommitdiff
path: root/protocols/MSN/src/msn_threads.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/MSN/src/msn_threads.cpp')
-rw-r--r--protocols/MSN/src/msn_threads.cpp769
1 files changed, 769 insertions, 0 deletions
diff --git a/protocols/MSN/src/msn_threads.cpp b/protocols/MSN/src/msn_threads.cpp
new file mode 100644
index 0000000000..e643bce91b
--- /dev/null
+++ b/protocols/MSN/src/msn_threads.cpp
@@ -0,0 +1,769 @@
+/*
+Plugin of Miranda IM for communicating with users of the MSN Messenger protocol.
+
+Copyright (c) 2012-2014 Miranda NG Team
+Copyright (c) 2006-2012 Boris Krasnovskiy.
+Copyright (c) 2003-2005 George Hazan.
+Copyright (c) 2002-2003 Richard Hughes (original version).
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "msn_global.h"
+#include "msn_proto.h"
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Keep-alive thread for the main connection
+
+void __cdecl CMsnProto::msn_keepAliveThread(void*)
+{
+ bool keepFlag = true;
+
+ hKeepAliveThreadEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ msnPingTimeout = 45;
+
+ while (keepFlag)
+ {
+ switch (WaitForSingleObject(hKeepAliveThreadEvt, msnPingTimeout * 1000))
+ {
+ case WAIT_TIMEOUT:
+ keepFlag = msnNsThread != NULL;
+ if (usingGateway)
+ msnPingTimeout = 45;
+ else
+ {
+ msnPingTimeout = 20;
+ keepFlag = keepFlag && msnNsThread->send("PNG\r\n", 5);
+ }
+ p2p_clearDormantSessions();
+ if (hHttpsConnection && (clock() - mHttpsTS) > 60 * CLOCKS_PER_SEC)
+ {
+ HANDLE hConn = hHttpsConnection;
+ hHttpsConnection = NULL;
+ Netlib_CloseHandle(hConn);
+ }
+ if (mStatusMsgTS && (clock() - mStatusMsgTS) > 60 * CLOCKS_PER_SEC)
+ {
+ mStatusMsgTS = 0;
+ ForkThread(&CMsnProto::msn_storeProfileThread, NULL);
+ }
+ break;
+
+ case WAIT_OBJECT_0:
+ keepFlag = msnPingTimeout > 0;
+ break;
+
+ default:
+ keepFlag = false;
+ break;
+ }
+ }
+
+ CloseHandle(hKeepAliveThreadEvt); hKeepAliveThreadEvt = NULL;
+ debugLogA("Closing keep-alive thread");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// MSN server thread - read and process commands from a server
+
+void __cdecl CMsnProto::MSNServerThread(void* arg)
+{
+ ThreadData* info = (ThreadData*)arg;
+
+ if (info->mIsMainThread)
+ isConnectSuccess = false;
+
+ char* tPortDelim = strrchr(info->mServer, ':');
+ if (tPortDelim != NULL)
+ *tPortDelim = '\0';
+
+ if (usingGateway)
+ {
+ if (info->mServer[0] == 0)
+ strcpy(info->mServer, MSN_DEFAULT_LOGIN_SERVER);
+ else if (info->mIsMainThread)
+ strcpy(info->mGatewayIP, info->mServer);
+
+ if (info->gatewayType)
+ strcpy(info->mGatewayIP, info->mServer);
+ else
+ {
+ if (info->mGatewayIP[0] == 0 && db_get_static(NULL, m_szModuleName, "GatewayServer", info->mGatewayIP, sizeof(info->mGatewayIP)))
+ strcpy(info->mGatewayIP, MSN_DEFAULT_GATEWAY);
+ }
+ }
+ else
+ {
+ if (info->mServer[0] == 0 && db_get_static(NULL, m_szModuleName, "DirectServer", info->mServer, sizeof(info->mServer)))
+ strcpy(info->mServer, MSN_DEFAULT_LOGIN_SERVER);
+ }
+
+ NETLIBOPENCONNECTION tConn = { 0 };
+ tConn.cbSize = sizeof(tConn);
+ tConn.flags = NLOCF_V2;
+ tConn.timeout = 5;
+
+ if (usingGateway)
+ {
+ tConn.flags |= NLOCF_HTTPGATEWAY;
+ tConn.szHost = info->mGatewayIP;
+ tConn.wPort = MSN_DEFAULT_GATEWAY_PORT;
+ }
+ else
+ {
+ tConn.szHost = info->mServer;
+ tConn.wPort = MSN_DEFAULT_PORT;
+ if (tPortDelim != NULL)
+ {
+ int tPortNumber = atoi(tPortDelim + 1);
+ if (tPortNumber)
+ tConn.wPort = (WORD)tPortNumber;
+ }
+ }
+
+ debugLogA("Thread started: server='%s:%d', type=%d", tConn.szHost, tConn.wPort, info->mType);
+
+ info->s = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)m_hNetlibUser, (LPARAM)&tConn);
+ if (info->s == NULL)
+ {
+ debugLogA("Connection Failed (%d) server='%s:%d'", WSAGetLastError(), tConn.szHost, tConn.wPort);
+
+ switch (info->mType)
+ {
+ case SERVER_NOTIFICATION:
+ goto LBL_Exit;
+ break;
+
+ case SERVER_SWITCHBOARD:
+ if (info->mCaller) msnNsThread->sendPacket("XFR", "SB");
+ break;
+ }
+ return;
+ }
+
+ if (usingGateway)
+ CallService(MS_NETLIB_SETPOLLINGTIMEOUT, WPARAM(info->s), info->mGatewayTimeout);
+
+ debugLogA("Connected with handle=%08X", info->s);
+
+ if (info->mType == SERVER_NOTIFICATION)
+ {
+ info->sendPacket("VER", "MSNP18 MSNP17 CVR0");
+ }
+ else if (info->mType == SERVER_SWITCHBOARD)
+ {
+ info->sendPacket(info->mCaller ? "USR" : "ANS", "%s;%s %s", MyOptions.szEmail, MyOptions.szMachineGuid, info->mCookie);
+ }
+ else if (info->mType == SERVER_FILETRANS && info->mCaller == 0)
+ {
+ info->send("VER MSNFTP\r\n", 12);
+ }
+
+ if (info->mIsMainThread)
+ {
+ msnNsThread = info;
+ }
+
+ debugLogA("Entering main recv loop");
+ info->mBytesInData = 0;
+ for (;;)
+ {
+ int recvResult = info->recv(info->mData + info->mBytesInData, sizeof(info->mData) - info->mBytesInData);
+ if (recvResult == SOCKET_ERROR)
+ {
+ debugLogA("Connection %08p [%08X] was abortively closed", info->s, GetCurrentThreadId());
+ break;
+ }
+
+ if (!recvResult)
+ {
+ debugLogA("Connection %08p [%08X] was gracefully closed", info->s, GetCurrentThreadId());
+ break;
+ }
+
+ info->mBytesInData += recvResult;
+
+ if (info->mCaller == 1 && info->mType == SERVER_FILETRANS)
+ {
+ if (MSN_HandleMSNFTP(info, info->mData))
+ break;
+ }
+ else
+ {
+ for (;;)
+ {
+ char* peol = strchr(info->mData, '\r');
+ if (peol == NULL)
+ break;
+
+ if (info->mBytesInData < peol-info->mData + 2)
+ break; //wait for full line end
+
+ char msg[sizeof(info->mData)];
+ memcpy(msg, info->mData, peol-info->mData); msg[peol-info->mData] = 0;
+
+ if (*++peol != '\n')
+ debugLogA("Dodgy line ending to command: ignoring");
+ else
+ peol++;
+
+ info->mBytesInData -= peol - info->mData;
+ memmove(info->mData, peol, info->mBytesInData);
+ debugLogA("RECV: %s", msg);
+
+ if (!isalnum(msg[0]) || !isalnum(msg[1]) || !isalnum(msg[2]) || (msg[3] && msg[3] != ' '))
+ {
+ debugLogA("Invalid command name");
+ continue;
+ }
+
+ if (info->mType != SERVER_FILETRANS)
+ {
+ int handlerResult;
+ if (isdigit(msg[0]) && isdigit(msg[1]) && isdigit(msg[2])) //all error messages
+ handlerResult = MSN_HandleErrors(info, msg);
+ else
+ handlerResult = MSN_HandleCommands(info, msg);
+
+ if (handlerResult)
+ {
+ if (info->sessionClosed) goto LBL_Exit;
+ info->sendTerminate();
+ }
+ }
+ else
+ if (MSN_HandleMSNFTP(info, msg))
+ goto LBL_Exit;
+ }
+ }
+
+ if (info->mBytesInData == sizeof(info->mData))
+ {
+ debugLogA("sizeof(data) is too small: the longest line won't fit");
+ break;
+ }
+ }
+
+LBL_Exit:
+ if (info->mIsMainThread)
+ {
+ if (!isConnectSuccess && !usingGateway && m_iDesiredStatus != ID_STATUS_OFFLINE)
+ {
+ msnNsThread = NULL;
+ usingGateway = true;
+
+ ThreadData* newThread = new ThreadData;
+ newThread->mType = SERVER_NOTIFICATION;
+ newThread->mIsMainThread = true;
+
+ newThread->startThread(&CMsnProto::MSNServerThread, this);
+ }
+ else
+ {
+ if (hKeepAliveThreadEvt)
+ {
+ msnPingTimeout *= -1;
+ SetEvent(hKeepAliveThreadEvt);
+ }
+
+ if (info->s == NULL)
+ ProtoBroadcastAck(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NONETWORK);
+ else
+ {
+ p2p_cancelAllSessions();
+ MSN_CloseConnections();
+ }
+
+ if (hHttpsConnection)
+ {
+ Netlib_CloseHandle(hHttpsConnection);
+ hHttpsConnection = NULL;
+ }
+
+ MSN_GoOffline();
+ msnNsThread = NULL;
+ }
+ }
+
+ debugLogA("Thread [%08X] ending now", GetCurrentThreadId());
+}
+
+void CMsnProto::MSN_CloseConnections(void)
+{
+ mir_cslockfull lck(csThreads);
+
+ NETLIBSELECTEX nls = {0};
+ nls.cbSize = sizeof(nls);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+
+ switch (T->mType)
+ {
+ case SERVER_NOTIFICATION :
+ case SERVER_SWITCHBOARD :
+ if (T->s != NULL && !T->sessionClosed && !T->termPending)
+ {
+ nls.hReadConns[0] = T->s;
+ int res = CallService(MS_NETLIB_SELECTEX, 0, (LPARAM)&nls);
+ if (res >= 0 || nls.hReadStatus[0] == 0)
+ T->sendTerminate();
+ }
+ break;
+
+ case SERVER_P2P_DIRECT :
+ CallService(MS_NETLIB_SHUTDOWN, (WPARAM)T->s, 0);
+ break;
+ }
+ }
+
+ lck.unlock();
+
+ if (hHttpsConnection)
+ CallService(MS_NETLIB_SHUTDOWN, (WPARAM)hHttpsConnection, 0);
+}
+
+void CMsnProto::Threads_Uninit(void)
+{
+ mir_cslock lck(csThreads);
+ sttThreads.destroy();
+}
+
+ThreadData* CMsnProto::MSN_GetThreadByContact(const char* wlid, TInfoType type)
+{
+ mir_cslock lck(csThreads);
+
+ if (type == SERVER_P2P_DIRECT)
+ {
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mType != SERVER_P2P_DIRECT || !T->mJoinedIdentContactsWLID.getCount() || T->s == NULL)
+ continue;
+
+ if (_stricmp(T->mJoinedIdentContactsWLID[0], wlid) == 0)
+ return T;
+ }
+ }
+
+ char *szEmail = NULL;
+ parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mType != type || !T->mJoinedContactsWLID.getCount() || T->mInitialContactWLID || T->s == NULL)
+ continue;
+
+ if (_stricmp(T->mJoinedContactsWLID[0], szEmail) == 0 && T->mChatID[0] == 0)
+ return T;
+ }
+
+ return NULL;
+}
+
+ThreadData* CMsnProto::MSN_GetThreadByChatId(const TCHAR* chatId)
+{
+ mir_cslock lck(csThreads);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+
+ if (_tcsicmp(T->mChatID, chatId) == 0)
+ return T;
+ }
+
+ return NULL;
+}
+
+ThreadData* CMsnProto::MSN_GetThreadByTimer(UINT timerId)
+{
+ mir_cslock lck(csThreads);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mType == SERVER_SWITCHBOARD && T->mTimerId == timerId)
+ return T;
+ }
+
+ return NULL;
+}
+
+ThreadData* CMsnProto::MSN_GetP2PThreadByContact(const char *wlid)
+{
+ mir_cslock lck(csThreads);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mType != SERVER_P2P_DIRECT || !T->mJoinedIdentContactsWLID.getCount())
+ continue;
+
+ if (_stricmp(T->mJoinedIdentContactsWLID[0], wlid) == 0)
+ return T;
+ }
+
+ char *szEmail = NULL;
+ parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL);
+
+ ThreadData *result = NULL;
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mJoinedContactsWLID.getCount() && !T->mInitialContactWLID &&
+ _stricmp(T->mJoinedContactsWLID[0], szEmail) == 0)
+ {
+ if (T->mType == SERVER_P2P_DIRECT)
+ return T;
+
+ if (T->mType == SERVER_SWITCHBOARD)
+ result = T;
+ }
+ }
+
+ return result;
+}
+
+
+void CMsnProto::MSN_StartP2PTransferByContact(const char* wlid)
+{
+ mir_cslock lck(csThreads);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mType == SERVER_FILETRANS && T->hWaitEvent != INVALID_HANDLE_VALUE)
+ {
+ if ((T->mInitialContactWLID && !_stricmp(T->mInitialContactWLID, wlid)) ||
+ (T->mJoinedContactsWLID.getCount() && !_stricmp(T->mJoinedContactsWLID[0], wlid)) ||
+ (T->mJoinedIdentContactsWLID.getCount() && !_stricmp(T->mJoinedIdentContactsWLID[0], wlid)))
+ ReleaseSemaphore(T->hWaitEvent, 1, NULL);
+ }
+ }
+}
+
+
+ThreadData* CMsnProto::MSN_GetOtherContactThread(ThreadData* thread)
+{
+ mir_cslock lck(csThreads);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mJoinedContactsWLID.getCount() == 0 || T->s == NULL)
+ continue;
+
+ if (T != thread && _stricmp(T->mJoinedContactsWLID[0], thread->mJoinedContactsWLID[0]) == 0)
+ return T;
+ }
+
+ return NULL;
+}
+
+ThreadData* CMsnProto::MSN_GetUnconnectedThread(const char* wlid, TInfoType type)
+{
+ mir_cslock lck(csThreads);
+
+ char* szEmail = (char*)wlid;
+
+ if (type == SERVER_SWITCHBOARD && strchr(wlid, ';'))
+ parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mType == type && T->mInitialContactWLID && _stricmp(T->mInitialContactWLID, szEmail) == 0)
+ return T;
+ }
+
+ return NULL;
+}
+
+
+ThreadData* CMsnProto::MSN_StartSB(const char* wlid, bool& isOffline)
+{
+ isOffline = false;
+ ThreadData* thread = MSN_GetThreadByContact(wlid);
+ if (thread == NULL)
+ {
+ MCONTACT hContact = MSN_HContactFromEmail(wlid);
+ WORD wStatus = getWord(hContact, "Status", ID_STATUS_OFFLINE);
+ if (wStatus != ID_STATUS_OFFLINE)
+ {
+ if (MSN_GetUnconnectedThread(wlid) == NULL && MsgQueue_CheckContact(wlid, 5) == NULL)
+ msnNsThread->sendPacket("XFR", "SB");
+ }
+ else isOffline = true;
+ }
+ return thread;
+}
+
+
+
+int CMsnProto::MSN_GetActiveThreads(ThreadData** parResult)
+{
+ int tCount = 0;
+ mir_cslock lck(csThreads);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mType == SERVER_SWITCHBOARD && T->mJoinedContactsWLID.getCount() != 0 && T->mJoinedContactsWLID.getCount())
+ parResult[tCount++] = T;
+ }
+
+ return tCount;
+}
+
+ThreadData* CMsnProto::MSN_GetThreadByConnection(HANDLE s)
+{
+ mir_cslock lck(csThreads);
+
+ for (int i = 0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->s == s)
+ return T;
+ }
+
+ return NULL;
+}
+
+ThreadData* CMsnProto::MSN_GetThreadByPort(WORD wPort)
+{
+ mir_cslock lck(csThreads);
+
+ for (int i=0; i < sttThreads.getCount(); i++)
+ {
+ ThreadData* T = &sttThreads[i];
+ if (T->mIncomingPort == wPort)
+ return T;
+ }
+
+ return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// class ThreadData members
+
+ThreadData::ThreadData()
+{
+ memset(&mInitialContactWLID, 0, sizeof(ThreadData) - 2*sizeof(STRLIST));
+ mGatewayTimeout = 2;
+ resetTimeout();
+ hWaitEvent = CreateSemaphore(NULL, 0, MSN_PACKETS_COMBINE, NULL);
+}
+
+ThreadData::~ThreadData()
+{
+ int i;
+
+ if (s != NULL)
+ {
+ proto->debugLogA("Closing connection handle %08X", s);
+ Netlib_CloseHandle(s);
+ }
+
+ if (mIncomingBoundPort != NULL)
+ {
+ Netlib_CloseHandle(mIncomingBoundPort);
+ }
+
+ if (mMsnFtp != NULL)
+ {
+ delete mMsnFtp;
+ mMsnFtp = NULL;
+ }
+
+ if (hWaitEvent != INVALID_HANDLE_VALUE)
+ CloseHandle(hWaitEvent);
+
+ if (mTimerId != 0)
+ KillTimer(NULL, mTimerId);
+
+ if (mType == SERVER_SWITCHBOARD)
+ {
+ for (i=0; i<mJoinedContactsWLID.getCount(); ++i)
+ {
+ const char* wlid = mJoinedContactsWLID[i];
+ MCONTACT hContact = proto->MSN_HContactFromEmail(wlid);
+ int temp_status = proto->getWord(hContact, "Status", ID_STATUS_OFFLINE);
+ if (temp_status == ID_STATUS_INVISIBLE && proto->MSN_GetThreadByContact(wlid) == NULL)
+ proto->setWord(hContact, "Status", ID_STATUS_OFFLINE);
+ }
+ }
+
+ mJoinedContactsWLID.destroy();
+ mJoinedIdentContactsWLID.destroy();
+
+ const char* wlid = NEWSTR_ALLOCA(mInitialContactWLID);
+ mir_free(mInitialContactWLID); mInitialContactWLID = NULL;
+
+ if (proto && mType == SERVER_P2P_DIRECT)
+ proto->p2p_clearDormantSessions();
+
+ if (wlid != NULL && mType == SERVER_SWITCHBOARD &&
+ proto->MSN_GetThreadByContact(wlid) == NULL &&
+ proto->MSN_GetUnconnectedThread(wlid) == NULL)
+ {
+ proto->MsgQueue_Clear(wlid, true);
+ }
+}
+
+void ThreadData::applyGatewayData(HANDLE hConn, bool isPoll)
+{
+ char szHttpPostUrl[300];
+ getGatewayUrl(szHttpPostUrl, sizeof(szHttpPostUrl), isPoll);
+
+ proto->debugLogA("applying '%s' to %08X [%08X]", szHttpPostUrl, this, GetCurrentThreadId());
+
+ NETLIBHTTPPROXYINFO nlhpi = {0};
+ nlhpi.cbSize = sizeof(nlhpi);
+ nlhpi.flags = NLHPIF_HTTP11;
+ nlhpi.szHttpGetUrl = NULL;
+ nlhpi.szHttpPostUrl = szHttpPostUrl;
+ nlhpi.combinePackets = 5;
+ CallService(MS_NETLIB_SETHTTPPROXYINFO, (WPARAM)hConn, (LPARAM)&nlhpi);
+}
+
+void ThreadData::getGatewayUrl(char* dest, int destlen, bool isPoll)
+{
+ static const char openFmtStr[] = "http://%s/gateway/gateway.dll?Action=open&Server=%s&IP=%s";
+ static const char pollFmtStr[] = "http://%s/gateway/gateway.dll?Action=poll&SessionID=%s";
+ static const char cmdFmtStr[] = "http://%s/gateway/gateway.dll?SessionID=%s";
+
+ if (mSessionID[0] == 0)
+ {
+ const char* svr = mType == SERVER_NOTIFICATION ? "NS" : "SB";
+ mir_snprintf(dest, destlen, openFmtStr, mGatewayIP, svr, mServer);
+ }
+ else
+ mir_snprintf(dest, destlen, isPoll ? pollFmtStr : cmdFmtStr, mGatewayIP, mSessionID);
+}
+
+void ThreadData::processSessionData(const char* xMsgr, const char* xHost)
+{
+ char tSessionID[40], tGateIP[80];
+
+ char* tDelim = (char*)strchr(xMsgr, ';');
+ if (tDelim == NULL)
+ return;
+
+ *tDelim = 0; tDelim += 2;
+
+ if (!sscanf(xMsgr, "SessionID=%s", tSessionID))
+ return;
+
+ char* tDelim2 = strchr(tDelim, ';');
+ if (tDelim2 != NULL)
+ *tDelim2 = '\0';
+ if (xHost)
+ strcpy(tGateIP, xHost);
+ else if (!sscanf(tDelim, "GW-IP=%s", tGateIP))
+ return;
+
+ strcpy(mGatewayIP, tGateIP);
+ if (gatewayType) strcpy(mServer, tGateIP);
+ strcpy(mSessionID, tSessionID);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// thread start code
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void __cdecl CMsnProto::ThreadStub(void* arg)
+{
+ ThreadData* info = (ThreadData*)arg;
+
+ debugLogA("Starting thread %08X (%08X)", GetCurrentThreadId(), info->mFunc);
+
+ (this->*(info->mFunc))(info);
+
+ debugLogA("Leaving thread %08X (%08X)", GetCurrentThreadId(), info->mFunc);
+ {
+ mir_cslock lck(csThreads);
+ sttThreads.LIST<ThreadData>::remove(info);
+ }
+ delete info;
+}
+
+void ThreadData::startThread(MsnThreadFunc parFunc, CMsnProto *prt)
+{
+ mFunc = parFunc;
+ proto = prt;
+ {
+ mir_cslock lck(prt->csThreads);
+ proto->sttThreads.insert(this);
+ }
+ proto->ForkThread(&CMsnProto::ThreadStub, this);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// HReadBuffer members
+
+HReadBuffer::HReadBuffer(ThreadData* T, int iStart)
+{
+ owner = T;
+ buffer = (BYTE*)T->mData;
+ totalDataSize = T->mBytesInData;
+ startOffset = iStart;
+}
+
+HReadBuffer::~HReadBuffer()
+{
+ if (totalDataSize > startOffset)
+ {
+ memmove(buffer, buffer + startOffset, (totalDataSize -= startOffset));
+ owner->mBytesInData = (int)totalDataSize;
+ }
+ else owner->mBytesInData = 0;
+}
+
+BYTE* HReadBuffer::surelyRead(size_t parBytes)
+{
+ const size_t bufferSize = sizeof(owner->mData);
+
+ if ((startOffset + parBytes) > bufferSize)
+ {
+ if (totalDataSize > startOffset)
+ memmove(buffer, buffer + startOffset, (totalDataSize -= startOffset));
+ else
+ totalDataSize = 0;
+
+ startOffset = 0;
+
+ if (parBytes > bufferSize)
+ {
+ owner->proto->debugLogA("HReadBuffer::surelyRead: not enough memory, %d %d %d", parBytes, bufferSize, startOffset);
+ return NULL;
+ }
+ }
+
+ while ((startOffset + parBytes) > totalDataSize)
+ {
+ int recvResult = owner->recv((char*)buffer + totalDataSize, bufferSize - totalDataSize);
+
+ if (recvResult <= 0)
+ return NULL;
+
+ totalDataSize += recvResult;
+ }
+
+ BYTE* result = buffer + startOffset; startOffset += parBytes;
+ return result;
+}