From 2d043179f4723b15eb63df92d6cdb031511f10ee Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Sun, 2 Nov 2014 10:29:30 +0000 Subject: MSN protocol stopped working (R.I.P), moved to deprecated, banned by the core, delete mask in PluginUpdater was created git-svn-id: http://svn.miranda-ng.org/main/trunk@10896 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/!Deprecated/MSN/src/msn_threads.cpp | 769 ++++++++++++++++++++++++++++ 1 file changed, 769 insertions(+) create mode 100644 plugins/!Deprecated/MSN/src/msn_threads.cpp (limited to 'plugins/!Deprecated/MSN/src/msn_threads.cpp') diff --git a/plugins/!Deprecated/MSN/src/msn_threads.cpp b/plugins/!Deprecated/MSN/src/msn_threads.cpp new file mode 100644 index 0000000000..e643bce91b --- /dev/null +++ b/plugins/!Deprecated/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 . +*/ + +#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; iMSN_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::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; +} -- cgit v1.2.3