From 016a44540e5f937f2144cfea312e46b414c34420 Mon Sep 17 00:00:00 2001
From: George Hazan <ghazan@miranda.im>
Date: Fri, 4 Sep 2020 19:23:29 +0300
Subject: Jabber: MAM code rework & optimization

---
 protocols/JabberG/src/jabber_mam.cpp    | 66 +++++++++++++++++----------------
 protocols/JabberG/src/jabber_proto.h    |  3 +-
 protocols/JabberG/src/jabber_thread.cpp | 31 +++++++++++-----
 3 files changed, 57 insertions(+), 43 deletions(-)

(limited to 'protocols/JabberG')

diff --git a/protocols/JabberG/src/jabber_mam.cpp b/protocols/JabberG/src/jabber_mam.cpp
index dbdd1a67b3..9d5ba27b64 100644
--- a/protocols/JabberG/src/jabber_mam.cpp
+++ b/protocols/JabberG/src/jabber_mam.cpp
@@ -45,8 +45,8 @@ void CJabberProto::OnIqResultMamInfo(const TiXmlElement *iqNode, CJabberIqInfo *
 	}
 
 	// shall we retrieve missing messages?
-	// if (pInfo->GetUserData())
-	//		MamRetrieveMissingMessages();
+	if (pInfo->GetUserData())
+		MamRetrieveMissingMessages();
 }
 
 void CJabberProto::MamSetMode(int iNewMode)
@@ -75,6 +75,7 @@ void CJabberProto::MamRetrieveMissingMessages()
 
 	if (szLastId.IsEmpty()) {
 		m_bMamDisableMessages = true; // our goal is to save message id, not to store messages
+		m_bMamCreateRead = false;
 
 		char buf[100];
 		time2str(time(0), buf, _countof(buf));
@@ -95,6 +96,27 @@ void CJabberProto::MamRetrieveMissingMessages()
 /////////////////////////////////////////////////////////////////////////////////////////
 // Contact's history loader
 
+void CJabberProto::MamSendForm(const char *pszWith, const char *pszAfter)
+{
+	auto *pReq = AddIQ(&CJabberProto::OnIqResultRsm, JABBER_IQ_TYPE_SET);
+	pReq->SetParamsToParse(JABBER_IQ_PARSE_FROM);
+
+	XmlNodeIq iq(pReq);
+	auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM);
+
+	auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit");
+	form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM);
+	if (pszWith != nullptr)
+		form << XCHILD("field") << XATTR("var", "with") << XCHILD("value", pszWith);
+
+	auto *rsm = query << XCHILDNS("set", "http://jabber.org/protocol/rsm");
+	rsm << XCHILD("max", "1000");
+	if (pszAfter != nullptr)
+		rsm << XCHILD("after", pszAfter);
+	m_ThreadInfo->send(iq);
+}
+
+
 void CJabberProto::OnIqResultRsm(const TiXmlElement *iqNode, CJabberIqInfo *pInfo)
 {
 	// even if that flag was enabled, unset it
@@ -105,23 +127,9 @@ void CJabberProto::OnIqResultRsm(const TiXmlElement *iqNode, CJabberIqInfo *pInf
 		if (!mir_strcmp(XmlGetAttr(fin, "complete"), "true"))
 			return;
 
-		auto *lastId = XmlGetChildText(fin, "last");
-		if (lastId) {
-			ptrA jid(getUStringA(pInfo->GetHContact(), "jid"));
-
-			auto *pReq = AddIQ(&CJabberProto::OnIqResultRsm, JABBER_IQ_TYPE_SET);
-			pReq->SetParamsToParse(JABBER_IQ_PARSE_FROM);
-
-			XmlNodeIq iq(pReq);
-			auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM);
-			auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit");
-			form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM);
-			form << XCHILD("field") << XATTR("var", "with") << XCHILD("value", jid);
-			auto *rsm = query << XCHILDNS("set", "http://jabber.org/protocol/rsm");
-			rsm << XCHILD("max", "100");
-			rsm << XCHILD("after", lastId);
-			m_ThreadInfo->send(iq);
-		}
+		if (auto *set = XmlGetChildByTag(fin, "set", "xmlns", "http://jabber.org/protocol/rsm"))
+			if (auto *lastId = XmlGetChildText(set, "last"))
+				MamSendForm(ptrA(getUStringA(pInfo->GetHContact(), "jid")), lastId);
 	}
 }
 
@@ -131,24 +139,18 @@ INT_PTR __cdecl CJabberProto::OnMenuLoadHistory(WPARAM hContact, LPARAM)
 		return 0;
 
 	// wipe out old history first
-	DB::ECPTR pCursor(DB::Events(hContact));
-	while (pCursor.FetchNext())
-		pCursor.DeleteEvent();
+	if (IDYES == MessageBoxW(NULL, TranslateT("Do you want to erase local history before loading it from server?"), m_tszUserName, MB_YESNOCANCEL | MB_ICONQUESTION)) {
+		DB::ECPTR pCursor(DB::Events(hContact));
+		while (pCursor.FetchNext())
+			pCursor.DeleteEvent();
+	}
 
 	// load remaining items from server
 	if (m_bJabberOnline) {
 		ptrA jid(getUStringA(hContact, "jid"));
 		if (jid != nullptr) {
-			auto *pReq = AddIQ(&CJabberProto::OnIqResultRsm, JABBER_IQ_TYPE_SET);
-			pReq->SetParamsToParse(JABBER_IQ_PARSE_FROM);
-
-			XmlNodeIq iq(pReq);
-			auto *query = iq << XCHILDNS("query", JABBER_FEAT_MAM);
-			auto *form = query << XCHILDNS("x", JABBER_FEAT_DATA_FORMS) << XATTR("type", "submit");
-			form << XCHILD("field") << XATTR("var", "FORM_TYPE") << XATTR("type", "hidden") << XCHILD("value", JABBER_FEAT_MAM);
-			form << XCHILD("field") << XATTR("var", "with") << XCHILD("value", jid);
-			query << XCHILDNS("set", "http://jabber.org/protocol/rsm") << XCHILD("max", "100");
-			m_ThreadInfo->send(iq);
+			m_bMamCreateRead = true;
+			MamSendForm(jid);
 		}
 	}
 	return 0;
diff --git a/protocols/JabberG/src/jabber_proto.h b/protocols/JabberG/src/jabber_proto.h
index 64e17bb539..9d78f99b63 100755
--- a/protocols/JabberG/src/jabber_proto.h
+++ b/protocols/JabberG/src/jabber_proto.h
@@ -251,7 +251,7 @@ struct CJabberProto : public PROTO<CJabberProto>, public IJabberInterface
 	bool   m_bPepSupported;
 	bool   m_bStreamSent;
 	bool   m_bMamPrefsAvailable;
-	bool   m_bMamDisableMessages;
+	bool   m_bMamDisableMessages, m_bMamCreateRead;
 
 	HWND   m_hwndJabberChangePassword;
 	HWND   m_hwndPrivacyRule;
@@ -644,6 +644,7 @@ struct CJabberProto : public PROTO<CJabberProto>, public IJabberInterface
 	void       OnIqResultMamInfo(const TiXmlElement *iqNode, CJabberIqInfo *pInfo);
 	void       OnIqResultRsm(const TiXmlElement *iqNode, CJabberIqInfo *pInfo);
 
+	void       MamSendForm(const char *pszWith, const char *pszAfter = nullptr);
 	void       MamRetrieveMissingMessages(void);
 	void       MamSetMode(int iNewMode);
 
diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp
index a9b3a6c78d..2f1b96a73a 100755
--- a/protocols/JabberG/src/jabber_thread.cpp
+++ b/protocols/JabberG/src/jabber_thread.cpp
@@ -1005,8 +1005,8 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info)
 	if (!node->Name() || mir_strcmp(node->Name(), "message"))
 		return;
 
-	bool bEnableDelivery = true;
 	time_t msgTime = 0;
+	bool bEnableDelivery = true, bCreateRead = false, bWasSent = false;
 	auto *from = XmlGetAttr(node, "from"), *type = XmlGetAttr(node, "type"), *idStr = XmlGetAttr(node, "id");
 	const char *szMsgId = nullptr; // MAM support
 
@@ -1025,17 +1025,28 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info)
 			node = xmlMessage;
 			type = XmlGetAttr(node, "type");
 			from = XmlGetAttr(node, "from");
-			if (!mir_strcmpi(from, info->fullJID)) {
+			auto *to = XmlGetAttr(node, "to");
+
+			char szJid[JABBER_MAX_JID_LEN];
+			JabberStripJid(from, szJid, _countof(szJid));
+			if (!mir_strcmpi(szJid, m_szJabberJID)) {
+				bWasSent = true;
+				std::swap(from, to);
+			}
+
+			// we disable message reading with our resource only for the missing messages
+			if (!m_bMamCreateRead && !mir_strcmpi(to, info->fullJID)) {
 				debugLogA("MAM: outgoing message from this machine (%s), ignored", from);
 				return;
 			}
 		}
 
-		if (auto *xmlDelay = XmlGetChildByTag(xmlForwarded, "delay", "xmlns", JABBER_FEAT_DELAY))
+		if (auto *xmlDelay = XmlGetChildByTag(xmlForwarded, "delay", "xmlns", "urn:xmpp:delay"))
 			if (auto *ptszTimeStamp = XmlGetAttr(xmlDelay, "stamp"))
-				msgTime = JabberIsoToUnixTime(ptszTimeStamp);
+				msgTime = str2time(ptszTimeStamp);
 
 		bEnableDelivery = false;
+		bCreateRead = m_bMamCreateRead;
 	}
 
 	if (from == nullptr) {
@@ -1073,12 +1084,11 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info)
 
 	// Handle carbons. The message MUST be coming from our bare JID.
 	const TiXmlElement *carbon = nullptr;
-	bool carbonSent = false; //2 cases: received or sent.
 	if (IsMyOwnJID(from)) {
 		carbon = XmlGetChildByTag(node, "received", "xmlns", JABBER_FEAT_CARBONS);
 		if (!carbon) {
 			if (carbon = XmlGetChildByTag(node, "sent", "xmlns", JABBER_FEAT_CARBONS))
-				carbonSent = true;
+				bWasSent = true;
 		}
 		if (carbon) {
 			// If carbons are disabled in options, we should ignore occasional carbons sent to us by server
@@ -1087,6 +1097,7 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info)
 				return;
 			}
 
+			bCreateRead = true;
 			auto *xmlForwarded = XmlGetChildByTag(carbon, "forwarded", "xmlns", JABBER_XMLNS_FORWARD);
 			auto *xmlMessage = XmlFirstChild(xmlForwarded, "message");
 			// Carbons MUST have forwarded/message content
@@ -1099,7 +1110,7 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info)
 			node = xmlMessage;
 			type = XmlGetAttr(node, "type");
 
-			if (!carbonSent) {
+			if (!bWasSent) {
 				// Received should just be treated like incoming messages, except maybe not flash the flasher. Simply unwrap.
 				from = XmlGetAttr(node, "from");
 				if (from == nullptr) {
@@ -1278,7 +1289,7 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info)
 				return;
 			}
 
-			if (carbon && carbonSent)
+			if (carbon && bWasSent)
 				szMessage = TranslateU("Unable to decrypt a carbon copy of the encrypted outgoing message");
 			else {
 				// XEP-0027 is not strict enough, different clients have different implementations
@@ -1387,9 +1398,9 @@ void CJabberProto::OnProcessMessage(const TiXmlElement *node, ThreadData *info)
 		msgTime = now;
 
 	PROTORECVEVENT recv = {};
-	if (carbon) {
+	if (bCreateRead) {
 		recv.flags |= PREF_CREATEREAD;
-		if (carbonSent)
+		if (bWasSent)
 			recv.flags |= PREF_SENT;
 	}
 	recv.timestamp = (DWORD)msgTime;
-- 
cgit v1.2.3