summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Piastucki <leech.miranda@gmail.com>2015-05-14 15:24:53 +0000
committerPiotr Piastucki <leech.miranda@gmail.com>2015-05-14 15:24:53 +0000
commitb7a1174511c3b7fad5b81a54bb4647662d94031c (patch)
tree91b3f36fd78ae9b481cb11d8602e61a79c388d04
parent01b66588ca510b49be5a06d2bfb5bd28b4416030 (diff)
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
-rw-r--r--protocols/MSN/src/msn_auth.cpp462
-rw-r--r--protocols/MSN/src/msn_chat.cpp351
-rw-r--r--protocols/MSN/src/msn_commands.cpp942
-rw-r--r--protocols/MSN/src/msn_contact.cpp63
-rw-r--r--protocols/MSN/src/msn_ftold.cpp14
-rw-r--r--protocols/MSN/src/msn_global.h92
-rw-r--r--protocols/MSN/src/msn_libstr.cpp96
-rw-r--r--protocols/MSN/src/msn_lists.cpp51
-rw-r--r--protocols/MSN/src/msn_menu.cpp1
-rw-r--r--protocols/MSN/src/msn_misc.cpp234
-rw-r--r--protocols/MSN/src/msn_opts.cpp3
-rw-r--r--protocols/MSN/src/msn_proto.cpp24
-rw-r--r--protocols/MSN/src/msn_proto.h43
-rw-r--r--protocols/MSN/src/msn_soapab.cpp113
-rw-r--r--protocols/MSN/src/msn_soapstore.cpp2
-rw-r--r--protocols/MSN/src/msn_std.cpp6
-rw-r--r--protocols/MSN/src/msn_svcs.cpp5
-rw-r--r--protocols/MSN/src/msn_threads.cpp91
-rw-r--r--protocols/MSN/src/version.h6
19 files changed, 2099 insertions, 500 deletions
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 <http://www.gnu.org/licenses/>.
#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[] =
"<wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
"<wsp:AppliesTo>"
"<wsa:EndpointReference>"
- "<wsa:Address>messengerclear.live.com</wsa:Address>"
+ "<wsa:Address>chatservice.live.com</wsa:Address>"
"</wsa:EndpointReference>"
"</wsp:AppliesTo>"
- "<wsp:PolicyReference URI=\"MBI_KEY_OLD\" />"
+ "<wsp:PolicyReference URI=\"MBI_SSL\" />"
"</wst:RequestSecurityToken>"
"<wst:RequestSecurityToken Id=\"RST2\">"
"<wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>"
@@ -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 <http://www.gnu.org/licenses/>.
#include "msn_proto.h"
#include <m_history.h>
+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",
+ "<thread><id>%d:%s</id><members><member><mri>%s</mri></member></members></thread>",
+ 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",
+ "<thread><id>%d:%s</id><members><member><mri>%s</mri><role>%s</role></member></members></thread>",
+ 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("<thread><id>%s</id></thread>", dbv.pszVal);
+ nThreads++;
+ db_free(&dbv);
+ }
+ }
+ }
+ if (nThreads)
+ msnNsThread->sendPacketPayload("GET", "MSGR\\THREADS", "<threads>%s</threads>", 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",
+ "<thread><id>%d:%s</id><members><member><mri>%s</mri><role>user</role></member></members></thread>",
+ 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("<thread><id></id><members><member><mri>%d:%s</mri><role>admin</role></member>",
+ 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("<member><mri>%s</mri><role>user</role></member>", (*cont)[i]);
+ }
+ buf.Append("</members></thread>");
+ 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",
+ "<msgr><ver>%d</ver>%s<client><name>%s</name><ver>%s</ver></client>"
+ "<epid>%.*s</epid></msgr>\r\n",
+ msnP24Ver, (msnP24Ver>1?"<altVersions><ver>1</ver></altVersions>":""),
+ 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",
+ "<challenge><appId>%s</appId><response>%s</response></challenge>\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 = "<ssl-site-name>chatservice.live.com</ssl-site-name>"; break;
+ default:
+ m_iDesiredStatus = ID_STATUS_OFFLINE;
+ return 1;
+ }
+
+ info->sendPacketPayload("ATH", "CON\\USER",
+ "<user><ssl-compact-ticket>t=%s</ssl-compact-ticket>"
+ "<uic>%s</uic>%s"
+ "<id>%s</id><alias>%s</alias></user>\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",
+ "<user><uic>%s</uic><id>%s</id></user>\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",
+ "<threads><thread><id>%s</id></thread></threads>", 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",
+ "<messagesbyconversation><id>%s</id><start>%llu</start><pagesize>100</pagesize></messagesbyconversation>",
+ 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<ezxml> 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; i<msgs.getCount(); i++) {
+ ezxml_t msg = msgs[i];
+ ezxml_t arrtime = ezxml_child(msg, "originalarrivaltime"), from = ezxml_child(msg, "from"),
+ msgtype = ezxml_child(msg, "messagetype"), content = ezxml_child(msg, "content");
+ time_t ts;
+ char *netId, *email, *message;
+ bool sentMsg;
+
+ if (!arrtime || !from || !content) continue;
+ ts=IsoToUnixTime(arrtime->txt);
+ 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",
+ "<messagesbyconversation><id>%s</id><start>%llu</start><messagessyncstate>%s</messagessyncstate><pagesize>100</pagesize></messagesbyconversation>",
+ 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 <http://www.gnu.org/licenses/>.
#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<GCUserItem> 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, "<p>", 3 )) { strcpy(q, "\r\n\r\n"); q += 3; p += 2; }
+ else if ( !strnicmp( p, "</p>", 4 )) { strcpy(q, "\r\n\r\n"); q += 3; p += 3; }
+ else if ( !strnicmp( p, "<br>", 4 )) { strcpy(q, "\r\n"); ++q; p += 3; }
+ else if ( !strnicmp( p, "<br />", 6 )) { strcpy(q, "\r\n"); ++q; p += 5; }
+ else if ( !strnicmp( p, "<hr>", 4 )) { strcpy(q, "\r\n"); ++q; p += 3; }
+ else if ( !strnicmp( p, "<hr />", 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", &timestamp.tm_hour, &timestamp.tm_min, &timestamp.tm_sec) != 3)
+ return (time_t)0;
+
+ timestamp.tm_isdst = 0; // DST is already present in _timezone below
+ time_t t = mktime(&timestamp);
+
+ _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), "<ml l=\"1\">");
+ cxml.Append("<ml l=\"1\">");
{
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), "<ml l=\"1\">");
if (newdom) {
- sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "<t>");
+ cxml.Append("<skp>");
newdom = false;
}
-
- sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "<c n=\"%s\" l=\"%d\"/>", 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 n=\"%s\" t=\"%d\"><s l=\"%d\" n=\"PE\"/><s l=\"%d\" n=\"IM\"/><s l=\"%d\" n=\"SKP\"/><s l=\"%d\" n=\"PUB\"/></c>", 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), "<ml l=\"1\">");
if (newdom) {
- sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "<d n=\"%s\">", lastds + 1);
+ cxml.AppendFormat("<d n=\"%s\">", lastds + 1);
newdom = false;
}
*(char*)dom = 0;
- sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "<c n=\"%s\" l=\"%d\" t=\"%d\"/>", C.email, C.list & ~(LIST_RL | LIST_LL), C.netId);
+ cxml.AppendFormat("<c n=\"%s\" t=\"%d\"><s n=\"IM\" l=\"%d\"/></c>", 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), "</%c></ml>", 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 ? "</d>" : "</t>");
+ if (!newdom) cxml.Append(lastds ? "</d>" : "</skp>");
}
}
- if (sz) {
- sz += mir_snprintf((cxml + sz), (SIZEOF(cxml) - sz), "</ml>");
- msnNsThread->sendPacket("ADL", "%d\r\n%s", sz, cxml);
- }
+ cxml.Append("</ml>");
+ msnNsThread->sendPacketPayload("PUT", "MSGR\\CONTACTS", "%s", cxml);
+
+ if (msnP24Ver > 1)
+ msnNsThread->sendPacketPayload("PUT", "MSGR\\SUBSCRIPTIONS", "<subscribe><presence><buddies><all /></buddies></presence><messaging><im /><conversations /></messaging><notifications><partners><partner>ABCH</partner></partners></notifications></subscribe>");
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),
"<EndpointData><Capabilities>%u:%u</Capabilities></EndpointData>", 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),
+ "<user>"
+ "<sep n=\"PE\" epid=\"%s\"><VER>%s</VER><TYP>11</TYP><Capabilities>0:0</Capabilities></sep>"
+ "<s n=\"IM\"><Status>%s</Status></s>"
+ "<sep n=\"IM\" epid=\"%s\"><Capabilities>%u:%u</Capabilities></sep>"
+ "<sep n=\"PD\" epid=\"%s\"><EpName>%s</EpName><ClientType>11</ClientType></sep>"
+ "<s n=\"SKP\"><Mood>%s</Mood><Skypename>%s</Skypename></s>"
+ "<sep n=\"SKP\" epid=\"%s\"><NodeInfo></NodeInfo><Version>24</Version><Seamless>true</Seamless></sep>"
+ "</user>",
+ 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),
"<PrivateEndpointData>"
"<EpName>%s</EpName>"
@@ -753,8 +850,10 @@ void CMsnProto::MSN_SetServerStatus(int newStatus)
"</PrivateEndpointData>",
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",
+ "<recentconversations><start>%llu</start><pagesize>100</pagesize></recentconversations>",
+ ((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<CMsnProto>
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<CMsnProto>
mir_cs m_csThreads;
OBJLIST<ThreadData> m_arThreads;
+ LIST<GCThreadData> m_arGCThreads;
mir_cs m_csSessions;
OBJLIST<filetransfer> m_arSessions;
@@ -149,6 +154,7 @@ struct CMsnProto : public PROTO<CMsnProto>
bool usingGateway;
char* msnExternalIP;
+ char* msnRegistration;
char* msnPreviousUUX;
char* msnLastStatusMsg;
@@ -191,15 +197,17 @@ struct CMsnProto : public PROTO<CMsnProto>
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<CMsnProto>
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<CMsnProto>
/////////////////////////////////////////////////////////////////////////////////////////
// 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<CMsnProto>
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<CMsnProto>
// 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<CMsnProto>
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<CMsnProto>
//////////////////////////////////////////////////////////////////////////////////////
+ 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", "<connect>%s%s%s<ver>2</ver><agent><os>winnt</os><osVer>5.2</osVer><proc>x86</proc><lcid>en-us</lcid></agent></connect>\r\n",
+ *info->mState?"<xfr><state>":"", *info->mState?info->mState:"", *info->mState?"</state></xfr>":"");
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 <http://www.gnu.org/licenses/>.
*/
#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 <stdver.h>
#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/"