From cb4a46e7fbe62d788e66ed6121c717a2d22a4d7c Mon Sep 17 00:00:00 2001 From: watcherhd Date: Thu, 21 Apr 2011 14:14:52 +0000 Subject: svn.miranda.im is moving to a new home! git-svn-id: http://miranda-plugins.googlecode.com/svn/trunk@7 e753b5eb-9565-29b2-b5c5-2cc6f99dfbcb --- miranda-wine/protocols/MSN/msn_threads.cpp | 723 +++++++++++++++++++++++++++++ 1 file changed, 723 insertions(+) create mode 100644 miranda-wine/protocols/MSN/msn_threads.cpp (limited to 'miranda-wine/protocols/MSN/msn_threads.cpp') diff --git a/miranda-wine/protocols/MSN/msn_threads.cpp b/miranda-wine/protocols/MSN/msn_threads.cpp new file mode 100644 index 0000000..7ce9ad2 --- /dev/null +++ b/miranda-wine/protocols/MSN/msn_threads.cpp @@ -0,0 +1,723 @@ +/* +Plugin of Miranda IM for communicating with users of the MSN Messenger protocol. +Copyright (c) 2003-5 George Hazan. +Copyright (c) 2002-3 Richard Hughes (original version). + +Miranda IM: the free icq client for MS Windows +Copyright (C) 2000-2002 Richard Hughes, Roland Rabien & Tristan Van de Vreede + +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 "msn_global.h" + +#include + +int MSN_HandleCommands(ThreadData *info,char *cmdString); +int MSN_HandleErrors(ThreadData *info,char *cmdString); +int MSN_HandleMSNFTP( ThreadData *info, char *cmdString ); + +extern LONG (WINAPI *MyInterlockedIncrement)(PLONG pVal); +extern unsigned long sl; + +HANDLE hKeepAliveThreadEvt = NULL; + +bool DoingNudge = false; + +///////////////////////////////////////////////////////////////////////////////////////// +// Keep-alive thread for the main connection + +int msnPingTimeout = 45, msnPingTimeoutCurrent = 45; + +void __cdecl msn_keepAliveThread( void* ) +{ + msnPingTimeout = msnPingTimeoutCurrent; + + while( TRUE ) + { + while ( --msnPingTimeout > 0 ) { + if ( ::WaitForSingleObject( hKeepAliveThreadEvt, 1000 ) != WAIT_TIMEOUT ) { + ::CloseHandle( hKeepAliveThreadEvt ); hKeepAliveThreadEvt = NULL; + MSN_DebugLog( "Closing keep-alive thread" ); + return; + } } + + msnPingTimeout = msnPingTimeoutCurrent = 45; + + /* + * if proxy is not used, every connection uses select() to send PNG + */ + + if ( msnLoggedIn && !MyOptions.UseGateway ) + if ( MSN_GetByte( "KeepAlive", 0 )) + msnNsThread->send( "PNG\r\n", 5 ); +} } + +///////////////////////////////////////////////////////////////////////////////////////// +// MSN redirector detection thread - refreshes the information about the redirector + +static bool sttRedirectorWasChecked = false; + +void __cdecl msn_RedirectorThread( ThreadData* info ) +{ + extern int MSN_CheckRedirector(); + MSN_CheckRedirector(); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// MSN server thread - read and process commands from a server + +void __cdecl MSNServerThread( ThreadData* info ) +{ + if ( !sttRedirectorWasChecked ) { + sttRedirectorWasChecked = true; + MSN_StartThread(( pThreadFunc )msn_RedirectorThread, NULL ); + } + + NETLIBOPENCONNECTION tConn = { 0 }; + tConn.cbSize = sizeof( tConn ); + tConn.flags = NLOCF_V2; + + char* tPortDelim = strrchr( info->mServer, ':' ); + if ( tPortDelim != NULL ) + *tPortDelim = '\0'; + + if ( MyOptions.UseGateway && !MyOptions.UseProxy ) { + tConn.szHost = MSN_DEFAULT_GATEWAY; + tConn.wPort = 80; + } + else { + tConn.szHost = info->mServer; + tConn.wPort = MSN_DEFAULT_PORT; + + if ( tPortDelim != NULL ) { + int tPortNumber; + if ( sscanf( tPortDelim+1, "%d", &tPortNumber ) == 1 ) + tConn.wPort = ( WORD )tPortNumber; + } } + + MSN_DebugLog( "Thread started: server='%s', type=%d", tConn.szHost, info->mType ); + + info->s = ( HANDLE )MSN_CallService( MS_NETLIB_OPENCONNECTION, ( WPARAM )hNetlibUser, ( LPARAM )&tConn ); + if ( info->s == NULL ) { + MSN_DebugLog( "Connection Failed (%d)", WSAGetLastError() ); + + switch ( info->mType ) { + case SERVER_NOTIFICATION: + case SERVER_DISPATCH: + MSN_SendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NOSERVER ); + MSN_GoOffline(); + break; + } + + return; + } + + if ( MyOptions.UseGateway ) + MSN_CallService( MS_NETLIB_SETPOLLINGTIMEOUT, WPARAM( info->s ), 2 ); + + MSN_DebugLog( "Connected with handle=%08X", info->s ); + + if ( info->mType == SERVER_DISPATCH || info->mType == SERVER_NOTIFICATION ) { + if ( MyOptions.UseMSNP11 ) + info->sendPacket( "VER", "MSNP11 MSNP10 CVR0" ); + else + info->sendPacket( "VER", "MSNP10 MSNP9 CVR0" ); + } + else if ( info->mType == SERVER_SWITCHBOARD ) { + char tEmail[ MSN_MAX_EMAIL_LEN ]; + MSN_GetStaticString( "e-mail", NULL, tEmail, sizeof( tEmail )); + info->sendPacket( info->mCaller ? "USR" : "ANS", "%s %s", tEmail, info->mCookie ); + } + else if ( info->mType == SERVER_FILETRANS && info->mCaller == 0 ) { + info->send( "VER MSNFTP\r\n", 12 ); + } + + if ( info->mIsMainThread ) { + MSN_EnableMenuItems( TRUE ); + + sl = time(NULL); //for hotmail + + msnPingTimeout = msnPingTimeoutCurrent; + + msnNsThread = info; + if (hKeepAliveThreadEvt == NULL) { + hKeepAliveThreadEvt = ::CreateEvent( NULL, TRUE, FALSE, NULL ); + MSN_StartThread(( pThreadFunc )msn_keepAliveThread, NULL ); + } } + + MSN_DebugLog( "Entering main recv loop" ); + info->mBytesInData = 0; + while ( TRUE ) { + int handlerResult; + + int recvResult = info->recv( info->mData + info->mBytesInData, sizeof( info->mData ) - info->mBytesInData ); + if ( recvResult == SOCKET_ERROR ) { + MSN_DebugLog( "Connection %08p [%d] was abortively closed", info->s, GetCurrentThreadId()); + break; + } + + if ( !recvResult ) { + MSN_DebugLog( "Connection %08p [%d] was gracefully closed", info->s, GetCurrentThreadId()); + break; + } + + info->mBytesInData += recvResult; + + if ( info->mCaller == 1 && info->mType == SERVER_FILETRANS ) { + handlerResult = MSN_HandleMSNFTP( info, info->mData ); + if ( handlerResult ) + break; + } + else { + while( TRUE ) { + 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' ) + MSN_DebugLog( "Dodgy line ending to command: ignoring" ); + else + peol++; + + info->mBytesInData -= peol - info->mData; + memmove( info->mData, peol, info->mBytesInData ); + MSN_DebugLog( "RECV:%s", msg ); + + if ( !isalnum( msg[0] ) || !isalnum(msg[1]) || !isalnum(msg[2]) || (msg[3] && msg[3]!=' ')) { + MSN_DebugLog( "Invalid command name" ); + continue; + } + + if ( info->mType != SERVER_FILETRANS ) { + if ( isdigit(msg[0]) && isdigit(msg[1]) && isdigit(msg[2])) //all error messages + handlerResult = MSN_HandleErrors( info, msg ); + else + handlerResult = MSN_HandleCommands( info, msg ); + } + else handlerResult = MSN_HandleMSNFTP( info, msg ); + + if ( handlerResult ) + goto LBL_Exit; + } } + + if ( info->mBytesInData == sizeof( info->mData )) { + MSN_DebugLog( "sizeof(data) is too small: the longest line won't fit" ); + break; + } } + +LBL_Exit: + if ( info->mIsMainThread ) { + MSN_GoOffline(); + msnNsThread = NULL; + if ( hKeepAliveThreadEvt ) + SetEvent( hKeepAliveThreadEvt ); + } + else if ( info->mType == SERVER_SWITCHBOARD ) { + if ( info->mJoinedContacts != NULL ) + free( info->mJoinedContacts ); + } + + MSN_DebugLog( "Thread [%d] ending now", GetCurrentThreadId() ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Added by George B. Hazan (ghazan@postman.ru) +// The following code is required to abortively stop all started threads upon exit + +static ThreadData* sttThreads[ MAX_THREAD_COUNT ]; // up to MAX_THREAD_COUNT threads +static CRITICAL_SECTION sttLock; + +void __stdcall MSN_InitThreads() +{ + memset( sttThreads, 0, sizeof( sttThreads )); + InitializeCriticalSection( &sttLock ); +} + +void __stdcall MSN_CloseConnections() +{ + int i; + + EnterCriticalSection( &sttLock ); + + for ( i=0; i < MAX_THREAD_COUNT; i++ ) { + ThreadData* T = sttThreads[ i ]; + if ( T == NULL ) + continue; + + switch (T->mType) { + case SERVER_DISPATCH : + case SERVER_NOTIFICATION : + case SERVER_SWITCHBOARD : + if (T->s != NULL) + T->sendPacket( "OUT", NULL ); + break; + + case SERVER_P2P_DIRECT : + if (p2p_sessionRegistered(T->mP2pSession)) { + filetransfer *ft = T->mP2pSession; + if ( ft->hWaitEvent != INVALID_HANDLE_VALUE ) + SetEvent( ft->hWaitEvent ); + + if ( ft->p2p_appID != 0 ) + p2p_sendCancel( T, ft ); + + ft->std.files = NULL; + ft->std.totalFiles = 0; + } + break; + } } + + LeaveCriticalSection( &sttLock ); +} + +void __stdcall MSN_CloseThreads() +{ + EnterCriticalSection( &sttLock ); + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) { + ThreadData* T = sttThreads[ i ]; + if ( T == NULL || T->s == NULL ) + continue; + + SOCKET s = MSN_CallService( MS_NETLIB_GETSOCKET, LPARAM( T->s ), 0 ); + if ( s != INVALID_SOCKET ) + shutdown( s, 2 ); + } + + LeaveCriticalSection( &sttLock ); +} + +void Threads_Uninit( void ) +{ + DeleteCriticalSection( &sttLock ); +} + +ThreadData* __stdcall MSN_GetThreadByContact( HANDLE hContact ) +{ + EnterCriticalSection( &sttLock ); + + ThreadData* T = NULL; + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { + T = sttThreads[ i ]; + if ( T == NULL ) + continue; + + if ( T->mJoinedCount == 0 || T->mJoinedContacts == NULL || T->s == NULL ) + continue; + + if ( T->mJoinedContacts[0] == hContact ) + break; + } + + LeaveCriticalSection( &sttLock ); + return T; +} + +ThreadData* __stdcall MSN_GetOtherContactThread( ThreadData* thread ) +{ + EnterCriticalSection( &sttLock ); + + ThreadData* T = NULL; + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { + T = sttThreads[ i ]; + if ( T == NULL ) + continue; + + if ( T->mJoinedCount == 0 || T->mJoinedContacts == NULL || T->s == NULL ) + continue; + + if ( T != thread && T->mJoinedContacts[0] == thread->mJoinedContacts[0] ) + break; + } + + LeaveCriticalSection( &sttLock ); + return T; +} + +ThreadData* __stdcall MSN_GetUnconnectedThread( HANDLE hContact ) +{ + EnterCriticalSection( &sttLock ); + + ThreadData* T = NULL; + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { + T = sttThreads[ i ]; + if ( T == NULL ) + continue; + + if ( T->mInitialContact == hContact ) + break; + } + + LeaveCriticalSection( &sttLock ); + return T; +} + +int __stdcall MSN_GetActiveThreads( ThreadData** parResult ) +{ + int tCount = 0; + EnterCriticalSection( &sttLock ); + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { + ThreadData* T = sttThreads[ i ]; + if ( T == NULL ) + continue; + + if ( T->mType == SERVER_SWITCHBOARD && T->mJoinedCount != 0 && T->mJoinedContacts != NULL ) + parResult[ tCount++ ] = T; + } + + LeaveCriticalSection( &sttLock ); + return tCount; +} + +ThreadData* __stdcall MSN_GetThreadByConnection( HANDLE s ) +{ + ThreadData* tResult = NULL; + + EnterCriticalSection( &sttLock ); + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { + ThreadData* T = sttThreads[ i ]; + if ( T == NULL ) + continue; + + if ( T->s == s ) + { tResult = T; + break; + } } + + LeaveCriticalSection( &sttLock ); + return tResult; +} + +ThreadData* __stdcall MSN_GetThreadByID( LONG id ) +{ + ThreadData* tResult = NULL; + + EnterCriticalSection( &sttLock ); + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { + ThreadData* T = sttThreads[ i ]; + if ( T != NULL && T->mUniqueID == id ) + { tResult = T; + break; + } } + + LeaveCriticalSection( &sttLock ); + return tResult; +} + +ThreadData* __stdcall MSN_GetThreadByPort( WORD wPort ) +{ + ThreadData* tResult = NULL; + + EnterCriticalSection( &sttLock ); + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { + ThreadData* T = sttThreads[ i ]; + if ( T == NULL ) + continue; + + if ( T->mP2pSession != NULL && T->mP2pSession->mIncomingPort == wPort ) { + tResult = T; + break; + } + + if ( T->mMsnFtp != NULL && T->mMsnFtp->mIncomingPort == wPort ) { + tResult = T; + break; + } } + + LeaveCriticalSection( &sttLock ); + return tResult; +} + +void __stdcall MSN_PingParentThread( ThreadData* parentThread, filetransfer* ft ) +{ + EnterCriticalSection( &sttLock ); + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { + if ( sttThreads[ i ] == parentThread ) { + parentThread->mP2pSession = ft; + ft->p2p_sendmsgid = ++ft->p2p_msgid; + parentThread->mP2PInitTrid = p2p_sendPortion( ft, parentThread ); + break; + } } + + LeaveCriticalSection( &sttLock ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// class ThreadData members + +static LONG sttThreadID = 1; + +ThreadData::ThreadData() +{ + memset( this, 0, sizeof ThreadData ); + mUniqueID = MyInterlockedIncrement( &sttThreadID ); + mGatewayTimeout = 2; + mWaitPeriod = 60; + mIsMainThread = false; +} + +ThreadData::~ThreadData() +{ + if ( s != NULL ) { + MSN_DebugLog( "Closing connection handle %08X", s ); + Netlib_CloseHandle( s ); + } + + if ( mMsnFtp != NULL ) { + delete mMsnFtp; + mMsnFtp = NULL; + } + + if ( mUniqueID ) + p2p_unregisterThreadSession( mUniqueID ); + + while (mFirstQueueItem != NULL) + { + TQueueItem* QI = mFirstQueueItem; + mFirstQueueItem = mFirstQueueItem->next; + free(QI); +} } + +void ThreadData::applyGatewayData( HANDLE hConn, bool isPoll ) +{ + char szHttpPostUrl[300]; + getGatewayUrl( szHttpPostUrl, sizeof( szHttpPostUrl ), isPoll ); + + MSN_DebugLog( "applying '%s' to %08X [%d]", szHttpPostUrl, this, GetCurrentThreadId() ); + + NETLIBHTTPPROXYINFO nlhpi = {0}; + nlhpi.cbSize = sizeof(nlhpi); + nlhpi.flags = NLHPIF_HTTP11; + nlhpi.szHttpGetUrl = NULL; + nlhpi.szHttpPostUrl = szHttpPostUrl; + nlhpi.firstPostSequence = 1; + MSN_CallService( MS_NETLIB_SETHTTPPROXYINFO, (WPARAM)hConn, (LPARAM)&nlhpi); +} + +static char sttFormatString[] = "http://gateway.messenger.hotmail.com/gateway/gateway.dll?Action=open&Server=%s&IP=%s"; + +void ThreadData::getGatewayUrl( char* dest, int destlen, bool isPoll ) +{ + if ( mSessionID[0] == 0 ) { + if ( mType == SERVER_NOTIFICATION || mType == SERVER_DISPATCH ) + mir_snprintf( dest, destlen, sttFormatString, "NS", "messenger.hotmail.com" ); + else + mir_snprintf( dest, destlen, sttFormatString, "SB", mServer ); + strcpy( mGatewayIP, MSN_DEFAULT_GATEWAY ); + } + else mir_snprintf( dest, destlen, "http://%s/gateway/gateway.dll?%sSessionID=%s", + mGatewayIP, ( isPoll ) ? "Action=poll&" : "", mSessionID ); +} + +void ThreadData::processSessionData( const char* str ) +{ + char tSessionID[40], tGateIP[ 40 ]; + + char* tDelim = ( char* )strchr( str, ';' ); + if ( tDelim == NULL ) + return; + + *tDelim = 0; tDelim += 2; + + if ( !sscanf( str, "SessionID=%s", tSessionID )) + return; + + char* tDelim2 = strchr( tDelim, ';' ); + if ( tDelim2 != NULL ) + *tDelim2 = '\0'; + + if ( !sscanf( tDelim, "GW-IP=%s", tGateIP )) + return; + +// MSN_DebugLog( "msn_httpGatewayUnwrapRecv printed '%s','%s' to %08X (%08X)", tSessionID, tGateIP, s, this ); + strcpy( mGatewayIP, tGateIP ); + strcpy( mSessionID, tSessionID ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// thread start code + +static void sttRegisterThread( ThreadData* s ) +{ + EnterCriticalSection( &sttLock ); + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + if ( sttThreads[ i ] == NULL ) + { sttThreads[ i ] = s; + break; + } + + LeaveCriticalSection( &sttLock ); +} + +static void sttUnregisterThread( ThreadData* s ) +{ + EnterCriticalSection( &sttLock ); + + for ( int i=0; i < MAX_THREAD_COUNT; i++ ) + { if ( sttThreads[ i ] == s ) + { sttThreads[ i ] = NULL; + break; + } } + + LeaveCriticalSection( &sttLock ); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct FORK_ARG { + HANDLE hEvent; + pThreadFunc threadcode; + ThreadData* arg; +}; + +static void __cdecl forkthread_r(struct FORK_ARG *fa) +{ + pThreadFunc callercode = fa->threadcode; + ThreadData* arg = fa->arg; + MSN_CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + sttRegisterThread( arg ); + MSN_DebugLog( "Starting thread %08X (%08X)", GetCurrentThreadId(), callercode ); + SetEvent(fa->hEvent); + __try { + callercode(arg); + } __finally { + MSN_DebugLog( "Leaving thread %08X (%08X)", GetCurrentThreadId(), callercode ); + sttUnregisterThread( arg ); + delete arg; + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + MSN_CallService(MS_SYSTEM_THREAD_POP, 0, 0); + } + return; +} + +void ThreadData::startThread( pThreadFunc parFunc ) +{ + FORK_ARG fa = { CreateEvent(NULL, FALSE, FALSE, NULL), parFunc, this }; + unsigned long rc = _beginthread(( pThreadFunc )forkthread_r, 0, &fa ); + if ((unsigned long) -1L != rc) + WaitForSingleObject(fa.hEvent, INFINITE); + CloseHandle(fa.hEvent); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +struct FORK_ARG2 { + HANDLE hEvent; + pThreadFunc threadcode; + void* arg; +}; + +static void __cdecl forkthread_r2(struct FORK_ARG2 *fa) +{ + pThreadFunc callercode = fa->threadcode; + void* arg = fa->arg; + MSN_CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + MSN_DebugLog( "Starting thread %08X (%08X)", GetCurrentThreadId(), callercode ); + SetEvent(fa->hEvent); + + __try { + callercode(arg); + } __finally { + MSN_DebugLog( "Leaving thread %08X (%08X)", GetCurrentThreadId(), callercode ); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + MSN_CallService(MS_SYSTEM_THREAD_POP, 0, 0); + } + return; +} + +void __stdcall MSN_StartThread( pThreadFunc parFunc, void* arg ) +{ + FORK_ARG2 fa = { CreateEvent(NULL, FALSE, FALSE, NULL), parFunc, arg }; + unsigned long rc = _beginthread(( pThreadFunc )forkthread_r2, 0, &fa ); + if ((unsigned long) -1L != rc) + WaitForSingleObject(fa.hEvent, INFINITE); + CloseHandle(fa.hEvent); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// HReadBuffer members + +HReadBuffer::HReadBuffer( ThreadData* T, int iStart ) +{ + owner = T; + buffer = ( BYTE* )T->mData; + totalDataSize = T->mBytesInData; + startOffset = iStart; +} + +HReadBuffer::~HReadBuffer() +{ + owner->mBytesInData = totalDataSize - startOffset; + if ( owner->mBytesInData != 0 ) + memmove( owner->mData, owner->mData + startOffset, owner->mBytesInData ); +} + +BYTE* HReadBuffer::surelyRead( int parBytes ) +{ + if ( startOffset + parBytes > totalDataSize ) + { + int tNewLen = totalDataSize - startOffset; + if ( tNewLen > 0 ) + memmove( buffer, buffer + startOffset, tNewLen ); + else + tNewLen = 0; + + startOffset = 0; + totalDataSize = tNewLen; + } + + int bufferSize = sizeof owner->mData; + if ( parBytes > bufferSize - startOffset ) { + MSN_DebugLog( "HReadBuffer::surelyRead: not enough memory, %d %d %d", parBytes, bufferSize, startOffset ); + return NULL; + } + + while( totalDataSize - startOffset < parBytes ) + { + 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