From b7a1174511c3b7fad5b81a54bb4647662d94031c Mon Sep 17 00:00:00 2001 From: Piotr Piastucki Date: Thu, 14 May 2015 15:24:53 +0000 Subject: Updated for partial MSNP24 protocol support, for detailed changes see MSNP24 branch. git-svn-id: http://svn.miranda-ng.org/main/trunk@13589 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/MSN/src/msn_auth.cpp | 462 +++++++++++++++++- protocols/MSN/src/msn_chat.cpp | 351 +++++++++++--- protocols/MSN/src/msn_commands.cpp | 942 ++++++++++++++++++++++++++---------- protocols/MSN/src/msn_contact.cpp | 63 ++- protocols/MSN/src/msn_ftold.cpp | 14 +- protocols/MSN/src/msn_global.h | 92 +++- protocols/MSN/src/msn_libstr.cpp | 96 ++++ protocols/MSN/src/msn_lists.cpp | 51 +- protocols/MSN/src/msn_menu.cpp | 1 + protocols/MSN/src/msn_misc.cpp | 234 +++++++-- protocols/MSN/src/msn_opts.cpp | 3 +- protocols/MSN/src/msn_proto.cpp | 24 +- protocols/MSN/src/msn_proto.h | 43 +- protocols/MSN/src/msn_soapab.cpp | 113 ++++- protocols/MSN/src/msn_soapstore.cpp | 2 +- protocols/MSN/src/msn_std.cpp | 6 +- protocols/MSN/src/msn_svcs.cpp | 5 +- protocols/MSN/src/msn_threads.cpp | 91 ++-- protocols/MSN/src/version.h | 6 +- 19 files changed, 2099 insertions(+), 500 deletions(-) (limited to 'protocols/MSN') diff --git a/protocols/MSN/src/msn_auth.cpp b/protocols/MSN/src/msn_auth.cpp index c64e686fdf..c91be0edb1 100644 --- a/protocols/MSN/src/msn_auth.cpp +++ b/protocols/MSN/src/msn_auth.cpp @@ -22,6 +22,19 @@ along with this program. If not, see . #include "msn_proto.h" #include "des.h" +/* SkyLogin Prototypes */ +typedef void* SkyLogin; +typedef SkyLogin (*pfnSkyLogin_Init)(); +typedef void (*pfnSkyLogin_Exit)(SkyLogin pInst); +typedef int (*pfnSkyLogin_LoadCredentials)(SkyLogin pInst, char *pszUser); +typedef int (*pfnSkyLogin_PerformLogin)(SkyLogin pInst, char *pszUser, char *pszPass); +typedef int (*pfnSkyLogin_CreateUICString)(SkyLogin pInst, const char *pszNonce, char *pszOutUIC); +typedef int (*pfnSkyLogin_PerformLoginOAuth)(SkyLogin pInst, const char *OAuth); +typedef int (*pfnSkyLogin_GetCredentialsUIC)(SkyLogin pInst, char *pszOutUIC); +typedef char *(*pfnSkyLogin_GetUser)(SkyLogin pInst); + +#define LOAD_FN(name) (##name = (pfn##name)GetProcAddress(hLibSkylogin, #name)) + static const char defaultPassportUrl[] = "https://login.live.com/RST2.srf"; static const char authPacket[] = @@ -71,10 +84,10 @@ static const char authPacket[] = "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" "" "" - "messengerclear.live.com" + "chatservice.live.com" "" "" - "" + "" "" "" "http://schemas.xmlsoap.org/ws/2005/02/trust/Issue" @@ -194,7 +207,7 @@ int CMsnProto::MSN_GetPassportAuth(void) node = ezxml_get(tokr, "wst:RequestedProofToken", 0, "wst:BinarySecret", -1); replaceStr(hotSecretToken, ezxml_txt(node)); } - else if (strcmp(addr, "messengerclear.live.com") == 0) { + else if (strcmp(addr, "chatservice.live.com") == 0) { ezxml_t node = ezxml_get(tokr, "wst:RequestedProofToken", 0, "wst:BinarySecret", -1); if (toks) { @@ -438,6 +451,444 @@ CMStringA CMsnProto::HotmailLogin(const char* url) return result; } +/* 1 - Login successful + 0 - Login failed + -1 - Loading Skylogin library failed + -2 - Functions cannot be loaded from Skylogin library + -3 - Initializing Skylogin library failed + */ +int CMsnProto::MSN_SkypeAuth(const char *pszNonce, char *pszUIC) +{ + int iRet = -1; + pfnSkyLogin_Init SkyLogin_Init; + pfnSkyLogin_Exit SkyLogin_Exit; + pfnSkyLogin_LoadCredentials SkyLogin_LoadCredentials; + pfnSkyLogin_PerformLogin SkyLogin_PerformLogin; + pfnSkyLogin_CreateUICString SkyLogin_CreateUICString; + + HMODULE hLibSkylogin; + + if ((hLibSkylogin = LoadLibraryA("Plugins\\skylogin.dll"))) { + SkyLogin hLogin; + char szPassword[100]; + + // load function pointers + if (!LOAD_FN(SkyLogin_Init) || + !LOAD_FN(SkyLogin_Exit) || + !LOAD_FN(SkyLogin_LoadCredentials) || + !LOAD_FN(SkyLogin_PerformLogin) || + !LOAD_FN(SkyLogin_CreateUICString)) + { + FreeLibrary(hLibSkylogin); + return -2; + } + + // Perform login + if (hLogin = SkyLogin_Init()) { + db_get_static(NULL, m_szModuleName, "Password", szPassword, sizeof(szPassword)); + if (SkyLogin_LoadCredentials(hLogin, MyOptions.szEmail) || + SkyLogin_PerformLogin(hLogin, MyOptions.szEmail, szPassword)) + { + if (SkyLogin_CreateUICString(hLogin, pszNonce, pszUIC)) + iRet = 1; + } else iRet = 0; + SkyLogin_Exit(hLogin); + } else iRet = -3; + FreeLibrary(hLibSkylogin); + } + return iRet; +} + +/* 1 - Login successful + 0 - Login failed + -1 - Loading Skylogin library failed + -2 - Functions cannot be loaded from Skylogin library + -3 - Initializing Skylogin library failed + */ +int CMsnProto::LoginSkypeOAuth(const char *pRefreshToken) +{ + int iRet = -1; + pfnSkyLogin_Init SkyLogin_Init; + pfnSkyLogin_Exit SkyLogin_Exit; + pfnSkyLogin_LoadCredentials SkyLogin_LoadCredentials; + pfnSkyLogin_PerformLoginOAuth SkyLogin_PerformLoginOAuth; + pfnSkyLogin_GetCredentialsUIC SkyLogin_GetCredentialsUIC; + pfnSkyLogin_GetUser SkyLogin_GetUser; + + HMODULE hLibSkylogin; + + if ((hLibSkylogin = LoadLibraryA("Plugins\\skylogin.dll"))) { + SkyLogin hLogin; + + // load function pointers + if (!LOAD_FN(SkyLogin_Init) || + !LOAD_FN(SkyLogin_Exit) || + !LOAD_FN(SkyLogin_LoadCredentials) || + !LOAD_FN(SkyLogin_PerformLoginOAuth) || + !LOAD_FN(SkyLogin_GetCredentialsUIC) || + !LOAD_FN(SkyLogin_GetUser)) + { + FreeLibrary(hLibSkylogin); + return -2; + } + + // Perform login + if (hLogin = SkyLogin_Init()) { + char szLoginToken[1024]; + if (RefreshOAuth(pRefreshToken, "service::login.skype.com::MBI_SSL", szLoginToken) && + SkyLogin_PerformLoginOAuth(hLogin, szLoginToken)) + { + char szUIC[1024]; + if (SkyLogin_GetCredentialsUIC(hLogin, szUIC)) { + char *pszPartner; + + replaceStr(authUIC, szUIC); + iRet = 1; + if (pszPartner = SkyLogin_GetUser(hLogin)) + setString("SkypePartner", pszPartner); + } + } else iRet = 0; + SkyLogin_Exit(hLogin); + } else iRet = -3; + FreeLibrary(hLibSkylogin); + } + return iRet; +} + +static int CopyCookies(NETLIBHTTPREQUEST *nlhrReply, NETLIBHTTPHEADER *hdr) +{ + int i, nSize = 1; + char *p; + + if (hdr) { + hdr->szName = "Cookie"; + *hdr->szValue = 0; + } + for (i = 0; i < nlhrReply->headersCount; i++) { + if (mir_strcmpi(nlhrReply->headers[i].szName, "Set-Cookie")) + continue; + if (p=strchr(nlhrReply->headers[i].szValue, ';')) *p=0; + if (hdr) { + if (*hdr->szValue) strcat (hdr->szValue, "; "); + strcat (hdr->szValue, nlhrReply->headers[i].szValue); + } else nSize += strlen(nlhrReply->headers[i].szValue) + 2; + } + return nSize; +} + +/* + pszService: + service::login.skype.com::MBI_SSL - For LoginSkypeOAuth + service::ssl.live.com::MBI_SSL - For ssl-compact-ticket + service::contacts.msn.com::MBI_SSL - Contact SOAP service -> authContactToken + service::m.hotmail.com::MBI_SSL - ActiveSync contactlist, not used by us + service::storage.live.com::MBI_SSL - Storage service (authStorageToken) + service::skype.com::MBI_SSL - ? + service::skype.net::MBI_SSL - ? +*/ +bool CMsnProto::RefreshOAuth(const char *pszRefreshToken, const char *pszService, char *pszAccessToken, char *pszOutRefreshToken, time_t *ptExpires) +{ + NETLIBHTTPREQUEST nlhr = { 0 }; + NETLIBHTTPREQUEST *nlhrReply; + NETLIBHTTPHEADER headers[3]; + bool bRet = false; + CMStringA post; + + if (!authCookies) return false; + + // initialize the netlib request + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_POST; + nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_PERSISTENT | NLHRF_REDIRECT; + nlhr.nlc = hHttpsConnection; + nlhr.headersCount = 3; + nlhr.headers = headers; + nlhr.headers[0].szName = "User-Agent"; + nlhr.headers[0].szValue = (char*)MSN_USER_AGENT; + nlhr.headers[1].szName = "Content-Type"; + nlhr.headers[1].szValue = "application/x-www-form-urlencoded"; + nlhr.headers[2].szName = "Cookie"; + nlhr.headers[2].szValue = authCookies; + post.Format("client_id=00000000480BC46C&scope=%s&grant_type=refresh_token&refresh_token=%s", + ptrA(mir_urlEncode(pszService)), pszRefreshToken); + + nlhr.pData = (char*)(const char*)post; + nlhr.dataLength = (int)strlen(nlhr.pData); + nlhr.szUrl = "https://login.live.com/oauth20_token.srf"; + + // Query + mHttpsTS = clock(); + nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); + mHttpsTS = clock(); + if (nlhrReply) { + hHttpsConnection = nlhrReply->nlc; + if (nlhrReply->resultCode == 200 && nlhrReply->pData) { + char *p; + bRet = true; + + if (pszAccessToken && (p=strstr(nlhrReply->pData, "\"access_token\":"))) + bRet &= sscanf(p+sizeof("\"access_token\""), "\"%[^\"]\"", pszAccessToken)==1; + if (pszOutRefreshToken && (p=strstr(nlhrReply->pData, "\"refresh_token\":"))) + bRet &= sscanf(p+sizeof("\"refresh_token\""), "\"%[^\"]\"", pszOutRefreshToken)==1; + if (ptExpires && (p=strstr(nlhrReply->pData, "\"expires_in\""))) { + int expires; + if (sscanf(p+sizeof("\"expires_in\""), "%d,", &expires) == 1) { + time(ptExpires); + *ptExpires+=expires; + bRet&=true; + } + } + } + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); + } else hHttpsConnection = NULL; + return bRet; +} + +void CMsnProto::LoadAuthTokensDB(void) +{ + DBVARIANT dbv; + + authTokenExpiretime = getDword("authTokenExpiretime", 0); + authMethod = getDword("authMethod", 0); + if (getString("authUser", &dbv) == 0) { + replaceStr(authUser, dbv.pszVal); + db_free(&dbv); + } + if (getString("authSSLToken", &dbv) == 0) { + replaceStr(authSSLToken, dbv.pszVal); + db_free(&dbv); + } + if (getString("authContactToken", &dbv) == 0) { + replaceStr(authContactToken, dbv.pszVal); + db_free(&dbv); + } + if (getString("authUIC", &dbv) == 0) { + replaceStr(authUIC, dbv.pszVal); + db_free(&dbv); + } + if (getString("authCookies", &dbv) == 0) { + replaceStr(authCookies, dbv.pszVal); + db_free(&dbv); + } +} + +void CMsnProto::SaveAuthTokensDB(void) +{ + setDword("authTokenExpiretime", authTokenExpiretime); + setDword("authMethod", authMethod); + setString("authUser", authUser); + setString("authSSLToken", authSSLToken); + setString("authContactToken", authContactToken); + setString("authUIC", authUIC); + setString("authCookies", authCookies); +} + +// -1 - Error on login sequence +// 0 - Login failed (invalid username?) +// 1 - Login via Skype login server succeeded +// 2 - Login via Skypeweb succeeded +int CMsnProto::MSN_AuthOAuth(void) +{ + int retVal = -1; + const char *pszPostParams = "client_id=00000000480BC46C&scope=service%3A%3Askype.com%3A%3AMBI_SSL&response_type=token&redirect_uri=https%3A%2F%2Flogin.live.com%2Foauth20_desktop.srf"; + NETLIBHTTPREQUEST nlhr = { 0 }; + NETLIBHTTPREQUEST *nlhrReply; + NETLIBHTTPHEADER headers[3]; + time_t t; + + // Load credentials from DB so that we don't have to do all this stuff if token isn't expired + if (!authTokenExpiretime) LoadAuthTokensDB(); + + // Is there already a valid token and we can skip this? + if (time(&t)+10 < authTokenExpiretime && !strcmp(authUser, MyOptions.szEmail)) return authMethod; + + // initialize the netlib request + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_GET; + nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_PERSISTENT | NLHRF_REDIRECT; + nlhr.nlc = hHttpsConnection; + nlhr.headersCount = 1; + nlhr.headers = headers; + nlhr.headers[0].szName = "User-Agent"; + nlhr.headers[0].szValue = (char*)MSN_USER_AGENT; + + // Get oauth20 login data + CMStringA url; + url.Format("https://login.live.com/oauth20_authorize.srf?%s", pszPostParams); + nlhr.szUrl = (char*)(const char*)url; + mHttpsTS = clock(); + nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); + mHttpsTS = clock(); + + if (nlhrReply) { + hHttpsConnection = nlhrReply->nlc; + + if (nlhrReply->resultCode == 200 && nlhrReply->pData) { + char *pPPFT, *pEnd; + + /* Get PPFT */ + if ((pPPFT = strstr(nlhrReply->pData, "name=\"PPFT\"")) && (pPPFT = strstr(pPPFT, "value=\"")) && (pEnd=strchr(pPPFT+7, '"'))) { + *pEnd=0; + pPPFT+=7; + + /* Get POST URL if available */ + if ((nlhr.szUrl = strstr(nlhrReply->pData, "urlPost:'")) && (pEnd=strchr(nlhr.szUrl+9, '\''))) { + *pEnd=0; + nlhr.szUrl += 9; + } else { + url.Format("https://login.live.com/ppsecure/post.srf?%s", pszPostParams); + nlhr.szUrl = (char*)(const char*)url; + } + + /* Get Cookies */ + nlhr.headers[1].szValue = (char*)alloca(CopyCookies(nlhrReply, NULL)); + CopyCookies(nlhrReply, &nlhr.headers[1]); + if (*nlhr.headers[1].szValue) nlhr.headersCount++; + + /* Create POST data */ + CMStringA post; + char szPassword[100]; + db_get_static(NULL, m_szModuleName, "Password", szPassword, sizeof(szPassword)); + szPassword[16] = 0; + post.Format("PPFT=%s&login=%s&passwd=%s", ptrA(mir_urlEncode(pPPFT)), + ptrA(mir_urlEncode(MyOptions.szEmail)), ptrA(mir_urlEncode(szPassword))); + + /* Setup headers */ + nlhr.headers[nlhr.headersCount].szName = "Content-Type"; + nlhr.headers[nlhr.headersCount++].szValue = "application/x-www-form-urlencoded"; + + /* Do the login and get the required tokens */ + nlhr.requestType = REQUEST_POST; + nlhr.flags &= (~NLHRF_REDIRECT); + mHttpsTS = clock(); + nlhr.dataLength = (int)strlen(post); + nlhr.pData = (char*)(const char*)post; + NETLIBHTTPREQUEST *nlhrReply2 = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); + nlhrReply = nlhrReply2; + mHttpsTS = clock(); + if (nlhrReply) { + hHttpsConnection = nlhrReply->nlc; + + if (nlhrReply->resultCode == 302) { + char *pAccessToken; + + /* Extract access_token from Location can be found */ + for (int i = 0; i < nlhrReply->headersCount; i++) + if (!mir_strcmpi(nlhrReply->headers[i].szName, "Location") && + (pAccessToken = strstr(nlhrReply->headers[i].szValue, "access_token=")) && + (pEnd=strchr(pAccessToken+13, '&'))) + { + char *pRefreshToken, *pExpires, szToken[1024]; + bool bLogin=false; + + *pEnd = 0; + pAccessToken+=13; + UrlDecode(pAccessToken); + replaceStr(authAccessToken, pAccessToken); + + /* Extract refresh token */ + if ((pRefreshToken = strstr(pEnd+1, "refresh_token=")) && (pEnd=strchr(pRefreshToken+14, '&'))) { + *pEnd = 0; + pRefreshToken+=14; + } + + /* Extract expire time */ + time(&authTokenExpiretime); + if ((pExpires = strstr(pEnd+1, "expires_in=")) && (pEnd=strchr(pExpires+11, '&'))) { + *pEnd = 0; + pExpires+=11; + authTokenExpiretime+=atoi(pExpires); + } else authTokenExpiretime+=86400; + + /* Copy auth Cookies to class for other web requests like contact list fetching to avoid ActiveSync */ + mir_free(authCookies); + authCookies = nlhr.headers[1].szValue = (char*)mir_alloc(CopyCookies(nlhrReply, NULL)); + CopyCookies(nlhrReply, &nlhr.headers[1]); + + int loginRet; + /* Do Login via Skype login server, if not possible switch to SkypeWebExperience login */ + if ((loginRet = LoginSkypeOAuth(pRefreshToken))<1) { + if (loginRet<0) bLogin=true; else retVal = 0; + } + + /* Request required tokens */ + if (RefreshOAuth(pRefreshToken, "service::ssl.live.com::MBI_SSL", szToken)) { + replaceStr(authSSLToken, szToken); + if (RefreshOAuth(pRefreshToken, "service::contacts.msn.com::MBI_SSL", szToken)) { + replaceStr(authContactToken, szToken); + if (!bLogin) { + authMethod=retVal=1; + replaceStr(authUser, MyOptions.szEmail); + } + } else bLogin=false; + } else bLogin=false; + + + /* If you need Skypewebexperience login, as i.e. skylogin.dll is not available, we do this here */ + if (bLogin) { + /* Prepare headers*/ + nlhr.headers[2].szValue = "application/json"; + nlhr.pData = "{\"trouterurl\":\"https://\",\"connectionid\":\"a\"}"; + nlhr.dataLength = (int)strlen(nlhr.pData); + nlhr.szUrl = "https://skypewebexperience.live.com/v1/User/Initialization"; + + /* Request MappingContainer */ + mHttpsTS = clock(); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); + nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); + mHttpsTS = clock(); + if (nlhrReply) { + hHttpsConnection = nlhrReply->nlc; + + if (nlhrReply->resultCode == 200 && nlhrReply->pData) { + /* Parse JSON stuff for MappingContainer */ + char *pMappingContainer; + + if ((pMappingContainer = strstr(nlhrReply->pData, "\"MappingContainer\":\"")) && + (pEnd=strchr(pMappingContainer+20, '"'))) + { + *pEnd = 0; + pMappingContainer+=20; + UrlDecode(pMappingContainer); + replaceStr(authUIC, pMappingContainer); + authMethod = retVal = 2; + } else retVal = 0; + } else retVal = 0; + } else hHttpsConnection = NULL; + } + } + } else hHttpsConnection = NULL; + } + } + } + if (nlhrReply) CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); + } else hHttpsConnection = NULL; + + if (retVal<=0) authTokenExpiretime=0; else SaveAuthTokensDB(); + return retVal; +} + +int CMsnProto::GetMyNetID(void) +{ + return strchr(MyOptions.szEmail, '@')?NETID_MSN:NETID_SKYPE; +} + +const char *CMsnProto::GetMyUsername(int netId) +{ + static char szPartner[128]; + + if (netId == NETID_SKYPE) + { + if (GetMyNetID()==NETID_MSN) + { + if (db_get_static(NULL, m_szModuleName, "SkypePartner", szPartner, sizeof(szPartner)) == 0) + return szPartner; + } + } + return MyOptions.szEmail; +} + void CMsnProto::FreeAuthTokens(void) { mir_free(pAuthToken); @@ -448,5 +899,10 @@ void CMsnProto::FreeAuthTokens(void) mir_free(authContactToken); mir_free(authStorageToken); mir_free(hotSecretToken); + mir_free(authUIC); + mir_free(authCookies); + mir_free(authSSLToken); + mir_free(authUser); + mir_free(authAccessToken); free(hotAuthToken); } diff --git a/protocols/MSN/src/msn_chat.cpp b/protocols/MSN/src/msn_chat.cpp index 0c050204b3..e928f7f403 100644 --- a/protocols/MSN/src/msn_chat.cpp +++ b/protocols/MSN/src/msn_chat.cpp @@ -25,6 +25,11 @@ along with this program. If not, see . #include "msn_proto.h" #include +static const TCHAR *m_ptszRoles[] = { + _T("admin"), + _T("user") +}; + MCONTACT CMsnProto::MSN_GetChatInernalHandle(MCONTACT hContact) { MCONTACT result = hContact; @@ -38,14 +43,20 @@ MCONTACT CMsnProto::MSN_GetChatInernalHandle(MCONTACT hContact) return result; } -int CMsnProto::MSN_ChatInit(ThreadData *info) +int CMsnProto::MSN_ChatInit(GCThreadData *info, const char *pszID, const char *pszTopic) { - InterlockedIncrement(&m_chatID); - _ltot(m_chatID, info->mChatID, 10); + char *szNet, *szEmail; + + _tcsncpy(info->mChatID, _A2T(pszID), SIZEOF(info->mChatID)); + parseWLID(NEWSTR_ALLOCA(pszID), &szNet, &szEmail, NULL); + info->netId = atoi(szNet); + strncpy(info->szEmail, szEmail, sizeof(info->szEmail)); TCHAR szName[512]; - mir_sntprintf(szName, SIZEOF(szName), _T("%s %s%s"), - m_tszUserName, TranslateT("Chat #"), info->mChatID); + InterlockedIncrement(&m_chatID); + if (*pszTopic) _tcsncpy(szName, _A2T(pszTopic), SIZEOF(szName)); + else mir_sntprintf(szName, SIZEOF(szName), _T("%s %s%d"), + m_tszUserName, TranslateT("Chat #"), m_chatID); GCSESSION gcw = { sizeof(gcw) }; gcw.iType = GCW_CHATROOM; @@ -56,19 +67,10 @@ int CMsnProto::MSN_ChatInit(ThreadData *info) GCDEST gcd = { m_szModuleName, info->mChatID, GC_EVENT_ADDGROUP }; GCEVENT gce = { sizeof(gce), &gcd }; - gce.ptszStatus = TranslateT("Me"); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); - - gcd.iType = GC_EVENT_JOIN; - gce.ptszUID = mir_a2t(MyOptions.szEmail); - gce.ptszNick = GetContactNameT(NULL); - gce.time = 0; - gce.bIsMe = TRUE; - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); - - gcd.iType = GC_EVENT_ADDGROUP; - gce.ptszStatus = TranslateT("Others"); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + for (int j = 0; j < SIZEOF(m_ptszRoles); j++) { + gce.ptszStatus = m_ptszRoles[j]; + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + } gcd.iType = GC_EVENT_CONTROL; CallServiceSync(MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gce); @@ -79,35 +81,70 @@ int CMsnProto::MSN_ChatInit(ThreadData *info) return 0; } -void CMsnProto::MSN_ChatStart(ThreadData* info) +void CMsnProto::MSN_ChatStart(ezxml_t xmli) { - if (info->mChatID[0] != 0) - return; + const char *pszID, *pszCreator; + GCThreadData* info; + int j; + + if (!strcmp(xmli->txt, "thread")) return; + + // If Chat ID already exists, don'T create a new one + pszID = ezxml_txt(ezxml_child(xmli, "id")); + if (!(*pszID && (info = MSN_GetThreadByChatId(_A2T(pszID))))) + { + info = new GCThreadData; + { + mir_cslock lck(m_csThreads); + m_arGCThreads.insert(info); + } - MSN_StartStopTyping(info, false); + MSN_ChatInit(info, pszID, ezxml_txt(ezxml_get(xmli, "properties", 0, "topic", -1))); + MSN_StartStopTyping(info, false); + } else { + GCDEST gcd = { m_szModuleName, info->mChatID, GC_EVENT_CONTROL }; + GCEVENT gce = { sizeof(gce), &gcd }; + CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce); + } - MSN_ChatInit(info); + pszCreator = ezxml_txt(ezxml_get(xmli, "properties", 0, "creator", -1)); - // add all participants onto the list - GCDEST gcd = { m_szModuleName, info->mChatID, GC_EVENT_JOIN }; - GCEVENT gce = { sizeof(gce), &gcd }; - gce.dwFlags = GCEF_ADDTOLOG; - gce.ptszStatus = TranslateT("Others"); - gce.time = time(NULL); - gce.bIsMe = FALSE; + for (ezxml_t memb = ezxml_get(xmli, "members", 0, "member", -1); memb != NULL; memb = ezxml_next(memb)) { + const char *mri = ezxml_txt(ezxml_child(memb, "mri")); + const char *role = ezxml_txt(ezxml_child(memb, "role")); + GCUserItem *gcu = NULL; - for (int j = 0; j < info->mJoinedContactsWLID.getCount(); j++) { - MCONTACT hContact = MSN_HContactFromEmail(info->mJoinedContactsWLID[j]); - TCHAR *wlid = mir_a2t(info->mJoinedContactsWLID[j]); - - gce.ptszNick = GetContactNameT(hContact); - gce.ptszUID = wlid; - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + for (j = 0; j < info->mJoinedContacts.getCount(); j++) { + if (!strcmp(info->mJoinedContacts[j]->WLID, mri)) { + gcu = info->mJoinedContacts[j]; + break; + } + } + if (!gcu) { + gcu = new GCUserItem; + info->mJoinedContacts.insert(gcu); + strncpy(gcu->WLID, mri, sizeof(gcu->WLID)); + } + _tcscpy(gcu->role, _A2T(role)); + + if (pszCreator && !strcmp(mri, pszCreator)) info->mCreator = gcu; + char* szEmail, *szNet; + parseWLID(NEWSTR_ALLOCA(mri), &szNet, &szEmail, NULL); + if (!stricmp(szEmail, GetMyUsername(atoi(szNet)))) + info->mMe = gcu; + gcu->btag = 1; + } - mir_free(wlid); + // Remove contacts not on list (not tagged) + for (j = 0; j < info->mJoinedContacts.getCount(); j++) { + if (!info->mJoinedContacts[j]->btag) { + info->mJoinedContacts.remove(j); + j--; + } else info->mJoinedContacts[j]->btag = 0; } } + void CMsnProto::MSN_KillChatSession(const TCHAR* id) { GCDEST gcd = { m_szModuleName, id, GC_EVENT_CONTROL }; @@ -117,17 +154,171 @@ void CMsnProto::MSN_KillChatSession(const TCHAR* id) CallServiceSync(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce); } -static void ChatInviteUser(ThreadData* info, const char* email) +void CMsnProto::MSN_Kickuser(GCHOOK *gch) { - if (info->mJoinedContactsWLID.getCount()) { - for (int j = 0; j < info->mJoinedContactsWLID.getCount(); j++) { - if (_stricmp(info->mJoinedContactsWLID[j], email) == 0) - return; + GCThreadData* thread = MSN_GetThreadByChatId(gch->pDest->ptszID); + msnNsThread->sendPacketPayload("DEL", "MSGR\\THREAD", + "%d:%s%s", + thread->netId, thread->szEmail, _T2A(gch->ptszUID)); +} + +void CMsnProto::MSN_Promoteuser(GCHOOK *gch, const char *pszRole) +{ + GCThreadData* thread = MSN_GetThreadByChatId(gch->pDest->ptszID); + msnNsThread->sendPacketPayload("PUT", "MSGR\\THREAD", + "%d:%s%s%s", + thread->netId, thread->szEmail, _T2A(gch->ptszUID), pszRole); +} + +const TCHAR *CMsnProto::MSN_GCGetRole(GCThreadData* thread, const char *pszWLID) +{ + if (thread) { + for (int j = 0; j < thread->mJoinedContacts.getCount(); j++) { + if (!strcmp(thread->mJoinedContacts[j]->WLID, pszWLID)) { + return thread->mJoinedContacts[j]->role; + } } + } + return NULL; +} - info->sendPacket("CAL", email); - info->proto->MSN_ChatStart(info); +void CMsnProto::MSN_GCProcessThreadActivity(ezxml_t xmli, const TCHAR *mChatID) +{ + if (!strcmp(xmli->name, "topicupdate")) { + ezxml_t initiator = ezxml_child(xmli, "initiator"); + GCDEST gcd = { m_szModuleName, mChatID, GC_EVENT_TOPIC}; + GCEVENT gce = { sizeof(gce), &gcd }; + gce.dwFlags = GCEF_ADDTOLOG; + gce.time = MsnTSToUnixtime(ezxml_txt(ezxml_child(xmli, "eventtime"))); + gce.ptszUID = initiator?mir_a2t(initiator->txt):NULL; + MCONTACT hContInitiator = MSN_HContactFromEmail(initiator->txt); + gce.ptszNick = GetContactNameT(hContInitiator); + gce.ptszText = mir_a2t(ezxml_txt(ezxml_child(xmli, "value"))); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + mir_free((TCHAR*)gce.ptszUID); } + else if (ezxml_t target = ezxml_child(xmli, "target")) { + MCONTACT hContInitiator = NULL; + GCDEST gcd = { m_szModuleName, mChatID, 0}; + GCEVENT gce = { sizeof(gce), &gcd }; + gce.dwFlags = GCEF_ADDTOLOG; + + if (!strcmp(xmli->name, "deletemember")) { + gcd.iType = GC_EVENT_PART; + if (ezxml_t initiator = ezxml_child(xmli, "initiator")) { + if (strcmp(initiator->txt, target->txt)) { + hContInitiator = MSN_HContactFromEmail(initiator->txt); + gce.ptszStatus = GetContactNameT(hContInitiator); + gcd.iType = GC_EVENT_KICK; + } + } + } + else if (!strcmp(xmli->name, "addmember")) { + gcd.iType = GC_EVENT_JOIN; + } + else if (!strcmp(xmli->name, "roleupdate")) { + gcd.iType = GC_EVENT_ADDSTATUS; + if (ezxml_t initiator = ezxml_child(xmli, "initiator")) { + hContInitiator = MSN_HContactFromEmail(initiator->txt); + gce.ptszText= GetContactNameT(hContInitiator); + } + gce.ptszStatus = _T("admin"); + } + + if (gcd.iType) { + gce.time = MsnTSToUnixtime(ezxml_txt(ezxml_child(xmli, "eventtime"))); + const char *pszTarget = NULL; + + while (target) { + switch (gcd.iType) + { + case GC_EVENT_JOIN: + gce.ptszStatus = MSN_GCGetRole(MSN_GetThreadByChatId(mChatID), target->txt); + // ..fall through.. // + case GC_EVENT_KICK: + case GC_EVENT_PART: + pszTarget = target->txt; + break; + case GC_EVENT_ADDSTATUS: + case GC_EVENT_REMOVESTATUS: + gcd.iType = strcmp(ezxml_txt(ezxml_child(target, "role")), "admin")==0?GC_EVENT_ADDSTATUS:GC_EVENT_REMOVESTATUS; + pszTarget = ezxml_txt(ezxml_child(target, "id")); + break; + } + char* szEmail, *szNet; + parseWLID(NEWSTR_ALLOCA(pszTarget), &szNet, &szEmail, NULL); + gce.bIsMe = !stricmp(szEmail, GetMyUsername(atoi(szNet))); + gce.ptszUID = mir_a2t(pszTarget); + MCONTACT hContTarget = MSN_HContactFromEmail(pszTarget); + gce.ptszNick =GetContactNameT(hContTarget); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + if ((gcd.iType == GC_EVENT_PART || gcd.iType == GC_EVENT_KICK) && gce.bIsMe) { + GCDEST gcd = { m_szModuleName, mChatID, GC_EVENT_CONTROL }; + GCEVENT gce = { sizeof(gce), &gcd }; + CallServiceSync(MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gce); + break; + } + target = ezxml_next(target); + } + mir_free((TCHAR*)gce.ptszUID); + } + } +} + +void CMsnProto::MSN_GCRefreshThreadsInfo(void) +{ + CMStringA buf; + MCONTACT hContact; + int nThreads = 0; + + for (hContact = db_find_first(m_szModuleName); hContact; + hContact = db_find_next(hContact, m_szModuleName)) + { + if (isChatRoom(hContact) != 0) { + DBVARIANT dbv; + if (getString(hContact, "ChatRoomID", &dbv) == 0) { + buf.AppendFormat("%s", dbv.pszVal); + nThreads++; + db_free(&dbv); + } + } + } + if (nThreads) + msnNsThread->sendPacketPayload("GET", "MSGR\\THREADS", "%s", buf); +} + +void CMsnProto::MSN_GCAddMessage(TCHAR *mChatID, MCONTACT hContact, char *email, time_t ts, bool sentMsg, char *msgBody) +{ + GCDEST gcd = { m_szModuleName, mChatID, GC_EVENT_MESSAGE }; + GCEVENT gce = { sizeof(gce), &gcd }; + gce.dwFlags = GCEF_ADDTOLOG; + gce.ptszUID = mir_a2t(email); + gce.ptszNick = GetContactNameT(hContact); + gce.time = ts; + gce.bIsMe = sentMsg; + + TCHAR* p = mir_utf8decodeT(msgBody); + gce.ptszText = EscapeChatTags(p); + mir_free(p); + + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + mir_free((void*)gce.ptszUID); + mir_free((void*)gce.ptszText); +} + + + +static void ChatInviteUser(ThreadData *thread, GCThreadData* info, const char* wlid) +{ + if (info->mJoinedContacts.getCount()) { + for (int j = 0; j < info->mJoinedContacts.getCount(); j++) { + if (_stricmp(info->mJoinedContacts[j]->WLID, wlid) == 0) + return; + } + } + thread->sendPacketPayload("PUT", "MSGR\\THREAD", + "%d:%s%suser", + info->netId, info->szEmail, wlid); } static void ChatInviteSend(HANDLE hItem, HWND hwndList, STRLIST &str, CMsnProto *ppro) @@ -151,7 +342,12 @@ static void ChatInviteSend(HANDLE hItem, HWND hwndList, STRLIST &str, CMsnProto } else { MsnContact *msc = ppro->Lists_Get((MCONTACT)hItem); - if (msc) str.insertn(msc->email); + if (msc) { + char szContact[MSN_MAX_EMAIL_LEN]; + + sprintf(szContact, "%d:%s", msc->netId, msc->email); + str.insertn(msc->email); + } } } } @@ -252,13 +448,13 @@ INT_PTR CALLBACK DlgInviteToChat(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l case IDOK: char tEmail[MSN_MAX_EMAIL_LEN]; tEmail[0] = 0; - ThreadData *info = NULL; + GCThreadData *info = NULL; if (param->id) info = param->ppro->MSN_GetThreadByChatId(param->id); - else if (param->hContact) { + /*else if (param->hContact) { if (!param->ppro->MSN_IsMeByContact(param->hContact, tEmail)) info = param->ppro->MSN_GetThreadByContact(tEmail); - } + }*/ HWND hwndList = GetDlgItem(hwndDlg, IDC_CCLIST); STRLIST *cont = new STRLIST; @@ -266,14 +462,28 @@ INT_PTR CALLBACK DlgInviteToChat(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM l if (info) { for (int i = 0; i < cont->getCount(); ++i) - ChatInviteUser(info, (*cont)[i]); + ChatInviteUser(param->ppro->msnNsThread, info, (*cont)[i]); delete cont; } else { + /* if (tEmail[0]) cont->insertn(tEmail); param->ppro->MsgQueue_Add("chat", 'X', NULL, 0, NULL, 0, cont); if (param->ppro->msnLoggedIn) param->ppro->msnNsThread->sendPacket("XFR", "SB"); + */ + CMStringA buf; + int myNetId = param->ppro->GetMyNetID(); + + /* Group chats only work for Skype users */ + buf.AppendFormat("%d:%sadmin", + NETID_SKYPE, param->ppro->GetMyUsername(NETID_SKYPE)); + for (int i = 0; i < cont->getCount(); ++i) { + // TODO: Add support for assigning role in invite dialog maybe? + buf.AppendFormat("%suser", (*cont)[i]); + } + buf.Append(""); + param->ppro->msnNsThread->sendPacketPayload("PUT", "MSGR\\THREAD", buf); } EndDialog(hwndDlg, IDOK); @@ -294,19 +504,23 @@ int CMsnProto::MSN_GCEventHook(WPARAM, LPARAM lParam) switch (gch->pDest->iType) { case GC_SESSION_TERMINATE: { - ThreadData* thread = MSN_GetThreadByChatId(gch->pDest->ptszID); - if (thread != NULL) - thread->sendTerminate(); + GCThreadData* thread = MSN_GetThreadByChatId(gch->pDest->ptszID); + if (thread != NULL) { + m_arGCThreads.remove(thread); + for (int i=0; i < thread->mJoinedContacts.getCount(); i++) + delete thread->mJoinedContacts[i]; + delete thread; + } } break; case GC_USER_MESSAGE: if (gch->ptszText && gch->ptszText[0]) { - ThreadData* thread = MSN_GetThreadByChatId(gch->pDest->ptszID); + GCThreadData* thread = MSN_GetThreadByChatId(gch->pDest->ptszID); if (thread) { TCHAR* pszMsg = UnEscapeChatTags(NEWTSTR_ALLOCA(gch->ptszText)); rtrimt(pszMsg); // remove the ending linebreak - thread->sendMessage('N', NULL, NETID_MSN, UTF8(pszMsg), 0); + msnNsThread->sendMessage('N', thread->szEmail, thread->netId, UTF8(pszMsg), 0); DBVARIANT dbv; int bError = getTString("Nick", &dbv); @@ -367,6 +581,16 @@ int CMsnProto::MSN_GCEventHook(WPARAM, LPARAM lParam) CallService(MS_HISTORY_SHOWCONTACTHISTORY, hContact, 0); break; + case 30: + MSN_Kickuser(gch); + break; + + case 40: + { + const TCHAR *pszRole = MSN_GCGetRole(MSN_GetThreadByChatId(gch->pDest->ptszID), _T2A(gch->ptszUID)); + MSN_Promoteuser(gch, (pszRole && !_tcscmp(pszRole, _T("admin")))?"user":"admin"); + break; + } case 110: MSN_KillChatSession(gch->pDest->ptszID); break; @@ -408,7 +632,7 @@ int CMsnProto::MSN_GCMenuHook(WPARAM, LPARAM lParam) } else if (gcmi->Type == MENU_ON_NICKLIST) { char *email = mir_t2a(gcmi->pszUID); - if (!_stricmp(MyOptions.szEmail, email)) { + if (!_stricmp(GetMyUsername(NETID_SKYPE), email)) { static const struct gc_item Items[] = { { LPGENT("User &details"), 10, MENU_ITEM, FALSE }, @@ -420,11 +644,22 @@ int CMsnProto::MSN_GCMenuHook(WPARAM, LPARAM lParam) gcmi->Item = (gc_item*)Items; } else { - static const struct gc_item Items[] = + static struct gc_item Items[] = { { LPGENT("User &details"), 10, MENU_ITEM, FALSE }, - { LPGENT("User &history"), 20, MENU_ITEM, FALSE } + { LPGENT("User &history"), 20, MENU_ITEM, FALSE }, + { LPGENT("&Kick user") , 30, MENU_ITEM, FALSE }, + { LPGENT("&Op user") , 40, MENU_ITEM, FALSE } }; + GCThreadData* thread = MSN_GetThreadByChatId(gcmi->pszID); + if (thread && thread->mMe && _tcsicmp(thread->mMe->role, _T("admin"))) { + Items[2].bDisabled = TRUE; + Items[3].bDisabled = TRUE; + } else { + const TCHAR *pszRole = MSN_GCGetRole(thread, email); + if (pszRole && !_tcsicmp(pszRole, _T("admin"))) + Items[3].pszDesc = LPGENT("&Deop user"); + } gcmi->nItems = SIZEOF(Items); gcmi->Item = (gc_item*)Items; } diff --git a/protocols/MSN/src/msn_commands.cpp b/protocols/MSN/src/msn_commands.cpp index ab9001d0c9..3c45f5d98d 100644 --- a/protocols/MSN/src/msn_commands.cpp +++ b/protocols/MSN/src/msn_commands.cpp @@ -193,9 +193,8 @@ void CMsnProto::MSN_InviteMessage(ThreadData* info, char* msgBody, char* email, 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" + info->sendPacketPayload("MSG", "N", + "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" @@ -203,22 +202,18 @@ void CMsnProto::MSN_InviteMessage(ThreadData* info, char* msgBody, char* email, "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; } // netmeeting receive 1 if (Appname != NULL && !_stricmp(Appname, "NetMeeting")) { - 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), + info->sendPacketPayload("MSG", "N", "MIME-Version: 1.0\r\n" "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n" "Invitation-Command: ACCEPT\r\n" @@ -231,7 +226,7 @@ void CMsnProto::MSN_InviteMessage(ThreadData* info, char* msgBody, char* email, Invcookie, MyConnection.GetMyExtIPStr()); } else { - nBytes = mir_snprintf(command, SIZEOF(command), + info->sendPacketPayload("MSG", "N", "MIME-Version: 1.0\r\n" "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n" "Invitation-Command: CANCEL\r\n" @@ -239,7 +234,6 @@ void CMsnProto::MSN_InviteMessage(ThreadData* info, char* msgBody, char* email, "Cancel-Code: REJECT\r\n\r\n", Invcookie); } - info->sendPacket("MSG", "N %d\r\n%s", nBytes, command); return; } @@ -320,31 +314,39 @@ void CMsnProto::MSN_ReceiveMessage(ThreadData* info, char* cmdString, char* para char* tWords[6]; struct { char *fromEmail, *fromNick, *strMsgBytes; } data; struct { char *fromEmail, *fromNetId, *toEmail, *toNetId, *typeId, *strMsgBytes; } datau; + struct { char *typeId, *strMsgBytes; } datas; }; - if (sttDivideWords(params, SIZEOF(tWords), tWords) < 3) { + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 2) { debugLogA("Invalid %.3s command, ignoring", cmdString); return; } int msgBytes; - char *nick, *email; + char *nick = NULL, *email; + TCHAR *mChatID = NULL; bool ubmMsg = strncmp(cmdString, "UBM", 3) == 0; + bool sdgMsg = strncmp(cmdString, "SDG", 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); + if (sdgMsg) { + msgBytes = atol(datas.strMsgBytes); + if (stricmp(datas.typeId, "MSGR")) return; + } else { + 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); } - stripBBCode(nick); - stripColorCode(nick); char* msg = (char*)alloca(msgBytes+1); @@ -360,11 +362,35 @@ void CMsnProto::MSN_ReceiveMessage(ThreadData* info, char* cmdString, char* para MimeHeaders tHeader; char* msgBody = tHeader.readFromBuffer(msg); + if (sdgMsg) { + if (tHeader["Ack-Id"]) { + CMStringA buf; + + buf.AppendFormat("Ack-Id: %s\r\n", tHeader["Ack-Id"]); + if (msnRegistration) buf.AppendFormat("Registration: %s\r\n", msnRegistration); + buf.AppendFormat("\r\n"); + msnNsThread->sendPacket("ACK", "MSGR %d\r\n%s", strlen(buf), buf); + } + msgBody = tHeader.readFromBuffer(msgBody); + if (!(email = NEWSTR_ALLOCA(tHeader["From"]))) return; + mChatID = mir_a2t(tHeader["To"]); + if (_tcsncmp(mChatID, _T("19:"), 3)) mChatID[0]=0; // NETID_THREAD + msgBody = tHeader.readFromBuffer(msgBody); + msgBody = tHeader.readFromBuffer(msgBody); + nick = NEWSTR_ALLOCA(tHeader["IM-Display-Name"]); + if (!strcmp(tHeader["Message-Type"], "RichText")) { + msgBody = NEWSTR_ALLOCA(msgBody); + stripHTML(msgBody); + HtmlDecode(msgBody); + } + } + else mChatID = info->mChatID; + const char* tMsgId = tHeader["Message-ID"]; // Chunked message char* newbody = NULL; - if (tMsgId) { + if (!sdgMsg && tMsgId) { int idx; const char* tChunks = tHeader["Chunks"]; if (tChunks) @@ -378,7 +404,7 @@ void CMsnProto::MSN_ReceiveMessage(ThreadData* info, char* cmdString, char* para } // message from the server (probably) - if (!ubmMsg && strchr(email, '@') == NULL && _stricmp(email, "Hotmail")) + if (!ubmMsg && !sdgMsg && strchr(email, '@') == NULL && _stricmp(email, "Hotmail")) return; const char* tContentType = tHeader["Content-Type"]; @@ -397,80 +423,80 @@ void CMsnProto::MSN_ReceiveMessage(ThreadData* info, char* cmdString, char* para delSetting(hContact, "StdMirVer"); } } - else if (!ubmMsg && !info->firstMsgRecv) { + else if (!ubmMsg && !sdgMsg && !info->firstMsgRecv) { info->firstMsgRecv = true; MsnContact *cont = Lists_Get(email); if (cont && cont->hContact != NULL) MSN_SetMirVer(cont->hContact, cont->cap1, true); } - if (!_strnicmp(tContentType, "text/plain", 10)) { - MCONTACT 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) - MSN_ChatStart(info); - else { - char *szEmail; - parseWLID(NEWSTR_ALLOCA(email), NULL, &szEmail, NULL); - sentMsg = _stricmp(szEmail, MyOptions.szEmail) == 0; - if (sentMsg) - hContact = ubmMsg ? MSN_HContactFromEmail(datau.toEmail, nick) : info->getContactHandle(); - } + if (!_strnicmp(tContentType, "text/plain", 10) || + (!_strnicmp(tContentType, "application/user+xml", 10) && tHeader["Message-Type"] && !strcmp(tHeader["Message-Type"], "RichText"))) { + MCONTACT hContact = strncmp(email, "19:", 3)?MSN_HContactFromEmail(email, nick, true, true):NULL; + + int iTyping = -1; + if (!_stricmp(tHeader["Message-Type"], "Control/Typing")) iTyping=7; else + if (!_stricmp(tHeader["Message-Type"], "Control/ClearTyping")) iTyping=0; + if (iTyping == -1) { + + const char* p = tHeader["X-MMS-IM-Format"]; + bool isRtl = p != NULL && strstr(p, "RL=1") != NULL; + + /*if (info->mJoinedContactsWLID.getCount() > 1) + MSN_ChatStart(info); + else */{ + char *szNet, *szEmail; + parseWLID(NEWSTR_ALLOCA(email), &szNet, &szEmail, NULL); + sentMsg = _stricmp(szEmail, GetMyUsername(atoi(szNet))) == 0; + if (sentMsg) + 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; - } + 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, info->mChatID, GC_EVENT_MESSAGE }; - GCEVENT gce = { sizeof(gce), &gcd }; - gce.dwFlags = GCEF_ADDTOLOG; - gce.ptszUID = mir_a2t(email); - gce.ptszNick = GetContactNameT(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.ptszText); - mir_free((void*)gce.ptszUID); - } - else if (hContact) { - if (!sentMsg) { - CallService(MS_PROTO_CONTACTISTYPING, WPARAM(hContact), 0); - - PROTORECVEVENT pre = { 0 }; - pre.szMessage = (char*)msgBody; - pre.flags = PREF_UTF + (isRtl ? PREF_RTL : 0); - pre.timestamp = (DWORD)time(NULL); - pre.lParam = 0; - ProtoChainRecvMsg(hContact, &pre); + if (mChatID[0]) { + if (!_strnicmp(tHeader["Message-Type"], "ThreadActivity/", 15)) { + ezxml_t xmli = ezxml_parse_str(msgBody, strlen(msgBody)); + if (xmli) { + MSN_GCProcessThreadActivity(xmli, mChatID); + ezxml_free(xmli); + } + } + else MSN_GCAddMessage(mChatID, hContact, email, time(NULL), sentMsg, msgBody); } - else { - bool haveWnd = MSN_MsgWndExist(hContact); - - DBEVENTINFO dbei = { 0 }; - 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; - db_event_add(hContact, &dbei); + else if (hContact) { + if (!sentMsg) { + CallService(MS_PROTO_CONTACTISTYPING, WPARAM(hContact), 0); + + PROTORECVEVENT pre = { 0 }; + pre.szMessage = (char*)msgBody; + pre.flags = PREF_UTF + (isRtl ? PREF_RTL : 0); + pre.timestamp = (DWORD)time(NULL); + pre.lParam = 0; + ProtoChainRecvMsg(hContact, &pre); + } + else { + bool haveWnd = MSN_MsgWndExist(hContact); + + DBEVENTINFO dbei = { 0 }; + 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; + db_event_add(hContact, &dbei); + } } - } + } else CallService(MS_PROTO_CONTACTISTYPING, hContact, iTyping); } else if (!_strnicmp(tContentType, "text/x-msmsgsprofile", 20)) { replaceStr(msnExternalIP, tHeader["ClientIP"]); @@ -487,12 +513,14 @@ void CMsnProto::MSN_ReceiveMessage(ThreadData* info, char* cmdString, char* para MSN_EnableMenuItems(true); } } - else if (!_strnicmp(tContentType, "text/x-msmsgscontrol", 20)) { - const char* tTypingUser = tHeader["TypingUser"]; + else if (!_strnicmp(tContentType, "text/x-msmsgscontrol", 20) || + (sdgMsg && !_strnicmp(tContentType, "Application/Message", 19))) { + const char* tTypingUser = sdgMsg?email:tHeader["TypingUser"]; if (tTypingUser != NULL && info->mChatID[0] == 0 && _stricmp(email, MyOptions.szEmail)) { MCONTACT hContact = MSN_HContactFromEmail(tTypingUser, tTypingUser); - CallService(MS_PROTO_CONTACTISTYPING, hContact, 7); + CallService(MS_PROTO_CONTACTISTYPING, hContact, + sdgMsg && !_stricmp(tHeader["Message-Type"], "Control/ClearTyping")?0:7); } } else if (!_strnicmp(tContentType, "text/x-msnmsgr-datacast", 23)) { @@ -562,6 +590,7 @@ void CMsnProto::MSN_ReceiveMessage(ThreadData* info, char* cmdString, char* para else if (!_strnicmp(tContentType, "text/x-mms-animemoticon", 23)) MSN_CustomSmiley(msgBody, email, nick, MSN_APPID_CUSTOMANIMATEDSMILEY); + mir_free(mChatID); mir_free(newbody); } @@ -705,35 +734,145 @@ void CMsnProto::MSN_ProcessRemove(char* buf, size_t len) // MSN_HandleCommands - process commands from the server ///////////////////////////////////////////////////////////////////////////////////////// -void CMsnProto::MSN_ProcessStatusMessage(char* buf, unsigned len, const char* wlid) +void CMsnProto::MSN_ProcessNLN(const char *userStatus, const char *wlid, char *userNick, const char *objid, char *cmdstring) { - MCONTACT hContact = MSN_HContactFromEmail(wlid); - if (hContact == NULL) return; + if (userNick) { + UrlDecode(userNick); + stripBBCode(userNick); + stripColorCode(userNick); + } - ezxml_t xmli = ezxml_parse_str(buf, len); + bool isMe = false; + char* szEmail, *szNet; + parseWLID(NEWSTR_ALLOCA(wlid), &szNet, &szEmail, NULL); + if (!stricmp(szEmail, GetMyUsername(atoi(szNet)))) { + isMe = true; + int newStatus = MSNStatusToMiranda(userStatus); + if (newStatus != m_iStatus && newStatus != ID_STATUS_IDLE) { + int oldMode = m_iStatus; + m_iDesiredStatus = m_iStatus = newStatus; + ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldMode, m_iStatus); + } + } - char* szEmail; - parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL); + WORD lastStatus = ID_STATUS_OFFLINE; - // 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); + MsnContact *cont = Lists_Get(szEmail); + + MCONTACT hContact = NULL; + if (!cont && !isMe) { + hContact = MSN_HContactFromEmail(wlid, userNick, true, true); + cont = Lists_Get(szEmail); } + if (cont) hContact = cont->hContact; - // Process status message info - const char* szStatMsg = ezxml_txt(ezxml_child(xmli, "PSM")); - if (*szStatMsg) { - stripBBCode((char*)szStatMsg); - stripColorCode((char*)szStatMsg); - db_set_utf(hContact, "CList", "StatusMsg", szStatMsg); + if (hContact != NULL) { + if (userNick) setStringUtf(hContact, "Nick", userNick); + lastStatus = getWord(hContact, "Status", ID_STATUS_OFFLINE); + if (lastStatus == ID_STATUS_OFFLINE || lastStatus == ID_STATUS_INVISIBLE) + db_unset(hContact, "CList", "StatusMsg"); + + int newStatus = MSNStatusToMiranda(userStatus); + setWord(hContact, "Status", newStatus != ID_STATUS_IDLE ? newStatus : ID_STATUS_AWAY); + setDword(hContact, "IdleTS", newStatus != ID_STATUS_IDLE ? 0 : time(NULL)); + } + + if (cont) { + if (objid) { + char* end = NULL; + cont->cap1 = strtoul(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) db_free(&dbv); + MSN_SetMirVer(hContact, cont->cap1, always); + } + + char *pszUrl, *pszAvatarHash; + if (cmdstring && *cmdstring && strcmp(cmdstring, "0") && + (pszAvatarHash = MSN_GetAvatarHash(cmdstring, &pszUrl))) + { + setString(hContact, "PictContext", cmdstring); + setString(hContact, "AvatarHash", pszAvatarHash); + if (pszUrl) + setString(hContact, "AvatarUrl", pszUrl); + else + delSetting(hContact, "AvatarUrl"); + + if (hContact != NULL) { + char szSavedHash[64] = ""; + db_get_static(hContact, m_szModuleName, "AvatarSavedHash", szSavedHash, sizeof(szSavedHash)); + if (stricmp(szSavedHash, pszAvatarHash)) + pushAvatarRequest(hContact, pszUrl); + else { + char szSavedContext[64]; + int result = db_get_static(hContact, m_szModuleName, "PictSavedContext", szSavedContext, sizeof(szSavedContext)); + if (result || strcmp(szSavedContext, cmdstring)) + pushAvatarRequest(hContact, pszUrl); + } + } + mir_free(pszAvatarHash); + mir_free(pszUrl); + } + else { + delSetting(hContact, "AvatarHash"); + delSetting(hContact, "AvatarSavedHash"); + delSetting(hContact, "AvatarUrl"); + delSetting(hContact, "PictContext"); + delSetting(hContact, "PictSavedContext"); + + ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); + } + } + else if (lastStatus == ID_STATUS_OFFLINE) + delSetting(hContact, "MirVer"); +} + +void CMsnProto::MSN_ProcessStatusMessage(ezxml_t xmli, const char* wlid) +{ + MCONTACT hContact = MSN_HContactFromEmail(wlid); + if (hContact == NULL) return; + + char* szEmail, *szNetId; + parseWLID(NEWSTR_ALLOCA(wlid), &szNetId, &szEmail, NULL); + + bool bHasPSM=false; + char* szStatMsg = NULL; + + for (ezxml_t s = ezxml_child(xmli, "s"); s; s = s->next) { + const char *n = ezxml_attr(s, "n"); + if (!strcmp(n, "SKP")) { + szStatMsg = ezxml_txt(ezxml_child(s, "Mood")); + if (*szStatMsg) db_set_utf(hContact, "CList", "StatusMsg", szStatMsg); + else if (!bHasPSM) db_unset(hContact, "CList", "StatusMsg"); + } else if (!strcmp(n, "PE")) { + szStatMsg = ezxml_txt(ezxml_child(s, "PSM")); + if (*szStatMsg) { + stripBBCode((char*)szStatMsg); + stripColorCode((char*)szStatMsg); + db_set_utf(hContact, "CList", "StatusMsg", szStatMsg); + bHasPSM = true; + } + else db_unset(hContact, "CList", "StatusMsg"); + } + } + + // Add endpoints + for (ezxml_t endp = ezxml_child(xmli, "sep"); endp; endp = ezxml_next(endp)) { + const char *n = ezxml_attr(endp, "n"); + if (!strcmp(n, "IM")) { + const char *id = ezxml_attr(endp, "epid"); + 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); + } } - else db_unset(hContact, "CList", "StatusMsg"); { ptrT tszStatus(mir_utf8decodeT(szStatMsg)); @@ -744,7 +883,6 @@ void CMsnProto::MSN_ProcessStatusMessage(char* buf, unsigned len, const char* wl const char* szCrntMda = ezxml_txt(ezxml_child(xmli, "CurrentMedia")); if (!*szCrntMda) { delSetting(hContact, "ListeningTo"); - ezxml_free(xmli); return; } @@ -766,7 +904,6 @@ void CMsnProto::MSN_ProcessStatusMessage(char* buf, unsigned len, const char* wl // Now let's mount the final string if (pCount <= 4) { delSetting(hContact, "ListeningTo"); - ezxml_free(xmli); return; } @@ -780,7 +917,6 @@ void CMsnProto::MSN_ProcessStatusMessage(char* buf, unsigned len, const char* wl } if (!foundUsefullInfo) { delSetting(hContact, "ListeningTo"); - ezxml_free(xmli); return; } @@ -847,7 +983,6 @@ void CMsnProto::MSN_ProcessStatusMessage(char* buf, unsigned len, const char* wl mir_free(lti.ptszPlayer); mir_free(lti.ptszType); } - ezxml_free(xmli); } void CMsnProto::MSN_ProcessPage(char* buf, unsigned len) @@ -918,6 +1053,7 @@ void CMsnProto::MSN_ProcessNotificationMessage(char* buf, unsigned len) void CMsnProto::MSN_InitSB(ThreadData* info, const char* szEmail) { +/* MSNP21: No more switchboard, bye, bye ... MsnContact *cont = Lists_Get(szEmail); if (cont->netId == NETID_MSN) @@ -936,7 +1072,7 @@ void CMsnProto::MSN_InitSB(ThreadData* info, const char* szEmail) ProtoBroadcastAck(cont->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE)E.seq, 0); } else { - if (E.msgType == 'D' && !info->mBridgeInit /*&& strchr(data.flags, ':')*/) { + if (E.msgType == 'D' && !info->mBridgeInit) { //&& strchr(data.flags, ':')) { info->mBridgeInit = true; // P2PV2_Header hdrdata(E.message); @@ -966,6 +1102,7 @@ void CMsnProto::MSN_InitSB(ThreadData* info, const char* szEmail) PROTO_AVATAR_INFORMATIONT ai = { sizeof(ai), cont->hContact }; GetAvatarInfo(GAIF_FORCE, (LPARAM)&ai); + */ } int CMsnProto::MSN_HandleCommands(ThreadData* info, char* cmdString) @@ -1029,12 +1166,77 @@ LBL_InvalidCommand: case ' SNA': //********* ANS: section 8.4 Getting Invited to a Switchboard Session break; + case ' HTA': //********* ATH: MSNP21+ Authentication + { + union { + char* tWords[2]; + struct { char *typeId, *strMsgBytes; } data; + }; + + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 2) + goto LBL_InvalidCommand; + + HReadBuffer buf(info, 0); + char* msgBody = (char*)buf.surelyRead(atol(data.strMsgBytes)); + + if (!bSentBND) + { + info->sendPacketPayload("BND", "CON\\MSGR", + "%d%s%s%s" + "%.*s\r\n", + msnP24Ver, (msnP24Ver>1?"1":""), + msnStoreAppId, msnProductVer, + strlen(MyOptions.szMachineGuid)-2, MyOptions.szMachineGuid+1); + bSentBND = true; + } + else + { + msnLoggedIn = true; + MSN_SetServerStatus(m_iStatus); + MSN_EnableMenuItems(true); + MSN_RefreshContactList(); + MSN_FetchRecentMessages(); + } + } + break; + case ' PRP': break; case ' PLB': //********* BLP: section 7.6 List Retrieval And Property Management break; + case ' DNB': //********* BND: MSNP21+ bind request answer? + { + union { + char* tWords[2]; + struct { char *typeId, *strMsgBytes; } data; + }; + + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 2) + goto LBL_InvalidCommand; + + MimeHeaders tHeader; + HReadBuffer buf(info, 0); + char* msgBody = tHeader.readFromBuffer((char*)buf.surelyRead(atol(data.strMsgBytes))); + + replaceStr(msnRegistration,tHeader["Set-Registration"]); + if (!strcmp(data.typeId, "CON")) { + ezxml_t xmlbnd = ezxml_parse_str(msgBody, strlen(msgBody)); + ezxml_t xmlbdy = ezxml_child(xmlbnd, "nonce"); + if (xmlbdy) + { + char dgst[64]; + MSN_MakeDigest(xmlbdy->txt, dgst); + info->sendPacketPayload("PUT", "MSGR\\CHALLENGE", + "%s%s\r\n", + msnProductID, dgst); + } + ezxml_free(xmlbnd); + } + } + break; + case ' EYB': //********* BYE: section 8.5 Session Participant Changes { union { @@ -1149,6 +1351,60 @@ LBL_InvalidCommand: info->sendPacket("QRY", "%s 32\r\n%s", msnProductID, dgst); } break; + case ' TNC': //********* CNT: Connect, MSNP21+ Authentication + { + union { + char* tWords[2]; + struct { char *typeId, *strMsgBytes; } data; + }; + + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 2) + goto LBL_InvalidCommand; + + HReadBuffer buf(info, 0); + char* msgBody = (char*)buf.surelyRead(atol(data.strMsgBytes)); + if (strcmp(data.typeId, "CON")) break; + + if (GetMyNetID()!=NETID_SKYPE) { + /* MSN account login */ + char *pszSite = ""; + + switch (MSN_AuthOAuth()) + { + case 1: break; + case 2: pszSite = "chatservice.live.com"; break; + default: + m_iDesiredStatus = ID_STATUS_OFFLINE; + return 1; + } + + info->sendPacketPayload("ATH", "CON\\USER", + "t=%s" + "%s%s" + "%s%s\r\n", + authSSLToken ? ptrA(HtmlEncode(authSSLToken)) : "", + authUIC, pszSite, + GetMyUsername(NETID_MSN), GetMyUsername(NETID_SKYPE)); + } else { + /* Skype username/pass login */ + ezxml_t xmlcnt = ezxml_parse_str(msgBody, strlen(msgBody)); + ezxml_t xmlnonce = ezxml_child(xmlcnt, "nonce"); + if (xmlnonce) { + char szUIC[1024]={0}; + + MSN_SkypeAuth(xmlnonce->txt, szUIC); + info->sendPacketPayload("ATH", "CON\\USER", + "%s%s\r\n", + szUIC, MyOptions.szEmail); + } + ezxml_free(xmlcnt); + } + + bSentBND = false; + ForkThread(&CMsnProto::msn_keepAliveThread, NULL); + ForkThread(&CMsnProto::MSNConnDetectThread, NULL); + } + break; case ' RVC': //********* CVR: MSNP8 break; @@ -1171,115 +1427,184 @@ LBL_InvalidCommand: } } break; - case ' NLI': - case ' NLN': //********* ILN/NLN: section 7.9 Notification Messages + + case ' TEG': //********* GET: MSNP21+ GET reply { union { - char* tWords[5]; - struct { char *userStatus, *wlid, *userNick, *objid, *cmdstring; } data; + char* tWords[2]; + struct { char *typeId, *strMsgBytes; } data; }; - int tArgs = sttDivideWords(params, 5, tWords); - if (tArgs < 3) + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 2) 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; - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldMode, m_iStatus); - } - } - - WORD lastStatus = ID_STATUS_OFFLINE; - - MsnContact *cont = Lists_Get(szEmail); - - MCONTACT 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) - db_unset(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; + MimeHeaders tHeader; + HReadBuffer buf(info, 0); + char* msgBody = tHeader.readFromBuffer((char*)buf.surelyRead(atol(data.strMsgBytes))); + ezxml_t xmli; - if (lastStatus == ID_STATUS_OFFLINE) { - DBVARIANT dbv; - bool always = getString(hContact, "MirVer", &dbv) != 0; - if (!always) db_free(&dbv); - MSN_SetMirVer(hContact, cont->cap1, always); + if (tHeader["Set-Registration"]) replaceStr(msnRegistration,tHeader["Set-Registration"]); + if (xmli = ezxml_parse_str(msgBody, strlen(msgBody))) + { + if (!strcmp(xmli->name, "recentconversations-response")) + { + for (ezxml_t conv = ezxml_get(xmli, "conversations", 0, "conversation", -1); conv != NULL; conv = ezxml_next(conv)) { + ezxml_t id; + MCONTACT hContact; + MEVENT hDbEvent; + DWORD ts = 1; + + if (ezxml_get(conv, "messages", 0, "message", -1) && (id=ezxml_child(conv, "id"))) + { + if (strncmp(id->txt, "19:", 3) == 0) { + /* This is a thread (Groupchat) + * Find out about its current state on the server */ + hContact = MSN_HContactFromChatID(id->txt); + msnNsThread->sendPacketPayload("GET", "MSGR\\THREADS", + "%s", id->txt); + } else hContact = MSN_HContactFromEmail(id->txt, NULL, false, false); + + if (hContact) { + // There are messages to be fetched + if (hDbEvent = db_event_last(hContact)) { + DBEVENTINFO dbei = { sizeof(dbei) }; + db_event_get(hDbEvent, &dbei); + ts = dbei.timestamp; + } + db_set_dw(hContact, m_szModuleName, "syncTS", ts); + } + msnNsThread->sendPacketPayload("GET", "MSGR\\MESSAGESBYCONVERSATION", + "%s%llu100", + id->txt, ((unsigned __int64)ts)*1000); + } + } } - - if (data.cmdstring[0] && strcmp(data.cmdstring, "0")) { - char *pszUrl, *pszAvatarHash = MSN_GetAvatarHash(data.cmdstring, &pszUrl); - if (pszAvatarHash == NULL) - goto remove; - - setString(hContact, "PictContext", data.cmdstring); - setString(hContact, "AvatarHash", pszAvatarHash); - if (pszUrl) - setString(hContact, "AvatarUrl", pszUrl); - else - delSetting(hContact, "AvatarUrl"); - - if (hContact != NULL) { - char szSavedHash[64] = ""; - db_get_static(hContact, m_szModuleName, "AvatarSavedHash", szSavedHash, sizeof(szSavedHash)); - if (stricmp(szSavedHash, pszAvatarHash)) - pushAvatarRequest(hContact, pszUrl); - else { - char szSavedContext[64]; - int result = db_get_static(hContact, m_szModuleName, "PictSavedContext", szSavedContext, sizeof(szSavedContext)); - if (result || strcmp(szSavedContext, data.cmdstring)) - pushAvatarRequest(hContact, pszUrl); + else if (!strcmp(xmli->name, "messagesbyconversation-response")) { + ezxml_t id; + MCONTACT hContact; + + if ((id=ezxml_child(xmli, "id"))) + { + bool bIsChat = strncmp(id->txt, "19:", 3)==0; + bool bHasMore = stricmp(ezxml_txt(ezxml_child(xmli, "hasmore")), "true") == 0; + ezxml_t syncstate; + hContact = MSN_HContactFromEmail(id->txt, NULL, false, false); + if (!bHasMore && hContact) db_unset(hContact, m_szModuleName, "syncTS"); + + /* We have to traverse the list in reverse order as newest events are on top (which is the opposite direction of Groupchat) */ + LIST msgs(10,PtrKeySortT); + for (ezxml_t msg = ezxml_get(xmli, "messages", 0, "message", -1); msg != NULL; msg = ezxml_next(msg)) msgs.insert(msg, 0); + for (int i=0; itxt); + parseWLID(NEWSTR_ALLOCA(from->txt), &netId, &email, NULL); + message=content->txt; + sentMsg = stricmp(email, GetMyUsername(atoi(netId)))==0; + if (msgtype) { + if (!strcmp(msgtype->txt, "RichText")) { + message = NEWSTR_ALLOCA(message); + stripHTML(message); + HtmlDecode(message); + } else if (!strncmp(msgtype->txt, "ThreadActivity/", 15)) { + if (ezxml_t xmlact = ezxml_parse_str(content->txt, strlen(content->txt))) { + MSN_GCProcessThreadActivity(xmlact, _A2T(id->txt)); + ezxml_free(xmlact); + } + continue; + } else if (strcmp(msgtype->txt, "Text")) continue; + /* TODO: Implement i.e. RichText/Files for announcement of file transfers */ + } + + if (bIsChat) { + hContact = MSN_HContactFromEmail(from->txt, NULL, false, false); + if (hContact) db_unset(hContact, m_szModuleName, "syncTS"); + MSN_GCAddMessage(_A2T(id->txt), hContact, email, ts, sentMsg, message); + } + else if (hContact) { + /* Protect against double sync (Miranda MSGs only have granularity in seconds) */ + MEVENT hDbEvent; + bool bDuplicate = false; + DBEVENTINFO dbei = { sizeof(dbei) }; + DWORD cbBlob = strlen(message); + dbei.cbBlob = cbBlob; + BYTE *pszMsgBuf = (BYTE*)mir_calloc(cbBlob); + if (pszMsgBuf) { + dbei.pBlob = pszMsgBuf; + for((hDbEvent = db_event_last(hContact)); + !bDuplicate && hDbEvent; + hDbEvent=db_event_prev(hContact, hDbEvent)) + { + if (db_event_get(hDbEvent, &dbei) || dbei.timestamp > ts+1 || dbei.timestamp < ts) break; + if (!memcmp((char*)dbei.pBlob, message, cbBlob)) bDuplicate = true; + dbei.cbBlob = cbBlob; + } + mir_free(pszMsgBuf); + if (bDuplicate) continue; + } + + if (!sentMsg) { + PROTORECVEVENT pre = { 0 }; + pre.szMessage = (char*)message; + pre.flags = PREF_UTF; + pre.timestamp = (DWORD)ts; + ProtoChainRecvMsg(hContact, &pre); + } + else { + DBEVENTINFO dbei = { 0 }; + dbei.cbSize = sizeof(dbei); + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.flags = DBEF_SENT | DBEF_UTF; + dbei.szModule = m_szModuleName; + dbei.timestamp = ts; + dbei.cbBlob = (unsigned)strlen(message) + 1; + dbei.pBlob = (PBYTE)message; + db_event_add(hContact, &dbei); + } + } + } + /* In groupchat it wouldn't make much sense to sync more as older messages are coming now and that would jumble our log */ + if (!bIsChat && bHasMore && (syncstate = ezxml_child(xmli, "messagessyncstate"))) { + msnNsThread->sendPacketPayload("GET", "MSGR\\MESSAGESBYCONVERSATION", + "%s%llu%s100", + id->txt, ((unsigned __int64)db_get_dw(hContact, m_szModuleName, "syncTS", 1000))*1000, syncstate->txt); } + msgs.destroy(); } - mir_free(pszAvatarHash); - mir_free(pszUrl); } - else { -remove: - delSetting(hContact, "AvatarHash"); - delSetting(hContact, "AvatarSavedHash"); - delSetting(hContact, "AvatarUrl"); - delSetting(hContact, "PictContext"); - delSetting(hContact, "PictSavedContext"); - - ProtoBroadcastAck(hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, NULL, 0); + else if (!strcmp(xmli->name, "threads-response")) { + for (ezxml_t thread = ezxml_get(xmli, "threads", 0, "thread", -1); thread != NULL; thread = ezxml_next(thread)) + MSN_ChatStart(thread); } - } - else if (lastStatus == ID_STATUS_OFFLINE) - delSetting(hContact, "MirVer"); + ezxml_free(xmli); + } } break; - case ' ORI': //********* IRO: section 8.4 Getting Invited to a Switchboard Session + + 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; + + if (data.cmdstring) UrlDecode(data.cmdstring); + MSN_ProcessNLN(data.userStatus, data.wlid, data.userNick, data.objid, data.cmdstring); + } + break; + case ' ORI': /********* IRO: section 8.4 Getting Invited to a Switchboard Session { union { char* tWords[5]; @@ -1314,9 +1639,9 @@ remove: MSN_ChatStart(info); } } - break; + break;*/ - case ' IOJ': //********* JOI: section 8.5 Session Participant Changes + case ' IOJ': /********* JOI: section 8.5 Session Participant Changes { union { char* tWords[3]; @@ -1421,7 +1746,7 @@ remove: else MSN_ChatStart(info); } } - break; + break;*/ case ' GSM': //********* MSG: sections 8.7 Instant Messages, 8.8 Receiving an Instant Message MSN_ReceiveMessage(info, cmdString, params); @@ -1439,6 +1764,67 @@ remove: debugLogA("Message send failed (trid=%d)", trid); break; + case ' YFN': //********* NFY: MSNP21+ Notifications + { + union { + char* tWords[2]; + struct { char *typeId, *strMsgBytes; } data; + }; + + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 2) + goto LBL_InvalidCommand; + + HReadBuffer buf(info, 0); + char* msgBody = (char*)buf.surelyRead(atol(data.strMsgBytes)); + if (msgBody == NULL) break; + + if (!strcmp(data.typeId, "MSGR\\PUT") || !strcmp(data.typeId, "MSGR\\DEL")) { + MimeHeaders tHeader; + + int i; + for (i=0; i<2; i++) msgBody = tHeader.readFromBuffer(msgBody); + char *pszTo = NULL, *pszToNet; + if (tHeader["To"]) parseWLID(NEWSTR_ALLOCA(tHeader["To"]), &pszToNet, &pszTo, NULL); + const char *pszFrom = tHeader["From"]; + for (i=0; i<2; i++) msgBody = tHeader.readFromBuffer(msgBody); + + if (pszFrom) + { + ezxml_t xmli; + if (xmli = ezxml_parse_str(msgBody, strlen(msgBody))) + { + if (!strcmp(xmli->name, "user")) + { + ezxml_t xmlstatus = ezxml_get(xmli, "s", 0, "Status", -1); + /* FIXME: MSGR\DEL: Instance of user with given EPID disconnected, not + * sure if this implies that contact is offline now... */ + if (xmlstatus || !strcmp(data.typeId, "MSGR\\DEL")) + { + // These capabilities seem to be something different than in previous MSNP versions? + //ezxml_t xmlcaps = ezxml_get(xmli, "sep", 0, "Capabilities", -1); + ezxml_t usertile = ezxml_get(xmli, "s", 1, "UserTileLocation", -1); + MSN_ProcessNLN(ezxml_txt(xmlstatus), pszFrom, NULL, NULL, usertile?usertile->txt:NULL); + } + MSN_ProcessStatusMessage(xmli, pszFrom); + } + ezxml_free(xmli); + } + } + } + else if (!strcmp(data.typeId, "MSGR\\THREAD")) { + MimeHeaders tHeader; + char *msgBody = tHeader.readFromBuffer(info->mData); + ezxml_t xmli; + + if (xmli = ezxml_parse_str(msgBody, strlen(msgBody))) + { + MSN_ChatStart(xmli); + ezxml_free(xmli); + } + } + } + break; + case ' TON': //********* NOT: notification message MSN_ProcessNotificationMessage((char*)HReadBuffer(info, 0).surelyRead(trid), trid); break; @@ -1462,6 +1848,54 @@ remove: return 1; + case ' TUP': //******** MSNP21+: PUT notifications + case ' GNP': //******** MSNP21+: PNG reply + { + union { + char* tWords[2]; + struct { char *typeId, *strMsgBytes; } data; + }; + + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 2) + goto LBL_InvalidCommand; + + MimeHeaders tHeader; + HReadBuffer buf(info, 0); + char* msgBody = tHeader.readFromBuffer((char*)buf.surelyRead(atol(data.strMsgBytes))); + + if (tHeader["Set-Registration"]) replaceStr(msnRegistration,tHeader["Set-Registration"]); + if (cmdString[1]=='N') { // PNG + if (ezxml_t xmli = ezxml_parse_str(msgBody, strlen(msgBody))) { + if (ezxml_t wait = ezxml_child(xmli, "wait")) { + msnPingTimeout = atoi(ezxml_txt(wait)); + if (msnPingTimeout && hKeepAliveThreadEvt != NULL) + SetEvent(hKeepAliveThreadEvt); + } + ezxml_free(xmli); + } + } else { // PUT + ezxml_t xmli; + if (*msgBody && (xmli = ezxml_parse_str(msgBody, strlen(msgBody)))) { + if (!strcmp(xmli->name, "presence-response")) { + ezxml_t user, from; + if ((user = ezxml_child(xmli, "user")) && (from = ezxml_child(xmli, "from"))) { + if (ezxml_t xmlstatus = ezxml_get(user, "s", 0, "Status", -1)) { + ezxml_t usertile = ezxml_get(user, "s", 1, "UserTileLocation", -1); + MSN_ProcessNLN(ezxml_txt(xmlstatus), from->txt, NULL, NULL, usertile?usertile->txt:NULL); + } else { + int oldMode = m_iStatus; + m_iStatus = m_iDesiredStatus; + ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)oldMode, m_iStatus); + } + MSN_ProcessStatusMessage(user, from->txt); + } + } + ezxml_free(xmli); + } + } + } + break; + case ' YRQ': //********* QRY: break; @@ -1521,6 +1955,10 @@ remove: } break; + case ' GDS': // SDG: MSNP21+ Messaging + MSN_ReceiveMessage(info, cmdString, params); + break; + case ' XBU': // UBX : MSNP11+ User Status Message { union { @@ -1535,7 +1973,11 @@ remove: if (len < 0 || len > 4000) goto LBL_InvalidCommand; - MSN_ProcessStatusMessage((char*)HReadBuffer(info, 0).surelyRead(len), len, data.wlid); + ezxml_t xmli = ezxml_parse_str((char*)HReadBuffer(info, 0).surelyRead(len), len); + if (xmli) { + MSN_ProcessStatusMessage(xmli, data.wlid); + ezxml_free(xmli); + } } break; @@ -1693,52 +2135,36 @@ remove: 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; + char* tWords[2]; + struct { char *typeId, *strMsgBytes; } data; }; - int numWords = sttDivideWords(params, 7, tWords); - if (numWords < 3) + if (sttDivideWords(params, SIZEOF(tWords), tWords) < 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; - usingGateway |= (*data.security == 'G'); - info->mIsMainThread = false; - - debugLogA("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")) { - debugLogA("Unknown XFR SB security package '%s'", data.security); - break; + MimeHeaders tHeader; + HReadBuffer buf(info, 0); + char* msgBody = tHeader.readFromBuffer((char*)buf.surelyRead(atol(data.strMsgBytes))); + if (!strcmp(data.typeId, "CON")) { + ezxml_t xmlxfr = ezxml_parse_str(msgBody, strlen(msgBody)); + ezxml_t xmltgt = ezxml_child(xmlxfr, "target"); + if (xmltgt) + { + ThreadData* newThread = new ThreadData; + strcpy(newThread->mServer, xmltgt->txt); + strcpy(newThread->mState, ezxml_txt(ezxml_child(xmlxfr, "state"))); + newThread->mType = SERVER_NOTIFICATION; + newThread->mTrid = info->mTrid; + newThread->mIsMainThread = true; + info->mIsMainThread = false; + + debugLogA("Switching to notification server '%s'...", xmltgt->txt); + newThread->startThread(&CMsnProto::MSNServerThread, this); + ezxml_free(xmlxfr); + return 1; //kill the old thread } - - 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); - - debugLogA("Opening switchboard server '%s'...", data.newServer); - newThread->startThread(&CMsnProto::MSNServerThread, this); + ezxml_free(xmlxfr); } - else debugLogA("Unknown referral server: %s", data.type); } break; diff --git a/protocols/MSN/src/msn_contact.cpp b/protocols/MSN/src/msn_contact.cpp index 7f55df3420..fab76bd362 100644 --- a/protocols/MSN/src/msn_contact.cpp +++ b/protocols/MSN/src/msn_contact.cpp @@ -27,27 +27,51 @@ MCONTACT CMsnProto::MSN_HContactFromEmail(const char* wlid, const char* msnNick, { MCONTACT hContact = NULL; - char *szEmail; - parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL); + char *szEmail, *szNet = NULL; + parseWLID(NEWSTR_ALLOCA(wlid), &szNet, &szEmail, NULL); MsnContact *msc = Lists_Get(szEmail); if (msc && msc->hContact) hContact = msc->hContact; if (hContact == NULL && addIfNeeded) { + int netId = msc->netId?msc->netId:(szNet?atoi(szNet):NETID_MSN); hContact = (MCONTACT)CallService(MS_DB_CONTACT_ADD, 0, 0); CallService(MS_PROTO_ADDTOCONTACT, hContact, (LPARAM)m_szModuleName); - setString(hContact, "e-mail", szEmail); + if (netId != NETID_SKYPE) setString(hContact, "e-mail", szEmail); setStringUtf(hContact, "Nick", msnNick ? msnNick : wlid); + setWord(hContact, "netId", netId); + setString(hContact, "wlid", szEmail); if (temporary) db_set_b(hContact, "CList", "NotOnList", 1); - Lists_Add(0, NETID_MSN, wlid, hContact); + Lists_Add(0, szNet?atoi(szNet):NETID_MSN, szEmail, hContact); } return hContact; } +MCONTACT CMsnProto::MSN_HContactFromChatID(const char* wlid) +{ + MCONTACT hContact = NULL; + + for (hContact = db_find_first(m_szModuleName); hContact; + hContact = db_find_next(hContact, m_szModuleName)) + { + if (isChatRoom(hContact) != 0) { + DBVARIANT dbv; + if (getString(hContact, "ChatRoomID", &dbv) == 0) { + if (strcmp(dbv.pszVal, wlid) == 0) { + db_free(&dbv); + return hContact; + } + db_free(&dbv); + } + } + } + return NULL; +} + void CMsnProto::MSN_SetContactDb(MCONTACT hContact, const char *szEmail) { @@ -226,21 +250,34 @@ bool CMsnProto::MSN_RefreshContactList(void) Lists_Wipe(); Lists_Populate(); - if (!MSN_SharingFindMembership()) return false; + if (GetMyNetID() != NETID_SKYPE) + { + if (!MSN_SharingFindMembership()) return false; + + if (m_iDesiredStatus == ID_STATUS_OFFLINE) return false; + + if (!MSN_ABFind("ABFindContactsPaged", NULL)) return false; + MSN_ABRefreshClist(); - if (m_iDesiredStatus == ID_STATUS_OFFLINE) return false; + if (m_iDesiredStatus == ID_STATUS_OFFLINE) return false; - if (!MSN_ABFind("ABFindContactsPaged", NULL)) return false; + MSN_CleanupLists(); - if (m_iDesiredStatus == ID_STATUS_OFFLINE) return false; + if (m_iDesiredStatus == ID_STATUS_OFFLINE) return false; - MSN_CleanupLists(); + msnLoggedIn = true; - if (m_iDesiredStatus == ID_STATUS_OFFLINE) return false; + MSN_CreateContList(); + //MSN_StoreGetProfile(); + } + else + { + /* TODO: Add pulling Skype contacts from event server or skypeweb or other unknown method.. */ + MSN_CreateContList(); + } - msnLoggedIn = true; + // Refresh Threads which are also part of the contact list + if (msnP24Ver>1) MSN_GCRefreshThreadsInfo(); - MSN_CreateContList(); - MSN_StoreGetProfile(); return true; } diff --git a/protocols/MSN/src/msn_ftold.cpp b/protocols/MSN/src/msn_ftold.cpp index 305e6319b7..c921ae3e11 100644 --- a/protocols/MSN/src/msn_ftold.cpp +++ b/protocols/MSN/src/msn_ftold.cpp @@ -295,9 +295,12 @@ void __cdecl CMsnProto::msnftp_sendFileThread(void* arg) } } - if (info->mBytesInData == sizeof(info->mData)) { - debugLogA("sizeof(data) is too small: the longest line won't fit"); - break; + if (info->mBytesInData == info->mDataSize) { + char *mData = (char*)mir_realloc(info->mData, (info->mDataSize*=2)+1); + if (mData) info->mData = mData; else { + debugLogA("sizeof(data) is too small: the longest line won't fit"); + break; + } } } @@ -333,8 +336,7 @@ void CMsnProto::msnftp_startFileSend(ThreadData* info, const char* Invcommand, c else hostname[0] = 0; - char command[1024]; - int nBytes = mir_snprintf(command, SIZEOF(command), + info->sendPacketPayload("MSG", "N", "MIME-Version: 1.0\r\n" "Content-Type: text/x-msmsgsinvite; charset=UTF-8\r\n\r\n" "Invitation-Command: %s\r\n" @@ -352,8 +354,6 @@ void CMsnProto::msnftp_startFileSend(ThreadData* info, const char* Invcommand, c nlb.wExPort, nlb.wExPort ^ 0x3141, nlb.wPort ^ 0x3141, MSN_GenRandom()); - info->sendPacket("MSG", "N %d\r\n%s", nBytes, command); - if (sb) { ThreadData* newThread = new ThreadData; newThread->mType = SERVER_FILETRANS; diff --git a/protocols/MSN/src/msn_global.h b/protocols/MSN/src/msn_global.h index 8756e2bd23..578de7ba52 100644 --- a/protocols/MSN/src/msn_global.h +++ b/protocols/MSN/src/msn_global.h @@ -124,10 +124,10 @@ along with this program. If not, see . #define MSN_GUID_LEN 40 #define MSN_PACKETS_COMBINE 7 -#define MSN_DEFAULT_PORT 1863 -#define MSN_DEFAULT_GATEWAY_PORT 80 -const char MSN_DEFAULT_LOGIN_SERVER[] = "messenger.hotmail.com"; -const char MSN_DEFAULT_GATEWAY[] = "gateway.messenger.hotmail.com"; +#define MSN_DEFAULT_PORT 443 +#define MSN_DEFAULT_GATEWAY_PORT 443 +const char MSN_DEFAULT_LOGIN_SERVER[] = "s.gateway.messenger.live.com"; +const char MSN_DEFAULT_GATEWAY[] = "geo.gateway.messenger.live.com"; const char MSN_USER_AGENT[] = NETLIB_USER_AGENT; #define MSN_BLOCK "/BlockCommand" @@ -158,6 +158,7 @@ char* HtmlEncode(const char* str); bool txtParseParam (const char* szData, const char* presearch, const char* start, const char* finish, char* param, const int size); void stripBBCode(char* src); void stripColorCode(char* src); +void stripHTML(char* str); void parseWLID(char* wlid, char** net, char** email, char** inst); char* GetGlobalIp(void); @@ -185,6 +186,8 @@ void MsnInitIcons(void); int sttDivideWords(char* parBuffer, int parMinItems, char** parDest); void MSN_MakeDigest(const char* chl, char* dgst); char* getNewUuid(void); +time_t IsoToUnixTime(const char *stamp); +time_t MsnTSToUnixtime(const char *pszTS); TCHAR* EscapeChatTags(const TCHAR* pszText); TCHAR* UnEscapeChatTags(TCHAR* str_in); @@ -476,6 +479,31 @@ bool p2p_IsDlFileOk(filetransfer* ft); struct CMsnProto; typedef void (__cdecl CMsnProto::*MsnThreadFunc)(void*); +/* Groupchat threadlist entry. As there is no more SB in MSNP21+, + * this is no longer in ThreadData and there are no more new + * Threads, but for code compatibility, we still have ThreadData + * as a "main connection" + */ +struct GCUserItem +{ + char WLID[MSN_MAX_EMAIL_LEN]; + TCHAR role[8]; + BYTE btag; +}; + +struct GCThreadData +{ + GCThreadData(); + ~GCThreadData(); + + LIST mJoinedContacts; + GCUserItem* mCreator; + GCUserItem* mMe; + TCHAR mChatID[MSN_MAX_EMAIL_LEN]; + int netId; // from mChatID + char szEmail[MSN_MAX_EMAIL_LEN]; // frim mChatID +}; + struct ThreadData { ThreadData(); @@ -488,6 +516,7 @@ struct ThreadData TInfoType mType; // thread type MsnThreadFunc mFunc; // thread entry point char mServer[80]; // server name + char mState[128]; // state on XFR HANDLE s; // NetLib connection for the thread HANDLE mIncomingBoundPort; // Netlib listen for the thread @@ -520,7 +549,8 @@ struct ThreadData //----| internal data buffer |-------------------------------------------------------- int mBytesInData; // bytes available in data buffer - char mData[8192]; // data buffer for connection + char *mData; // data buffer for connection + size_t mDataSize; //----| methods |--------------------------------------------------------------------- void applyGatewayData(HANDLE hConn, bool isPoll); @@ -539,6 +569,7 @@ struct ThreadData int sendMessage(int msgType, const char* email, int netId, const char* msg, int parFlags); int sendRawMessage(int msgType, const char* data, int datLen); int sendPacket(const char* cmd, const char* fmt, ...); + int sendPacketPayload(const char* cmd, const char *param, const char* fmt, ...); int contactJoined(const char* email); int contactLeft(const char* email); @@ -710,18 +741,32 @@ struct MsnContact #define capex_SupportsP4Activity 0x40000000 #define capex_SupportsChats 0x80000000 -#define NETID_UNKNOWN 0x0000 -#define NETID_MSN 0x0001 -#define NETID_LCS 0x0002 -#define NETID_MOB 0x0004 -#define NETID_MOBNET 0x0008 -#define NETID_CIRCLE 0x0009 -#define NETID_TMPCIRCLE 0x000A -#define NETID_CID 0x000B -#define NETID_CONNECT 0x000D -#define NETID_REMOTE 0x000E -#define NETID_SMTP 0x0010 -#define NETID_YAHOO 0x0020 +#define NETID_UNKNOWN 0 +#define NETID_MSN 1 +#define NETID_LCS 2 +#define NETID_ALIAS 3 +#define NETID_MOB 4 +#define NETID_DOMAIN 5 +#define NETID_SINK 6 +#define NETID_CONTACT 7 +#define NETID_SKYPE 8 +#define NETID_CIRCLE 9 +#define NETID_TMPCIRCLE 10 +#define NETID_CID 11 +#define NETID_APPID 12 +#define NETID_CONNECT 13 +#define NETID_REMOTE 14 +#define NETID_SMTP 16 +#define NETID_LVIDSINK 17 +#define NETID_MULTICAST 18 +#define NETID_THREAD 19 +#define NETID_1TO1TEXT 21 +#define NETID_GROUPTEXT 22 +#define NETID_BOT 28 +#define NETID_YAHOO 32 +#define NETID_PUBSUBTPC 33 +#define NETID_PUBSUBSUB 34 +#define NETID_WNSSID 35 #define LIST_FL 0x0001 #define LIST_AL 0x0002 @@ -769,12 +814,13 @@ struct TWinErrorCode #define MSN_NUM_MODES 9 -const char msnProtChallenge[] = "C1BX{V4W}Q3*10SM"; -const char msnProductID[] = "PROD0120PW!CCV9@"; -const char msnAppID[] = "484AAC02-7F59-41B7-9601-772045DCC569"; -const char msnStoreAppId[] = "Windows Live Messenger 2012"; -const char msnProductVer[] = "16.4.3528"; -const char msnProtID[] = "MSNP18"; +const char msnProtChallenge[] = "YMM8C_H7KCQ2S_KL"; +const char msnProductID[] = "PROD0090YUAUV{2B"; +const char msnAppID[] = "F6D2794D-501F-443A-ADBE-8F1490FF30FD"; +const int msnP24Ver = 2; +const char msnStoreAppId[] = "Skype"; +const char msnProductVer[] = "0/6.16.0.105/259/"; +const char msnProtID[] = "MSNP24"; extern HINSTANCE hInst; diff --git a/protocols/MSN/src/msn_libstr.cpp b/protocols/MSN/src/msn_libstr.cpp index 127328230e..a77a343a2d 100644 --- a/protocols/MSN/src/msn_libstr.cpp +++ b/protocols/MSN/src/msn_libstr.cpp @@ -88,6 +88,7 @@ void parseWLID(char* wlid, char** net, char** email, char** inst) if (net) *net = wlid; if (email) *email = col + 1; ++col; + wlid=col; } else { if (net) *net = NULL; @@ -266,6 +267,32 @@ void stripColorCode(char* src) *pd = 0; } +void stripHTML(char* str) +{ + char *p, *q; + + for ( p=q=str; *p!='\0'; p++,q++ ) + { + if ( *p == '<' ) + { + if ( !strnicmp( p, "

", 3 )) { strcpy(q, "\r\n\r\n"); q += 3; p += 2; } + else if ( !strnicmp( p, "

", 4 )) { strcpy(q, "\r\n\r\n"); q += 3; p += 3; } + else if ( !strnicmp( p, "
", 4 )) { strcpy(q, "\r\n"); ++q; p += 3; } + else if ( !strnicmp( p, "
", 6 )) { strcpy(q, "\r\n"); ++q; p += 5; } + else if ( !strnicmp( p, "
", 4 )) { strcpy(q, "\r\n"); ++q; p += 3; } + else if ( !strnicmp( p, "
", 6 )) { strcpy(q, "\r\n"); ++q; p += 5; } + else { + char *l = strchr(p, '>'); + if (l) { p = l; --q; } else *q = *p; + } + } + else + *q = *p; + } + *q = '\0'; +} + + // Process a string, and double all % characters, according to chat.dll's restrictions // Returns a pointer to the new string (old one is not freed) TCHAR* EscapeChatTags(const TCHAR* pszText) @@ -318,3 +345,72 @@ char* getNewUuid(void) RpcStringFreeA(&p); return result; } + +time_t IsoToUnixTime(const char *stamp) +{ + char date[9]; + int i, y; + + if (stamp == NULL) + return 0; + + char *p = NEWSTR_ALLOCA(stamp); + + // skip '-' chars + int si = 0, sj = 0; + while (true) { + if (p[si] == '-') + si++; + else if (!(p[sj++] = p[si++])) + break; + } + + // Get the date part + for (i = 0; *p != '\0' && i < 8 && isdigit(*p); p++, i++) + date[i] = *p; + + // Parse year + if (i == 6) { + // 2-digit year (1970-2069) + y = (date[0] - '0') * 10 + (date[1] - '0'); + if (y < 70) y += 100; + } + else if (i == 8) { + // 4-digit year + y = (date[0] - '0') * 1000 + (date[1] - '0') * 100 + (date[2] - '0') * 10 + date[3] - '0'; + y -= 1900; + } + else return 0; + + struct tm timestamp; + timestamp.tm_year = y; + + // Parse month + timestamp.tm_mon = (date[i - 4] - '0') * 10 + date[i - 3] - '0' - 1; + + // Parse date + timestamp.tm_mday = (date[i - 2] - '0') * 10 + date[i - 1] - '0'; + + // Skip any date/time delimiter + for (; *p != '\0' && !isdigit(*p); p++); + + // Parse time + if (sscanf(p, "%d:%d:%d", ×tamp.tm_hour, ×tamp.tm_min, ×tamp.tm_sec) != 3) + return (time_t)0; + + timestamp.tm_isdst = 0; // DST is already present in _timezone below + time_t t = mktime(×tamp); + + _tzset(); + t -= _timezone; + return (t >= 0) ? t : 0; +} + +time_t MsnTSToUnixtime(const char *pszTS) +{ + char szTS[16]; + + if (!*pszTS) return time(NULL); + strncpy(szTS, pszTS, 10); + return (time_t)atoi(szTS); +} diff --git a/protocols/MSN/src/msn_lists.cpp b/protocols/MSN/src/msn_lists.cpp index 2bc9a1cf65..bc6e1aefa5 100644 --- a/protocols/MSN/src/msn_lists.cpp +++ b/protocols/MSN/src/msn_lists.cpp @@ -166,7 +166,7 @@ int CMsnProto::Lists_Add(int list, int netId, const char* email, MCONTACT hConta p->invite = mir_strdup(invite); p->nick = mir_strdup(nick); p->hContact = hContact; - p->p2pMsgId = 0; + p->p2pMsgId = p->cap1 = p->cap2 = 0; m_arContacts.insert(p); } else { @@ -205,12 +205,13 @@ void CMsnProto::Lists_Populate(void) db_get_static(hContact, m_szModuleName, "e-mail", szEmail, sizeof(szEmail)); if (szEmail[0]) { bool localList = getByte(hContact, "LocalList", 0) != 0; + int netId = getWord(hContact, "netId", localList?NETID_MSN:NETID_UNKNOWN); if (localList) - Lists_Add(LIST_LL, NETID_MSN, szEmail, hContact); + Lists_Add(LIST_LL, netId, szEmail, hContact); else - Lists_Add(0, NETID_UNKNOWN, szEmail, hContact); + Lists_Add(0, netId, szEmail, hContact); } - else CallService(MS_DB_CONTACT_DELETE, hContact, 0); + else if (!isChatRoom(hContact)) CallService(MS_DB_CONTACT_DELETE, hContact, 0); hContact = hNext; } } @@ -273,9 +274,9 @@ void CMsnProto::MSN_CreateContList(void) { bool *used = (bool*)mir_calloc(m_arContacts.getCount()*sizeof(bool)); - char cxml[8192]; + CMStringA cxml; - size_t sz = mir_snprintf(cxml, SIZEOF(cxml), ""); + cxml.Append(""); { mir_cslock lck(m_csLists); @@ -296,44 +297,36 @@ void CMsnProto::MSN_CreateContList(void) const char *dom = strchr(C.email, '@'); if (dom == NULL && lastds == NULL) { - if (sz == 0) sz = mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), ""); if (newdom) { - sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), ""); + cxml.Append(""); newdom = false; } - - sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "", C.email, C.list & ~(LIST_RL | LIST_LL)); + int list = C.list & ~(LIST_RL | LIST_LL); + list = LIST_FL | LIST_AL; /* Seems to be always 3 in Skype... */ + cxml.AppendFormat("", C.email, C.netId, list, list, list, list); used[j] = true; } else if (dom != NULL && lastds != NULL && _stricmp(lastds, dom) == 0) { - if (sz == 0) sz = mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), ""); if (newdom) { - sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "", lastds + 1); + cxml.AppendFormat("", lastds + 1); newdom = false; } *(char*)dom = 0; - sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "", C.email, C.list & ~(LIST_RL | LIST_LL), C.netId); + cxml.AppendFormat("", C.email, C.netId, C.list & ~(LIST_RL | LIST_LL)); *(char*)dom = '@'; used[j] = true; } - - if (used[j] && sz > 7400) { - sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "", lastds ? 'd' : 't'); - msnNsThread->sendPacket("ADL", "%d\r\n%s", sz, cxml); - sz = 0; - newdom = true; - } } - if (!newdom) - sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), lastds ? "" : ""); + if (!newdom) cxml.Append(lastds ? "" : ""); } } - if (sz) { - sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), ""); - msnNsThread->sendPacket("ADL", "%d\r\n%s", sz, cxml); - } + cxml.Append(""); + msnNsThread->sendPacketPayload("PUT", "MSGR\\CONTACTS", "%s", cxml); + + if (msnP24Ver > 1) + msnNsThread->sendPacketPayload("PUT", "MSGR\\SUBSCRIPTIONS", "ABCH"); mir_free(used); } @@ -382,7 +375,8 @@ static void SetContactIcons(MCONTACT hItem, HWND hwndList, CMsnProto* proto) } char szEmail[MSN_MAX_EMAIL_LEN]; - if (db_get_static(hItem, proto->m_szModuleName, "e-mail", szEmail, sizeof(szEmail))) { + if (db_get_static(hItem, proto->m_szModuleName, "wlid", szEmail, sizeof(szEmail)) && + db_get_static(hItem, proto->m_szModuleName, "e-mail", szEmail, sizeof(szEmail))) { SendMessage(hwndList, CLM_DELETEITEM, (WPARAM)hItem, 0); return; } @@ -448,7 +442,8 @@ static void SaveSettings(MCONTACT hItem, HWND hwndList, CMsnProto* proto) char szEmail[MSN_MAX_EMAIL_LEN]; if (IsHContactContact(hItem)) { - if (db_get_static(hItem, proto->m_szModuleName, "e-mail", szEmail, sizeof(szEmail))) + if (db_get_static(hItem, proto->m_szModuleName, "wlid", szEmail, sizeof(szEmail)) && + db_get_static(hItem, proto->m_szModuleName, "e-mail", szEmail, sizeof(szEmail))) continue; } else if (IsHContactInfo(hItem)) { diff --git a/protocols/MSN/src/msn_menu.cpp b/protocols/MSN/src/msn_menu.cpp index 6af7d01a5e..8675781a19 100644 --- a/protocols/MSN/src/msn_menu.cpp +++ b/protocols/MSN/src/msn_menu.cpp @@ -34,6 +34,7 @@ INT_PTR CMsnProto::MsnBlockCommand(WPARAM hContact, LPARAM) { if (msnLoggedIn) { char tEmail[MSN_MAX_EMAIL_LEN]; + if (db_get_static(hContact, m_szModuleName, "wlid", tEmail, sizeof(tEmail))) db_get_static(hContact, m_szModuleName, "e-mail", tEmail, sizeof(tEmail)); if (Lists_IsInList(LIST_BL, tEmail)) diff --git a/protocols/MSN/src/msn_misc.cpp b/protocols/MSN/src/msn_misc.cpp index 2b75d3d88e..1b2d956dc3 100644 --- a/protocols/MSN/src/msn_misc.cpp +++ b/protocols/MSN/src/msn_misc.cpp @@ -399,14 +399,25 @@ void CMsnProto::MSN_GoOffline(void) ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)msnOldStatus, ID_STATUS_OFFLINE); isIdle = false; - int count = -1; - for (;;) { - MsnContact *msc = Lists_GetNext(count); - if (msc == NULL) break; - - if (ID_STATUS_OFFLINE != getWord(msc->hContact, "Status", ID_STATUS_OFFLINE)) { - setWord(msc->hContact, "Status", ID_STATUS_OFFLINE); - setDword(msc->hContact, "IdleTS", 0); + MCONTACT hContact = NULL; + + for (hContact = db_find_first(m_szModuleName); hContact; + hContact = db_find_next(hContact, m_szModuleName)) + { + if (isChatRoom(hContact) != 0) { + DBVARIANT dbv; + if (getTString(hContact, "ChatRoomID", &dbv) == 0) { + GCDEST gcd = { m_szModuleName, dbv.ptszVal, GC_EVENT_CONTROL }; + GCEVENT gce = { sizeof(gce), &gcd }; + CallServiceSync(MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gce); + db_free(&dbv); + } + } + else { + if (ID_STATUS_OFFLINE != getWord(hContact, "Status", ID_STATUS_OFFLINE)) { + setWord(hContact, "Status", ID_STATUS_OFFLINE); + setDword(hContact, "IdleTS", 0); + } } } } @@ -417,12 +428,10 @@ void CMsnProto::MSN_GoOffline(void) int ThreadData::sendMessage(int msgType, const char* email, int netId, const char* parMsg, int parFlags) { - char buf[2048]; - int off; - - off = mir_snprintf(buf, SIZEOF(buf), "MIME-Version: 1.0\r\n"); + CMStringA buf; if ((parFlags & MSG_DISABLE_HDR) == 0) { + /* char tFontName[100], tFontStyle[3]; DWORD tFontColor; @@ -459,18 +468,55 @@ int ThreadData::sendMessage(int msgType, const char* email, int netId, const cha if (parFlags & MSG_OFFLINE) off += mir_snprintf((buf + off), (SIZEOF(buf) - off), "Dest-Agent: client\r\n"); - off += mir_snprintf((buf + off), (SIZEOF(buf) - off), "Content-Type: text/plain; charset=UTF-8\r\n"); - off += mir_snprintf((buf + off), (SIZEOF(buf) - off), "X-MMS-IM-Format: FN=%s; EF=%s; CO=%x; CS=0; PF=31%s\r\n\r\n", + buf.AppendFormat("X-MMS-IM-Format: FN=%s; EF=%s; CO=%x; CS=0; PF=31%s\r\n\r\n", tFontName, tFontStyle, tFontColor, (parFlags & MSG_RTL) ? ";RL=1" : ""); + */ + char *pszNick=proto->MyOptions.szEmail; + DBVARIANT dbv; + time_t cur_time; + + /* FIXME: Use a real message ID and save it, not just this random UUID */ + unsigned __int64 msgid; + time(&cur_time); + msgid = ((unsigned __int64)cur_time<<32)|GetTickCount(); + + if (!proto->getString("Nick", &dbv)) + pszNick = dbv.pszVal; + + buf.AppendFormat( + "Messaging: 2.0\r\n" + "Client-Message-ID: %llu\r\n" + "Message-Type: Text\r\n" + "IM-Display-Name: %s\r\n" + "Content-Type: Text/plain; charset=UTF-8\r\n" + "Content-Length: %d\r\n\r\n%s", + msgid, + pszNick, + strlen(parMsg), parMsg); + + if (pszNick!=proto->MyOptions.szEmail) db_free(&dbv); + parMsg = buf; } - int seq; + // TODO: Handle msgType! + + int seq = sendPacketPayload("SDG", "MSGR", + "Routing: 1.0\r\n" + "To: %d:%s\r\n" + "From: %d:%s;epid=%s\r\n\r\n" + "Reliability: 1.0\r\n\r\n%s", + netId, email, + netId == NETID_SKYPE?netId:proto->GetMyNetID(), proto->GetMyUsername(netId), proto->MyOptions.szMachineGuid, + parMsg); + + /* if (netId == NETID_YAHOO || netId == NETID_MOB || (parFlags & MSG_OFFLINE)) seq = sendPacket("UUM", "%s %d %c %d\r\n%s%s", email, netId, msgType, strlen(parMsg) + off, buf, parMsg); else seq = sendPacket("MSG", "%c %d\r\n%s%s", msgType, strlen(parMsg) + off, buf, parMsg); + */ return seq; } @@ -491,7 +537,7 @@ void ThreadData::sendCaps(void) void ThreadData::sendTerminate(void) { if (!termPending) { - sendPacket("OUT", NULL); + sendPacket("OUT", "CON 0"); termPending = true; } } @@ -521,46 +567,22 @@ int ThreadData::sendRawMessage(int msgType, const char* data, int datLen) // Typing notifications support -void CMsnProto::MSN_SendTyping(ThreadData* info, const char* email, int netId) +void CMsnProto::MSN_SendTyping(ThreadData* info, const char* email, int netId, bool bTyping) { char tCommand[1024]; mir_snprintf(tCommand, SIZEOF(tCommand), - "Content-Type: text/x-msmsgscontrol\r\n" - "TypingUser: %s\r\n\r\n\r\n", MyOptions.szEmail); + "Messaging: 2.0\r\n" + "Message-Type: %s\r\n" + "Content-Type: Application/Message\r\n" + "Content-Length: 0\r\n", + bTyping?"Control/Typing":"Control/ClearTyping"); info->sendMessage(netId == NETID_MSN ? 'U' : '2', email, netId, tCommand, MSG_DISABLE_HDR); } - -static ThreadData* FindThreadTimer(UINT timerId) -{ - ThreadData* res = NULL; - for (int i = 0; i < g_Instances.getCount() && res == NULL; ++i) - res = g_Instances[i].MSN_GetThreadByTimer(timerId); - - return res; -} - -static VOID CALLBACK TypingTimerProc(HWND, UINT, UINT_PTR idEvent, DWORD) -{ - ThreadData* T = FindThreadTimer(idEvent); - if (T != NULL) - T->proto->MSN_SendTyping(T, NULL, 1); - else - KillTimer(NULL, idEvent); -} - - -void CMsnProto::MSN_StartStopTyping(ThreadData* info, bool start) +void CMsnProto::MSN_StartStopTyping(GCThreadData* info, bool start) { - if (start && info->mTimerId == 0) { - info->mTimerId = SetTimer(NULL, 0, 5000, TypingTimerProc); - MSN_SendTyping(info, NULL, 1); - } - else if (!start && info->mTimerId != 0) { - KillTimer(NULL, info->mTimerId); - info->mTimerId = 0; - } + MSN_SendTyping(msnNsThread, info->szEmail, info->netId, start); } @@ -581,6 +603,9 @@ void CMsnProto::MSN_SendStatusMessage(const char* msg) if (!msnLoggedIn) return; + /* FIXME: Currently not implemented, shuold be set on status change anyway */ + return; + char* msgEnc = HtmlEncode(msg ? msg : ""); size_t sz; @@ -690,6 +715,37 @@ int ThreadData::sendPacket(const char* cmd, const char* fmt, ...) return (result > 0) ? thisTrid : -1; } +int ThreadData::sendPacketPayload(const char* cmd, const char *param, const char* fmt, ...) +{ + int thisTrid = 0; + + if (this == NULL) return 0; + + size_t strsize = 512; + char* str = (char*)mir_alloc(strsize); + + va_list vararg; + va_start(vararg, fmt); + + thisTrid = InterlockedIncrement(&mTrid); + int regSz = proto->msnRegistration?strlen(proto->msnRegistration)+16:0; + int paramStart = mir_snprintf(str, strsize, "%s %d %s ", cmd, thisTrid, param), strszstart = 0, strSz; + while ((strSz = mir_vsnprintf(str + paramStart, strsize - paramStart - regSz - 10, fmt, vararg)) == -1) + str = (char*)mir_realloc(str, strsize += 512); + if (strSz) strSz+=2; + paramStart+=mir_snprintf(str+paramStart, strsize - paramStart , "%d\r\n", strSz+regSz); + if (proto->msnRegistration) paramStart+=mir_snprintf(str+paramStart, strsize - paramStart, "Registration: %s\r\n", proto->msnRegistration); + if (strSz) paramStart+=mir_snprintf(str+paramStart, strsize - paramStart, "\r\n"); + mir_vsnprintf(str + paramStart, strsize - paramStart, fmt, vararg); + str[strsize - 3] = 0; + va_end(vararg); + + int result = send(str, strlen(str)); + mir_free(str); + return (result > 0) ? thisTrid : -1; +} + + ///////////////////////////////////////////////////////////////////////////////////////// // MSN_SetServerStatus - changes plugins status at the server @@ -717,7 +773,8 @@ void CMsnProto::MSN_SetServerStatus(int newStatus) unsigned myFlagsEx = capex_SupportsPeerToPeerV2; - char szMsg[256]; + char szMsg[2048]; + /* if (m_iStatus < ID_STATUS_ONLINE) { int sz = mir_snprintf(szMsg, SIZEOF(szMsg), "%u:%u", myFlags, myFlagsEx); @@ -732,6 +789,7 @@ void CMsnProto::MSN_SetServerStatus(int newStatus) db_free(&dbv); } } + */ char *szPlace; DBVARIANT dbv; @@ -744,6 +802,45 @@ void CMsnProto::MSN_SetServerStatus(int newStatus) szPlace = mir_utf8encodeT(buf); } + char** msgptr = GetStatusMsgLoc(newStatus); + /* FIXME: This is what Skype client sends + myFlags = 0; + myFlagsEx = cap_SupportsSDrive | cap_SupportsActivities; + */ + int sz = mir_snprintf(szMsg, SIZEOF(szMsg), + "" + "%s110:0" + "%s" + "%u:%u" + "%s11" + "%s%s" + "24true" + "", + MyOptions.szMachineGuid, msnProductVer, + szStatusName, + MyOptions.szMachineGuid, myFlags, myFlagsEx, + MyOptions.szMachineGuid, szPlace, + msgptr?ptrA(HtmlEncode(*msgptr)):"", GetMyUsername(NETID_SKYPE), + MyOptions.szMachineGuid, + MyOptions.szMachineGuid); + msnNsThread->sendPacketPayload("PUT", "MSGR\\PRESENCE", + "Routing: 1.0\r\n" + "To: %d:%s\r\n" + "From: %d:%s;epid=%s\r\n\r\n" + "Reliability: 1.0\r\n\r\n" + "Publication: 1.0\r\n" + "Uri: /user\r\n" + "Content-Type: application/user+xml\r\n" + "Status-Priority: low\r\n" + "Content-Length: %d\r\n\r\n%s", + GetMyNetID(), MyOptions.szEmail, + GetMyNetID(), MyOptions.szEmail, + MyOptions.szMachineGuid, + sz, szMsg); + + + // TODO: Send, MSN_SendStatusMessage anpassen. + /* int sz = mir_snprintf(szMsg, SIZEOF(szMsg), "" "%s" @@ -753,8 +850,10 @@ void CMsnProto::MSN_SetServerStatus(int newStatus) "", szPlace, newStatus == ID_STATUS_IDLE ? "true" : "false", szStatusName); msnNsThread->sendPacket("UUX", "%d\r\n%s", sz, szMsg); + */ mir_free(szPlace); + /* if (newStatus != ID_STATUS_IDLE) { char** msgptr = GetStatusMsgLoc(newStatus); if (msgptr != NULL) @@ -762,9 +861,38 @@ void CMsnProto::MSN_SetServerStatus(int newStatus) } msnNsThread->sendPacket("CHG", "%s %u:%u %s", szStatusName, myFlags, myFlagsEx, msnObject.pszVal ? msnObject.pszVal : "0"); + */ db_free(&msnObject); } - else msnNsThread->sendPacket("CHG", szStatusName); + //else msnNsThread->sendPacket("CHG", szStatusName); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// MSN_FetchRecentMessages - fetches missed offline messages + +void CMsnProto::MSN_FetchRecentMessages(time_t since) +{ + if (!since) { + /* Assuming that you want all messages that were sent after the last + * user conversation according to DB + */ + MCONTACT hContact; + MEVENT hDbEvent; + for (hContact = db_find_first(m_szModuleName); hContact; + hContact = db_find_next(hContact, m_szModuleName)) + { + if (!(hDbEvent = db_event_last(hContact))) + continue; + + DBEVENTINFO dbei = { sizeof(dbei) }; + db_event_get(hDbEvent, &dbei); + if (dbei.timestamp>since) since=dbei.timestamp; + } + } + + msnNsThread->sendPacketPayload("GET", "MSGR\\RECENTCONVERSATIONS", + "%llu100", + ((unsigned __int64)since)*1000); } @@ -959,8 +1087,7 @@ void CMsnProto::MSN_ShowPopup(const TCHAR* nickname, const TCHAR* msg, int flags void CMsnProto::MSN_ShowPopup(const MCONTACT hContact, const TCHAR* msg, int flags) { - const TCHAR* nickname = hContact ? GetContactNameT(hContact) : _T("Me"); - MSN_ShowPopup(nickname, msg, flags, NULL); + MSN_ShowPopup(GetContactNameT(hContact), msg, flags, NULL); } @@ -1195,7 +1322,8 @@ bool CMsnProto::MSN_IsMeByContact(MCONTACT hContact, char* szEmail) char *emailPtr = szEmail ? szEmail : tEmail; *emailPtr = 0; - if (db_get_static(hContact, m_szModuleName, "e-mail", emailPtr, sizeof(tEmail))) + if (db_get_static(hContact, m_szModuleName, "wlid", emailPtr, sizeof(tEmail)) && + db_get_static(hContact, m_szModuleName, "e-mail", emailPtr, sizeof(tEmail))) return false; return _stricmp(emailPtr, MyOptions.szEmail) == 0; diff --git a/protocols/MSN/src/msn_opts.cpp b/protocols/MSN/src/msn_opts.cpp index a05c96c4d7..6d9f338973 100644 --- a/protocols/MSN/src/msn_opts.cpp +++ b/protocols/MSN/src/msn_opts.cpp @@ -596,7 +596,8 @@ INT_PTR CALLBACK DlgDeleteContactUI(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA DeleteParam *param = (DeleteParam*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); char szEmail[MSN_MAX_EMAIL_LEN]; - if (!db_get_static(param->hContact, param->proto->m_szModuleName, "e-mail", szEmail, sizeof(szEmail))) { + if (!db_get_static(param->hContact, param->proto->m_szModuleName, "wlid", szEmail, sizeof(szEmail)) || + !db_get_static(param->hContact, param->proto->m_szModuleName, "e-mail", szEmail, sizeof(szEmail))) { param->proto->MSN_AddUser(param->hContact, szEmail, 0, LIST_FL | (isHot ? LIST_REMOVE : LIST_REMOVENH)); if (isBlock) { param->proto->MSN_AddUser(param->hContact, szEmail, 0, LIST_AL | LIST_REMOVE); diff --git a/protocols/MSN/src/msn_proto.cpp b/protocols/MSN/src/msn_proto.cpp index ab752712b4..d13086edd7 100644 --- a/protocols/MSN/src/msn_proto.cpp +++ b/protocols/MSN/src/msn_proto.cpp @@ -38,6 +38,7 @@ CMsnProto::CMsnProto(const char* aProtoName, const TCHAR* aUserName) : m_arContacts(10, CompareLists), m_arGroups(10, CompareId), m_arThreads(10, PtrKeySortT), + m_arGCThreads(10, PtrKeySortT), m_arSessions(10, PtrKeySortT), m_arDirect(10, PtrKeySortT), lsMessageQueue(1), @@ -140,6 +141,8 @@ CMsnProto::CMsnProto(const char* aProtoName, const TCHAR* aUserName) : mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s plugin connections"), m_tszUserName); m_hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); + + m_DisplayNameCache = NULL; } CMsnProto::~CMsnProto() @@ -168,10 +171,12 @@ CMsnProto::~CMsnProto() mir_free(msnLastStatusMsg); mir_free(msnPreviousUUX); mir_free(msnExternalIP); + mir_free(msnRegistration); mir_free(abCacheKey); mir_free(sharingCacheKey); mir_free(storageCacheKey); + mir_free(m_DisplayNameCache); FreeAuthTokens(); } @@ -280,12 +285,13 @@ int __cdecl CMsnProto::AuthRequest(MCONTACT hContact, const TCHAR* szMessage) { if (msnLoggedIn) { char email[MSN_MAX_EMAIL_LEN]; - if (db_get_static(hContact, m_szModuleName, "e-mail", email, sizeof(email))) + if (db_get_static(hContact, m_szModuleName, "wlid", email, sizeof(email)) && + db_get_static(hContact, m_szModuleName, "e-mail", email, sizeof(email))) return 1; char* szMsg = mir_utf8encodeT(szMessage); - int netId = strncmp(email, "tel:", 4) == 0 ? NETID_MOB : NETID_MSN; + int netId = strncmp(email, "tel:", 4) == 0 ? NETID_MOB : (strncmp(email, "live:", 5) == 0 ? NETID_SKYPE : NETID_MSN); if (MSN_AddUser(hContact, email, netId, LIST_FL, szMsg)) { MSN_AddUser(hContact, email, netId, LIST_PL + LIST_REMOVE); MSN_AddUser(hContact, email, netId, LIST_BL + LIST_REMOVE); @@ -653,7 +659,7 @@ DWORD_PTR __cdecl CMsnProto::GetCaps(int type, MCONTACT) return (UINT_PTR)Translate("Live ID"); case PFLAG_UNIQUEIDSETTING: - return (UINT_PTR)"e-mail"; + return (UINT_PTR)"wlid"; case PFLAG_MAXLENOFMESSAGE: return 1202; @@ -668,7 +674,8 @@ DWORD_PTR __cdecl CMsnProto::GetCaps(int type, MCONTACT) int __cdecl CMsnProto::RecvMsg(MCONTACT hContact, PROTORECVEVENT* pre) { char tEmail[MSN_MAX_EMAIL_LEN]; - db_get_static(hContact, m_szModuleName, "e-mail", tEmail, sizeof(tEmail)); + if (db_get_static(hContact, m_szModuleName, "wlid", tEmail, sizeof(tEmail))) + db_get_static(hContact, m_szModuleName, "e-mail", tEmail, sizeof(tEmail)); if (Lists_IsInList(LIST_FL, tEmail)) db_unset(hContact, "CList", "Hidden"); @@ -824,7 +831,10 @@ int __cdecl CMsnProto::SendMsg(MCONTACT hContact, int flags, const char* pszSrc) else { const char msgType = MyOptions.SlowSend ? 'A' : 'N'; bool isOffline; - ThreadData* thread = MSN_StartSB(tEmail, isOffline); + ThreadData* thread; // = MSN_StartSB(tEmail, isOffline); + /* MSNP24 doesn't have a switchboard anymore */ + thread = NULL; isOffline = true; + if (thread == NULL) { if (isOffline) { if (netId != NETID_LCS) { @@ -948,6 +958,7 @@ int __cdecl CMsnProto::UserIsTyping(MCONTACT hContact, int type) bool typing = type == PROTOTYPE_SELFTYPING_ON; int netId = Lists_GetNetId(tEmail); + /* switch (netId) { case NETID_UNKNOWN: case NETID_MSN: @@ -971,6 +982,9 @@ int __cdecl CMsnProto::UserIsTyping(MCONTACT hContact, int type) default: break; } + */ + if (getWord(hContact, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) + MSN_SendTyping(msnNsThread, tEmail, netId, typing); return 0; } diff --git a/protocols/MSN/src/msn_proto.h b/protocols/MSN/src/msn_proto.h index d8d26d3344..89791e86de 100644 --- a/protocols/MSN/src/msn_proto.h +++ b/protocols/MSN/src/msn_proto.h @@ -110,6 +110,10 @@ struct CMsnProto : public PROTO char *authContactToken; char *authStorageToken; char *hotSecretToken, *hotAuthToken; + char *authUser, *authUIC, *authCookies, *authSSLToken, *authAccessToken; + int authMethod; + time_t authTokenExpiretime; + bool bSentBND; char *abCacheKey, *sharingCacheKey, *storageCacheKey; @@ -120,6 +124,7 @@ struct CMsnProto : public PROTO mir_cs m_csThreads; OBJLIST m_arThreads; + LIST m_arGCThreads; mir_cs m_csSessions; OBJLIST m_arSessions; @@ -149,6 +154,7 @@ struct CMsnProto : public PROTO bool usingGateway; char* msnExternalIP; + char* msnRegistration; char* msnPreviousUUX; char* msnLastStatusMsg; @@ -191,15 +197,17 @@ struct CMsnProto : public PROTO void MSN_SendStatusMessage(const char* msg); void MSN_SetServerStatus(int newStatus); - void MSN_StartStopTyping(ThreadData* info, bool start); - void MSN_SendTyping(ThreadData* info, const char* email, int netId ); + void MSN_FetchRecentMessages(time_t since = 0); + void MSN_StartStopTyping(GCThreadData* info, bool start); + void MSN_SendTyping(ThreadData* info, const char* email, int netId, bool bTyping ); void MSN_InitSB(ThreadData* info, const char* szEmail); void MSN_ReceiveMessage(ThreadData* info, char* cmdString, char* params); int MSN_HandleCommands(ThreadData* info, char* cmdString); int MSN_HandleErrors(ThreadData* info, char* cmdString); void MSN_ProcessNotificationMessage(char* buf, unsigned len); - void MSN_ProcessStatusMessage(char* buf, unsigned len, const char* wlid); + void MSN_ProcessStatusMessage(ezxml_t xmli, const char* wlid); + void MSN_ProcessNLN(const char *userStatus, const char *wlid, char *userNick, const char *objid, char *cmdstring); void MSN_ProcessPage(char* buf, unsigned len); void MSN_ProcessRemove(char* buf, size_t len); void MSN_ProcessAdd(char* buf, size_t len); @@ -280,14 +288,13 @@ struct CMsnProto : public PROTO int MSN_GetActiveThreads(ThreadData**); ThreadData* MSN_GetThreadByConnection(HANDLE hConn); ThreadData* MSN_GetThreadByContact(const char* wlid, TInfoType type = SERVER_SWITCHBOARD); - ThreadData* MSN_GetThreadByChatId(const TCHAR* chatId); + GCThreadData*MSN_GetThreadByChatId(const TCHAR* chatId); ThreadData* MSN_GetP2PThreadByContact(const char *wlid); void MSN_StartP2PTransferByContact(const char* wlid); ThreadData* MSN_GetThreadByPort(WORD wPort); ThreadData* MSN_GetUnconnectedThread(const char* wlid, TInfoType type = SERVER_SWITCHBOARD); ThreadData* MSN_GetOtherContactThread(ThreadData* thread); - ThreadData* MSN_GetThreadByTimer(UINT timerId); - + ThreadData* MSN_StartSB(const char* uid, bool& isOffline); void __cdecl ThreadStub(void* arg); @@ -395,9 +402,15 @@ struct CMsnProto : public PROTO ///////////////////////////////////////////////////////////////////////////////////////// // MSN Chat support - int MSN_ChatInit(ThreadData* info); - void MSN_ChatStart(ThreadData* info); + int MSN_ChatInit(GCThreadData *info, const char *pszID, const char *pszTopic); + void MSN_ChatStart(ezxml_t xmli); void MSN_KillChatSession(const TCHAR* id); + void MSN_Kickuser(GCHOOK *gch); + void MSN_Promoteuser(GCHOOK *gch, const char *pszRole); + const TCHAR *MSN_GCGetRole(GCThreadData* thread, const char *pszWLID); + void MSN_GCProcessThreadActivity(ezxml_t xmli, const TCHAR *mChatID); + void MSN_GCAddMessage(TCHAR *mChatID, MCONTACT hContact, char *email, time_t ts, bool sentMsg, char *msgBody); + void MSN_GCRefreshThreadsInfo(void); MCONTACT MSN_GetChatInernalHandle(MCONTACT hContact); @@ -437,6 +450,7 @@ struct CMsnProto : public PROTO void MSN_AddAuthRequest(const char *email, const char *nick, const char *reason); void MSN_SetContactDb(MCONTACT hContact, const char *szEmail); MCONTACT MSN_HContactFromEmail(const char* msnEmail, const char* msnNick = NULL, bool addIfNeeded = false, bool temporary = false); + MCONTACT MSN_HContactFromChatID(const char* wlid); MCONTACT AddToListByEmail(const char *email, const char *nick, DWORD flags); ///////////////////////////////////////////////////////////////////////////////////////// @@ -460,9 +474,18 @@ struct CMsnProto : public PROTO // MSN Authentication int MSN_GetPassportAuth(void); - char* GenerateLoginBlob(char* challenge); + int MSN_SkypeAuth(const char *pszNonce, char *pszUIC); + int MSN_DoOAuth(void); + char* GenerateLoginBlob(char* challenge); + void LoadAuthTokensDB(void); + void SaveAuthTokensDB(void); + int LoginSkypeOAuth(const char *pRefreshToken); + bool RefreshOAuth(const char *pszRefreshToken, const char *pszService, char *pszAccessToken, char *pszOutRefreshToken=NULL, time_t *ptExpires=NULL); + int MSN_AuthOAuth(void); CMStringA HotmailLogin(const char* url); void FreeAuthTokens(void); + int GetMyNetID(void); + const char *GetMyUsername(int netId); ///////////////////////////////////////////////////////////////////////////////////////// // MSN avatars support @@ -508,6 +531,7 @@ struct CMsnProto : public PROTO bool MSN_ABAddRemoveContact(const char* szCntId, int netId, bool add, bool allowRecurse = true); unsigned MSN_ABContactAdd(const char* szEmail, const char* szNick, int netId, const char* szInvite, bool search, bool retry = false, bool allowRecurse = true); void MSN_ABUpdateDynamicItem(bool allowRecurse = true); + bool MSN_ABRefreshClist(void); ezxml_t abSoapHdr(const char* service, const char* scenario, ezxml_t& tbdy, char*& httphdr); char* GetABHost(const char* service, bool isSharing); @@ -545,6 +569,7 @@ struct CMsnProto : public PROTO ////////////////////////////////////////////////////////////////////////////////////// + TCHAR *m_DisplayNameCache; TCHAR* GetContactNameT(MCONTACT hContact); int getStringUtf(MCONTACT hContact, const char* name, DBVARIANT* result); diff --git a/protocols/MSN/src/msn_soapab.cpp b/protocols/MSN/src/msn_soapab.cpp index 94764b2f9e..d32301b291 100644 --- a/protocols/MSN/src/msn_soapab.cpp +++ b/protocols/MSN/src/msn_soapab.cpp @@ -37,7 +37,7 @@ ezxml_t CMsnProto::abSoapHdr(const char* service, const char* scenario, ezxml_t& ezxml_t node = ezxml_add_child(apphdr, "ApplicationId", 0); ezxml_set_txt(node, msnAppID); node = ezxml_add_child(apphdr, "IsMigration", 0); - ezxml_set_txt(node, abchMigrated ? "false" : "true"); + ezxml_set_txt(node, "false"); // abchMigrated ? "false" : "true"); node = ezxml_add_child(apphdr, "PartnerScenario", 0); ezxml_set_txt(node, scenario); @@ -828,6 +828,16 @@ bool CMsnProto::MSN_ABFind(const char* szMethod, const char* szGuid, bool deltas anot = ezxml_next(anot); } + + ezxml_t nil = ezxml_get(contInf, "NetworkInfoList", 0, "NetworkInfo", -1); + while (nil != NULL) { + if (strcmp(ezxml_txt(ezxml_child(nil, "SourceId")), "SKYPE") == 0) { + const char *pszPartner = ezxml_txt(ezxml_child(nil, "DomainTag")); + if (*pszPartner) setString("SkypePartner", pszPartner); + } + nil = ezxml_next(nil); + } + } } if (!msnLoggedIn && msnNsThread) { @@ -859,6 +869,107 @@ bool CMsnProto::MSN_ABFind(const char* szMethod, const char* szGuid, bool deltas return status == 200; } +bool CMsnProto::MSN_ABRefreshClist(void) +{ + NETLIBHTTPREQUEST nlhr = { 0 }; + NETLIBHTTPREQUEST *nlhrReply; + NETLIBHTTPHEADER headers[2]; + bool bRet = false; + + if (!authCookies) return false; + + // initialize the netlib request + nlhr.cbSize = sizeof(nlhr); + nlhr.requestType = REQUEST_GET; + nlhr.flags = NLHRF_HTTP11 | NLHRF_DUMPASTEXT | NLHRF_PERSISTENT | NLHRF_REDIRECT; + nlhr.nlc = hHttpsConnection; + nlhr.headersCount = 2; + nlhr.headers = headers; + nlhr.headers[0].szName = "User-Agent"; + nlhr.headers[0].szValue = (char*)MSN_USER_AGENT; + nlhr.headers[1].szName = "Cookie"; + nlhr.headers[1].szValue = authCookies; + nlhr.szUrl = "https://people.directory.live.com/people/abcore?SerializeAs=xml&market=en-gb&appid=3C48F07D-DE71-490C-B263-A78CFB1CA351&version=W5.M2"; + + // Query addressbook + mHttpsTS = clock(); + nlhrReply = (NETLIBHTTPREQUEST*)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)hNetlibUserHttps, (LPARAM)&nlhr); + mHttpsTS = clock(); + if (nlhrReply) { + hHttpsConnection = nlhrReply->nlc; + if (nlhrReply->resultCode == 200 && nlhrReply->pData) { + ezxml_t xmlm = ezxml_parse_str(nlhrReply->pData, strlen(nlhrReply->pData)); + + if (xmlm) { + bRet = true; + ezxml_t abinf = ezxml_child(xmlm, "ab"); + + for (ezxml_t pers = ezxml_get(abinf, "persons", 0, "Person", -1); pers != NULL; pers = ezxml_next(pers)) { + const char *cid = ezxml_txt(ezxml_child(pers, "cid")); + if (mycid && !strcmp(cid, mycid)) continue; + + for (ezxml_t cont = ezxml_get(pers, "contacts", 0, "Contact", -1); cont != NULL; cont = ezxml_next(cont)) { + int netId = NETID_UNKNOWN; + const char* szEmail; + + const char *src = ezxml_txt(ezxml_child(cont, "sourceId")); + if (!strcmp(src, "WL")) { + netId = NETID_MSN; + szEmail = ezxml_txt(ezxml_child(cont, "domainTag")); + } else if (!strcmp(src, "SKYPE")) { + netId = NETID_SKYPE; + szEmail = ezxml_txt(ezxml_child(cont, "objectId")); + } + + if (netId == NETID_UNKNOWN || szEmail[0] == 0) + continue; + + ezxml_t xmlnick = ezxml_child(pers, "nickname"); + const char *pszNickname = xmlnick?xmlnick->txt:NULL; + Lists_Add(LIST_FL, netId, szEmail, NULL, pszNickname); + char szWLId[128]; + mir_snprintf(szWLId, sizeof(szWLId), "%d:%s", netId, szEmail); + + MCONTACT hContact = MSN_HContactFromEmail(szWLId, pszNickname, true, false); + if (!hContact) continue; + + const char* szNick = ezxml_txt(ezxml_child(pers, "orderedName")); + if (*szNick) db_set_utf(hContact, "CList", "MyHandle", szNick); + else db_unset(hContact, "CList", "MyHandle"); + setString(hContact, "ID", ezxml_txt(ezxml_child(pers, "id"))); + SetAbParam(hContact, "CID", cid); + + const char *szTmp = ezxml_txt(ezxml_child(pers, "firstName")); + SetAbParam(hContact, "FirstName", szTmp); + + szTmp = ezxml_txt(ezxml_child(pers, "lastName")); + SetAbParam(hContact, "LastName", szTmp); + + szTmp = ezxml_txt(ezxml_child(pers, "birthday")); + char *szPtr; + if (strtol(szTmp, &szPtr, 10) > 1) { + setWord(hContact, "BirthYear", (WORD)strtol(szTmp, &szPtr, 10)); + setByte(hContact, "BirthMonth", (BYTE)strtol(szPtr + 1, &szPtr, 10)); + setByte(hContact, "BirthDay", (BYTE)strtol(szPtr + 1, &szPtr, 10)); + } + + setByte("MobileEnabled", atoi(ezxml_txt(ezxml_child(cont, "phonesIMEnabled")))); + } + } + ezxml_free(xmlm); + } + } + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)nlhrReply); + } else hHttpsConnection = NULL; + return bRet; +} + +/* +bool MSN_ABSearchSkypeDirectory() +{ + //authAccessToken +} +*/ // "ABGroupContactAdd" : "ABGroupContactDelete", "ABGroupDelete", "ABContactDelete" bool CMsnProto::MSN_ABAddDelContactGroup(const char* szCntId, const char* szGrpId, const char* szMethod, bool allowRecurse) diff --git a/protocols/MSN/src/msn_soapstore.cpp b/protocols/MSN/src/msn_soapstore.cpp index ccec429d07..27393513a6 100644 --- a/protocols/MSN/src/msn_soapstore.cpp +++ b/protocols/MSN/src/msn_soapstore.cpp @@ -74,7 +74,7 @@ char* CMsnProto::GetStoreHost(const char* service) mir_snprintf(hostname, SIZEOF(hostname), "StoreHost-%s", service); char* host = (char*)mir_alloc(256); - if (db_get_static(NULL, m_szModuleName, hostname, host, 256)) + if (db_get_static(NULL, m_szModuleName, hostname, host, 256) || !*host) strcpy(host, "https://tkrdr.storage.msn.com/storageservice/SchematizedStore.asmx"); return host; diff --git a/protocols/MSN/src/msn_std.cpp b/protocols/MSN/src/msn_std.cpp index c0dfa87833..8950d469cb 100644 --- a/protocols/MSN/src/msn_std.cpp +++ b/protocols/MSN/src/msn_std.cpp @@ -52,8 +52,10 @@ TCHAR* CMsnProto::GetContactNameT(MCONTACT hContact) ci.cbSize = sizeof(ci); ci.dwFlag = CNF_DISPLAY | CNF_TCHAR; ci.szProto = m_szModuleName; - if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci)) - return (TCHAR*)ci.pszVal; + if (!CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci)) { + if (m_DisplayNameCache) mir_free(m_DisplayNameCache); + return (TCHAR*)m_DisplayNameCache = ci.pszVal; + } else return _T("Me"); } diff --git a/protocols/MSN/src/msn_svcs.cpp b/protocols/MSN/src/msn_svcs.cpp index 61161e0ea2..864a3770d1 100644 --- a/protocols/MSN/src/msn_svcs.cpp +++ b/protocols/MSN/src/msn_svcs.cpp @@ -71,8 +71,10 @@ INT_PTR CMsnProto::GetAvatarInfo(WPARAM wParam, LPARAM lParam) cont = Lists_Get(AI->hContact); if (cont == NULL) return GAIR_NOAVATAR; + /* if ((cont->cap1 & 0xf0000000) == 0) return GAIR_NOAVATAR; + */ } if (AI->hContact == NULL || _stricmp(cont->email, MyOptions.szEmail) == 0) { @@ -454,7 +456,8 @@ int CMsnProto::OnDbSettingChanged(WPARAM hContact, LPARAM lParam) if (!strcmp(cws->szSetting, "ApparentMode")) { char tEmail[MSN_MAX_EMAIL_LEN]; - if (!db_get_static(hContact, m_szModuleName, "e-mail", tEmail, sizeof(tEmail))) { + if (!db_get_static(hContact, m_szModuleName, "wlid", tEmail, sizeof(tEmail)) || + !db_get_static(hContact, m_szModuleName, "e-mail", tEmail, sizeof(tEmail))) { bool isBlocked = Lists_IsInList(LIST_BL, tEmail); if (isBlocked && (cws->value.type == DBVT_DELETED || cws->value.wVal == 0)) { diff --git a/protocols/MSN/src/msn_threads.cpp b/protocols/MSN/src/msn_threads.cpp index 5878171337..34c446d3e6 100644 --- a/protocols/MSN/src/msn_threads.cpp +++ b/protocols/MSN/src/msn_threads.cpp @@ -42,7 +42,7 @@ void __cdecl CMsnProto::msn_keepAliveThread(void*) msnPingTimeout = 45; else { msnPingTimeout = 20; - keepFlag = keepFlag && msnNsThread->send("PNG\r\n", 5); + keepFlag = keepFlag && msnNsThread->sendPacket("PNG", "CON 0"); } p2p_clearDormantSessions(); if (hHttpsConnection && (clock() - mHttpsTS) > 60 * CLOCKS_PER_SEC) { @@ -72,6 +72,18 @@ void __cdecl CMsnProto::msn_keepAliveThread(void*) ///////////////////////////////////////////////////////////////////////////////////////// // MSN server thread - read and process commands from a server +static bool ReallocInfoBuffer(ThreadData *info, int mDataSize) +{ + char *mData = (char*)mir_realloc(info->mData, mDataSize+1); + if (mData) { + info->mData = mData; + info->mDataSize = mDataSize; + ZeroMemory(&mData[info->mBytesInData], info->mDataSize-info->mBytesInData+1); + return true; + } + return false; +} + void __cdecl CMsnProto::MSNServerThread(void* arg) { @@ -120,6 +132,7 @@ void __cdecl CMsnProto::MSNServerThread(void* arg) tConn.wPort = MSN_DEFAULT_GATEWAY_PORT; } else { + tConn.flags = NLOCF_SSL; tConn.szHost = info->mServer; tConn.wPort = MSN_DEFAULT_PORT; } @@ -149,9 +162,9 @@ void __cdecl CMsnProto::MSNServerThread(void* arg) debugLogA("Connected with handle=%08X", info->s); - if (info->mType == SERVER_NOTIFICATION) { - info->sendPacket("VER", "MSNP18 MSNP17 CVR0"); - } + if (info->mType == SERVER_NOTIFICATION) + info->sendPacketPayload("CNT", "CON", "%s%s%s2winnt5.2x86en-us\r\n", + *info->mState?"":"", *info->mState?info->mState:"", *info->mState?"":""); else if (info->mType == SERVER_SWITCHBOARD) { info->sendPacket(info->mCaller ? "USR" : "ANS", "%s;%s %s", MyOptions.szEmail, MyOptions.szMachineGuid, info->mCookie); } @@ -166,7 +179,7 @@ void __cdecl CMsnProto::MSNServerThread(void* arg) debugLogA("Entering main recv loop"); info->mBytesInData = 0; for (;;) { - int recvResult = info->recv(info->mData + info->mBytesInData, sizeof(info->mData) - info->mBytesInData); + int recvResult = info->recv(info->mData + info->mBytesInData, info->mDataSize - info->mBytesInData); if (recvResult == SOCKET_ERROR) { debugLogA("Connection %08p [%08X] was abortively closed", info->s, GetCurrentThreadId()); break; @@ -192,7 +205,7 @@ void __cdecl CMsnProto::MSNServerThread(void* arg) if (info->mBytesInData < peol - info->mData + 2) break; //wait for full line end - char msg[sizeof(info->mData)]; + char msg[1024]; memcpy(msg, info->mData, peol - info->mData); msg[peol - info->mData] = 0; if (*++peol != '\n') @@ -227,9 +240,11 @@ void __cdecl CMsnProto::MSNServerThread(void* arg) } } - if (info->mBytesInData == sizeof(info->mData)) { - debugLogA("sizeof(data) is too small: the longest line won't fit"); - break; + if (info->mBytesInData == info->mDataSize) { + if (!ReallocInfoBuffer(info, info->mDataSize*2)) { + debugLogA("sizeof(data) is too small: the longest line won't fit"); + break; + } } } @@ -340,27 +355,14 @@ ThreadData* CMsnProto::MSN_GetThreadByContact(const char* wlid, TInfoType type) return NULL; } -ThreadData* CMsnProto::MSN_GetThreadByChatId(const TCHAR* chatId) -{ - mir_cslock lck(m_csThreads); - - for (int i = 0; i < m_arThreads.getCount(); i++) { - ThreadData &T = m_arThreads[i]; - if (_tcsicmp(T.mChatID, chatId) == 0) - return &T; - } - - return NULL; -} - -ThreadData* CMsnProto::MSN_GetThreadByTimer(UINT timerId) +GCThreadData* CMsnProto::MSN_GetThreadByChatId(const TCHAR* chatId) { mir_cslock lck(m_csThreads); - for (int i = 0; i < m_arThreads.getCount(); i++) { - ThreadData &T = m_arThreads[i]; - if (T.mType == SERVER_SWITCHBOARD && T.mTimerId == timerId) - return &T; + for (int i = 0; i < m_arGCThreads.getCount(); i++) { + GCThreadData *T = m_arGCThreads[i]; + if (_tcsicmp(T->mChatID, chatId) == 0) + return T; } return NULL; @@ -517,6 +519,7 @@ ThreadData::ThreadData() mGatewayTimeout = 2; resetTimeout(); hWaitEvent = CreateSemaphore(NULL, 0, MSN_PACKETS_COMBINE, NULL); + mData = (char*)mir_calloc((mDataSize=8192)+1); } ThreadData::~ThreadData() @@ -567,6 +570,8 @@ ThreadData::~ThreadData() proto->MSN_GetUnconnectedThread(wlid) == NULL) { proto->MsgQueue_Clear(wlid, true); } + + mir_free(mData); } void ThreadData::applyGatewayData(HANDLE hConn, bool isPoll) @@ -677,9 +682,7 @@ HReadBuffer::~HReadBuffer() BYTE* HReadBuffer::surelyRead(size_t parBytes) { - const size_t bufferSize = sizeof(owner->mData); - - if ((startOffset + parBytes) > bufferSize) { + if ((startOffset + parBytes) > owner->mDataSize) { if (totalDataSize > startOffset) memmove(buffer, buffer + startOffset, (totalDataSize -= startOffset)); else @@ -687,14 +690,19 @@ BYTE* HReadBuffer::surelyRead(size_t parBytes) startOffset = 0; - if (parBytes > bufferSize) { - owner->proto->debugLogA("HReadBuffer::surelyRead: not enough memory, %d %d %d", parBytes, bufferSize, startOffset); - return NULL; + if (parBytes > owner->mDataSize) { + size_t mDataSize = owner->mDataSize; + while (parBytes > mDataSize) mDataSize*=2; + if (!ReallocInfoBuffer(owner, mDataSize)) { + owner->proto->debugLogA("HReadBuffer::surelyRead: not enough memory, %d %d %d", parBytes, owner->mDataSize, startOffset); + return NULL; + } + buffer = (BYTE*)owner->mData; } } while ((startOffset + parBytes) > totalDataSize) { - int recvResult = owner->recv((char*)buffer + totalDataSize, bufferSize - totalDataSize); + int recvResult = owner->recv((char*)buffer + totalDataSize, owner->mDataSize - totalDataSize); if (recvResult <= 0) return NULL; @@ -703,5 +711,20 @@ BYTE* HReadBuffer::surelyRead(size_t parBytes) } BYTE *result = buffer + startOffset; startOffset += parBytes; + buffer[totalDataSize] = 0; return result; } + +///////////////////////////////////////////////////////////////////////////////////////// +// class GCThreadData members + +GCThreadData::GCThreadData() : +mJoinedContacts(10, PtrKeySortT) +{ + memset(&mCreator, 0, sizeof(GCThreadData) - sizeof(mJoinedContacts)); +} + +GCThreadData::~GCThreadData() +{ +} + diff --git a/protocols/MSN/src/version.h b/protocols/MSN/src/version.h index 11fefe7035..3b8e7343f5 100644 --- a/protocols/MSN/src/version.h +++ b/protocols/MSN/src/version.h @@ -19,15 +19,15 @@ along with this program. If not, see . */ #define __MAJOR_VERSION 0 -#define __MINOR_VERSION 11 +#define __MINOR_VERSION 12 #define __RELEASE_NUM 1 -#define __BUILD_NUM 2 +#define __BUILD_NUM 1 #include #define __PLUGIN_NAME "MSN protocol" #define __DESCRIPTION "Microsoft Network (MSN) protocol support for Miranda NG." -#define __AUTHOR "Boris Krasnovskiy, George Hazan, Richard Hughes" +#define __AUTHOR "Boris Krasnovskiy, George Hazan, Richard Hughes, leecher" #define __AUTHOREMAIL "borkra@miranda-im.org" #define __COPYRIGHT "© 2001-2012 Richard Hughes, George Hazan, Boris Krasnovskiy" #define __AUTHORWEB "http://miranda-ng.org/p/MSN/" -- cgit v1.2.3