From 6810c23ee8e069c9156349638e1bad770854372e Mon Sep 17 00:00:00 2001 From: George Hazan Date: Fri, 26 Jun 2020 15:35:48 +0300 Subject: Revert "Jabber: XEP-0136 support removed" This reverts commit e6bf9c2a41622424c2cd4006326455e08c80812a. # Conflicts: # protocols/JabberG/src/jabber_caps.cpp # protocols/JabberG/src/jabber_caps.h # protocols/JabberG/src/jabber_iqid.cpp --- protocols/JabberG/jabber.vcxproj | 1 + protocols/JabberG/jabber.vcxproj.filters | 3 + protocols/JabberG/src/jabber_archive.cpp | 272 +++++++++++++++++++++++++++++++ protocols/JabberG/src/jabber_caps.cpp | 2 + protocols/JabberG/src/jabber_caps.h | 10 +- protocols/JabberG/src/jabber_iqid.cpp | 4 +- protocols/JabberG/src/jabber_menu.cpp | 4 +- protocols/JabberG/src/jabber_opt.cpp | 1 + protocols/JabberG/src/jabber_proto.cpp | 1 + protocols/JabberG/src/jabber_proto.h | 9 + 10 files changed, 302 insertions(+), 5 deletions(-) create mode 100644 protocols/JabberG/src/jabber_archive.cpp (limited to 'protocols/JabberG') diff --git a/protocols/JabberG/jabber.vcxproj b/protocols/JabberG/jabber.vcxproj index 7eb80b6e17..81294626ef 100755 --- a/protocols/JabberG/jabber.vcxproj +++ b/protocols/JabberG/jabber.vcxproj @@ -30,6 +30,7 @@ + diff --git a/protocols/JabberG/jabber.vcxproj.filters b/protocols/JabberG/jabber.vcxproj.filters index 3a72f63b4b..01919b4315 100755 --- a/protocols/JabberG/jabber.vcxproj.filters +++ b/protocols/JabberG/jabber.vcxproj.filters @@ -14,6 +14,9 @@ Source Files + + Source Files + Source Files diff --git a/protocols/JabberG/src/jabber_archive.cpp b/protocols/JabberG/src/jabber_archive.cpp new file mode 100644 index 0000000000..2c478b2b61 --- /dev/null +++ b/protocols/JabberG/src/jabber_archive.cpp @@ -0,0 +1,272 @@ +/* + +Jabber Protocol Plugin for Miranda NG + +Copyright (c) 2002-04 Santithorn Bunchua +Copyright (c) 2005-12 George Hazan +Copyright (C) 2012-20 Miranda NG team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#include "stdafx.h" +#include "jabber_iq.h" +#include "jabber_caps.h" + +bool operator==(const DBEVENTINFO &ev1, const DBEVENTINFO &ev2) +{ + return ev1.timestamp == ev2.timestamp && ev1.eventType == ev2.eventType && ev1.cbBlob == ev2.cbBlob && (ev1.flags & DBEF_SENT) == (ev2.flags & DBEF_SENT); +} + +void CJabberProto::EnableArchive(bool bEnable) +{ + m_ThreadInfo->send(XmlNodeIq("set", SerialNext()) + << XCHILDNS("auto", JABBER_FEAT_ARCHIVE) << XATTR("save", (bEnable) ? "true" : "false")); +} + +void CJabberProto::RetrieveMessageArchive(MCONTACT hContact, JABBER_LIST_ITEM *pItem) +{ + if (pItem->bHistoryRead) + return; + + pItem->bHistoryRead = true; + + XmlNodeIq iq(AddIQ(&CJabberProto::OnIqResultGetCollectionList, JABBER_IQ_TYPE_GET)); + TiXmlElement *list = iq << XCHILDNS("list", JABBER_FEAT_ARCHIVE) << XATTR("with", pItem->jid); + + time_t tmLast = getDword(hContact, "LastCollection", 0); + if (tmLast) { + char buf[40]; + list << XATTR("start", time2str(tmLast, buf, _countof(buf))); + } + m_ThreadInfo->send(iq); +} + +void CJabberProto::OnIqResultGetCollectionList(const TiXmlElement *iqNode, CJabberIqInfo*) +{ + const char *to = XmlGetAttr(iqNode, "to"); + if (to == nullptr || mir_strcmp(XmlGetAttr(iqNode, "type"), "result")) + return; + + auto *list = XmlFirstChild(iqNode, "list"); + if (mir_strcmp(XmlGetAttr(list, "xmlns"), JABBER_FEAT_ARCHIVE)) + return; + + for (auto *itemNode : TiXmlFilter(list, "chat")) { + const char *start = XmlGetAttr(itemNode, "start"); + const char *with = XmlGetAttr(itemNode, "with"); + if (!start || !with) + continue; + + m_ThreadInfo->send(XmlNodeIq(AddIQ(&CJabberProto::OnIqResultGetCollection, JABBER_IQ_TYPE_GET)) + << XCHILDNS("retrieve", JABBER_FEAT_ARCHIVE) << XATTR("with", with) << XATTR("start", start)); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static DWORD dwPreviousTimeStamp = -1; +static MCONTACT hPreviousContact = INVALID_CONTACT_ID; +static MEVENT hPreviousDbEvent = 0; + +// Returns TRUE if the event already exist in the database +bool IsDuplicateEvent(MCONTACT hContact, DBEVENTINFO& dbei) +{ + // get last event + MEVENT hExistingDbEvent = db_event_last(hContact); + if (!hExistingDbEvent) + return false; + + DBEVENTINFO dbeiExisting = {}; + db_event_get(hExistingDbEvent, &dbeiExisting); + DWORD dwEventTimeStamp = dbeiExisting.timestamp; + + // compare with last timestamp + if (dbei.timestamp > dwEventTimeStamp) { + // remember event + hPreviousDbEvent = hExistingDbEvent; + dwPreviousTimeStamp = dwEventTimeStamp; + return false; + } + + if (hContact != hPreviousContact) { + hPreviousContact = hContact; + // remember event + hPreviousDbEvent = hExistingDbEvent; + dwPreviousTimeStamp = dwEventTimeStamp; + + // get first event + if (!(hExistingDbEvent = db_event_first(hContact))) + return false; + + memset(&dbeiExisting, 0, sizeof(dbeiExisting)); + db_event_get(hExistingDbEvent, &dbeiExisting); + dwEventTimeStamp = dbeiExisting.timestamp; + + // compare with first timestamp + if (dbei.timestamp <= dwEventTimeStamp) { + // remember event + dwPreviousTimeStamp = dwEventTimeStamp; + hPreviousDbEvent = hExistingDbEvent; + + if (dbei.timestamp != dwEventTimeStamp) + return false; + } + } + + // check for equal timestamps + if (dbei.timestamp == dwPreviousTimeStamp) { + memset(&dbeiExisting, 0, sizeof(dbeiExisting)); + db_event_get(hPreviousDbEvent, &dbeiExisting); + + if (dbei == dbeiExisting) + return true; + + // find event with another timestamp + hExistingDbEvent = db_event_next(hContact, hPreviousDbEvent); + while (hExistingDbEvent != 0) { + memset(&dbeiExisting, 0, sizeof(dbeiExisting)); + db_event_get(hExistingDbEvent, &dbeiExisting); + + if (dbeiExisting.timestamp != dwPreviousTimeStamp) { + // use found event + hPreviousDbEvent = hExistingDbEvent; + dwPreviousTimeStamp = dbeiExisting.timestamp; + break; + } + + hPreviousDbEvent = hExistingDbEvent; + hExistingDbEvent = db_event_next(hContact, hExistingDbEvent); + } + } + + hExistingDbEvent = hPreviousDbEvent; + + if (dbei.timestamp <= dwPreviousTimeStamp) { + // look back + while (hExistingDbEvent != 0) { + memset(&dbeiExisting, 0, sizeof(dbeiExisting)); + db_event_get(hExistingDbEvent, &dbeiExisting); + + if (dbei.timestamp > dbeiExisting.timestamp) { + // remember event + hPreviousDbEvent = hExistingDbEvent; + dwPreviousTimeStamp = dbeiExisting.timestamp; + return false; + } + + // Compare event with import candidate + if (dbei == dbeiExisting) { + // remember event + hPreviousDbEvent = hExistingDbEvent; + dwPreviousTimeStamp = dbeiExisting.timestamp; + return true; + } + + // Get previous event in chain + hExistingDbEvent = db_event_prev(hContact, hExistingDbEvent); + } + } + else { + // look forward + while (hExistingDbEvent != 0) { + memset(&dbeiExisting, 0, sizeof(dbeiExisting)); + db_event_get(hExistingDbEvent, &dbeiExisting); + + if (dbei.timestamp < dbeiExisting.timestamp) { + // remember event + hPreviousDbEvent = hExistingDbEvent; + dwPreviousTimeStamp = dbeiExisting.timestamp; + return false; + } + + // Compare event with import candidate + if (dbei == dbeiExisting) { + // remember event + hPreviousDbEvent = hExistingDbEvent; + dwPreviousTimeStamp = dbeiExisting.timestamp; + return true; + } + + // Get next event in chain + hExistingDbEvent = db_event_next(hContact, hExistingDbEvent); + } + } + // reset last event + hPreviousContact = INVALID_CONTACT_ID; + return false; +} + +void CJabberProto::OnIqResultGetCollection(const TiXmlElement *iqNode, CJabberIqInfo*) +{ + if (mir_strcmp(XmlGetAttr(iqNode, "type"), "result")) + return; + + auto *chatNode = XmlFirstChild(iqNode, "chat"); + if (!chatNode || mir_strcmp(XmlGetAttr(chatNode, "xmlns"), JABBER_FEAT_ARCHIVE)) + return; + + const char* start = XmlGetAttr(chatNode, "start"); + const char* with = XmlGetAttr(chatNode, "with"); + if (!start || !with) + return; + + _tzset(); + + MCONTACT hContact = HContactFromJID(with); + time_t tmStart = str2time(start); + if (hContact == 0 || tmStart == 0) + return; + + time_t tmLast = getDword(hContact, "LastCollection", 0); + + for (auto *itemNode : TiXmlEnum(chatNode)) { + int from; + const char *itemName = itemNode->Name(); + if (!mir_strcmp(itemName, "to")) + from = DBEF_SENT; + else if (!mir_strcmp(itemName, "from")) + from = 0; + else + continue; + + const TiXmlElement *body = XmlFirstChild(itemNode, "body"); + if (!body) + continue; + + const char *tszBody = body->GetText(); + const char *tszSecs = XmlGetAttr(itemNode, "secs"); + if (!tszBody || !tszSecs) + continue; + + DBEVENTINFO dbei = {}; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.szModule = m_szModuleName; + dbei.cbBlob = (DWORD)mir_strlen(tszBody) + 1; + dbei.flags = DBEF_READ + DBEF_UTF + from; + dbei.pBlob = (BYTE*)tszBody; + dbei.timestamp = tmStart + atol(tszSecs); + if (!IsDuplicateEvent(hContact, dbei)) + db_event_add(hContact, &dbei); + + tmStart = dbei.timestamp; + if (dbei.timestamp > tmLast) + tmLast = dbei.timestamp; + } + + if (tmLast != 0) + setDword(hContact, "LastCollection", tmLast + 1); +} diff --git a/protocols/JabberG/src/jabber_caps.cpp b/protocols/JabberG/src/jabber_caps.cpp index bce11fb61f..6d2d6c7095 100755 --- a/protocols/JabberG/src/jabber_caps.cpp +++ b/protocols/JabberG/src/jabber_caps.cpp @@ -69,6 +69,8 @@ const JabberFeatCapPair g_JabberFeatCapPairs[] = { JABBER_FEAT_PRIVATE_STORAGE, JABBER_CAPS_PRIVATE_STORAGE, LPGEN("Supports private XML Storage (for bookmarks and other)") }, { JABBER_FEAT_ATTENTION, JABBER_CAPS_ATTENTION, LPGEN("Supports attention requests ('nudge')") }, { JABBER_FEAT_MAM, JABBER_CAPS_MAM, LPGEN("Support Message Archive Management (XEP-0313)") }, + { JABBER_FEAT_ARCHIVE_AUTO, JABBER_CAPS_ARCHIVE_AUTO, LPGEN("Supports chat history retrieving") }, + { JABBER_FEAT_ARCHIVE_MANAGE, JABBER_CAPS_ARCHIVE_MANAGE, LPGEN("Supports chat history management") }, { JABBER_FEAT_USER_ACTIVITY, JABBER_CAPS_USER_ACTIVITY, LPGEN("Can report information about user activity") }, { JABBER_FEAT_USER_ACTIVITY_NOTIFY, JABBER_CAPS_USER_ACTIVITY_NOTIFY, LPGEN("Receives information about user activity") }, { JABBER_FEAT_MIRANDA_NOTES, JABBER_CAPS_MIRANDA_NOTES, LPGEN("Supports Miranda NG notes extension") }, diff --git a/protocols/JabberG/src/jabber_caps.h b/protocols/JabberG/src/jabber_caps.h index 980ff36777..34343fe5b0 100755 --- a/protocols/JabberG/src/jabber_caps.h +++ b/protocols/JabberG/src/jabber_caps.h @@ -102,6 +102,9 @@ typedef unsigned __int64 JabberCapsBits; #define JABBER_FEAT_VCARD_TEMP "vcard-temp" #define JABBER_CAPS_VCARD_TEMP ((JabberCapsBits)1<<18) +#define JABBER_FEAT_MAM "urn:xmpp:mam:2" +#define JABBER_CAPS_MAM ((JabberCapsBits)1<<19) + #define JABBER_FEAT_XHTML "http://jabber.org/protocol/xhtml-im" #define JABBER_CAPS_XHTML ((JabberCapsBits)1<<20) @@ -144,8 +147,11 @@ typedef unsigned __int64 JabberCapsBits; #define JABBER_FEAT_PRIVATE_STORAGE "jabber:iq:private" #define JABBER_CAPS_PRIVATE_STORAGE ((JabberCapsBits)1<<33) -#define JABBER_FEAT_MAM "urn:xmpp:mam:2" -#define JABBER_CAPS_MAM ((JabberCapsBits)1<<34) +#define JABBER_FEAT_ARCHIVE_AUTO "urn:xmpp:archive:auto" +#define JABBER_CAPS_ARCHIVE_AUTO ((JabberCapsBits)1<<34) + +#define JABBER_FEAT_ARCHIVE_MANAGE "urn:xmpp:archive:manage" +#define JABBER_CAPS_ARCHIVE_MANAGE ((JabberCapsBits)1<<35) #define JABBER_FEAT_ATTENTION "urn:xmpp:attention:0" #define JABBER_CAPS_ATTENTION ((JabberCapsBits)1<<36) diff --git a/protocols/JabberG/src/jabber_iqid.cpp b/protocols/JabberG/src/jabber_iqid.cpp index 690e936ad8..c6e0432206 100755 --- a/protocols/JabberG/src/jabber_iqid.cpp +++ b/protocols/JabberG/src/jabber_iqid.cpp @@ -123,9 +123,11 @@ void CJabberProto::OnProcessLoginRq(ThreadData *info, DWORD rq) DWORD dwMask = JABBER_LOGIN_ROSTER | JABBER_LOGIN_BOOKMARKS | JABBER_LOGIN_SERVERINFO; if ((info->dwLoginRqs & dwMask) == dwMask && !(info->dwLoginRqs & JABBER_LOGIN_BOOKMARKS_AJ)) { + if (info->jabberServerCaps & JABBER_CAPS_ARCHIVE_AUTO) + EnableArchive(m_bEnableMsgArchive != 0); - // Server seems to support carbon copies, let's enable/disable them if (info->jabberServerCaps & JABBER_CAPS_CARBONS) + // Server seems to support carbon copies, let's enable/disable them m_ThreadInfo->send(XmlNodeIq("set", SerialNext()) << XCHILDNS((m_bEnableCarbons) ? "enable" : "disable", JABBER_FEAT_CARBONS)); // Server seems to support MAM, let's retrieve MAM settings diff --git a/protocols/JabberG/src/jabber_menu.cpp b/protocols/JabberG/src/jabber_menu.cpp index 92acc5ca6d..6bb8ec5444 100644 --- a/protocols/JabberG/src/jabber_menu.cpp +++ b/protocols/JabberG/src/jabber_menu.cpp @@ -803,8 +803,8 @@ int CJabberProto::OnProcessSrmmEvent(WPARAM, LPARAM lParam) ptrA jid(getUStringA(event->hContact, "jid")); if (jid != nullptr) { JABBER_LIST_ITEM *pItem = ListGetItemPtr(LIST_ROSTER, jid); - // if (pItem) - // RetrieveMessageArchive(event->hContact, pItem); + if (pItem && m_ThreadInfo && (m_ThreadInfo->jabberServerCaps & JABBER_CAPS_ARCHIVE_AUTO) && m_bEnableMsgArchive) + RetrieveMessageArchive(event->hContact, pItem); } } else if (event->uType == MSG_WINDOW_EVT_CLOSING) { diff --git a/protocols/JabberG/src/jabber_opt.cpp b/protocols/JabberG/src/jabber_opt.cpp index acc94be5fd..4a273b5168 100755 --- a/protocols/JabberG/src/jabber_opt.cpp +++ b/protocols/JabberG/src/jabber_opt.cpp @@ -782,6 +782,7 @@ public: m_otvOptions.AddOption(LPGENW("Messaging") L"/" LPGENW("Automatically save received notes"), m_proto->m_bAutosaveNotes); m_otvOptions.AddOption(LPGENW("Messaging") L"/" LPGENW("Inline pictures in messages (XEP-0231)"), m_proto->m_bInlinePictures); m_otvOptions.AddOption(LPGENW("Messaging") L"/" LPGENW("Enable chat states sending (XEP-0085)"), m_proto->m_bEnableChatStates); + m_otvOptions.AddOption(LPGENW("Messaging") L"/" LPGENW("Enable server-side history (XEP-0136)"), m_proto->m_bEnableMsgArchive); m_otvOptions.AddOption(LPGENW("Messaging") L"/" LPGENW("Enable carbon copies (XEP-0280)"), m_proto->m_bEnableCarbons); m_otvOptions.AddOption(LPGENW("Messaging") L"/" LPGENW("Use Stream Management (XEP-0198) if possible (Testing)"), m_proto->m_bEnableStreamMgmt); diff --git a/protocols/JabberG/src/jabber_proto.cpp b/protocols/JabberG/src/jabber_proto.cpp index de68283c16..ad3626164f 100755 --- a/protocols/JabberG/src/jabber_proto.cpp +++ b/protocols/JabberG/src/jabber_proto.cpp @@ -93,6 +93,7 @@ CJabberProto::CJabberProto(const char *aProtoName, const wchar_t *aUserName) : m_bEnableAvatars(this, "EnableAvatars", true), m_bEnableCarbons(this, "EnableCarbons", true), m_bEnableChatStates(this, "EnableChatStates", true), + m_bEnableMsgArchive(this, "EnableMsgArchive", false), m_bEnableRemoteControl(this, "EnableRemoteControl", false), m_bEnableStreamMgmt(this, "UseStreamMgmt", false), m_bEnableUserActivity(this, "EnableUserActivity", true), diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h index b6a599c496..8539ec23f6 100755 --- a/protocols/JabberG/src/jabber_proto.h +++ b/protocols/JabberG/src/jabber_proto.h @@ -188,6 +188,7 @@ struct CJabberProto : public PROTO, public IJabberInterface CMOption m_bEnableAvatars; CMOption m_bEnableCarbons; CMOption m_bEnableChatStates; + CMOption m_bEnableMsgArchive; CMOption m_bEnableRemoteControl; CMOption m_bEnableStreamMgmt; CMOption m_bEnableUserActivity; @@ -359,6 +360,14 @@ struct CJabberProto : public PROTO, public IJabberInterface void ContactMenuAdhocCommands(struct CJabberAdhocStartupParams *param); + //---- jabber_archive.c -------------------------------------------------------------- + + void EnableArchive(bool bEnable); + void RetrieveMessageArchive(MCONTACT hContact, JABBER_LIST_ITEM *pItem); + + void OnIqResultGetCollection(const TiXmlElement *iqNode, CJabberIqInfo*); + void OnIqResultGetCollectionList(const TiXmlElement *iqNode, CJabberIqInfo*); + //---- jabber_bookmarks.c ------------------------------------------------------------ INT_PTR __cdecl OnMenuHandleBookmarks(WPARAM wParam, LPARAM lParam); -- cgit v1.2.3