/* 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" static char sttGatewayHeader[] = "POST %s HTTP/1.1\r\n" "Accept: */*\r\n" "Content-Type: text/xml; charset=utf-8\r\n" "Content-Length: %d\r\n" "User-Agent: %s\r\n" "Host: %s\r\n" "Connection: Keep-Alive\r\n" "Cache-Control: no-cache\r\n\r\n"; //======================================================================================= int ThreadData::send( char* data, int datalen ) { if ( this == NULL ) return 0; NETLIBBUFFER nlb = { data, datalen, 0 }; mWaitPeriod = 60; if ( MyOptions.UseGateway && !( mType == SERVER_FILETRANS && mP2pSession != NULL )) { mGatewayTimeout = 2; if ( !MyOptions.UseProxy ) { TQueueItem* tNewItem = ( TQueueItem* )malloc( datalen + sizeof( void* ) + sizeof( int ) + 1 ); tNewItem->datalen = datalen; memcpy( tNewItem->data, data, datalen ); tNewItem->data[datalen] = 0; TQueueItem* p = mFirstQueueItem; if ( p != NULL ) { while ( p->next != NULL ) p = p->next; p ->next = tNewItem; } else mFirstQueueItem = tNewItem; tNewItem->next = NULL; return TRUE; } MSN_CallService( MS_NETLIB_SETPOLLINGTIMEOUT, WPARAM( s ), mGatewayTimeout ); } int rlen = MSN_CallService( MS_NETLIB_SEND, ( WPARAM )s, ( LPARAM )&nlb ); if ( rlen == SOCKET_ERROR ) { // should really also check if sendlen is the same as datalen MSN_DebugLog( "Send failed: %d", WSAGetLastError() ); return FALSE; } return TRUE; } //======================================================================================= // Receving data //======================================================================================= int ThreadData::recv_dg( char* data, long datalen ) { if ( mReadAheadBuffer != NULL ) { int tBytesToCopy = ( datalen >= mEhoughData ) ? mEhoughData : datalen; memcpy( data, mReadAheadBuffer, tBytesToCopy ); mEhoughData -= tBytesToCopy; if ( mEhoughData == 0 ) { free( mReadAheadBuffer ); mReadAheadBuffer = NULL; } else memmove( mReadAheadBuffer, mReadAheadBuffer + tBytesToCopy, mEhoughData ); return tBytesToCopy; } bool bCanPeekMsg = true; LBL_RecvAgain: int ret = 0; { NETLIBSELECT tSelect = {0}; tSelect.cbSize = sizeof( tSelect ); tSelect.dwTimeout = 1000; tSelect.hReadConns[ 0 ] = ( HANDLE )s; for ( int i=0; i < mGatewayTimeout || !bCanPeekMsg; i++ ) { if ( bCanPeekMsg ) { TQueueItem* QI = mFirstQueueItem; if ( QI != NULL ) { char szHttpPostUrl[300]; getGatewayUrl( szHttpPostUrl, sizeof( szHttpPostUrl ), QI->datalen == 0 ); char* tBuffer = ( char* )alloca( QI->datalen+400 ); int cbBytes = mir_snprintf( tBuffer, QI->datalen+400, sttGatewayHeader, szHttpPostUrl, QI->datalen, MSN_USER_AGENT, mGatewayIP); memcpy( tBuffer+cbBytes, QI->data, QI->datalen ); cbBytes += QI->datalen; tBuffer[ cbBytes ] = 0; NETLIBBUFFER nlb = { tBuffer, cbBytes, 0 }; ret = MSN_CallService( MS_NETLIB_SEND, ( WPARAM )s, ( LPARAM )&nlb ); if ( ret == SOCKET_ERROR ) { MSN_DebugLog( "Send failed: %d", WSAGetLastError() ); return 0; } mFirstQueueItem = QI->next; free( QI ); ret = 1; break; } } ret = MSN_CallService( MS_NETLIB_SELECT, 0, ( LPARAM )&tSelect ); if ( ret != 0 ) break; // Timeout switchboard session if inactive if ( !mIsMainThread && mJoinedCount <= 1 && --mWaitPeriod <= 0 ) { if (mJoinedCount == 0 || p2p_getFirstSession(mJoinedContacts[0]) == NULL) { MSN_DebugLog( "Dropping the idle switchboard due to the 60 sec timeout" ); return 0; } else mWaitPeriod = 60; } } } bCanPeekMsg = false; if ( ret == 0 ) { mGatewayTimeout += 2; if ( mGatewayTimeout > 8 ) mGatewayTimeout = 8; char szHttpPostUrl[300]; getGatewayUrl( szHttpPostUrl, sizeof( szHttpPostUrl ), true ); char szCommand[ 400 ]; int cbBytes = mir_snprintf( szCommand, sizeof( szCommand ), sttGatewayHeader, szHttpPostUrl, 0, MSN_USER_AGENT, mGatewayIP); NETLIBBUFFER nlb = { szCommand, cbBytes, 0 }; MSN_CallService( MS_NETLIB_SEND, ( WPARAM )s, ( LPARAM )&nlb ); goto LBL_RecvAgain; } NETLIBBUFFER nlb = { data, datalen, 0 }; ret = MSN_CallService( MS_NETLIB_RECV, ( WPARAM )s, ( LPARAM )&nlb ); if ( ret == 0 ) { MSN_DebugLog( "Connection closed gracefully"); return 0; } if ( ret < 0 ) { MSN_DebugLog( "Connection abortively closed, error %d", WSAGetLastError() ); return ret; } bCanPeekMsg = true; char* p = strstr( data, "\r\n" ); if ( p == NULL ) { MSN_DebugLog( "ACHTUNG! it's not a valid header: '%s'", data ); goto LBL_RecvAgain; } int status = 0; sscanf( data, "HTTP/1.1 %d", &status ); if ( status == 100 ) goto LBL_RecvAgain; int tContentLength = 0, hdrLen; { MimeHeaders tHeaders; const char* rest = tHeaders.readFromBuffer( p+2 ); if ( *rest == '\r' ) rest += 2; for ( int i=0; i < tHeaders.mCount; i++ ) { MimeHeader& H = tHeaders.mVals[i]; if ( stricmp( H.name, "X-MSN-Messenger" ) == 0 ) { if ( strstr( H.value, "Session=close" ) != 0 ) { return 0; } processSessionData( H.value ); } if ( stricmp( H.name, "Content-Length" ) == 0 ) tContentLength = atol( H.value ); } hdrLen = int( rest - data ); } if ( tContentLength == 0 ) goto LBL_RecvAgain; else { mGatewayTimeout = 1; mWaitPeriod = 60; } ret -= hdrLen; if ( ret <= 0 ) { nlb.buf = data; nlb.len = datalen; ret = MSN_CallService( MS_NETLIB_RECV, ( WPARAM )s, ( LPARAM )&nlb ); if ( ret <= 0 ) return ret; } else memmove( data, data+hdrLen, ret ); if ( tContentLength > ret ) { tContentLength -= ret; mReadAheadBuffer = ( char* )calloc( tContentLength+1, 1 ); mReadAheadBuffer[ tContentLength ] = 0; mEhoughData = tContentLength; nlb.buf = mReadAheadBuffer; while ( tContentLength > 0 ) { nlb.len = tContentLength; int ret2 = MSN_CallService( MS_NETLIB_RECV, ( WPARAM )s, ( LPARAM )&nlb ); if ( ret2 <= 0 ) { free( mReadAheadBuffer ); mReadAheadBuffer = NULL; return ret2; } tContentLength -= ret2; nlb.buf += ret2; } } return ret; } int ThreadData::recv( char* data, long datalen ) { if ( MyOptions.UseGateway && !MyOptions.UseProxy ) if ( mType != SERVER_FILETRANS || mP2pSession == 0 ) return recv_dg( data, datalen ); NETLIBBUFFER nlb = { data, datalen, 0 }; LBL_RecvAgain: if ( !mIsMainThread && !MyOptions.UseGateway && !MyOptions.UseProxy ) { mWaitPeriod = 60; while ( --mWaitPeriod >= 0 ) { NETLIBSELECT nls = { 0 }; nls.cbSize = sizeof( nls ); nls.dwTimeout = 1000; nls.hReadConns[0] = s; if ( MSN_CallService( MS_NETLIB_SELECT, 0, ( LPARAM )&nls ) != 0 ) break; } if ( mWaitPeriod < 0 && mJoinedCount <= 1 ) { if (mJoinedCount == 0 || p2p_getFirstSession(mJoinedContacts[0]) == NULL) { MSN_DebugLog( "Dropping the idle switchboard due to the 60 sec timeout" ); return 0; } else mWaitPeriod = 60; } } int ret = MSN_CallService( MS_NETLIB_RECV, ( WPARAM )s, ( LPARAM )&nlb ); if ( ret == 0 ) { MSN_DebugLog( "Connection closed gracefully" ); return 0; } if ( ret < 0 ) { MSN_DebugLog( "Connection abortively closed, error %d", WSAGetLastError() ); return ret; } if ( MyOptions.UseGateway) { if ( ret == 1 && *data == 0 ) { int tOldTimeout = MSN_CallService( MS_NETLIB_SETPOLLINGTIMEOUT, WPARAM( s ), 2 ); tOldTimeout += 2; if ( tOldTimeout > 8 ) tOldTimeout = 8; MSN_CallService( MS_NETLIB_SETPOLLINGTIMEOUT, WPARAM( s ), tOldTimeout ); goto LBL_RecvAgain; } else MSN_CallService( MS_NETLIB_SETPOLLINGTIMEOUT, WPARAM( s ), 1 ); } return ret; }