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_commands.cpp | 1841 +++++++++++++++++++++++++++ 1 file changed, 1841 insertions(+) create mode 100644 miranda-wine/protocols/MSN/msn_commands.cpp (limited to 'miranda-wine/protocols/MSN/msn_commands.cpp') diff --git a/miranda-wine/protocols/MSN/msn_commands.cpp b/miranda-wine/protocols/MSN/msn_commands.cpp new file mode 100644 index 0000000..4e84931 --- /dev/null +++ b/miranda-wine/protocols/MSN/msn_commands.cpp @@ -0,0 +1,1841 @@ +/* +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 +#include +#include +#include + +#include "resource.h" + +#include "msn_md5.h" + +void __cdecl MSNNudgeThread( ThreadData* info ); +void __cdecl MSNServerThread( ThreadData* info ); +void __cdecl MSNSendfileThread( ThreadData* info ); + +int MSN_GetPassportAuth( char* authChallengeInfo, char*& parResult ); + +void mmdecode(char *trg, char *str); + +void MSN_ChatStart(ThreadData* info); + + int tridUrlInbox = -1, tridUrlEdit = -1; + +char* sid = NULL; +char* kv = NULL; +char* MSPAuth = NULL; +char* passport = NULL; +char* profileURL = NULL; +char* rru = NULL; +extern HANDLE hMSNNudge; + +extern int msnPingTimeout, msnPingTimeoutCurrent; + +unsigned long sl; + +///////////////////////////////////////////////////////////////////////////////////////// +// MSN_ReceiveMessage - receives message or a file from the server +///////////////////////////////////////////////////////////////////////////////////////// + +static int sttDivideWords( char* parBuffer, int parMinItems, char** parDest ) +{ + int i; + for ( i=0; i < parMinItems; i++ ) { + parDest[ i ] = parBuffer; + + int tWordLen = strcspn( parBuffer, " \t" ); + if ( tWordLen == 0 ) + return i; + + parBuffer += tWordLen; + if ( *parBuffer != '\0' ) { + int tSpaceLen = strspn( parBuffer, " \t" ); + memset( parBuffer, 0, tSpaceLen ); + parBuffer += tSpaceLen; + } } + + return i; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// MSN_GetMyHostAsString - retrieves a host address as a string + +int __stdcall MSN_GetMyHostAsString( char* parBuf, int parBufSize ) +{ + IN_ADDR in; + hostent* myhost; + + if ( msnExternalIP != NULL ) + strncpy( parBuf, msnExternalIP, parBufSize ); + else + gethostname( parBuf, parBufSize ); + + if ( MSN_GetByte( "AutoGetHost", 1 )) + MSN_SetString( NULL, "YourHost", parBuf ); + else + MSN_GetStaticString( "YourHost", NULL, parBuf, parBufSize ); + + long ipaddrlong = inet_addr( parBuf ); + if ( ipaddrlong != INADDR_NONE ) + in.S_un.S_addr = ipaddrlong; + else + { myhost = gethostbyname( parBuf ); + if ( myhost == NULL ) { + { TWinErrorCode tError; + MSN_ShowError( "Unknown or invalid host name was specified (%s). Error %d: %s.", + parBuf, tError.mErrorCode, tError.getText()); + } + + return 1; + } + memcpy( &in, myhost->h_addr, 4 ); + } + + strncpy( parBuf, inet_ntoa( in ), parBufSize ); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Starts a file sending thread + +void MSN_ConnectionProc( HANDLE hNewConnection, DWORD dwRemoteIP, void* ) +{ + MSN_DebugLog( "File transfer connection accepted" ); + + WORD localPort = 0; + SOCKET s = MSN_CallService( MS_NETLIB_GETSOCKET, ( WPARAM )hNewConnection, 0 ); + if ( s != INVALID_SOCKET) { + SOCKADDR_IN saddr; + int len = sizeof( saddr ); + if ( getsockname( s, ( SOCKADDR* )&saddr, &len ) != SOCKET_ERROR ) + localPort = ntohs( saddr.sin_port ); + } + + if ( localPort != 0 ) { + ThreadData* T = MSN_GetThreadByPort( localPort ); + if ( T != NULL ) { + T->s = hNewConnection; + if ( T->mMsnFtp != NULL ) { + T->mMsnFtp->mIncomingPort = 0; + SetEvent( T->mMsnFtp->hWaitEvent ); + } + else { + T->mP2pSession->mIncomingPort = 0; + SetEvent( T->mP2pSession->hWaitEvent ); + } + return; + } + MSN_DebugLog( "There's no registered file transfers for incoming port #%d, connection closed", localPort ); + } + else MSN_DebugLog( "Unable to determine the local port, file server connection closed." ); + + Netlib_CloseHandle( hNewConnection ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Processes e-mail notification + +static void sttNotificationMessage( const char* msgBody, bool isInitial ) +{ + char tBuffer[512]; + char tBuffer2[512]; + bool tIsPopup = ServiceExists( MS_POPUP_ADDPOPUP ) != 0; + int UnreadMessages = 0, UnreadJunkEmails = 0; + + MimeHeaders tFileInfo; + tFileInfo.readFromBuffer( msgBody ); + + const char* From = tFileInfo[ "From" ]; + const char* Subject = tFileInfo[ "Subject" ]; + const char* Fromaddr = tFileInfo[ "From-Addr" ]; + { + const char* p; + if (( p = tFileInfo[ "Inbox-Unread" ] ) != NULL ) + UnreadMessages = atoi( p ); + if (( p = tFileInfo[ "Folders-Unread" ] ) != NULL ) + UnreadJunkEmails = atoi( p ); + } + + if ( From != NULL && Subject != NULL && Fromaddr != NULL ) { + const char* SrcFolder = tFileInfo[ "Src-Folder" ]; + const char* DestFolder = tFileInfo[ "Dest-Folder" ]; + if ( DestFolder != NULL && SrcFolder == NULL ) { + UnreadMessages = strcmp( DestFolder, "ACTIVE" ) == 0; + UnreadJunkEmails = strcmp( DestFolder, "HM_BuLkMail_" ) == 0; + } + + // nothing to do, a fake notification + if ( UnreadMessages == 0 && UnreadJunkEmails == 0 ) + return; + + char mimeFrom[ 1024 ], mimeSubject[ 1024 ]; + mmdecode( mimeFrom, ( char* )From ); + mmdecode( mimeSubject, ( char* )Subject ); + + if ( !strcmpi( From, Fromaddr )) { + if ( tIsPopup ) { + mir_snprintf( tBuffer, sizeof( tBuffer ), MSN_Translate( "Hotmail from %s" ), mimeFrom ); + mir_snprintf( tBuffer2, sizeof( tBuffer2 ), MSN_Translate( "Subject: %s" ), mimeSubject ); + } + else mir_snprintf( tBuffer, sizeof( tBuffer ), MSN_Translate("A new mail has come from %s (title: %s)."), mimeFrom, mimeSubject ); + } + else { + if ( tIsPopup ) { + mir_snprintf( tBuffer, sizeof( tBuffer ), MSN_Translate("Hotmail from %s (%s)"),mimeFrom, Fromaddr ); + mir_snprintf( tBuffer2, sizeof( tBuffer2 ), MSN_Translate("Subject: %s"), mimeSubject ); + } + else mir_snprintf( tBuffer, sizeof( tBuffer ), MSN_Translate("A new mail has come from %s (%s) (title: %s)."),mimeFrom, Fromaddr, mimeSubject ); + } } + else { + const char* MailData = tFileInfo[ "Mail-Data" ]; + if ( MailData != NULL ) { + const char* p = strstr( MailData, "" ); + if ( p != NULL ) + UnreadMessages = atoi( p+4 ); + if (( p = strstr( MailData, "" )) != NULL ) + UnreadJunkEmails = atoi( p+4 ); + } + + // nothing to do, a fake notification + if ( UnreadMessages == 0 && UnreadJunkEmails == 0 ) + return; + + char* dest; + if ( tIsPopup ) { + mir_snprintf( tBuffer, sizeof( tBuffer ), MSN_Translate( "Hotmail" )); + dest = tBuffer2; + } + else dest = tBuffer; + mir_snprintf( dest, sizeof( tBuffer ), MSN_Translate( "Unread mail is available: %d messages (%d junk e-mails)." ), + UnreadMessages, UnreadJunkEmails ); + } + + // Disable to notify receiving hotmail + if ( !MSN_GetByte( "DisableHotmail", 1 )) { + if ( UnreadMessages != 0 || !MSN_GetByte( "DisableHotmailJunk", 0 )) { + SkinPlaySound( mailsoundname ); + if ( tIsPopup ) + MSN_ShowPopup( tBuffer, tBuffer2, MSN_ALLOW_ENTER + MSN_ALLOW_MSGBOX + MSN_HOTMAIL_POPUP ); + else + MessageBoxA( NULL, tBuffer, "MSN Protocol", MB_OK | MB_ICONINFORMATION ); + } } + + if ( !MSN_GetByte( "RunMailerOnHotmail", 0 )) + return; + + if ( !MSN_GetStaticString( "MailerPath", NULL, tBuffer, sizeof( tBuffer ))) { + if ( tBuffer[0] ) { + char* tParams = ""; + char* p = tBuffer; + + if ( *p == '\"' ) { + char* tEndPtr = strchr( p+1, '\"' ); + if ( tEndPtr != NULL ) { + *tEndPtr = 0; + strdel( p, 1 ); + tParams = tEndPtr+1; + goto LBL_Run; + } } + + p = strchr( p+1, ' ' ); + if ( p != NULL ) { + *p = 0; + tParams = p+1; + } +LBL_Run: + while ( *tParams == ' ' ) + tParams++; + + MSN_DebugLog( "Running mailer \"%s\" with params \"%s\"", tBuffer, tParams ); + ShellExecuteA( NULL, "open", tBuffer, tParams, NULL, TRUE ); +} } } + +///////////////////////////////////////////////////////////////////////////////////////// +// Processes various invitations + +static void sttInviteMessage( ThreadData* info, const char* msgBody, char* email, char* nick ) +{ + MimeHeaders tFileInfo; + tFileInfo.readFromBuffer( msgBody ); + + const char* Appname = tFileInfo[ "Application-Name" ]; + const char* AppGUID = tFileInfo[ "Application-GUID" ]; + const char* Invcommand = tFileInfo[ "Invitation-Command" ]; + const char* Invcookie = tFileInfo[ "Invitation-Cookie" ]; + const char* Appfile = tFileInfo[ "Application-File" ]; + const char* Appfilesize = tFileInfo[ "Application-FileSize" ]; + const char* IPAddress = tFileInfo[ "IP-Address" ]; + const char* Port = tFileInfo[ "Port" ]; + const char* AuthCookie = tFileInfo[ "AuthCookie" ]; + const char* SessionID = tFileInfo[ "Session-ID" ]; + const char* SessionProtocol = tFileInfo[ "Session-Protocol" ]; + + if ( AppGUID != NULL ) { + if ( !strcmp( AppGUID, "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}" )) { + MSN_ShowPopup( MSN_GetContactName( info->mJoinedContacts[0] ), + MSN_Translate( "Contact tried to open an audio conference (currently not supported)" ), MSN_ALLOW_MSGBOX ); + return; + } } + + if ( Invcommand && ( strcmp( Invcommand, "CANCEL" ) == 0 )) { + delete info->mMsnFtp; + info->mMsnFtp = NULL; + } + + if ( Appname != NULL && Appfile != NULL && Appfilesize != NULL ) { // receive first + filetransfer* ft = info->mMsnFtp = new filetransfer(); + + ft->mThreadId = info->mUniqueID; + ft->std.hContact = MSN_HContactFromEmail( email, nick, 1, 1 ); + replaceStr( ft->std.currentFile, Appfile ); + Utf8Decode( ft->std.currentFile, &ft->wszFileName ); + ft->fileId = -1; + ft->std.currentFileSize = atol( Appfilesize ); + ft->std.totalBytes = atol( Appfilesize ); + ft->std.totalFiles = 1; + ft->szInvcookie = strdup( Invcookie ); + + int tFileNameLen = strlen( ft->std.currentFile ); + char tComment[ 40 ]; + int tCommentLen = mir_snprintf( tComment, sizeof( tComment ), "%lu bytes", ft->std.currentFileSize ); + char* szBlob = ( char* )malloc( sizeof( DWORD ) + tFileNameLen + tCommentLen + 2 ); + *( PDWORD )szBlob = ( DWORD )ft; + strcpy( szBlob + sizeof( DWORD ), ft->std.currentFile ); + strcpy( szBlob + sizeof( DWORD ) + tFileNameLen + 1, tComment ); + + PROTORECVEVENT pre; + pre.flags = 0; + pre.timestamp = ( DWORD )time( NULL ); + pre.szMessage = ( char* )szBlob; + pre.lParam = ( LPARAM )( char* )Invcookie; + + CCSDATA ccs; + ccs.hContact = MSN_HContactFromEmail( email, nick, 1, 1 ); + ccs.szProtoService = PSR_FILE; + ccs.wParam = 0; + ccs.lParam = ( LPARAM )⪯ + MSN_CallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs ); + return; + } + + if ( IPAddress != NULL && Port != NULL && AuthCookie != NULL ) { // receive Second + ThreadData* newThread = new ThreadData; + strcpy( newThread->mServer, IPAddress ); + strcat( newThread->mServer, ":" ); + strcat( newThread->mServer, Port ); + newThread->mType = SERVER_FILETRANS; + + if ( info->mMsnFtp == NULL ) + { + ThreadData* otherThread = MSN_GetOtherContactThread( info ); + if ( otherThread ) + { + info->mMsnFtp = otherThread->mMsnFtp; + otherThread->mMsnFtp = NULL; + } + } + + newThread->mMsnFtp = info->mMsnFtp; info->mMsnFtp = NULL; + strcpy( newThread->mCookie, AuthCookie ); + + MSN_DebugLog( "Connecting to '%s'...", newThread->mServer ); + newThread->startThread(( pThreadFunc )MSNServerThread ); + return; + } + + if ( Invcommand != NULL && Invcookie != NULL && Port == NULL && AuthCookie == NULL && SessionID == NULL ) { // send 1 + ft_startFileSend( info, Invcommand, Invcookie ); + return; + } + + if ( Appname == NULL && SessionID != NULL && SessionProtocol != NULL ) { // netmeeting send 1 + if ( !strcmpi( Invcommand,"ACCEPT" )) { + char ipaddr[256]; + MSN_GetMyHostAsString( ipaddr, sizeof( ipaddr )); + + ShellExecuteA(NULL, "open", "conf.exe", NULL, NULL, SW_SHOW); + Sleep(3000); + + char command[ 1024 ]; + int nBytes = mir_snprintf( command, sizeof( command ), + "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n" + "Invitation-Command: ACCEPT\r\n" + "Invitation-Cookie: %s\r\n" + "Session-ID: {1A879604-D1B8-11D7-9066-0003FF431510}\r\n" + "Launch-Application: TRUE\r\n" + "IP-Address: %s\r\n\r\n", + Invcookie, ipaddr); + info->sendPacket( "MSG", "N %d\r\n%s", nBytes, command ); + } + return; + } + + if ( Appname != NULL && !strcmpi( Appname,"NetMeeting" )) { // netmeeting receive 1 + char command[ 1024 ]; + int nBytes; + + mir_snprintf( command, sizeof( command ), "Accept NetMeeting request from %s?", email ); + + if ( MessageBoxA( NULL, command, "MSN Protocol", MB_YESNO | MB_ICONQUESTION ) == IDYES ) { + char ipaddr[256]; + MSN_GetMyHostAsString( ipaddr, sizeof( ipaddr )); + + nBytes = mir_snprintf( command, sizeof( command ), + "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n" + "Invitation-Command: ACCEPT\r\n" + "Invitation-Cookie: %s\r\n" + "Session-ID: {A2ED5ACF-F784-4B47-A7D4-997CD8F643CC}\r\n" + "Session-Protocol: SM1\r\n" + "Launch-Application: TRUE\r\n" + "Request-Data: IP-Address:\r\n" + "IP-Address: %s\r\n\r\n", + Invcookie, ipaddr); + } + else { + nBytes = mir_snprintf( command, sizeof( command ), + "MIME-Version: 1.0\r\n" + "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n" + "Invitation-Command: CANCEL\r\n" + "Invitation-Cookie: %s\r\n" + "Cancel-Code: REJECT\r\n\r\n", + Invcookie); + } + info->sendPacket( "MSG", "N %d\r\n%s", nBytes, command ); + return; + } + + if ( IPAddress != NULL && Port == NULL && SessionID != NULL && SessionProtocol == NULL ) { // netmeeting receive 2 + char ipaddr[256]; + mir_snprintf( ipaddr, sizeof( ipaddr ), "callto://%s", IPAddress); + ShellExecuteA(NULL, "open", ipaddr, NULL, NULL, SW_SHOW); +} } + +///////////////////////////////////////////////////////////////////////////////////////// +// Processes any received MSG + +void MSN_ReceiveMessage( ThreadData* info, char* cmdString, char* params ) +{ + union { + char* tWords[ 3 ]; + struct { char *fromEmail, *fromNick, *strMsgBytes; } data; + }; + + if ( sttDivideWords( params, 3, tWords ) != 3 ) { + MSN_DebugLog( "Invalid %.3s command, ignoring", cmdString ); + return; + } + + int msgBytes = atol( data.strMsgBytes ); + + UrlDecode( data.fromEmail ); UrlDecode( data.fromNick ); + + char* msg = ( char* )alloca( msgBytes+1 ); + + int bytesFromData = min( info->mBytesInData, msgBytes ); + memcpy( msg, info->mData, bytesFromData ); + info->mBytesInData -= bytesFromData; + memmove( info->mData, info->mData + bytesFromData, info->mBytesInData ); + + while ( bytesFromData < msgBytes ) { + int recvResult; + recvResult = info->recv( msg + bytesFromData, msgBytes - bytesFromData ); + if ( recvResult <= 0 ) + return; + + bytesFromData += recvResult; + } + + msg[ msgBytes ] = 0; + MSN_DebugLog( "Message:\n%s", msg ); + + MimeHeaders tHeader; + const char* msgBody = tHeader.readFromBuffer( msg ); + + // message from the server (probably) + if (( strchr( data.fromEmail, '@' ) == NULL ) && strcmpi( data.fromEmail, "Hotmail" )) + return; + + const char* tContentType = tHeader[ "Content-Type" ]; + if ( tContentType == NULL ) + return; + + if ( !strnicmp( tContentType, "text/plain", 10 )) { + CCSDATA ccs; + HANDLE tContact = MSN_HContactFromEmail( data.fromEmail, data.fromNick, 1, 1 ); + + int isRtl = FALSE; + { const char* p = tHeader[ "X-MMS-IM-Format" ]; + if ( p != NULL ) + if ( strstr( p, "RL=1" ) != NULL ) + isRtl = TRUE; + } + + wchar_t* tRealBody = NULL; + int tRealBodyLen = 0; + if ( strstr( tContentType, "charset=UTF-8" )) + { Utf8Decode(( char* )msgBody, &tRealBody ); + tRealBodyLen = wcslen( tRealBody ); + } + int tMsgBodyLen = strlen( msgBody ); + + char* tPrefix = NULL; + int tPrefixLen = 0; + + if ( info->mJoinedCount > 1 && info->mJoinedContacts != NULL ) { + if ( msnHaveChatDll ) + MSN_ChatStart( info ); + else if ( info->mJoinedContacts[0] != tContact ) { + for ( int j=1; j < info->mJoinedCount; j++ ) { + if ( info->mJoinedContacts[j] == tContact ) { + char* tNickName = MSN_GetContactName( info->mJoinedContacts[j] ); + tPrefixLen = strlen( tNickName )+2; + tPrefix = ( char* )alloca( tPrefixLen+1 ); + strcpy( tPrefix, "<" ); + strcat( tPrefix, tNickName ); + strcat( tPrefix, "> " ); + ccs.hContact = info->mJoinedContacts[ 0 ]; + break; + } } } + } + else ccs.hContact = tContact; + + int tMsgBufLen = tMsgBodyLen+1 + (tRealBodyLen+1)*sizeof( wchar_t ) + tPrefixLen*(sizeof( wchar_t )+1); + char* tMsgBuf = ( char* )alloca( tMsgBufLen ); + char* p = tMsgBuf; + + if ( tPrefixLen != 0 ) { + memcpy( p, tPrefix, tPrefixLen ); + p += tPrefixLen; + } + strcpy( p, msgBody ); + p += tMsgBodyLen+1; + + if ( tPrefixLen != 0 ) { + MultiByteToWideChar( CP_ACP, 0, tPrefix, tPrefixLen, ( wchar_t* )p, tPrefixLen ); + p += tPrefixLen*sizeof( wchar_t ); + } + if ( tRealBodyLen != 0 ) { + memcpy( p, tRealBody, sizeof( wchar_t )*( tRealBodyLen+1 )); + free( tRealBody ); + } + + if ( info->mChatID[0] ) { + GCDEST gcd = {0}; + GCEVENT gce = {0}; + + gcd.pszModule = msnProtocolName; + gcd.pszID = info->mChatID; + gcd.iType = GC_EVENT_MESSAGE; + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gce.pszUID = data.fromEmail; + gce.pszNick = MSN_GetContactName(MSN_HContactFromEmail(data.fromEmail, NULL, 1, 1)); + gce.time = time(NULL); + gce.bIsMe = FALSE; + gce.pszText = (char*)EscapeChatTags(tMsgBuf); + //gce.pszText = tMsgBuf; + gce.bAddToLog = TRUE; + MSN_CallService(MS_GC_EVENT, NULL, (LPARAM)&gce); + free(( void* )gce.pszText); + } + else { + PROTORECVEVENT pre; + pre.szMessage = ( char* )tMsgBuf; + pre.flags = PREF_UNICODE + (( isRtl ) ? PREF_RTL : 0); + pre.timestamp = ( DWORD )time(NULL); + pre.lParam = 0; + + ccs.szProtoService = PSR_MESSAGE; + ccs.wParam = 0; + ccs.lParam = ( LPARAM )⪯ + MSN_CallService( MS_PROTO_CHAINRECV, 0, ( LPARAM )&ccs ); + } + return; + } + + if ( !strnicmp( tContentType, "text/x-msmsgsprofile", 20 )) { + MimeHeaders tFileInfo; + tFileInfo.readFromBuffer( msg ); + replaceStr( sid, tFileInfo[ "sid" ] ); + replaceStr( kv, tFileInfo[ "kv" ] ); + replaceStr( MSPAuth, tFileInfo[ "MSPAuth" ] ); + replaceStr( msnExternalIP, tFileInfo[ "ClientIP" ] ); + + if ( msnExternalIP != NULL && MSN_GetByte( "AutoGetHost", 1 )) + MSN_SetString( NULL, "YourHost", msnExternalIP ); + + return; + } + + if ( !strnicmp( tContentType, "text/x-msmsgscontrol", 20 )) { + MimeHeaders tFileInfo; + tFileInfo.readFromBuffer( msg ); + + char* tTypingUser = NULL; + + for ( int j=0; j < tFileInfo.mCount; j++ ) + if ( !strcmpi( tFileInfo.mVals[ j ].name, "TypingUser" )) + tTypingUser = tFileInfo.mVals[ j ].value; + + if ( tTypingUser != NULL && info->mChatID[0] == 0 ) { + char userNick[ 388 ]; + strcpy( userNick, tTypingUser ); + + HANDLE hContact = MSN_HContactFromEmail( tTypingUser, userNick, 1, 0 ); + if ( hContact != NULL ) + strcpy( userNick, MSN_GetContactName( hContact )); + + MSN_CallService( MS_PROTO_CONTACTISTYPING, WPARAM( hContact ), 5 ); + + if ( MSN_GetByte( "DisplayTyping", 0 )) + MSN_ShowPopup( userNick, MSN_Translate( "typing..." ), 0 ); + } + + return; + } + + //WIZZ + if ( !strnicmp( tContentType, "text/x-msnmsgr-datacast", 23 )) { + CCSDATA ccs; + HANDLE tContact = MSN_HContactFromEmail( data.fromEmail, data.fromNick, 1, 1 ); + + wchar_t* tRealBody = NULL; + int tRealBodyLen = 0; + if ( strstr( tContentType, "charset=UTF-8" )) + { Utf8Decode(( char* )msgBody, &tRealBody ); + tRealBodyLen = wcslen( tRealBody ); + } + int tMsgBodyLen = strlen( msgBody ); + + char* tPrefix = NULL; + int tPrefixLen = 0; + + if ( info->mJoinedCount > 1 && info->mJoinedContacts != NULL ) { + if ( msnHaveChatDll ) + MSN_ChatStart( info ); + else if ( info->mJoinedContacts[0] != tContact ) { + for ( int j=1; j < info->mJoinedCount; j++ ) { + if ( info->mJoinedContacts[j] == tContact ) { + char* tNickName = MSN_GetContactName( info->mJoinedContacts[j] ); + tPrefixLen = strlen( tNickName )+2; + tPrefix = ( char* )alloca( tPrefixLen+1 ); + strcpy( tPrefix, "<" ); + strcat( tPrefix, tNickName ); + strcat( tPrefix, "> " ); + ccs.hContact = info->mJoinedContacts[ 0 ]; + break; + } } } + } + else ccs.hContact = tContact; + + int tMsgBufLen = tMsgBodyLen+1 + (tRealBodyLen+1)*sizeof( wchar_t ) + tPrefixLen*(sizeof( wchar_t )+1); + char* tMsgBuf = ( char* )alloca( tMsgBufLen ); + char* p = tMsgBuf; + + if ( tPrefixLen != 0 ) { + memcpy( p, tPrefix, tPrefixLen ); + p += tPrefixLen; + } + strcpy( p, msgBody ); + p += tMsgBodyLen+1; + + if ( tPrefixLen != 0 ) { + MultiByteToWideChar( CP_ACP, 0, tPrefix, tPrefixLen, ( wchar_t* )p, tPrefixLen ); + p += tPrefixLen*sizeof( wchar_t ); + } + if ( tRealBodyLen != 0 ) { + memcpy( p, tRealBody, sizeof( wchar_t )*( tRealBodyLen+1 )); + free( tRealBody ); + } + + if( !strnicmp(tMsgBuf,"ID: 1",5)) + NotifyEventHooks(hMSNNudge,(WPARAM) tContact,0); + return; + } + + if ( !strnicmp( tContentType,"text/x-msmsgsemailnotification", 30 )) + sttNotificationMessage( msgBody, false ); + else if ( !strnicmp( tContentType, "text/x-msmsgsinitialemailnotification", 37 )) + sttNotificationMessage( msgBody, true ); + else if ( !strnicmp( tContentType, "text/x-msmsgsinitialmdatanotification", 37 )) + sttNotificationMessage( msgBody, true ); + else if ( !strnicmp( tContentType, "text/x-msmsgsinvite", 19 )) + sttInviteMessage( info, msgBody, data.fromEmail, data.fromNick ); + else if ( !strnicmp( tContentType, "application/x-msnmsgrp2p", 24 )) + p2p_processMsg( info, msgBody ); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Process user addition + +HANDLE sttProcessAdd( int trid, int listId, char* userEmail, char* userNick ) +{ + if ( trid == msnSearchID ) { + msnNsThread->sendPacket( "REM", "BL %s", userEmail ); + + PROTOSEARCHRESULT isr; + memset( &isr, 0, sizeof( isr )); + isr.cbSize = sizeof( isr ); + isr.nick = userNick; + isr.email = userEmail; + MSN_SendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, ( HANDLE )msnSearchID, ( LPARAM )&isr ); + MSN_SendBroadcast( NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, ( HANDLE )msnSearchID, 0 ); + + msnSearchID = -1; + return NULL; + } + + UrlDecode( userEmail ); UrlDecode( userNick ); + if ( !IsValidListCode( listId )) + return NULL; + + HANDLE hContact = MSN_HContactFromEmail( userEmail, userNick, 1, 1 ); + Utf8Decode( userNick ); + int mask = Lists_Add( listId, userEmail, userNick ); + + if ( listId == LIST_RL && ( mask & ( LIST_FL+LIST_AL+LIST_BL )) == 0 ) + MSN_AddAuthRequest( hContact, userEmail, userNick ); + + return hContact; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// MSN_HandleCommands - process commands from the server +///////////////////////////////////////////////////////////////////////////////////////// + +static bool sttIsSync = false; +static int sttListNumber = 0; +static HANDLE sttListedContact = NULL; +static long sttListedContactMask; + +static void sttDeleteUnusedSetting( long mask, const char* settingName ) +{ if (( sttListedContactMask & mask ) == 0 ) + DBDeleteContactSetting( sttListedContact, msnProtocolName, settingName ); +} + +static void sttProcessListedContactMask() +{ + if ( sttListedContact == NULL ) + return; + + sttDeleteUnusedSetting( 0x0001, "Phone" ); + sttDeleteUnusedSetting( 0x0002, "CompanyPhone" ); + sttDeleteUnusedSetting( 0x0004, "Cellular" ); + sttDeleteUnusedSetting( 0x0008, "OnMobile" ); + sttDeleteUnusedSetting( 0x0010, "OnMsnMobile" ); +} + +static bool sttAddGroup( char* params, bool isFromBoot ) +{ + union { + char* tWords[ 2 ]; + struct { char *grpName, *grpId; } data; + }; + + if ( sttDivideWords( params, 2, tWords ) != 2 ) + return false; + + UrlDecode( data.grpName ); + MSN_AddGroup( data.grpName, data.grpId ); + if ( hGroupAddEvent != NULL ) + SetEvent( hGroupAddEvent ); + + int i; + char str[ 10 ]; + + for ( i=0; true; i++ ) { + ltoa( i, str, 10 ); + + DBVARIANT dbv; + if ( DBGetContactSettingStringUtf( NULL, "CListGroups", str, &dbv )) + break; + + bool result = !stricmp( dbv.pszVal+1, data.grpName ); + MSN_FreeVariant( &dbv ); + if ( result ) { + MSN_SetGroupNumber( data.grpId, i ); + return true; + } } + + if ( isFromBoot ) { + MSN_SetGroupNumber( data.grpId, i ); + + if ( MyOptions.ManageServer ) { + char szNewName[ 128 ]; + mir_snprintf( szNewName, sizeof szNewName, "%c%s", 1 | GROUPF_EXPANDED, data.grpName ); + DBWriteContactSettingStringUtf( NULL, "CListGroups", str, szNewName ); + CallService( MS_CLUI_GROUPADDED, i, 0 ); + } } + return true; +} + +static void sttSwapInt64( LONGLONG* parValue ) +{ + BYTE* p = ( BYTE* )parValue; + for ( int i=0; i < 4; i++ ) { + BYTE temp = p[i]; + p[i] = p[7-i]; + p[7-i] = temp; +} } + +int MSN_HandleCommands( ThreadData* info, char* cmdString ) +{ + char* params = ""; + int trid = -1; + sttIsSync = false; + + if ( cmdString[3] ) { + if ( isdigit(( BYTE )cmdString[ 4 ] )) { + trid = strtol( cmdString+4, ¶ms, 10 ); + switch ( *params ) { + case ' ': case '\0': case '\t': case '\n': + while ( *params == ' ' || *params == '\t' ) + params++; + break; + + default: params = cmdString+4; + } } + else params = cmdString+4; + } + MSN_DebugLog("%S", cmdString); + + if ( info->mType == SERVER_NOTIFICATION ) + msnPingTimeout = msnPingTimeoutCurrent; + + switch(( *( PDWORD )cmdString & 0x00FFFFFF ) | 0x20000000 ) + { + case ' KCA': //********* ACK: section 8.7 Instant Messages + if ( info->mP2PInitTrid == trid ) { + info->mP2PInitTrid = 0; + p2p_sendFeedStart( info->mP2pSession, info ); + info->mP2pSession = NULL; + } + else if ( info->mJoinedCount > 0 && MyOptions.SlowSend ) + MSN_SendBroadcast( info->mJoinedContacts[0], ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE )trid, 0 ); + break; + + case ' CDA': // ADC - MSN v10 addition command + { + char* tWords[ 10 ]; + char *userNick = NULL, *userEmail = NULL, *userId = NULL, *groupId = NULL; + int nTerms = sttDivideWords( params, 10, tWords ); + if ( nTerms < 2 ) + goto LBL_InvalidCommand; + + for ( int i=1; i < nTerms; i++ ) { + char* p = tWords[ i ]; + if ( *p == 'F' && p[1] == '=' ) + userNick = p+2; + else if ( *p == 'N' && p[1] == '=' ) + userEmail = p+2; + else if ( *p == 'C' && p[1] == '=' ) + userId = p+2; + else + groupId = p; + } + + HANDLE hContact; + if ( userEmail == NULL ) { + if ( userId == NULL || groupId == NULL ) + goto LBL_InvalidCommand; + hContact = MSN_HContactById( userId ); + } + else { + if ( userNick == NULL ) + userNick = userEmail; + + int listId = Lists_NameToCode( tWords[0] ); + hContact = sttProcessAdd( trid, listId, userEmail, userNick ); + } + + if ( hContact != NULL ) { + if ( userId != NULL ) MSN_SetString( hContact, "ID", userId ); + if ( groupId != NULL ) MSN_SetString( hContact, "GroupID", groupId ); + } + break; + } + case ' DDA': //********* ADD: section 7.8 List Modifications + { + union { + char* tWords[ 4 ]; + struct { char *list, *serial, *userEmail, *userNick; } data; + }; + + if ( sttDivideWords( params, 4, tWords ) != 4 ) { +LBL_InvalidCommand: + MSN_DebugLog( "Invalid %.3s command, ignoring", cmdString ); + break; + } + + sttProcessAdd( trid, Lists_NameToCode( data.list ), data.userEmail, data.userNick ); + break; + } + case ' GDA': //********* ADG: group addition + if ( !sttAddGroup( params, false )) + goto LBL_InvalidCommand; + break; + + case ' SNA': //********* ANS: section 8.4 Getting Invited to a Switchboard Session + if ( info->mJoinedCount == 1 ) { + MsgQueueEntry E; + HANDLE hContact = info->mJoinedContacts[0]; + if ( MsgQueue_GetNext( hContact, E ) != 0 ) { + do { + if ( E.msgSize == 0 ) { + info->sendMessage( E.msgType, E.message, E.flags ); + MSN_SendBroadcast( hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE )E.seq, 0 ); + } + else info->sendRawMessage( E.msgType, E.message, E.msgSize ); + + free( E.message ); + + if ( E.ft != NULL ) { + info->mMsnFtp = E.ft; + info->mMsnFtp->mOwnsThread = true; + } + } + while (MsgQueue_GetNext( hContact, E ) != 0 ); + + if ( MSN_GetByte( "EnableDeliveryPopup", 1 )) + MSN_ShowPopup( MSN_GetContactName( hContact ), MSN_Translate( "First message delivered" ), 0 ); + } } + + break; + + case ' PLB': //********* BLP: section 7.6 List Retrieval And Property Management + { + union { + char* tWords[ 2 ]; + struct { char *junk, *listName; } data; + }; + + if ( sttDivideWords( params, 2, tWords ) == 1 ) + data.listName = data.junk; + + msnOtherContactsBlocked = stricmp( data.listName, "BL" ) == 0; + break; + } + case ' RPB': //********* BPR: + { + char* tWords[ 2 ]; + if ( sttDivideWords( params, 2, tWords ) != 2 ) + goto LBL_InvalidCommand; + + if ( sttListedContact != NULL ) { + UrlDecode( tWords[1] ); + if ( !strcmp( tWords[0], "PHH" )) { + MSN_SetString( sttListedContact, "Phone", tWords[1] ); + sttListedContactMask |= 0x0001; + } + else if ( !strcmp( tWords[0], "PHW" )) { + MSN_SetString( sttListedContact, "CompanyPhone", tWords[1] ); + sttListedContactMask |= 0x0002; + } + else if ( !strcmp( tWords[0], "PHM" )) { + MSN_SetString( sttListedContact, "Cellular", tWords[1] ); + sttListedContactMask |= 0x0004; + } + else if ( !strcmp( tWords[0], "MOB" )) { + MSN_SetString( sttListedContact, "OnMobile", tWords[1] ); + sttListedContactMask |= 0x0008; + } + else if ( !strcmp( tWords[0], "MBE" )) { + MSN_SetString( sttListedContact, "OnMsnMobile", tWords[1] ); + sttListedContactMask |= 0x0010; + } } + break; + } + case ' EYB': //********* BYE: section 8.5 Session Participant Changes + { + union { + char* tWords[ 2 ]; + // modified for chat, orginally param2 = junk + // param 2: quit due to idle = "1", normal quit = nothing + struct { char *userEmail, *isIdle; } data; + }; + + sttDivideWords( params, 2, tWords ); + UrlDecode( data.userEmail ); + + if ( MSN_GetByte( "EnableSessionPopup", 0 )) + MSN_ShowPopup( data.userEmail, MSN_Translate( "Contact left channel" ), 0 ); + + // modified for chat + if ( msnHaveChatDll ) { + GCDEST gcd = {0}; + gcd.pszModule = msnProtocolName; + gcd.pszID = info->mChatID; + gcd.iType = GC_EVENT_QUIT; + + GCEVENT gce = {0}; + gce.cbSize = sizeof( GCEVENT ); + gce.pDest = &gcd; + gce.pszNick = MSN_GetContactName( MSN_HContactFromEmail( data.userEmail, NULL, 1, 1 )); + gce.pszUID = data.userEmail; + gce.time = time( NULL ); + gce.bIsMe = FALSE; + gce.bAddToLog = TRUE; + MSN_CallService( MS_GC_EVENT, NULL, ( LPARAM )&gce ); + } + + // in here, the first contact is the chat ID, starting from the second will be actual contact + // if only 1 person left in conversation + int personleft = MSN_ContactLeft( info, MSN_HContactFromEmail( data.userEmail, NULL, 0, 0 )); + // see if the session is quit due to idleness + if ( personleft == 1 && !lstrcmpA( data.isIdle, "1" ) ) { + GCDEST gcd = {0}; + gcd.pszModule = msnProtocolName; + gcd.pszID = info->mChatID; + gcd.iType = GC_EVENT_INFORMATION; + + GCEVENT gce = {0}; + gce.cbSize = sizeof( GCEVENT ); + gce.pDest = &gcd; + gce.bIsMe = FALSE; + gce.time = time(NULL); + gce.bAddToLog = TRUE; + gce.pszText = Translate("This conversation has been inactive, participants will be removed."); + MSN_CallService( MS_GC_EVENT, NULL, ( LPARAM )&gce ); + gce.pszText = Translate("To resume the conversation, please quit this session and start a new chat session."); + MSN_CallService( MS_GC_EVENT, NULL, ( LPARAM )&gce ); + } + else if ( personleft == 2 && lstrcmpA( data.isIdle, "1" ) ) { + if ( MessageBoxA( NULL, Translate( "There is only 1 person left in the chat, do you want to switch back to standard message window?"), Translate("MSN Chat"), MB_YESNO|MB_ICONQUESTION) == IDYES) { + // kill chat dlg and open srmm dialog + GCEVENT gce = {0}; + GCDEST gcd = {0}; + gce.cbSize = sizeof( GCEVENT ); + gce.pDest = &gcd; + + // a flag to let the kill function know what to do + // if the value is 1, then it'll open up the srmm window + info->mJoinedCount--; + + gcd.pszModule = msnProtocolName; + gcd.pszID = info->mChatID; + gcd.iType = GC_EVENT_CONTROL; + MSN_CallService( MS_GC_EVENT, WINDOW_OFFLINE, ( LPARAM )&gce ); + MSN_CallService( MS_GC_EVENT, WINDOW_TERMINATE, ( LPARAM )&gce ); + } } + // this is not in chat session, quit the session when everyone left + else if ( personleft == 0 ) + return 1; // die + + break; + } + case ' LAC': //********* CAL: section 8.3 Inviting Users to a Switchboard Session + break; + + case ' GHC': //********* CHG: section 7.7 Client States + { + int oldMode = msnStatusMode; + msnStatusMode = MSNStatusToMiranda( params ); + + MSN_SendBroadcast( NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS,( HANDLE )oldMode, msnStatusMode ); + MSN_DebugLog( "Status change acknowledged: %s", params ); + break; + } + case ' LHC': //********* CHL: Query from Server on MSNP7 + { + char authChallengeInfo[ 30 ]; + if ( sscanf( params, "%30s", authChallengeInfo ) < 1 ) + goto LBL_InvalidCommand; + + //Digest it + DWORD md5hash[ 4 ]; + MD5_CTX context; + MD5Init(&context); + MD5Update(&context, ( BYTE* )authChallengeInfo, strlen( authChallengeInfo )); + MD5Update(&context, ( BYTE* )msnProtChallenge, strlen( msnProtChallenge )); + MD5Final(( BYTE* )md5hash, &context); + + if ( MyOptions.UseMSNP11 ) { + LONGLONG hash1 = *( LONGLONG* )&md5hash[0], hash2 = *( LONGLONG* )&md5hash[2]; + size_t i; + for ( i=0; i < 4; i++ ) + md5hash[i] &= 0x7FFFFFFF; + + char chlString[128]; + _snprintf( chlString, sizeof( chlString ), "%s%s00000000", authChallengeInfo, msnProductID ); + chlString[ (strlen(authChallengeInfo)+strlen(msnProductID)+7) & 0xF8 ] = 0; + + LONGLONG high=0, low=0, bskey=0; + int* chlStringArray = ( int* )chlString; + for ( i=0; i < strlen( chlString )/4; i += 2) { + LONGLONG temp = chlStringArray[i]; + + temp = (0x0E79A9C1 * temp) % 0x7FFFFFFF; + temp += high; + temp = md5hash[0] * temp + md5hash[1]; + temp = temp % 0x7FFFFFFF; + + high = chlStringArray[i + 1]; + high = (high + temp) % 0x7FFFFFFF; + high = md5hash[2] * high + md5hash[3]; + high = high % 0x7FFFFFFF; + + low = low + high + temp; + } + high = (high + md5hash[1]) % 0x7FFFFFFF; + low = (low + md5hash[3]) % 0x7FFFFFFF; + + LONGLONG key = (low << 32) + high; + sttSwapInt64( &key ); + sttSwapInt64( &hash1 ); + sttSwapInt64( &hash2 ); + + info->sendPacket( "QRY", "%s 32\r\n%016I64x%016I64x", msnProductID, hash1 ^ key, hash2 ^ key ); + } + else info->sendPacket( "QRY", "%s 32\r\n%08x%08x%08x%08x", msnProductID, + htonl( md5hash[0] ), htonl( md5hash[1] ), htonl( md5hash[2] ), htonl( md5hash[3] )); + break; + } + case ' RVC': //********* CVR: MSNP8 + { + char tEmail[ MSN_MAX_EMAIL_LEN ]; + MSN_GetStaticString( "e-mail", NULL, tEmail, sizeof( tEmail )); + info->sendPacket( "USR", "TWN I %s", tEmail ); + break; + } + case ' NLF': //********* FLN: section 7.9 Notification Messages + { HANDLE hContact; + UrlDecode( params ); + if (( hContact = MSN_HContactFromEmail( params, NULL, 0, 0 )) != NULL ) + { + MSN_SetWord( hContact, "Status", ID_STATUS_OFFLINE ); + MsgQueue_Clear( hContact ); + } + break; + } + case ' CTG': //********* GTC: section 7.6 List Retrieval And Property Management + break; + + case ' FNI': //********* INF: section 7.2 Server Policy Information + { + char security1[ 10 ]; + //can be more security packages on the end, comma delimited + if ( sscanf( params, "%9s", security1 ) < 1 ) + goto LBL_InvalidCommand; + + //SEND USR I packet, section 7.3 Authentication + if ( !strcmp( security1, "MD5" )) { + char tEmail[ MSN_MAX_EMAIL_LEN ]; + if ( !MSN_GetStaticString( "e-mail", NULL, tEmail, sizeof( tEmail ))) + info->sendPacket( "USR", "MD5 I %s", tEmail ); + else + info->sendPacket( "USR", "MD5 I " ); //this will fail, of course + } + else { + MSN_DebugLog( "Unknown security package '%s'", security1 ); + if ( info->mType == SERVER_NOTIFICATION || info->mType == SERVER_DISPATCH ) { + MSN_SendBroadcast(NULL,ACKTYPE_LOGIN,ACKRESULT_FAILED,NULL,LOGINERR_WRONGPROTOCOL); + MSN_GoOffline(); + } + return 1; + } + break; + } + case ' NLI': + case ' NLN': //********* ILN/NLN: section 7.9 Notification Messages + { + union { + char* tWords[ 5 ]; + struct { char *userStatus, *userEmail, *userNick, *objid, *cmdstring; } data; + }; + + int tArgs = sttDivideWords( params, 5, tWords ); + if ( tArgs < 3 ) + goto LBL_InvalidCommand; + + UrlDecode( data.userEmail ); UrlDecode( data.userNick ); + + HANDLE hContact = MSN_HContactFromEmail( data.userEmail, NULL, 0, 0 ); + if ( hContact != NULL) { + // is there an uninitialized switchboard for this contact? + ThreadData* T = MSN_GetUnconnectedThread( hContact ); + if ( T != NULL ) + if ( hContact == T->mInitialContact ) + T->sendPacket( "CAL", "%s", data.userEmail ); + + MSN_SetStringUtf( hContact, "Nick", data.userNick ); + MSN_SetWord( hContact, "Status", ( WORD )MSNStatusToMiranda( data.userStatus )); +// DBDeleteContactSetting( hContact, "CList", "StatusMsg" ); + } + + MSN_SetString( hContact, "MirVer", "" ); + + if ( tArgs > 3 && tArgs <= 5 ) { + UrlDecode( data.cmdstring ); + DWORD dwValue = ( DWORD )atol( data.objid ); + MSN_SetDword( hContact, "FlagBits", dwValue ); + if ( dwValue & 0x200 ) + MSN_SetString( hContact, "MirVer", "Webmessenger" ); + else if ( dwValue == 1342177280 ) + MSN_SetString( hContact, "MirVer", "Miranda IM 0.5.x" ); + else if ( dwValue == 805306404 ) + MSN_SetString( hContact, "MirVer", "Miranda IM 0.4.x" ); + else if (( dwValue & 0x60000000 ) == 0x60000000 ) + MSN_SetString( hContact, "MirVer", "MSN 8.x" ); + else if (( dwValue & 0x50000000 ) == 0x50000000 ) + MSN_SetString( hContact, "MirVer", "MSN 7.5" ); + else if ( dwValue & 0x40000000 ) + MSN_SetString( hContact, "MirVer", "MSN 7.0" ); + else if (( dwValue & 0x30000000 ) == 0x30000000 ) + MSN_SetString( hContact, "MirVer", "MSN 6.2" ); + else if ( dwValue & 0x20000000 ) + MSN_SetString( hContact, "MirVer", "MSN 6.1" ); + else if ( dwValue & 0x10000000 ) + MSN_SetString( hContact, "MirVer", "MSN 6.0" ); + else + MSN_SetString( hContact, "MirVer", "MSN 4.x-5.x" ); + + if ( data.cmdstring[0] ) { + int temp_status = MSN_GetWord(hContact, "Status", ID_STATUS_OFFLINE); + if (temp_status == (WORD)ID_STATUS_OFFLINE) + MSN_SetWord( hContact, "Status", (WORD)ID_STATUS_INVISIBLE); + MSN_SetString( hContact, "PictContext", data.cmdstring ); + if ( hContact != NULL ) { + char szSavedContext[ 256 ]; + int result = MSN_GetStaticString( "PictSavedContext", hContact, szSavedContext, sizeof szSavedContext ); + if ( result || strcmp( szSavedContext, data.cmdstring )) + MSN_SendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, NULL ); + } } + else { + DBDeleteContactSetting( hContact, msnProtocolName, "PictContext" ); + DBDeleteContactSetting( hContact, msnProtocolName, "PictSavedContext" ); + MSN_SendBroadcast( hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, NULL ); + } } + + break; + } + case ' ORI': //********* IRO: section 8.4 Getting Invited to a Switchboard Session + { + union { + char* tWords[ 4 ]; + struct { char *strThisContact, *totalContacts, *userEmail, *userNick; } data; + }; + + if ( sttDivideWords( params, 4, tWords ) != 4 ) + goto LBL_InvalidCommand; + + UrlDecode( data.userEmail ); + UrlDecode( data.userNick ); + + MSN_ContactJoined( info, MSN_HContactFromEmail( data.userEmail, data.userNick, 1, 1 )); + + int thisContact = atol( data.strThisContact ); + if ( thisContact != 1 ) { + char* tContactName = MSN_GetContactName( info->mJoinedContacts[0] ); + Utf8Decode( data.userNick ); + + char multichatmsg[256]; + mir_snprintf( multichatmsg, sizeof( multichatmsg ), + MSN_Translate( "%s (%s) has joined the chat with %s" ), + data.userNick, data.userEmail, tContactName ); + + MSN_ShowPopup( tContactName, multichatmsg, MSN_ALLOW_MSGBOX ); + } + + // only start the chat session after all the IRO messages has been recieved + if ( msnHaveChatDll && info->mJoinedCount > 1 && !lstrcmpA(data.strThisContact, data.totalContacts) ) { + if ( info->mChatID[0] == 0 ) + MSN_ChatStart(info); + } + + break; + } + case ' IOJ': //********* JOI: section 8.5 Session Participant Changes + { + union { + char* tWords[ 2 ]; + struct { char *userEmail, *userNick; } data; + }; + + if ( sttDivideWords( params, 2, tWords ) != 2 ) + goto LBL_InvalidCommand; + + UrlDecode( data.userEmail ); UrlDecode( data.userNick ); + HANDLE hContact = MSN_HContactFromEmail( data.userEmail, data.userNick, 1, 1 ); + + Utf8Decode( data.userNick ); + MSN_DebugLog( "New contact in channel %s %s", data.userEmail, data.userNick ); + + info->mInitialContact = NULL; + info->mMessageCount = 0; + + if ( MSN_ContactJoined( info, hContact ) == 1 ) { + MsgQueueEntry E; + if ( MsgQueue_GetNext( hContact, E ) != 0 ) { + do { + if ( E.msgSize == 0 ) { + info->sendMessage( E.msgType, E.message, E.flags ); + MSN_SendBroadcast( hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE )E.seq, 0 ); + } + else info->sendRawMessage( E.msgType, E.message, E.msgSize ); + + free( E.message ); + + if ( E.ft != NULL ) { + info->mMsnFtp = E.ft; + info->mMsnFtp->mOwnsThread = true; + } + } + while (MsgQueue_GetNext( hContact, E ) != 0 ); + + if ( MSN_GetByte( "EnableDeliveryPopup", 1 )) + MSN_ShowPopup( MSN_GetContactName( hContact ), MSN_Translate( "First message delivered" ), 0 ); + } } + else { + char* tContactName = MSN_GetContactName( info->mJoinedContacts[0] ); + + char multichatmsg[ 256 ]; + mir_snprintf( + multichatmsg, sizeof( multichatmsg ), + MSN_Translate( "%s (%s) has joined the chat with %s" ), + data.userNick, data.userEmail, tContactName ); + + MSN_ShowPopup( tContactName, multichatmsg, MSN_ALLOW_MSGBOX ); + + if ( msnHaveChatDll ) { + GCDEST gcd = {0}; + GCEVENT gce = {0}; + + gcd.pszModule = msnProtocolName; + gcd.pszID = info->mChatID; + gcd.iType = GC_EVENT_JOIN; + + gce.cbSize = sizeof(GCEVENT); + gce.pDest = &gcd; + gce.pszNick = MSN_GetContactName( MSN_HContactFromEmail( data.userEmail, NULL, 1, 1 )); + gce.pszUID = data.userEmail; + gce.pszStatus = Translate( "Others" ); + gce.time = time(NULL); + gce.bIsMe = FALSE; + gce.bAddToLog = TRUE; + MSN_CallService( MS_GC_EVENT, NULL, ( LPARAM )&gce ); + } } + return 0; + } + + case ' GSL': //********* LSG: lists existing groups + if ( !sttAddGroup( params, true )) + goto LBL_InvalidCommand; + break; + + case ' TSL': //********* LST: section 7.6 List Retrieval And Property Management + { + int listId; + char *userEmail = NULL, *userNick = NULL, *userId = NULL, *groupId = NULL; + + union { + char* tWords[ 10 ]; + struct { char *userEmail, *userNick, *list, *groupid; } data; + }; + + int tNumTokens = sttDivideWords( params, 10, tWords ); + + if ( --sttListNumber == 0 ) + MSN_SetServerStatus( msnDesiredStatus ); + + for ( int i=0; i < tNumTokens; i++ ) { + char* p = tWords[ i ]; + if ( *p == 'N' && p[1] == '=' ) + userEmail = p+2; + else if ( *p == 'F' && p[1] == '=' ) + userNick = p+2; + else if ( *p == 'C' && p[1] == '=' ) + userId = p+2; + else { + listId = atol( p ); + if ( i < tNumTokens-1 ) + groupId = tWords[i+1]; + break; + } } + + if ( userEmail == NULL ) + goto LBL_InvalidCommand; + + if ( userNick == NULL ) + userNick = userEmail; + + UrlDecode( userEmail ); UrlDecode( userNick ); + + if ( !IsValidListCode( listId ) || !strcmp( userEmail, "messenger@microsoft.com" )) + break; + + // add user if it wasn't included into a contact list + sttProcessListedContactMask(); + sttListedContact = MSN_HContactFromEmail( userEmail, userNick, 1, 0 ); + + Utf8Decode( userNick ); + Lists_Add( listId, userEmail, userNick ); + + if (( listId & ( LIST_AL + LIST_BL + LIST_FL )) == LIST_BL ) { + DBDeleteContactSetting( sttListedContact, "CList", "NotOnList" ); + DBWriteContactSettingByte( sttListedContact, "CList", "Hidden", 1 ); + } + + if ( listId & LIST_PL ) { + if ( !Lists_IsInList( LIST_RL, userEmail )) { + MSN_AddUser( sttListedContact, userEmail, LIST_PL + LIST_REMOVE ); + MSN_AddUser( sttListedContact, userEmail, LIST_RL ); + } + + if (( listId & ( LIST_AL + LIST_BL + LIST_FL )) == 0 ) + MSN_AddAuthRequest( sttListedContact, userEmail, userNick ); + } + + if ( listId & ( LIST_BL | LIST_AL )) { + WORD tApparentMode = MSN_GetWord( sttListedContact, "ApparentMode", 0 ); + if (( listId & LIST_BL ) && tApparentMode == 0 ) + MSN_SetWord( sttListedContact, "ApparentMode", ID_STATUS_OFFLINE ); + else if (( listId & LIST_AL ) && tApparentMode != 0 ) + MSN_SetWord( sttListedContact, "ApparentMode", 0 ); + } + + if ( sttListedContact != NULL ) { + if ( userId != NULL ) + MSN_SetString( sttListedContact, "ID", userId ); + + if ( MyOptions.ManageServer ) { + if ( groupId != NULL ) { + char* p = strchr( groupId, ',' ); + if ( p != NULL ) + *p = 0; + + MSN_SetString( sttListedContact, "GroupID", groupId ); + + if (( p = ( char* )MSN_GetGroupById( groupId )) != NULL ) { + DBVARIANT dbv; + if ( !DBGetContactSettingStringUtf( sttListedContact, "CList", "Group", &dbv )) { + if ( strcmp( dbv.pszVal, p )) + DBWriteContactSettingStringUtf( sttListedContact, "CList", "Group", p ); + MSN_FreeVariant( &dbv ); + } + else DBWriteContactSettingStringUtf( sttListedContact, "CList", "Group", p ); + } } + else { + DBDeleteContactSetting( sttListedContact, "CList", "Group" ); + DBDeleteContactSetting( sttListedContact, msnProtocolName, "GroupID" ); + } } } + break; + } + case ' GSM': //********* MSG: sections 8.7 Instant Messages, 8.8 Receiving an Instant Message + MSN_ReceiveMessage( info, cmdString, params ); + break; + + case ' KAN': //********* NAK: section 8.7 Instant Messages + MSN_DebugLog( "Message send failed (trid=%d)", trid ); + break; + + case ' TON': //********* NOT: notification message + { + char* buffer = ( char* )alloca( trid+1 ); + BYTE* p = HReadBuffer( info ).surelyRead( trid ); + if ( p != NULL ) { + memcpy( buffer, p, trid ); + buffer[ trid ] = 0; + } + else buffer[0] = 0; + + MSN_DebugLog( "Notification message: %s", buffer ); + break; + } + case ' TUO': //********* OUT: sections 7.10 Connection Close, 8.6 Leaving a Switchboard Session + if ( !stricmp( params, "OTH" )) { + MSN_SendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_OTHERLOCATION ); + MSN_DebugLog( "You have been disconnected from the MSN server because you logged on from another location using the same MSN passport." ); + } + + if ( !stricmp( params, "MIG" )) // ignore it + break; + + return 1; + + case ' PRP': //********* PRP: user property + { + union { + char* tWords[ 2 ]; + struct { char *name, *value; } data; + }; + + if ( sttDivideWords( params, 2, tWords ) != 2 ) + goto LBL_InvalidCommand; + + UrlDecode( data.value ); + if ( !stricmp( data.name, "MFN" )) + if ( !sttIsSync || !MSN_GetByte( "NeverUpdateNickname", 0 )) + MSN_SetStringUtf( NULL, "Nick", data.value ); + break; + } + case ' YRQ': //********* QRY: + sttProcessListedContactMask(); + break; + + case ' GNQ': //********* QNG: reply to PNG + msnPingTimeoutCurrent = msnPingTimeout = trid; + if ( msnGetInfoContact != NULL ) { + MSN_SendBroadcast( msnGetInfoContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, ( HANDLE )1, 0 ); + msnGetInfoContact = NULL; + } + break; + + case ' GER': //********* REG: rename group + { + union { + char* tWords[ 2 ]; + struct { char *id, *groupName; } data; + }; + + if ( sttDivideWords( params, 2, tWords ) != 2 ) + goto LBL_InvalidCommand; + + UrlDecode( data.groupName ); + MSN_SetGroupName( data.id, data.groupName ); + break; + } + case ' MER': //********* REM: section 7.8 List Modifications + { + union { + char* tWords[ 3 ]; + struct { char *list, *serial, *groupId; } data; + }; + + if ( sttDivideWords( params, 3, tWords ) == 3 ) { // remove from a group + HANDLE hContact = MSN_HContactById( data.serial ); + if ( hContact != NULL ) + DBDeleteContactSetting( hContact, msnProtocolName, "GroupID" ); + } + else { // remove a user from a list + int listId = Lists_NameToCode( data.list ); + if ( IsValidListCode( listId )) { + if ( listId == LIST_FL ) { + HANDLE hContact = MSN_HContactById( data.serial ); + if ( hContact != NULL ) { + char tEmail[ MSN_MAX_EMAIL_LEN ]; + if ( !MSN_GetStaticString( "e-mail", hContact, tEmail, sizeof tEmail )) + Lists_Remove( listId, tEmail ); + } } + else { + UrlDecode( data.serial ); + Lists_Remove( listId, data.serial ); + } } } + break; + } + case ' GMR': //********* RMG: remove a group + { + MSN_DeleteGroup( params ); + break; + } + case ' GNR': //********* RNG: section 8.4 Getting Invited to a Switchboard Session + //note: unusual message encoding: trid==sessionid + { + union { + char* tWords[ 5 ]; + struct { char *newServer, *security, *authChallengeInfo, *callerEmail, *callerNick; } data; + }; + + if ( sttDivideWords( params, 5, tWords ) != 5 ) + goto LBL_InvalidCommand; + + UrlDecode( data.newServer ); UrlDecode( data.callerEmail ); + UrlDecode( data.callerNick ); Utf8Decode( data.callerNick ); + + if ( strcmp( data.security, "CKI" )) { + MSN_DebugLog( "Unknown security package in RNG command: %s", data.security ); + break; + } + + ThreadData* newThread = new ThreadData; + strcpy( newThread->mServer, data.newServer ); + newThread->mType = SERVER_SWITCHBOARD; + MSN_ContactJoined( newThread, MSN_HContactFromEmail( data.callerEmail, data.callerNick, false, true )); + mir_snprintf( newThread->mCookie, sizeof( newThread->mCookie ), "%s %d", data.authChallengeInfo, trid ); + + MSN_DebugLog( "Opening caller's switchboard server '%s'...", data.newServer ); + newThread->startThread(( pThreadFunc )MSNServerThread ); + break; + } + case ' PBS': //********* SBP: Server Property was changed + break; + + case ' NYS': //********* SYN: section 7.5 Client User Property Synchronization + { + char* tWords[ 4 ]; + if ( sttDivideWords( params, 4, tWords ) != 4 ) + goto LBL_InvalidCommand; + + Lists_Wipe(); + sttIsSync = true; + if (( sttListNumber = atol( tWords[ 2 ] )) == 0 ) + MSN_SetServerStatus( msnDesiredStatus ); + + sttListedContact = NULL; + tridUrlInbox = msnNsThread->sendPacket( "URL", "INBOX" ); + tridUrlEdit = msnNsThread->sendPacket( "URL", "PROFILE 0x%04x", GetUserDefaultLCID() ); + break; + } + case ' XBU': // UBX : MSNP11+ User Status Message + { + union { + char* tWords[ 2 ]; + struct { char *email, *datalen; } data; + }; + + if ( sttDivideWords( params, 2, tWords ) != 2 ) + goto LBL_InvalidCommand; + + HANDLE hContact = MSN_HContactFromEmail( data.email, data.email, 0, 0 ); + if ( hContact == NULL ) + break; + + int len = atol( data.datalen ); + if ( len < 0 || len > 4000 ) + goto LBL_InvalidCommand; + + char* dataBuf = ( char* )alloca( len+1 ), *p = dataBuf; + BYTE* buff = HReadBuffer( info, 0 ).surelyRead( len ); + if ( buff != NULL ) { + memcpy( dataBuf, buff, len ); + dataBuf[ len ] = 0; + } + else dataBuf[0] = 0; + + p = strstr( dataBuf, "" ); + if ( p ) { + p += 5; + char* p1 = strstr( p, "" ); + if ( p1 ) { + *p1 = 0; + if ( *p != 0 ) { + HtmlDecode( p ); + DBWriteContactSettingStringUtf( hContact, "CList", "StatusMsg", p ); + } + else DBDeleteContactSetting( hContact, "CList", "StatusMsg" ); + } } + break; + } + case ' LRU': + { + union { + char* tWords[ 3 ]; + struct { char *rru, *passport, *urlID; } data; + }; + + if ( sttDivideWords( params, 3, tWords ) != 3 ) + goto LBL_InvalidCommand; + + if ( trid == tridUrlInbox ) { + replaceStr( passport, data.passport ); + replaceStr( rru, data.rru ); + tridUrlInbox = -1; + } + else if ( trid == tridUrlEdit ) { + replaceStr( profileURL, data.rru ); + tridUrlEdit = -1; + } + break; + } + case ' RSU': //********* USR: sections 7.3 Authentication, 8.2 Switchboard Connections and Authentication + if ( info->mType == SERVER_SWITCHBOARD ) { //(section 8.2) + union { + char* tWords[ 3 ]; + struct { char *status, *userHandle, *friendlyName; } data; + }; + + if ( sttDivideWords( params, 3, tWords ) != 3 ) + goto LBL_InvalidCommand; + + UrlDecode( data.userHandle ); UrlDecode( data.friendlyName ); + Utf8Decode( data.friendlyName ); + + if ( strcmp( data.status, "OK" )) { + MSN_DebugLog( "Unknown status to USR command (SB): '%s'", data.status ); + break; + } + + HANDLE hContact = MsgQueue_GetNextRecipient(); + if ( hContact == NULL ) { //can happen if both parties send first message at the same time + MSN_DebugLog( "USR (SB) internal: thread created for no reason" ); + info->sendPacket( "OUT", NULL ); + break; + } + + char tEmail[ MSN_MAX_EMAIL_LEN ]; + if ( MSN_GetStaticString( "e-mail", hContact, tEmail, sizeof( tEmail ))) { + MSN_DebugLog( "USR (SB) internal: Contact is not MSN" ); + info->sendPacket( "OUT", NULL ); + break; + } + + info->mInitialContact = hContact; + info->sendPacket( "CAL", "%s", tEmail ); + } + else //dispatch or notification server (section 7.3) + { + union { + char* tWords[ 3 ]; + struct { char *security, *sequence, *authChallengeInfo; } data; + }; + + if ( sttDivideWords( params, 3, tWords ) != 3 ) + goto LBL_InvalidCommand; + + if ( !strcmp( data.security, "TWN" )) { + char* tAuth; + if ( MSN_GetPassportAuth( data.authChallengeInfo, tAuth )) { + MSN_SendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPASSWORD ); + MSN_GoOffline(); + return 1; + } + + info->sendPacket( "USR", "TWN S %s", tAuth ); + free( tAuth ); + } + else if ( !strcmp( data.security, "OK" )) { + UrlDecode( tWords[1] ); UrlDecode( tWords[2] ); + + if ( MSN_GetByte( "NeverUpdateNickname", 0 )) { + DBVARIANT dbv; + if ( !DBGetContactSettingTString( NULL, msnProtocolName, "Nick", &dbv )) { + MSN_SendNicknameT( dbv.ptszVal ); + MSN_FreeVariant( &dbv ); + } + } + else MSN_SetStringUtf( NULL, "Nick", tWords[2] ); + + msnLoggedIn = true; + sttListNumber = 0; + info->sendPacket( "SYN", "0 0" ); + } + else { + MSN_DebugLog( "Unknown security package '%s'", data.security ); + + if ( info->mType == SERVER_NOTIFICATION || info->mType == SERVER_DISPATCH ) { + MSN_SendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPROTOCOL ); + MSN_GoOffline(); + } + + return 1; + } } + break; + + case ' XUU': // UUX: MSNP11 addition + break; + + case ' REV': //******** VER: section 7.1 Protocol Versioning + { + char protocol1[ 7 ]; + if ( sscanf( params, "%6s", protocol1 ) < 1 ) + goto LBL_InvalidCommand; + + char tEmail[ MSN_MAX_EMAIL_LEN ]; + if ( MSN_GetStaticString( "e-mail", NULL, tEmail, sizeof( tEmail ))) { + MSN_ShowError( "You must specify your e-mail in Options/Network/MSN" ); + return 1; + } + + if ( !strcmp( protocol1, "MSNP10" )) { + info->sendPacket( "CVR","0x0409 winnt 5.1 i386 MSNMSGR 6.2.0205 MSMSGS %s", tEmail ); + msnProtChallenge = "Q1P7W2E4J9R8U3S5"; + msnProductID = "msmsgs@msnmsgr.com"; + } + else if ( !strcmp( protocol1, "MSNP11" )) { + info->sendPacket( "CVR","0x0409 winnt 5.1 i386 MSNMSGR 7.5.0311 MSMSGS %s", tEmail ); + msnProtChallenge = "YMM8C_H7KCQ2S_KL"; + msnProductID = "PROD0090YUAUV{2B"; + } + else { + MSN_ShowError( "Server has requested an unknown protocol set (%s)", params ); + + if ( info->mType == SERVER_NOTIFICATION || info->mType == SERVER_DISPATCH ) { + MSN_SendBroadcast( NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPROTOCOL ); + MSN_GoOffline(); + } + + return 1; + } + break; + } + case ' RFX': //******** XFR: sections 7.4 Referral, 8.1 Referral to Switchboard + { + union { + char* tWords[ 4 ]; + struct { char *type, *newServer, *security, *authChallengeInfo; } data; + }; + + int numWords = sttDivideWords( params, 4, tWords ); + if ( numWords < 2 ) + goto LBL_InvalidCommand; + + if ( !strcmp( data.type, "NS" )) { //notification server + UrlDecode( data.newServer ); + ThreadData* newThread = new ThreadData; + strcpy( newThread->mServer, data.newServer ); + newThread->mType = SERVER_NOTIFICATION; + newThread->mTrid = info->mTrid; + newThread->mIsMainThread = true; + info->mIsMainThread = false; + + MSN_DebugLog( "Switching to notification server '%s'...", data.newServer ); + newThread->startThread(( pThreadFunc )MSNServerThread ); + return 1; //kill the old thread + } + + if ( !strcmp( data.type, "SB" )) { //switchboard server + UrlDecode( data.newServer ); + + if ( numWords < 4 ) + goto LBL_InvalidCommand; + + if ( strcmp( data.security, "CKI" )) { + MSN_DebugLog( "Unknown XFR SB security package '%s'", data.security ); + break; + } + + ThreadData* newThread = new ThreadData; + strcpy( newThread->mServer, data.newServer ); + newThread->mType = SERVER_SWITCHBOARD; + newThread->mCaller = 1; + strcpy( newThread->mCookie, data.authChallengeInfo ); + + MSN_DebugLog( "Opening switchboard server '%s'...", data.newServer ); + newThread->startThread(( pThreadFunc )MSNServerThread ); + } + else MSN_DebugLog( "Unknown referral server: %s", data.type ); + break; + } + + default: + MSN_DebugLog( "Unrecognised message: %s", cmdString ); + break; + } + + info->mMessageCount++; + return 0; +} -- cgit v1.2.3