From 6860b3202f3c8218288fad2778529ba76463015c Mon Sep 17 00:00:00 2001 From: Kirill Volinsky Date: Sat, 13 Oct 2012 09:07:09 +0000 Subject: MSN: folders restructurization git-svn-id: http://svn.miranda-ng.org/main/trunk@1901 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/MSN/src/msn_commands.cpp | 1980 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1980 insertions(+) create mode 100644 protocols/MSN/src/msn_commands.cpp (limited to 'protocols/MSN/src/msn_commands.cpp') diff --git a/protocols/MSN/src/msn_commands.cpp b/protocols/MSN/src/msn_commands.cpp new file mode 100644 index 0000000000..5a239dd986 --- /dev/null +++ b/protocols/MSN/src/msn_commands.cpp @@ -0,0 +1,1980 @@ +/* +Plugin of Miranda IM for communicating with users of the MSN Messenger protocol. +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" + +///////////////////////////////////////////////////////////////////////////////////////// +// Starts a file sending thread + +void MSN_ConnectionProc(HANDLE hNewConnection, DWORD /* dwRemoteIP */, void* extra) +{ + CMsnProto *proto = (CMsnProto*)extra; + + proto->MSN_DebugLog("File transfer connection accepted"); + + NETLIBCONNINFO connInfo = { sizeof(connInfo) }; + CallService(MS_NETLIB_GETCONNECTIONINFO, (WPARAM)hNewConnection, (LPARAM)&connInfo); + + ThreadData* T = proto->MSN_GetThreadByPort(connInfo.wPort); + if (T != NULL && T->s == NULL) + { + T->s = hNewConnection; + ReleaseSemaphore(T->hWaitEvent, 1, NULL); + } + else + { + proto->MSN_DebugLog("There's no registered file transfers for incoming port #%u, connection closed", connInfo.wPort); + Netlib_CloseHandle(hNewConnection); + } +} + + +void CMsnProto::sttSetMirVer(HANDLE hContact, DWORD dwValue, bool always) +{ + static const char* MirVerStr[] = + { + "MSN 4.x-5.x", + "MSN 6.0", + "MSN 6.1", + "MSN 6.2", + "MSN 7.0", + "MSN 7.5", + "WLM 8.0", + "WLM 8.1", + "WLM 8.5", + "WLM 9.0 Beta", + "WLM 2009", + "WLM 2011", + "WLM 2012", + "WLM Unknown", + }; + + if (dwValue == 0) + setString(hContact, "MirVer", "Windows Phone"); + else if (dwValue & 0x1) + setString(hContact, "MirVer", "MSN Mobile"); + else if (dwValue & 0x200) + setString(hContact, "MirVer", "Webmessenger"); + else if (dwValue == 0x800800) + setString(hContact, "MirVer", "Yahoo"); + else if (dwValue == 0x800) + setString(hContact, "MirVer", "LCS"); + else if (dwValue == 0x50000000) + setString(hContact, "MirVer", "Miranda IM 0.5.x (MSN v.0.5.x)"); + else if (dwValue == 0x30000024) + setString(hContact, "MirVer", "Miranda IM 0.4.x (MSN v.0.4.x)"); + else if (always || getByte(hContact, "StdMirVer", 0)) + { + unsigned wlmId = min(dwValue >> 28 & 0xff, SIZEOF(MirVerStr)-1); + setString(hContact, "MirVer", MirVerStr[wlmId]); + } + else + return; + + setByte(hContact, "StdMirVer", 1); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Processes various invitations + +void CMsnProto::sttInviteMessage(ThreadData* info, 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* IPAddressInt = tFileInfo["IP-Address-Internal"]; + const char* Port = tFileInfo["Port"]; + const char* PortX = tFileInfo["PortX"]; + const char* PortXInt = tFileInfo["PortX-Internal"]; + const char* AuthCookie = tFileInfo["AuthCookie"]; + const char* SessionID = tFileInfo["Session-ID"]; + const char* SessionProtocol = tFileInfo["Session-Protocol"]; +// const char* Connectivity = tFileInfo["Connectivity"]; + + if (AppGUID != NULL) + { + if (!strcmp(AppGUID, "{02D3C01F-BF30-4825-A83A-DE7AF41648AA}")) + { + MSN_ShowPopup(info->getContactHandle(), + TranslateT("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(this); + + ft->std.hContact = MSN_HContactFromEmail(email, nick, true, true); + mir_free(ft->std.tszCurrentFile); + ft->std.tszCurrentFile = mir_utf8decodeT(Appfile); + ft->std.totalBytes = ft->std.currentFileSize = _atoi64(Appfilesize); + ft->std.totalFiles = 1; + ft->szInvcookie = mir_strdup(Invcookie); + ft->p2p_dest = mir_strdup(email); + + TCHAR tComment[40]; + mir_sntprintf(tComment, SIZEOF(tComment), TranslateT("%I64u bytes"), ft->std.currentFileSize); + + PROTORECVFILET pre = {0}; + pre.flags = PREF_TCHAR; + pre.fileCount = 1; + pre.timestamp = time(NULL); + pre.tszDescription = tComment; + pre.ptszFiles = &ft->std.tszCurrentFile; + pre.lParam = (LPARAM)ft; + + CCSDATA ccs; + ccs.hContact = ft->std.hContact; + ccs.szProtoService = PSR_FILE; + ccs.wParam = 0; + ccs.lParam = (LPARAM)⪯ + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + return; + } + + if (IPAddress != NULL && Port != NULL && AuthCookie != NULL) // receive Second + { + ThreadData* newThread = new ThreadData; + + if (inet_addr(IPAddress) != MyConnection.extIP || !IPAddressInt) + mir_snprintf(newThread->mServer, sizeof(newThread->mServer), "%s:%s", IPAddress, Port); + else + mir_snprintf(newThread->mServer, sizeof(newThread->mServer), "%s:%u", IPAddressInt, atol(PortXInt) ^ 0x3141); + + 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); + + newThread->startThread(&CMsnProto::MSNServerThread, this); + return; + } + + if (Invcommand != NULL && Invcookie != NULL && Port == NULL && AuthCookie == NULL && SessionID == NULL) // send 1 + { + msnftp_startFileSend(info, Invcommand, Invcookie); + return; + } + + if (Appname == NULL && SessionID != NULL && SessionProtocol != NULL) // netmeeting send 1 + { + if (!_stricmp(Invcommand,"ACCEPT")) + { + 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, MyConnection.GetMyExtIPStr()); + info->sendPacket("MSG", "N %d\r\n%s", nBytes, command); + } + return; + } + + if (Appname != NULL && !_stricmp(Appname,"NetMeeting")) // netmeeting receive 1 + { + char command[1024]; + int nBytes; + + TCHAR text[512], *tszEmail = mir_a2t(email); + mir_sntprintf(text, SIZEOF(text), TranslateT("Accept NetMeeting request from %s?"), tszEmail); + mir_free(tszEmail); + + if (MessageBox(NULL, text, TranslateT("MSN Protocol"), MB_YESNO | MB_ICONQUESTION) == IDYES) + { + 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, MyConnection.GetMyExtIPStr()); + } + 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 custom smiley messages + +void CMsnProto::sttCustomSmiley(const char* msgBody, char* email, char* nick, int iSmileyType) +{ + HANDLE hContact = MSN_HContactFromEmail(email, nick, true, true); + + char smileyList[500] = ""; + + const char *tok1 = msgBody, *tok2; + char *smlp = smileyList; + char lastsml[50]; + + unsigned iCount = 0; + bool parseSmiley = true; + + for (;;) + { + tok2 = strchr(tok1, '\t'); + if (tok2 == NULL) break; + + size_t sz = tok2 - tok1; + if (parseSmiley) + { + sz = min(sz, sizeof(lastsml) - 1); + memcpy(lastsml, tok1, sz); + lastsml[sz] = 0; + + memcpy(smlp, tok1, sz); smlp += sz; + *(smlp++) = '\n'; *smlp = 0; + ++iCount; + } + else + { + filetransfer* ft = new filetransfer(this); + ft->std.hContact = hContact; + + ft->p2p_object = (char*)mir_alloc(sz + 1); + memcpy(ft->p2p_object, tok1, sz); + ft->p2p_object[sz] = 0; + + size_t slen = strlen(lastsml); + size_t rlen = Netlib_GetBase64EncodedBufferSize(slen); + char* buf = (char*)mir_alloc(rlen); + + NETLIBBASE64 nlb = { buf, (int)rlen, (PBYTE)lastsml, (int)slen }; + CallService(MS_NETLIB_BASE64ENCODE, 0, LPARAM(&nlb)); + + char* smileyName = (char*)mir_alloc(rlen*3); + UrlEncode(buf, smileyName, rlen*3); + mir_free(buf); + + TCHAR path[MAX_PATH]; + MSN_GetCustomSmileyFileName(hContact, path, SIZEOF(path), smileyName, iSmileyType); + ft->std.tszCurrentFile = mir_tstrdup(path); + mir_free(smileyName); + + if (p2p_IsDlFileOk(ft)) + delete ft; + else + { + MSN_DebugLog("Custom Smiley p2p invite for object : %s", ft->p2p_object); + p2p_invite(iSmileyType, ft, email); + Sleep(3000); + } + } + parseSmiley = !parseSmiley; + tok1 = tok2 + 1; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// MSN_ReceiveMessage - receives message or a file from the server +///////////////////////////////////////////////////////////////////////////////////////// + + +void CMsnProto::MSN_ReceiveMessage(ThreadData* info, char* cmdString, char* params) +{ + union + { + char* tWords[6]; + struct { char *fromEmail, *fromNick, *strMsgBytes; } data; + struct { char *fromEmail, *fromNetId, *toEmail, *toNetId, *typeId, *strMsgBytes; } datau; + }; + + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 3) + { + MSN_DebugLog("Invalid %.3s command, ignoring", cmdString); + return; + } + + int msgBytes; + char *nick, *email; + bool ubmMsg = strncmp(cmdString, "UBM", 3) == 0; + bool sentMsg = false; + + if (ubmMsg) + { + msgBytes = atol(datau.strMsgBytes); + nick = datau.fromEmail; + email = datau.fromEmail; + } + else + { + msgBytes = atol(data.strMsgBytes); + nick = data.fromNick; + email = data.fromEmail; + UrlDecode(nick); + } + stripBBCode(nick); + stripColorCode(nick); + + char* msg = (char*)alloca(msgBytes+1); + + HReadBuffer buf(info, 0); + BYTE* msgb = buf.surelyRead(msgBytes); + if (msgb == NULL) return; + + memcpy(msg, msgb, msgBytes); + msg[msgBytes] = 0; + + MSN_DebugLog("Message:\n%s", msg); + + MimeHeaders tHeader; + char* msgBody = tHeader.readFromBuffer(msg); + + const char* tMsgId = tHeader["Message-ID"]; + + // Chunked message + char* newbody = NULL; + if (tMsgId) + { + int idx; + const char* tChunks = tHeader["Chunks"]; + if (tChunks) + idx = addCachedMsg(tMsgId, msg, 0, msgBytes, atol(tChunks), true); + else + idx = addCachedMsg(tMsgId, msgBody, 0, strlen(msgBody), 0, true); + + size_t newsize; + if (!getCachedMsg(idx, newbody, newsize)) return; + msgBody = tHeader.readFromBuffer(newbody); + } + + // message from the server (probably) + if (!ubmMsg && strchr(email, '@') == NULL && _stricmp(email, "Hotmail")) + return; + + const char* tContentType = tHeader["Content-Type"]; + if (tContentType == NULL) + return; + + if (!_strnicmp(tContentType, "text/x-clientcaps", 17)) + { + MimeHeaders tFileInfo; + tFileInfo.readFromBuffer(msgBody); + info->firstMsgRecv = true; + + HANDLE hContact = MSN_HContactFromEmail(email); + const char* mirver = tFileInfo["Client-Name"]; + if (hContact != NULL && mirver != NULL) + { + setString(hContact, "MirVer", mirver); + deleteSetting(hContact, "StdMirVer"); + } + } + else if (!ubmMsg && !info->firstMsgRecv) + { + info->firstMsgRecv = true; + MsnContact *cont = Lists_Get(email); + if (cont && cont->hContact != NULL) + sttSetMirVer(cont->hContact, cont->cap1, true); + } + + if (!_strnicmp(tContentType, "text/plain", 10)) + { + CCSDATA ccs = {0}; + + ccs.hContact = MSN_HContactFromEmail(email, nick, true, true); + + const char* p = tHeader["X-MMS-IM-Format"]; + bool isRtl = p != NULL && strstr(p, "RL=1") != NULL; + + if (info->mJoinedContactsWLID.getCount() > 1) + { + if (msnHaveChatDll) + MSN_ChatStart(info); + else + { + for (int j=0; j < info->mJoinedContactsWLID.getCount(); j++) + { + if (_stricmp(info->mJoinedContactsWLID[j], email) == 0 && j != 0) + { + ccs.hContact = info->getContactHandle(); + break; + } + } + } + } + else + { + char* szEmail; + parseWLID(NEWSTR_ALLOCA(email), NULL, &szEmail, NULL); + sentMsg = _stricmp(szEmail, MyOptions.szEmail) == 0; + if (sentMsg) + ccs.hContact = ubmMsg ? MSN_HContactFromEmail(datau.toEmail, nick) : + info->getContactHandle(); + } + + const char* tP4Context = tHeader["P4-Context"]; + if (tP4Context) + { + size_t newlen = strlen(msgBody) + strlen(tP4Context) + 4; + char* newMsgBody = (char*)mir_alloc(newlen); + mir_snprintf(newMsgBody, newlen, "[%s] %s", tP4Context, msgBody); + mir_free(newbody); + msgBody = newbody = newMsgBody; + } + + if (info->mChatID[0]) + { + GCDEST gcd = { m_szModuleName, { NULL }, GC_EVENT_MESSAGE }; + gcd.ptszID = info->mChatID; + + GCEVENT gce = {0}; + gce.cbSize = sizeof(GCEVENT); + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.pDest = &gcd; + gce.ptszUID = mir_a2t(email); + gce.ptszNick = GetContactNameT(ccs.hContact); + gce.time = time(NULL); + gce.bIsMe = FALSE; + + TCHAR* p = mir_utf8decodeT(msgBody); + gce.ptszText = EscapeChatTags(p); + mir_free(p); + + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + mir_free((void*)gce.pszText); + mir_free((void*)gce.ptszUID); + } + else if (ccs.hContact) + { + if (!sentMsg) + { + CallService(MS_PROTO_CONTACTISTYPING, WPARAM(ccs.hContact), 0); + + PROTORECVEVENT pre; + pre.szMessage = (char*)msgBody; + pre.flags = PREF_UTF + (isRtl ? PREF_RTL : 0); + pre.timestamp = (DWORD)time(NULL); + pre.lParam = 0; + + ccs.szProtoService = PSR_MESSAGE; + ccs.wParam = 0; + ccs.lParam = (LPARAM)⪯ + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + } + else + { + DBEVENTINFO dbei = {0}; + + bool haveWnd = MSN_MsgWndExist(ccs.hContact); + + dbei.cbSize = sizeof(dbei); + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.flags = DBEF_SENT | DBEF_UTF | (haveWnd ? 0 : DBEF_READ) | (isRtl ? DBEF_RTL : 0); + dbei.szModule = m_szModuleName; + dbei.timestamp = time(NULL); + dbei.cbBlob = (unsigned)strlen(msgBody) + 1; + dbei.pBlob = (PBYTE)msgBody; + CallService(MS_DB_EVENT_ADD, (WPARAM)ccs.hContact, (LPARAM)&dbei); + } + } + } + else if (!_strnicmp(tContentType, "text/x-msmsgsprofile", 20)) + { + replaceStr(msnExternalIP, tHeader["ClientIP"]); + abchMigrated = atol(tHeader["ABCHMigrated"]); + langpref = atol(tHeader["lang_preference"]); + emailEnabled = atol(tHeader["EmailEnabled"]); + + if (!MSN_RefreshContactList()) + { + SendBroadcast(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_NOSERVER); + info->sendTerminate(); + } + else + { + MSN_SetServerStatus(m_iDesiredStatus); + MSN_EnableMenuItems(true); + } + } + else if (!_strnicmp(tContentType, "text/x-msmsgscontrol", 20)) + { + const char* tTypingUser = tHeader["TypingUser"]; + + if (tTypingUser != NULL && info->mChatID[0] == 0 && _stricmp(email, MyOptions.szEmail)) + { + HANDLE hContact = MSN_HContactFromEmail(tTypingUser, tTypingUser); + CallService(MS_PROTO_CONTACTISTYPING, (WPARAM) hContact, 7); + } + } + else if (!_strnicmp(tContentType, "text/x-msnmsgr-datacast", 23)) + { + if (info->mJoinedContactsWLID.getCount()) + { + HANDLE tContact; + + if (info->mChatID[0]) + { + GC_INFO gci = {0}; + gci.Flags = HCONTACT; + gci.pszModule = m_szModuleName; + gci.pszID = info->mChatID; + CallServiceSync(MS_GC_GETINFO, 0, (LPARAM)&gci); + tContact = gci.hContact; + } + else + tContact = info->getContactHandle(); + + MimeHeaders tFileInfo; + tFileInfo.readFromBuffer(msgBody); + + const char* id = tFileInfo["ID"]; + if (id != NULL) + { + switch (atol(id)) + { + case 1: // Nudge + NotifyEventHooks(hMSNNudge, (WPARAM)tContact, 0); + break; + + case 2: // Wink + break; + + case 4: // Action Message + break; + } + } + } + } + else 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-msmsgsactivemailnotification", 35)) + sttNotificationMessage(msgBody, false); + else if (!_strnicmp(tContentType, "text/x-msmsgsinitialmdatanotification", 37)) + sttNotificationMessage(msgBody, true); + else if (!_strnicmp(tContentType, "text/x-msmsgsoimnotification", 28)) + sttNotificationMessage(msgBody, false); + else if (!_strnicmp(tContentType, "text/x-msmsgsinvite", 19)) + sttInviteMessage(info, msgBody, email, nick); + else if (!_strnicmp(tContentType, "application/x-msnmsgrp2p", 24)) + { + const char* dest = tHeader["P2P-Dest"]; + if (dest) + { + char *szEmail, *szInst; + parseWLID(NEWSTR_ALLOCA(dest), NULL, &szEmail, &szInst); + + if (stricmp(szEmail, MyOptions.szEmail) == 0) + { + const char* src = tHeader["P2P-Src"]; + if (src == NULL) src = email; + + if (szInst == NULL) + p2p_processMsg(info, msgBody, src); + else if (stricmp(szInst, MyOptions.szMachineGuidP2P) == 0) + p2p_processMsgV2(info, msgBody, src); + } + } + } + else if (!_strnicmp(tContentType, "text/x-mms-emoticon", 19)) + sttCustomSmiley(msgBody, email, nick, MSN_APPID_CUSTOMSMILEY); + else if (!_strnicmp(tContentType, "text/x-mms-animemoticon", 23)) + sttCustomSmiley(msgBody, email, nick, MSN_APPID_CUSTOMANIMATEDSMILEY); + + mir_free(newbody); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// Process Yahoo Find + +void CMsnProto::sttProcessYFind(char* buf, size_t len) +{ + if (buf == NULL) return; + ezxml_t xmli = ezxml_parse_str(buf, len); + + ezxml_t dom = ezxml_child(xmli, "d"); + const char* szDom = ezxml_attr(dom, "n"); + + ezxml_t cont = ezxml_child(dom, "c"); + const char* szCont = ezxml_attr(cont, "n"); + + char szEmail[128]; + mir_snprintf(szEmail, sizeof(szEmail), "%s@%s", szCont, szDom); + + const char *szNetId = ezxml_attr(cont, "t"); + if (msnSearchId != NULL) + { + if (szNetId != NULL) + { + TCHAR* szEmailT = mir_utf8decodeT(szEmail); + PROTOSEARCHRESULT isr = {0}; + isr.cbSize = sizeof(isr); + isr.flags = PSR_TCHAR; + isr.id = szEmailT; + isr.nick = szEmailT; + isr.email = szEmailT; + + SendBroadcast(NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, msnSearchId, (LPARAM)&isr); + mir_free(szEmailT); + } + SendBroadcast(NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, msnSearchId, 0); + + msnSearchId = NULL; + } + else + { + if (szNetId != NULL) + { + int netId = atol(szNetId); + HANDLE hContact = MSN_HContactFromEmail(szEmail, szEmail, true, false); + if (MSN_AddUser(hContact, szEmail, netId, LIST_FL)) + { + MSN_AddUser(hContact, szEmail, netId, LIST_PL + LIST_REMOVE); + MSN_AddUser(hContact, szEmail, netId, LIST_BL + LIST_REMOVE); + MSN_AddUser(hContact, szEmail, netId, LIST_AL); + DBDeleteContactSetting(hContact, "CList", "Hidden"); + } + MSN_SetContactDb(hContact, szEmail); + } + } + + ezxml_free(xmli); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// Process user addition + +void CMsnProto::sttProcessAdd(char* buf, size_t len) +{ + if (buf == NULL) return; + + ezxml_t xmli = ezxml_parse_str(buf, len); + ezxml_t dom = ezxml_child(xmli, "d"); + while (dom != NULL) + { + const char* szDom = ezxml_attr(dom, "n"); + ezxml_t cont = ezxml_child(dom, "c"); + while (cont != NULL) + { + const char* szCont = ezxml_attr(cont, "n"); + const char* szNick = ezxml_attr(cont, "f"); + int listId = atol(ezxml_attr(cont, "l")); + int netId = atol(ezxml_attr(cont, "t")); + + char szEmail[128]; + mir_snprintf(szEmail, sizeof(szEmail), "%s@%s", szCont, szDom); + + UrlDecode((char*)szNick); + + if (listId == LIST_FL) + { + HANDLE hContact = MSN_HContactFromEmail(szEmail, szNick, true, false); + MSN_SetContactDb(hContact, szEmail); + } + + if (listId == LIST_RL) + MSN_SharingFindMembership(true); + else + MSN_AddUser(NULL, szEmail, netId, listId); + + MsnContact* msc = Lists_Get(szEmail); + if (msc == NULL) + { + Lists_Add(listId, netId, szEmail); + msc = Lists_Get(szEmail); + } + + if (listId == LIST_RL) + { + if ((msc->list & (LIST_AL | LIST_BL)) == 0) + { + MSN_AddAuthRequest(szEmail, szNick, msc->invite); + msc->netId = netId; + } + else + MSN_AddUser(NULL, szEmail, netId, LIST_PL + LIST_REMOVE); + } + + cont = ezxml_next(cont); + } + dom = ezxml_next(dom); + } + ezxml_free(xmli); +} + + +void CMsnProto::sttProcessRemove(char* buf, size_t len) +{ + ezxml_t xmli = ezxml_parse_str(buf, len); + ezxml_t dom = ezxml_child(xmli, "d"); + while (dom != NULL) + { + const char* szDom = ezxml_attr(dom, "n"); + ezxml_t cont = ezxml_child(dom, "c"); + while (cont != NULL) + { + const char* szCont = ezxml_attr(cont, "n"); + int listId = atol(ezxml_attr(cont, "l")); + + char szEmail[128]; + mir_snprintf(szEmail, sizeof(szEmail), "%s@%s", szCont, szDom); + Lists_Remove(listId, szEmail); + + MsnContact* msc = Lists_Get(szEmail); + if (msc == NULL || (msc->list & (LIST_RL | LIST_FL | LIST_LL)) == 0) + { + if (msc->hContact && _stricmp(szEmail, MyOptions.szEmail)) + { + CallService(MS_DB_CONTACT_DELETE, (WPARAM)msc->hContact, 0); + msc->hContact = NULL; + } + } + + cont = ezxml_next(cont); + } + dom = ezxml_next(dom); + } + ezxml_free(xmli); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +// MSN_HandleCommands - process commands from the server +///////////////////////////////////////////////////////////////////////////////////////// + +void CMsnProto::sttProcessStatusMessage(char* buf, unsigned len, const char* wlid) +{ + HANDLE hContact = MSN_HContactFromEmail(wlid); + if (hContact == NULL) return; + + ezxml_t xmli = ezxml_parse_str(buf, len); + + char* szEmail; + parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL); + + // Add endpoints + for (ezxml_t endp = ezxml_child(xmli, "EndpointData"); endp; endp = ezxml_next(endp)) + { + const char *id = ezxml_attr(endp, "id"); + const char *caps = ezxml_txt(ezxml_child(endp, "Capabilities")); + char* end = NULL; + unsigned cap1 = caps ? strtoul(caps, &end, 10) : 0; + unsigned cap2 = end && *end == ':' ? strtoul(end + 1, NULL, 10) : 0; + + Lists_AddPlace(szEmail, id, cap1, cap2); + } + + // Process status message info + const char* szStatMsg = ezxml_txt(ezxml_child(xmli, "PSM")); + if (*szStatMsg) + { + stripBBCode((char*)szStatMsg); + stripColorCode((char*)szStatMsg); + DBWriteContactSettingStringUtf(hContact, "CList", "StatusMsg", szStatMsg); + } + else DBDeleteContactSetting(hContact, "CList", "StatusMsg"); + + { + mir_ptr tszStatus( mir_utf8decodeT(szStatMsg)); + SendBroadcast(hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, NULL, tszStatus); + } + + // Process current media info + const char* szCrntMda = ezxml_txt(ezxml_child(xmli, "CurrentMedia")); + if (!*szCrntMda) + { + deleteSetting(hContact, "ListeningTo"); + ezxml_free(xmli); + return; + } + + // Get parts separeted by "\\0" + char *parts[16]; + unsigned pCount; + + char* p = (char*)szCrntMda; + for (pCount = 0; pCount < SIZEOF(parts); ++pCount) + { + parts[pCount] = p; + + char* p1 = strstr(p, "\\0"); + if (p1 == NULL) break; + + *p1 = '\0'; + p = p1 + 2; + } + + // Now let's mount the final string + if (pCount <= 4) + { + deleteSetting(hContact, "ListeningTo"); + ezxml_free(xmli); + return; + } + + // Check if there is any info in the string + bool foundUsefullInfo = false; + for (unsigned i = 4; i < pCount; i++) + { + if (parts[i][0] != '\0') + { + foundUsefullInfo = true; + break; + } + } + if (!foundUsefullInfo) + { + deleteSetting(hContact, "ListeningTo"); + ezxml_free(xmli); + return; + } + + if (!ServiceExists(MS_LISTENINGTO_GETPARSEDTEXT) || + !ServiceExists(MS_LISTENINGTO_OVERRIDECONTACTOPTION) || + !CallService(MS_LISTENINGTO_OVERRIDECONTACTOPTION, 0, (LPARAM) hContact)) + { + // User contact options + char *format = mir_strdup(parts[3]); + char *unknown = NULL; + if (ServiceExists(MS_LISTENINGTO_GETUNKNOWNTEXT)) + unknown = mir_utf8encodeT((TCHAR *) CallService(MS_LISTENINGTO_GETUNKNOWNTEXT, 0, 0)); + + for (unsigned i = 4; i < pCount; i++) + { + char part[16]; + size_t lenPart = mir_snprintf(part, sizeof(part), "{%d}", i - 4); + if (parts[i][0] == '\0' && unknown != NULL) + parts[i] = unknown; + size_t lenPartsI = strlen(parts[i]); + for (p = strstr(format, part); p; p = strstr(p + lenPartsI, part)) + { + if (lenPart < lenPartsI) + { + int loc = p - format; + format = (char *)mir_realloc(format, strlen(format) + (lenPartsI - lenPart) + 1); + p = format + loc; + } + memmove(p + lenPartsI, p + lenPart, strlen(p + lenPart) + 1); + memmove(p, parts[i], lenPartsI); + } + } + + setStringUtf(hContact, "ListeningTo", format); + mir_free(unknown); + mir_free(format); + } + else + { + // Use user options + LISTENINGTOINFO lti = {0}; + lti.cbSize = sizeof(LISTENINGTOINFO); + + lti.ptszTitle = mir_utf8decodeT(parts[4]); + if (pCount > 5) lti.ptszArtist = mir_utf8decodeT(parts[5] ); + if (pCount > 6) lti.ptszAlbum = mir_utf8decodeT(parts[6] ); + if (pCount > 7) lti.ptszTrack = mir_utf8decodeT(parts[7] ); + if (pCount > 8) lti.ptszYear = mir_utf8decodeT(parts[8] ); + if (pCount > 9) lti.ptszGenre = mir_utf8decodeT(parts[9] ); + if (pCount > 10) lti.ptszLength = mir_utf8decodeT(parts[10]); + if (pCount > 11) lti.ptszPlayer = mir_utf8decodeT(parts[11]); + else lti.ptszPlayer = mir_utf8decodeT(parts[0]); + if (pCount > 12) lti.ptszType = mir_utf8decodeT(parts[12]); + else lti.ptszType = mir_utf8decodeT(parts[1]); + + TCHAR *cm = (TCHAR *) CallService(MS_LISTENINGTO_GETPARSEDTEXT, (WPARAM) _T("%title% - %artist%"), (LPARAM) <i); + setTString(hContact, "ListeningTo", cm); + + mir_free(cm); + + mir_free(lti.ptszArtist); + mir_free(lti.ptszAlbum); + mir_free(lti.ptszTitle); + mir_free(lti.ptszTrack); + mir_free(lti.ptszYear); + mir_free(lti.ptszGenre); + mir_free(lti.ptszLength); + mir_free(lti.ptszPlayer); + mir_free(lti.ptszType); + } + ezxml_free(xmli); +} + + +void CMsnProto::sttProcessPage(char* buf, unsigned len) +{ + if (buf == NULL) return; + ezxml_t xmlnot = ezxml_parse_str(buf, len); + + ezxml_t xmlbdy = ezxml_get(xmlnot, "MSG", 0, "BODY", -1); + const char* szMsg = ezxml_txt(ezxml_child(xmlbdy, "TEXT")); + const char* szTel = ezxml_attr(ezxml_child(xmlnot, "FROM"), "name"); + + if (szTel && *szMsg) + { + PROTORECVEVENT pre = {0}; + pre.szMessage = (char*)szMsg; + pre.flags = PREF_UTF /*+ ((isRtl) ? PREF_RTL : 0)*/; + pre.timestamp = time(NULL); + + CCSDATA ccs = {0}; + ccs.hContact = MSN_HContactFromEmail(szTel, szTel, true, true); + ccs.szProtoService = PSR_MESSAGE; + ccs.lParam = (LPARAM)⪯ + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + + } + ezxml_free(xmlnot); +} + + +void CMsnProto::sttProcessNotificationMessage(char* buf, unsigned len) +{ + if (buf == NULL) return; + ezxml_t xmlnot = ezxml_parse_str(buf, len); + + if (strcmp(ezxml_attr(xmlnot, "siteid"), "0") == 0) + { + ezxml_free(xmlnot); + return; + } + + ezxml_t xmlmsg = ezxml_child(xmlnot, "MSG"); + ezxml_t xmlact = ezxml_child(xmlmsg, "ACTION"); + ezxml_t xmlbdy = ezxml_child(xmlmsg, "BODY"); + ezxml_t xmltxt = ezxml_child(xmlbdy, "TEXT"); + + if (xmltxt != NULL) + { + char fullurl[1024]; + size_t sz = 0; + + const char* acturl = ezxml_attr(xmlact, "url"); + if (acturl == NULL || strstr(acturl, "://") == NULL) + sz += mir_snprintf(fullurl+sz, sizeof(fullurl)-sz, "%s", ezxml_attr(xmlnot, "siteurl")); + + sz += mir_snprintf(fullurl+sz, sizeof(fullurl)-sz, "%s", acturl); + if (sz != 0 && fullurl[sz-1] != '?') + sz += mir_snprintf(fullurl+sz, sizeof(fullurl)-sz, "?"); + + mir_snprintf(fullurl+sz, sizeof(fullurl)-sz, "notification_id=%s&message_id=%s", + ezxml_attr(xmlnot, "id"), ezxml_attr(xmlmsg, "id")); + + SkinPlaySound(alertsoundname); + + TCHAR* alrt = mir_utf8decodeT(ezxml_txt(xmltxt)); + MSN_ShowPopup(TranslateT("MSN Alert"), alrt, MSN_ALERT_POPUP | MSN_ALLOW_MSGBOX, fullurl); + mir_free(alrt); + } + else if (xmlbdy) + { + const char *txt = ezxml_txt(xmlbdy); + if (strstr(txt, "ABCHInternal")) + { + MSN_SharingFindMembership(true); + MSN_ABFind("ABFindContactsPaged", NULL, true); + MSN_StoreGetProfile(); + } + } + ezxml_free(xmlnot); +} + +void CMsnProto::MSN_InitSB(ThreadData* info, const char* szEmail) +{ + MsnContact *cont = Lists_Get(szEmail); + + if (cont->netId == NETID_MSN) + info->sendCaps(); + + bool typing = false; + + for (int i=3; --i;) + { + MsgQueueEntry E; + while (MsgQueue_GetNext(szEmail, E)) + { + if (E.msgType == 'X') ; + else if (E.msgType == 2571) + typing = E.flags != 0; + else if (E.msgSize == 0) + { + info->sendMessage(E.msgType, NULL, 1, E.message, E.flags); + SendBroadcast(cont->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)E.seq, 0); + } + else + { + if (E.msgType == 'D' && !info->mBridgeInit /*&& strchr(data.flags, ':')*/) + { + info->mBridgeInit = true; + +// P2PV2_Header hdrdata(E.message); +// P2PV2_Header tHdr; +// tHdr.mID = hdrdata.mID; +// p2p_sendMsg(info, E.wlid, 0, tHdr, NULL, 0); + } + info->sendRawMessage(E.msgType, E.message, E.msgSize); + } + + mir_free(E.message); + mir_free(E.wlid); + + if (E.ft != NULL) + info->mMsnFtp = E.ft; + } + mir_free(info->mInitialContactWLID); info->mInitialContactWLID = NULL; + } + + if (typing) + MSN_StartStopTyping(info, true); + + if (getByte("EnableDeliveryPopup", 0)) + { + MSN_ShowPopup(cont->hContact, info->mCaller ? + TranslateT("Chat session established by my request") : + TranslateT("Chat session established by contact request"), + 0); + } + + PROTO_AVATAR_INFORMATIONT ai = { sizeof(ai), cont->hContact }; + GetAvatarInfo(GAIF_FORCE, (LPARAM)&ai); +} + +int CMsnProto::MSN_HandleCommands(ThreadData* info, char* cmdString) +{ + char* params = ""; + int trid = -1; + + 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); + + switch((*(PDWORD)cmdString & 0x00FFFFFF) | 0x20000000) + { + case ' KCA': //********* ACK: section 8.7 Instant Messages + ReleaseSemaphore(info->hWaitEvent, 1, NULL); + + if (info->mJoinedContactsWLID.getCount() > 0 && MyOptions.SlowSend) + SendBroadcast(info->getContactHandle(), ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)trid, 0); + break; + + case ' YQF': //********* FQY: Find Yahoo User + char* tWords[1]; + if (sttDivideWords(params, 1, tWords) != 1) + { + MSN_DebugLog("Invalid %.3s command, ignoring", cmdString); + } + else + { + size_t len = atol(tWords[0]); + sttProcessYFind((char*)HReadBuffer(info, 0).surelyRead(len), len); + } + break; + + case ' LDA': //********* ADL: Add to the list + { + char* tWords[1]; + if (sttDivideWords(params, 1, tWords) != 1) + { +LBL_InvalidCommand: + MSN_DebugLog("Invalid %.3s command, ignoring", cmdString); + break; + } + + if (strcmp(tWords[0], "OK") != 0) + { + size_t len = atol(tWords[0]); + sttProcessAdd((char*)HReadBuffer(info, 0).surelyRead(len), len); + } + break; + } + + case ' SBS': + break; + + case ' SNA': //********* ANS: section 8.4 Getting Invited to a Switchboard Session + break; + + case ' PRP': + break; + + case ' PLB': //********* BLP: section 7.6 List Retrieval And Property Management + 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 (strchr(data.userEmail, ';')) + { + if (info->mJoinedContactsWLID.getCount() == 1) + p2p_clearThreadSessions(info->mJoinedContactsWLID[0], info->mType); + info->contactLeft(data.userEmail); + break; + } + + HANDLE hContact = MSN_HContactFromEmail(data.userEmail); + + if (getByte("EnableSessionPopup", 0)) + MSN_ShowPopup(hContact, TranslateT("Contact left channel"), 0); + + // modified for chat + if (msnHaveChatDll) + { + GCDEST gcd = { m_szModuleName, { NULL }, GC_EVENT_QUIT }; + gcd.ptszID = info->mChatID; + + GCEVENT gce = {0}; + gce.cbSize = sizeof(GCEVENT); + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.pDest = &gcd; + gce.ptszNick = GetContactNameT(hContact); + gce.ptszUID = mir_a2t(data.userEmail); + gce.time = time(NULL); + gce.bIsMe = FALSE; + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + mir_free((void*)gce.pszUID); + } + + int personleft = info->contactLeft(data.userEmail); + + int temp_status = getWord(hContact, "Status", ID_STATUS_OFFLINE); + if (temp_status == ID_STATUS_INVISIBLE && MSN_GetThreadByContact(data.userEmail) == NULL) + setWord(hContact, "Status", ID_STATUS_OFFLINE); + + // see if the session is quit due to idleness + if (info->mChatID[0] && personleft == 1) + { + if (!strcmp(data.isIdle, "1")) + { + GCDEST gcd = { m_szModuleName, { NULL }, GC_EVENT_INFORMATION }; + gcd.ptszID = info->mChatID; + + GCEVENT gce = {0}; + gce.cbSize = sizeof(GCEVENT); + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.pDest = &gcd; + gce.bIsMe = FALSE; + gce.time = time(NULL); + gce.ptszText = TranslateT("This conversation has been inactive, participants will be removed."); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + gce.ptszText = TranslateT("To resume the conversation, please quit this session and start a new chat session."); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + } + else + { + if (!Miranda_Terminated() && MessageBox(NULL, + TranslateT("There is only 1 person left in the chat, do you want to switch back to standard message window?"), + TranslateT("MSN Chat"), MB_YESNO|MB_ICONQUESTION) == IDYES) + { + // kill chat dlg and open srmm dialog + MSN_KillChatSession(info->mChatID); + + // open up srmm dialog when quit while 1 person left + HANDLE hContact = info->getContactHandle(); + if (hContact) CallServiceSync(MS_MSG_SENDMESSAGE, (WPARAM)hContact, 0); + } + } + } + // this is not in chat session, quit the session when everyone left + else if (info->mJoinedContactsWLID.getCount() < 1) + return 1; + + break; + } + case ' LAC': //********* CAL: section 8.3 Inviting Users to a Switchboard Session + break; + + case ' GHC': //********* CHG: section 7.7 Client States + { + int oldStatus = m_iStatus; + int newStatus = MSNStatusToMiranda(params); + if (oldStatus <= ID_STATUS_OFFLINE) + { + isConnectSuccess = true; + int count = -1; + for (;;) + { + MsnContact *msc = Lists_GetNext(count); + if (msc == NULL) break; + + if (msc->netId == NETID_MOB) + setWord(msc->hContact, "Status", ID_STATUS_ONTHEPHONE); + } + } + if (newStatus != ID_STATUS_IDLE) + { + m_iStatus = newStatus; + SendBroadcast(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldStatus, newStatus); + MSN_DebugLog("Status change acknowledged: %s", params); + MSN_RemoveEmptyGroups(); + } + if (newStatus == ID_STATUS_OFFLINE) return 1; + break; + } + case ' LHC': //********* CHL: Query from Server on MSNP7 + { + char* authChallengeInfo; + if (sttDivideWords(params, 1, &authChallengeInfo) != 1) + goto LBL_InvalidCommand; + + char dgst[64]; + MSN_MakeDigest(authChallengeInfo, dgst); + info->sendPacket("QRY", "%s 32\r\n%s", msnProductID, dgst); + break; + } + case ' RVC': //********* CVR: MSNP8 + break; + + case ' NLF': //********* FLN: section 7.9 Notification Messages + { + union + { + char* tWords[2]; + struct { char *userEmail, *netId; } data; + }; + + int tArgs = sttDivideWords(params, 2, tWords); + if (tArgs < 2) + goto LBL_InvalidCommand; + + HANDLE hContact = MSN_HContactFromEmail(data.userEmail); + if (hContact != NULL) + { + setWord(hContact, "Status", MSN_GetThreadByContact(data.userEmail) ? + ID_STATUS_INVISIBLE : ID_STATUS_OFFLINE); + setDword(hContact, "IdleTS", 0); + ForkThread(&CMsnProto::MsgQueue_AllClearThread, mir_strdup(data.userEmail)); + } + break; + } + case ' NLI': + case ' NLN': //********* ILN/NLN: section 7.9 Notification Messages + { + union + { + char* tWords[5]; + struct { char *userStatus, *wlid, *userNick, *objid, *cmdstring; } data; + }; + + int tArgs = sttDivideWords(params, 5, tWords); + if (tArgs < 3) + goto LBL_InvalidCommand; + + UrlDecode(data.userNick); + stripBBCode(data.userNick); + stripColorCode(data.userNick); + + bool isMe = false; + char* szEmail, *szNet; + parseWLID(NEWSTR_ALLOCA(data.wlid), &szNet, &szEmail, NULL); + if (!stricmp(szEmail, MyOptions.szEmail) && !strcmp(szNet, "1")) + { + isMe = true; + int newStatus = MSNStatusToMiranda(params); + if (newStatus != m_iStatus && newStatus != ID_STATUS_IDLE) + { + int oldMode = m_iStatus; + m_iDesiredStatus = m_iStatus = newStatus; + SendBroadcast(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldMode, m_iStatus); + } + } + + WORD lastStatus = ID_STATUS_OFFLINE; + + MsnContact *cont = Lists_Get(szEmail); + + HANDLE hContact = NULL; + if (!cont && !isMe) + { + hContact = MSN_HContactFromEmail(data.wlid, data.userNick, true, true); + cont = Lists_Get(szEmail); + } + if (cont) hContact = cont->hContact; + + if (hContact != NULL) + { + setStringUtf(hContact, "Nick", data.userNick); + lastStatus = getWord(hContact, "Status", ID_STATUS_OFFLINE); + if (lastStatus == ID_STATUS_OFFLINE || lastStatus == ID_STATUS_INVISIBLE) + DBDeleteContactSetting(hContact, "CList", "StatusMsg"); + + int newStatus = MSNStatusToMiranda(params); + setWord(hContact, "Status", newStatus != ID_STATUS_IDLE ? newStatus : ID_STATUS_AWAY); + setDword(hContact, "IdleTS", newStatus != ID_STATUS_IDLE ? 0 : time(NULL)); + } + + if (tArgs > 3 && tArgs <= 5 && cont) + { + UrlDecode(data.cmdstring); + + char* end = NULL; + cont->cap1 = strtoul(data.objid, &end, 10); + cont->cap2 = end && *end == ':' ? strtoul(end + 1, NULL, 10) : 0; + + if (lastStatus == ID_STATUS_OFFLINE) + { + DBVARIANT dbv; + bool always = getString(hContact, "MirVer", &dbv) != 0; + if (!always) MSN_FreeVariant(&dbv); + sttSetMirVer(hContact, cont->cap1, always); + } + + if (/*(cont->cap1 & 0xf0000000) &&*/ data.cmdstring[0] && strcmp(data.cmdstring, "0")) + { + char* szAvatarHash = MSN_GetAvatarHash(data.cmdstring); + if (szAvatarHash == NULL) goto remove; + + setString(hContact, "PictContext", data.cmdstring); + setString(hContact, "AvatarHash", szAvatarHash); + + if (hContact != NULL) + { + char szSavedHash[64] = ""; + getStaticString(hContact, "AvatarSavedHash", szSavedHash, sizeof(szSavedHash)); + if (stricmp(szSavedHash, szAvatarHash)) + { + SendBroadcast(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); + } + else + { + char szSavedContext[64]; + int result = getStaticString(hContact, "PictSavedContext", szSavedContext, sizeof(szSavedContext)); + if (result || strcmp(szSavedContext, data.cmdstring)) + SendBroadcast(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); + } + mir_free(szAvatarHash); + } + } + else + { +remove: + deleteSetting(hContact, "AvatarHash"); + deleteSetting(hContact, "AvatarSavedHash"); + deleteSetting(hContact, "PictContext"); + deleteSetting(hContact, "PictSavedContext"); + +// char tFileName[MAX_PATH]; +// MSN_GetAvatarFileName(hContact, tFileName, sizeof(tFileName)); +// remove(tFileName); + + SendBroadcast(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); + } + } + else + { + if (lastStatus == ID_STATUS_OFFLINE) + deleteSetting(hContact, "MirVer"); + } + + break; + } + case ' ORI': //********* IRO: section 8.4 Getting Invited to a Switchboard Session + { + union + { + char* tWords[5]; + struct { char *strThisContact, *totalContacts, *userEmail, *userNick, *flags; } data; + }; + + int tNumTokens = sttDivideWords(params, 5, tWords); + if (tNumTokens < 4) + goto LBL_InvalidCommand; + + info->contactJoined(data.userEmail); + + if (!strchr(data.userEmail, ';')) + { + UrlDecode(data.userNick); + HANDLE hContact = MSN_HContactFromEmail(data.userEmail, data.userNick, true, true); + + if (tNumTokens == 5 && strcmp(data.flags, "0:0")) + { + MsnContact *cont = Lists_Get(data.userEmail); + if (cont) + { + char* end = NULL; + cont->cap1 = strtoul(data.flags, &end, 10); + cont->cap2 = end && *end == ':' ? strtoul(end + 1, NULL, 10) : 0; + } + } + + int temp_status = getWord(hContact, "Status", ID_STATUS_OFFLINE); + if (temp_status == ID_STATUS_OFFLINE && Lists_IsInList(LIST_FL, data.userEmail)) + setWord(hContact, "Status", ID_STATUS_INVISIBLE); + + // only start the chat session after all the IRO messages has been recieved + if (msnHaveChatDll && info->mJoinedContactsWLID.getCount() > 1 && !strcmp(data.strThisContact, data.totalContacts)) + MSN_ChatStart(info); + } + break; + } + case ' IOJ': //********* JOI: section 8.5 Session Participant Changes + { + union { + char* tWords[3]; + struct { char *userEmail, *userNick, *flags; } data; + }; + + int tNumTokens = sttDivideWords(params, 3, tWords); + if (tNumTokens < 2) + goto LBL_InvalidCommand; + + UrlDecode(data.userEmail); + + if (strchr(data.userEmail, ';')) + { + info->contactJoined(data.userEmail); + break; + } + + if (_stricmp(MyOptions.szEmail, data.userEmail) == 0) + { + if (!info->mCaller) + { + if (info->mJoinedContactsWLID.getCount() == 1) + { + MSN_InitSB(info, info->mJoinedContactsWLID[0]); + } + else + { + info->sendCaps(); + if (info->mInitialContactWLID && MsgQueue_CheckContact(info->mInitialContactWLID)) + msnNsThread->sendPacket("XFR", "SB"); + mir_free(info->mInitialContactWLID); info->mInitialContactWLID = NULL; + } + break; + } + + const char* wlid; + do { + wlid = MsgQueue_GetNextRecipient(); + } while (wlid != NULL && MSN_GetUnconnectedThread(wlid) != NULL); + + if (wlid == NULL) //can happen if both parties send first message at the same time + { + MSN_DebugLog("USR (SB) internal: thread created for no reason"); + return 1; + } + + if (strcmp(wlid, "chat") == 0) + { + MsgQueueEntry E; + MsgQueue_GetNext(wlid, E); + + for (int i = 0; i < E.cont->getCount(); ++i) + info->sendPacket("CAL", (*E.cont)[i]); + + MSN_ChatStart(info); + + delete E.cont; + mir_free(E.wlid); + break; + } + + char* szEmail; + parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL); + + info->mInitialContactWLID = mir_strdup(szEmail); + info->sendPacket("CAL", szEmail); + break; + } + + UrlDecode(data.userNick); + stripBBCode(data.userNick); + stripColorCode(data.userNick); + + HANDLE hContact = MSN_HContactFromEmail(data.userEmail, data.userNick, true, true); + if (tNumTokens == 3) + { + MsnContact *cont = Lists_Get(data.userEmail); + if (cont) + { + char* end = NULL; + cont->cap1 = strtoul(data.flags, &end, 10); + cont->cap2 = end && *end == ':' ? strtoul(end + 1, NULL, 10) : 0; + } + } + + mir_utf8decode(data.userNick, NULL); + MSN_DebugLog("New contact in channel %s %s", data.userEmail, data.userNick); + + if (info->contactJoined(data.userEmail) <= 1) + { + MSN_InitSB(info, data.userEmail); + } + else + { + bool chatCreated = info->mChatID[0] != 0; + + info->sendCaps(); + + if (msnHaveChatDll) + { + if (chatCreated) + { + GCDEST gcd = { m_szModuleName, { NULL }, GC_EVENT_JOIN }; + gcd.ptszID = info->mChatID; + + GCEVENT gce = {0}; + gce.cbSize = sizeof(GCEVENT); + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.pDest = &gcd; + gce.ptszNick = GetContactNameT(hContact); + gce.ptszUID = mir_a2t(data.userEmail); + gce.ptszStatus = TranslateT("Others"); + gce.time = time(NULL); + gce.bIsMe = FALSE; + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + mir_free((void*)gce.ptszUID); + } + else MSN_ChatStart(info); + } + } + break; + } + + case ' GSM': //********* MSG: sections 8.7 Instant Messages, 8.8 Receiving an Instant Message + MSN_ReceiveMessage(info, cmdString, params); + break; + + case ' MBU': + MSN_ReceiveMessage(info, cmdString, params); + break; + + case ' KAN': //********* NAK: section 8.7 Instant Messages + if (info->mJoinedContactsWLID.getCount() > 0 && MyOptions.SlowSend) + SendBroadcast(info->getContactHandle(), + ACKTYPE_MESSAGE, ACKRESULT_FAILED, + (HANDLE)trid, (LPARAM)MSN_Translate("Message delivery failed")); + MSN_DebugLog("Message send failed (trid=%d)", trid); + break; + + case ' TON': //********* NOT: notification message + sttProcessNotificationMessage((char*)HReadBuffer(info, 0).surelyRead(trid), trid); + break; + + case ' GPI': //********* IPG: mobile page + sttProcessPage((char*)HReadBuffer(info, 0).surelyRead(trid), trid); + break; + + case ' FCG': //********* GCF: + HReadBuffer(info, 0).surelyRead(atol(params)); + break; + + case ' TUO': //********* OUT: sections 7.10 Connection Close, 8.6 Leaving a Switchboard Session + if (!_stricmp(params, "OTH")) + { + 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 ' YRQ': //********* QRY: + break; + + case ' GNQ': //********* QNG: reply to PNG + msnPingTimeout = trid; + if (info->mType == SERVER_NOTIFICATION && hKeepAliveThreadEvt != NULL) + SetEvent(hKeepAliveThreadEvt); + break; + + case ' LMR': //********* RML: Remove from the list + { + char* tWords[1]; + if (sttDivideWords(params, 1, tWords) != 1) + goto LBL_InvalidCommand; + + if (strcmp(tWords[0], "OK") != 0) + { + size_t len = atol(tWords[0]); + sttProcessRemove((char*)HReadBuffer(info, 0).surelyRead(len), len); + } + break; + } + + case ' GNR': //********* RNG: section 8.4 Getting Invited to a Switchboard Session + //note: unusual message encoding: trid==sessionid + { + union { + char* tWords[8]; + struct { char *newServer, *security, *authChallengeInfo, *callerEmail, *callerNick, + *type, *srcUrl, *genGateway; } data; + }; + + if (sttDivideWords(params, 8, tWords) != 8) + goto LBL_InvalidCommand; + + UrlDecode(data.newServer); UrlDecode(data.callerEmail); + UrlDecode(data.callerNick); + stripBBCode(data.callerNick); + stripColorCode(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->gatewayType = atol(data.genGateway) != 0; + newThread->mType = SERVER_SWITCHBOARD; + newThread->mInitialContactWLID = mir_strdup(data.callerEmail); + MSN_HContactFromEmail(data.callerEmail, data.callerNick, true, true); + mir_snprintf(newThread->mCookie, sizeof(newThread->mCookie), "%s %d", data.authChallengeInfo, trid); + + ReleaseSemaphore(newThread->hWaitEvent, MSN_PACKETS_COMBINE, NULL); + + MSN_DebugLog("Opening caller's switchboard server '%s'...", data.newServer); + newThread->startThread(&CMsnProto::MSNServerThread, this); + break; + } + + case ' XBU': // UBX : MSNP11+ User Status Message + { + union + { + char* tWords[2]; + struct { char *wlid, *datalen; } data; + }; + + if (sttDivideWords(params, 2, tWords) != 2) + goto LBL_InvalidCommand; + + int len = atol(data.datalen); + if (len < 0 || len > 4000) + goto LBL_InvalidCommand; + + sttProcessStatusMessage((char*)HReadBuffer(info, 0).surelyRead(len), len, data.wlid); + break; + } + + case ' NBU': // UBN : MSNP13+ File sharing, P2P Bootstrap, TURN setup. + { + union + { + char* tWords[3]; + struct { char *email, *typeId, *datalen; } data; + }; + + if (sttDivideWords(params, 3, tWords) != 3) + goto LBL_InvalidCommand; + + int len = atol(data.datalen); + if (len < 0 || len > 4000) + goto LBL_InvalidCommand; + + HReadBuffer buf(info, 0); + char* msgBody = (char*)buf.surelyRead(len); + + char *szEmail = data.email; + if (strstr(data.email, sttVoidUid)) + parseWLID(NEWSTR_ALLOCA(data.email), NULL, &szEmail, NULL); + + switch (atol(data.typeId)) + { + case 1: + // File sharing stuff + // sttProcessFileSharing(msgBody, len, hContact); + break; + + case 3: + // P2P Bootstrap + p2p_processSIP(info, msgBody, NULL, szEmail); + break; + + case 4: + case 8: + 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." ); + break; + + case 6: + MSN_SharingFindMembership(true); + MSN_ABFind("ABFindContactsPaged", NULL, true); + break; + + case 10: + // TURN setup + p2p_processSIP(info, msgBody, NULL, szEmail); + break; + } + break; + } + + case ' NUU': // UUN : MSNP13+ File sharing, P2P Bootstrap, TURN setup. + 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); + + if (strcmp(data.status, "OK")) + { + MSN_DebugLog("Unknown status to USR command (SB): '%s'", data.status); + break; + } + + info->sendPacket("CAL", MyOptions.szEmail); + } + else //dispatch or notification server (section 7.3) + { + union + { + char* tWords[4]; + struct { char *security, *sequence, *authChallengeInfo, *nonce; } data; + }; + + if (sttDivideWords(params, 4, tWords) != 4) + goto LBL_InvalidCommand; + + if (!strcmp(data.security, "SSO")) + { + if (MSN_GetPassportAuth()) + { + m_iDesiredStatus = ID_STATUS_OFFLINE; + return 1; + } + + char* sec = GenerateLoginBlob(data.nonce); + info->sendPacket("USR", "SSO S %s %s %s", authStrToken ? authStrToken : "", sec, MyOptions.szMachineGuid); + mir_free(sec); + + ForkThread(&CMsnProto::msn_keepAliveThread, NULL); + ForkThread(&CMsnProto::MSNConnDetectThread, NULL); + } + else if (!strcmp(data.security, "OK")) + { + } + else + { + MSN_DebugLog("Unknown security package '%s'", data.security); + + if (info->mType == SERVER_NOTIFICATION) + { + SendBroadcast(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPROTOCOL); + } + return 1; + } + } + break; + + case ' SFR': // RFS: Refresh Contact List + if (!MSN_RefreshContactList()) + { + MSN_ShowError("Cannot retrieve contact list"); + return 1; + } + break; + + case ' XUU': // UUX: MSNP11 addition + { + char* tWords[1]; + + if (sttDivideWords(params, SIZEOF(tWords), tWords) != SIZEOF(tWords)) + goto LBL_InvalidCommand; + + int len = atol(tWords[0]); + if (len < 0 || len > 4000) + goto LBL_InvalidCommand; + + HReadBuffer(info, 0).surelyRead(len); + break; + } + case ' REV': //******** VER: section 7.1 Protocol Versioning + { + char* protocol1; + if (sttDivideWords(params, 1, &protocol1) != 1) + goto LBL_InvalidCommand; + + if (MyOptions.szEmail[0] == 0) + { + MSN_ShowError("You must specify your e-mail in Options/Network/MSN"); + return 1; + } +/* + if (strcmp(protocol1, msnProtID)) + { + MSN_ShowError("Server has requested an unknown protocol set (%s)", params); + + if (info->mType == SERVER_NOTIFICATION) + { + SendBroadcast(NULL, ACKTYPE_LOGIN, ACKRESULT_FAILED, NULL, LOGINERR_WRONGPROTOCOL); + } + return 1; + } +*/ + OSVERSIONINFOEX osvi = {0}; + osvi.dwOSVersionInfoSize = sizeof(osvi); + GetVersionEx((LPOSVERSIONINFO)&osvi); + + info->sendPacket("CVR","0x0409 %s %d.%d.%d i386 MSNMSGR %s msmsgs %s", + osvi.dwPlatformId >= 2 ? "winnt" : "win", osvi.dwMajorVersion, + osvi.dwMinorVersion, osvi.wServicePackMajor, + msnProductVer, MyOptions.szEmail); + + info->sendPacket("USR", "SSO I %s", MyOptions.szEmail); + break; + } + case ' RFX': //******** XFR: sections 7.4 Referral, 8.1 Referral to Switchboard + { + union + { + char* tWords[7]; + struct { char *type, *newServer, *security, *authChallengeInfo, + *type2, *srcUrl, *genGateway; } data; + }; + + int numWords = sttDivideWords(params, 7, tWords); + if (numWords < 3) + 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; + usingGateway |= (*data.security == 'G'); + info->mIsMainThread = false; + + MSN_DebugLog("Switching to notification server '%s'...", data.newServer); + newThread->startThread(&CMsnProto::MSNServerThread, this); + 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->gatewayType = data.genGateway && atol(data.genGateway) != 0; + newThread->mType = SERVER_SWITCHBOARD; + newThread->mCaller = 1; + strcpy(newThread->mCookie, data.authChallengeInfo); + + MSN_DebugLog("Opening switchboard server '%s'...", data.newServer); + newThread->startThread(&CMsnProto::MSNServerThread, this); + } + else MSN_DebugLog("Unknown referral server: %s", data.type); + break; + } + + default: + MSN_DebugLog("Unrecognised message: %s", cmdString); + break; + } + + return 0; +} -- cgit v1.2.3