/* Plugin of Miranda IM for communicating with users of the MSN Messenger protocol. Copyright (c) 2012-2018 Miranda NG team Copyright (c) 2007-2012 Boris Krasnovskiy. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "stdafx.h" #include "msn_proto.h" static const char oimRecvUrl[] = "https://rsi.hotmail.com/rsi/rsi.asmx"; static const char mailReqHdr[] = "SOAPAction: \"http://www.hotmail.msn.com/ws/2004/09/oim/rsi/%s\"\r\n"; ezxml_t CMsnProto::oimRecvHdr(const char* service, ezxml_t& tbdy, char*& httphdr) { ezxml_t xmlp = ezxml_new("soap:Envelope"); ezxml_set_attr(xmlp, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); ezxml_set_attr(xmlp, "xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); ezxml_set_attr(xmlp, "xmlns:soap", "http://schemas.xmlsoap.org/soap/envelope/"); ezxml_t hdr = ezxml_add_child(xmlp, "soap:Header", 0); ezxml_t cook = ezxml_add_child(hdr, "PassportCookie", 0); ezxml_set_attr(cook, "xmlns", "http://www.hotmail.msn.com/ws/2004/09/oim/rsi"); ezxml_t tcook = ezxml_add_child(cook, "t", 0); ezxml_set_txt(tcook, tAuthToken ? tAuthToken : ""); ezxml_t pcook = ezxml_add_child(cook, "p", 0); ezxml_set_txt(pcook, pAuthToken ? pAuthToken : ""); ezxml_t bdy = ezxml_add_child(xmlp, "soap:Body", 0); tbdy = ezxml_add_child(bdy, service, 0); ezxml_set_attr(tbdy, "xmlns", "http://www.hotmail.msn.com/ws/2004/09/oim/rsi"); size_t hdrsz = mir_strlen(service) + sizeof(mailReqHdr) + 20; httphdr = (char*)mir_alloc(hdrsz); mir_snprintf(httphdr, hdrsz, mailReqHdr, service); return xmlp; } void CMsnProto::getOIMs(ezxml_t xmli) { ezxml_t toki = ezxml_child(xmli, "M"); if (toki == nullptr) return; char* getReqHdr; ezxml_t reqmsg; ezxml_t xmlreq = oimRecvHdr("GetMessage", reqmsg, getReqHdr); ezxml_t reqmid = ezxml_add_child(reqmsg, "messageId", 0); ezxml_t reqmrk = ezxml_add_child(reqmsg, "alsoMarkAsRead", 0); ezxml_set_txt(reqmrk, "false"); char* delReqHdr; ezxml_t delmsg; ezxml_t xmldel = oimRecvHdr("DeleteMessages", delmsg, delReqHdr); ezxml_t delmids = ezxml_add_child(delmsg, "messageIds", 0); while (toki != nullptr) { const char* szId = ezxml_txt(ezxml_child(toki, "I")); const char* szEmail = ezxml_txt(ezxml_child(toki, "E")); ezxml_set_txt(reqmid, szId); char* szData = ezxml_toxml(xmlreq, true); unsigned status; char* url = (char*)mir_strdup(oimRecvUrl); char* tResult = getSslResult(&url, szData, getReqHdr, status); free(szData); mir_free(url); if (tResult != nullptr && status == 200) { ezxml_t xmlm = ezxml_parse_str(tResult, mir_strlen(tResult)); ezxml_t body = getSoapResponse(xmlm, "GetMessage"); MimeHeaders mailInfo; const char* mailbody = mailInfo.readFromBuffer((char*)ezxml_txt(body)); time_t evtm = time(0); const char* arrTime = mailInfo["X-OriginalArrivalTime"]; if (arrTime != nullptr) { char szTime[32], *p; txtParseParam(arrTime, "FILETIME", "[", "]", szTime, sizeof(szTime)); unsigned filetimeLo = strtoul(szTime, &p, 16); if (*p == ':') { unsigned __int64 filetime = strtoul(p + 1, &p, 16); filetime <<= 32; filetime |= filetimeLo; filetime /= 10000000; #ifndef __GNUC__ filetime -= 11644473600ui64; #else filetime -= 11644473600ull; #endif evtm = (time_t)filetime; } } PROTORECVEVENT pre = { 0 }; pre.szMessage = mailInfo.decodeMailBody((char*)mailbody); pre.timestamp = evtm; ProtoChainRecvMsg(MSN_HContactFromEmail(szEmail), &pre); mir_free(pre.szMessage); ezxml_t delmid = ezxml_add_child(delmids, "messageId", 0); ezxml_set_txt(delmid, szId); ezxml_free(xmlm); } mir_free(tResult); toki = ezxml_next(toki); } ezxml_free(xmlreq); mir_free(getReqHdr); if (ezxml_child(delmids, "messageId") != nullptr) { char* szData = ezxml_toxml(xmldel, true); unsigned status; char* url = (char*)mir_strdup(oimRecvUrl); char* tResult = getSslResult(&url, szData, delReqHdr, status); mir_free(url); mir_free(tResult); free(szData); } ezxml_free(xmldel); mir_free(delReqHdr); } void CMsnProto::getMetaData(void) { char* getReqHdr; ezxml_t reqbdy; ezxml_t xmlreq = oimRecvHdr("GetMetadata", reqbdy, getReqHdr); char* szData = ezxml_toxml(xmlreq, true); ezxml_free(xmlreq); unsigned status; char* url = (char*)mir_strdup(oimRecvUrl); char* tResult = getSslResult(&url, szData, getReqHdr, status); mir_free(url); free(szData); mir_free(getReqHdr); if (tResult != nullptr && status == 200) { ezxml_t xmlm = ezxml_parse_str(tResult, mir_strlen(tResult)); ezxml_t xmli = ezxml_get(xmlm, "s:Body", 0, "GetMetadataResponse", 0, "MD", -1); if (!xmli) xmli = ezxml_get(xmlm, "soap:Body", 0, "GetMetadataResponse", 0, "MD", -1); getOIMs(xmli); ezxml_free(xmlm); } mir_free(tResult); } void CMsnProto::processMailData(char* mailData) { if (mir_strcmp(mailData, "too-large") == 0) { getMetaData(); } else { ezxml_t xmli = ezxml_parse_str(mailData, mir_strlen(mailData)); ezxml_t toke = ezxml_child(xmli, "E"); const char* szIU = ezxml_txt(ezxml_child(toke, "IU")); if (*szIU) mUnreadMessages = atol(szIU); const char* szOU = ezxml_txt(ezxml_child(toke, "OU")); if (*szOU) mUnreadJunkEmails = atol(szOU); getOIMs(xmli); ezxml_free(xmli); } } ///////////////////////////////////////////////////////////////////////////////////////// // Processes e-mail notification void CMsnProto::sttNotificationMessage(char* msgBody, bool isInitial) { wchar_t tBuffer[512]; wchar_t tBuffer2[512]; int UnreadMessages = mUnreadMessages; int UnreadJunkEmails = mUnreadJunkEmails; bool ShowPopup = isInitial; MimeHeaders tFileInfo; tFileInfo.readFromBuffer(msgBody); const char* From = tFileInfo["From"]; const char* Subject = tFileInfo["Subject"]; const char* Fromaddr = tFileInfo["From-Addr"]; const char* MsgDelta = tFileInfo["Message-Delta"]; const char* SrcFolder = tFileInfo["Src-Folder"]; const char* DestFolder = tFileInfo["Dest-Folder"]; const char* InboxUnread = tFileInfo["Inbox-Unread"]; const char* FoldersUnread = tFileInfo["Folders-Unread"]; if (InboxUnread != nullptr) mUnreadMessages = atol(InboxUnread); if (FoldersUnread != nullptr) mUnreadJunkEmails = atol(FoldersUnread); if (MsgDelta != nullptr) { int iDelta = atol(MsgDelta); if (SrcFolder && mir_strcmp(SrcFolder, "ACTIVE") == 0) mUnreadMessages -= iDelta; else if (DestFolder && mir_strcmp(DestFolder, "ACTIVE") == 0) mUnreadMessages += iDelta; if (SrcFolder && mir_strcmp(SrcFolder, "HM_BuLkMail_") == 0) mUnreadJunkEmails -= iDelta; else if (DestFolder && mir_strcmp(DestFolder, "HM_BuLkMail_") == 0) mUnreadJunkEmails += iDelta; if (mUnreadJunkEmails < 0) mUnreadJunkEmails = 0; if (mUnreadMessages < 0) mUnreadMessages = 0; } if (From != nullptr && Subject != nullptr && Fromaddr != nullptr) { if (DestFolder != nullptr && SrcFolder == nullptr) { mUnreadMessages += mir_strcmp(DestFolder, "ACTIVE") == 0; mUnreadJunkEmails += mir_strcmp(DestFolder, "HM_BuLkMail_") == 0; } wchar_t* mimeFromW = tFileInfo.decode(From); wchar_t* mimeSubjectW = tFileInfo.decode(Subject); mir_snwprintf(tBuffer2, TranslateT("Subject: %s"), mimeSubjectW); wchar_t* msgtxt = _stricmp(From, Fromaddr) ? TranslateT("Hotmail from %s (%S)") : TranslateT("Hotmail from %s"); mir_snwprintf(tBuffer, msgtxt, mimeFromW, Fromaddr); mir_free(mimeFromW); mir_free(mimeSubjectW); ShowPopup = true; } else { const char* MailData = tFileInfo["Mail-Data"]; if (MailData != nullptr) processMailData((char*)MailData); mir_snwprintf(tBuffer, m_tszUserName); mir_snwprintf(tBuffer2, TranslateT("Unread mail is available: %d in Inbox and %d in other folders."), mUnreadMessages, mUnreadJunkEmails); } if (UnreadMessages == mUnreadMessages && UnreadJunkEmails == mUnreadJunkEmails && !isInitial) return; ShowPopup &= mUnreadMessages != 0 || (mUnreadJunkEmails != 0 && !getByte("DisableHotmailJunk", 0)); MCONTACT hContact = MSN_HContactFromEmail(MyOptions.szEmail); if (hContact) { pcli->pfnRemoveEvent(hContact, 1); displayEmailCount(hContact); if (ShowPopup && !getByte("DisableHotmailTray", 1)) { char buf[64]; mir_snprintf(buf, "%s%s", m_szModuleName, MS_GOTO_INBOX); CLISTEVENT cle = {}; cle.hContact = hContact; cle.hDbEvent = 1; cle.flags = CLEF_URGENT | CLEF_UNICODE; cle.hIcon = Skin_LoadIcon(SKINICON_OTHER_SENDEMAIL); cle.szTooltip.w = tBuffer2; cle.pszService = buf; pcli->pfnAddEvent(&cle); } } ProtoBroadcastAck(NULL, ACKTYPE_EMAIL, ACKRESULT_STATUS, nullptr, 0); // Disable to notify receiving hotmail if (ShowPopup && !getByte("DisableHotmail", 0)) { Skin_PlaySound(mailsoundname); const char *msgurl = tFileInfo["Message-URL"]; if (msgurl) { const char *p = strchr(msgurl, '&'); if (p) *(char*)p = 0; p = strstr(msgurl, "getmsg"); if (p) msgurl = p; } else msgurl = "inbox"; char szUrl[256]; mir_snprintf(szUrl, "http://mail.live.com?rru=%s", msgurl); MSN_ShowPopup(tBuffer, tBuffer2, MSN_ALLOW_ENTER | MSN_ALLOW_MSGBOX | MSN_HOTMAIL_POPUP, szUrl); } if (!getByte("RunMailerOnHotmail", 0) || !ShowPopup || isInitial) return; char mailerpath[MAX_PATH]; if (!db_get_static(NULL, m_szModuleName, "MailerPath", mailerpath, sizeof(mailerpath))) { if (mailerpath[0]) { char* tParams = nullptr; char* tCmd = mailerpath; if (*tCmd == '\"') { ++tCmd; char* tEndPtr = strchr(tCmd, '\"'); if (tEndPtr != nullptr) { *tEndPtr = 0; tParams = tEndPtr + 1; } } if (tParams == nullptr) { tParams = strchr(tCmd, ' '); tParams = tParams ? tParams + 1 : strchr(tCmd, '\0'); } while (*tParams == ' ') ++tParams; debugLogA("Running mailer \"%s\" with params \"%s\"", tCmd, tParams); ShellExecuteA(nullptr, "open", tCmd, tParams, nullptr, TRUE); } } } static void TruncUtf8(char *str, size_t sz) { size_t len = mir_strlen(str); if (sz > len) sz = len; size_t cntl = 0, cnt = 0; for (;;) { unsigned char p = (unsigned char)str[cnt]; if (p >= 0xE0) cnt += 3; else if (p >= 0xC0) cnt += 2; else if (p != 0) ++cnt; else break; if (cnt <= sz) cntl = cnt; else break; } str[cntl] = 0; } void CMsnProto::displayEmailCount(MCONTACT hContact) { if (!emailEnabled || getByte("DisableHotmailCL", 0)) return; wchar_t* name = GetContactNameT(hContact); if (name == nullptr) return; wchar_t* ch = name - 1; do { ch = wcschr(ch + 1, '['); } while (ch && !iswdigit(ch[1])); if (ch) *ch = 0; rtrimw(name); wchar_t szNick[128]; mir_snwprintf(szNick, getByte("DisableHotmailJunk", 0) ? L"%s [%d]" : L"%s [%d][%d]", name, mUnreadMessages, mUnreadJunkEmails); nickChg = true; setWString(hContact, "Nick", szNick); nickChg = false; }