From 899e841aea8e54721ce9771780b05a07de05ce71 Mon Sep 17 00:00:00 2001
From: George Hazan <ghazan@miranda.im>
Date: Mon, 25 Feb 2019 15:16:23 +0300
Subject: Jabber:

- obsolete class Xpath removed, its functionality replaced with helpers & iterators;
- JABBER_FEAT_BIND added to enchance code reading & understanding;
- unused function CNoteItem::AddNote removed;
- fix for improper jabber:x:last behavior
---
 protocols/JabberG/src/jabber_caps.cpp      |  41 ++++---
 protocols/JabberG/src/jabber_caps.h        |   1 +
 protocols/JabberG/src/jabber_iqid.cpp      |  75 ++++++------
 protocols/JabberG/src/jabber_notes.cpp     |  14 +--
 protocols/JabberG/src/jabber_notes.h       |   1 -
 protocols/JabberG/src/jabber_strm_mgmt.cpp |   2 +-
 protocols/JabberG/src/jabber_thread.cpp    |  10 +-
 protocols/JabberG/src/jabber_xml.cpp       | 188 -----------------------------
 protocols/JabberG/src/jabber_xml.h         | 133 --------------------
 protocols/JabberG/src/jabber_xstatus.cpp   |   2 +-
 10 files changed, 74 insertions(+), 393 deletions(-)

(limited to 'protocols/JabberG')

diff --git a/protocols/JabberG/src/jabber_caps.cpp b/protocols/JabberG/src/jabber_caps.cpp
index 305cf845eb..94e949cbbf 100755
--- a/protocols/JabberG/src/jabber_caps.cpp
+++ b/protocols/JabberG/src/jabber_caps.cpp
@@ -161,25 +161,30 @@ void CJabberProto::OnIqResultCapsDiscoInfo(const TiXmlElement*, CJabberIqInfo *p
 		}
 
 		for (auto *xform : TiXmlFilter(query, "x")) {
-			const char *szFormTypeValue = XPath(xform, "field[@var='FORM_TYPE']/value");
-			if (!mir_strcmp(szFormTypeValue, "urn:xmpp:dataforms:softwareinfo")) {
-				JSONNode root;
-				if (pCaps->m_szOs = mir_strdup(XPath(xform, "field[@var='os']/value")))
-					root.push_back(JSONNode("o", pCaps->m_szOs.get()));
-				if (pCaps->m_szOsVer = mir_strdup(XPath(xform, "field[@var='os_version']/value")))
-					root.push_back(JSONNode("ov", pCaps->m_szOsVer.get()));
-				if (pCaps->m_szSoft = mir_strdup(XPath(xform, "field[@var='software']/value")))
-					root.push_back(JSONNode("s", pCaps->m_szSoft.get()));
-				if (pCaps->m_szSoftVer = mir_strdup(XPath(xform, "field[@var='software_version']/value")))
-					root.push_back(JSONNode("sv", pCaps->m_szSoftVer.get()));
-				if (pCaps->m_szSoftMir = mir_strdup(XPath(xform, "field[@var='x-miranda-core-version']/value")))
-					root.push_back(JSONNode("sm", pCaps->m_szSoftMir.get()));
-				root.push_back(JSONNode("c", CMStringA(FORMAT, "%lld", jcbCaps)));
-
-				CMStringA szName(FORMAT, "%s#%s", pCaps->GetNode(), pCaps->GetHash());
-				json_string szValue = root.write();
-				db_set_s(0, "JabberCaps", szName, szValue.c_str());
+			// check that this is a form of required type
+			auto *formType = XmlGetChildText(XmlGetChildByTag(xform, "field", "var", "FORM_TYPE"), "value");
+			if (!formType || mir_strcmp(formType, "urn:xmpp:dataforms:softwareinfo"))
+				continue;
+
+			JSONNode root;
+			for (auto *field : TiXmlFilter(xform, "field")) {
+				const char *fieldName = field->Attribute("var"), *fieldValue = XmlGetChildText(field, "value");
+				if (!mir_strcmp(fieldName, "os"))
+					root.push_back(JSONNode("o", pCaps->m_szOs = mir_strdup(fieldValue)));
+				else if (!mir_strcmp(fieldName, "os_version"))
+					root.push_back(JSONNode("ov", pCaps->m_szOsVer = mir_strdup(fieldValue)));
+				else if (!mir_strcmp(fieldName, "software"))
+					root.push_back(JSONNode("s", pCaps->m_szSoft = mir_strdup(fieldValue)));
+				else if (!mir_strcmp(fieldName, "software_version"))
+					root.push_back(JSONNode("sv", pCaps->m_szSoftVer = mir_strdup(fieldValue)));
+				else if (!mir_strcmp(fieldName, "x-miranda-core-version"))
+					root.push_back(JSONNode("sm", pCaps->m_szSoftMir = mir_strdup(fieldValue)));
 			}
+			root.push_back(JSONNode("c", CMStringA(FORMAT, "%lld", jcbCaps)));
+
+			CMStringA szName(FORMAT, "%s#%s", pCaps->GetNode(), pCaps->GetHash());
+			json_string szValue = root.write();
+			db_set_s(0, "JabberCaps", szName, szValue.c_str());
 		}
 
 		pCaps->SetCaps(jcbCaps, pInfo->GetIqId());
diff --git a/protocols/JabberG/src/jabber_caps.h b/protocols/JabberG/src/jabber_caps.h
index 4c8e985990..683b44b897 100755
--- a/protocols/JabberG/src/jabber_caps.h
+++ b/protocols/JabberG/src/jabber_caps.h
@@ -132,6 +132,7 @@ typedef unsigned __int64 JabberCapsBits;
 #define JABBER_CAPS_ARCHIVE_MANAGE              ((JabberCapsBits)1<<35)
 
 #define JABBER_FEAT_CAPTCHA                     "urn:xmpp:captcha"
+#define JABBER_FEAT_BIND                        "urn:ietf:params:xml:ns:xmpp-bind"
 
 #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 627f404a8a..ad5b04c556 100755
--- a/protocols/JabberG/src/jabber_iqid.cpp
+++ b/protocols/JabberG/src/jabber_iqid.cpp
@@ -82,14 +82,14 @@ void CJabberProto::OnIqResultServerDiscoInfo(const TiXmlElement *iqNode, CJabber
 
 void CJabberProto::OnIqResultNestedRosterGroups(const TiXmlElement *iqNode, CJabberIqInfo *pInfo)
 {
-	const char *szGroupDelimeter = nullptr;
+	const char *szGroupDelimiter = nullptr;
 	bool bPrivateStorageSupport = false;
 
 	if (iqNode && pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) {
 		bPrivateStorageSupport = true;
-		szGroupDelimeter = XPathFmt(iqNode, "query[@xmlns='%s']/roster[@xmlns='%s']", JABBER_FEAT_PRIVATE_STORAGE, JABBER_FEAT_NESTED_ROSTER_GROUPS);
-		if (szGroupDelimeter && !szGroupDelimeter[0])
-			szGroupDelimeter = nullptr; // "" as roster delimeter is not supported :)
+		auto *xmlDelimiter = XmlGetChildByTag(XmlGetChildByTag(iqNode, "query", "xmlns", JABBER_FEAT_PRIVATE_STORAGE), "roster", "xmlns", JABBER_FEAT_NESTED_ROSTER_GROUPS);
+		if (xmlDelimiter)
+			szGroupDelimiter = xmlDelimiter->GetText();
 	}
 
 	// global fuckup
@@ -97,13 +97,13 @@ void CJabberProto::OnIqResultNestedRosterGroups(const TiXmlElement *iqNode, CJab
 		return;
 
 	// is our default delimiter?
-	if ((!szGroupDelimeter && bPrivateStorageSupport) || (szGroupDelimeter && mir_strcmp(szGroupDelimeter, "\\")))
+	if ((!szGroupDelimiter && bPrivateStorageSupport) || (szGroupDelimiter && mir_strcmp(szGroupDelimiter, "\\")))
 		m_ThreadInfo->send(
 			XmlNodeIq("set", SerialNext()) << XQUERY(JABBER_FEAT_PRIVATE_STORAGE)
 				<< XCHILD("roster", "\\") << XATTR("xmlns", JABBER_FEAT_NESTED_ROSTER_GROUPS));
 
 	// roster request
-	char *szUserData = mir_strdup(szGroupDelimeter ? szGroupDelimeter : "\\");
+	char *szUserData = mir_strdup(szGroupDelimiter ? szGroupDelimiter : "\\");
 	m_ThreadInfo->send(
 		XmlNodeIq(AddIQ(&CJabberProto::OnIqResultGetRoster, JABBER_IQ_TYPE_GET, nullptr, 0, -1, (void*)szUserData))
 			<< XCHILDNS("query", JABBER_FEAT_IQ_ROSTER));
@@ -293,7 +293,7 @@ void CJabberProto::OnIqResultBind(const TiXmlElement *iqNode, CJabberIqInfo *pIn
 	if (!m_ThreadInfo || !iqNode)
 		return;
 	if (pInfo->GetIqType() == JABBER_IQ_TYPE_RESULT) {
-		const char *szJid = XPath(iqNode, "bind[@xmlns='urn:ietf:params:xml:ns:xmpp-bind']/jid");
+		const char *szJid = XmlGetChildText(XmlGetChildByTag(iqNode, "bind", "xmlns", JABBER_FEAT_BIND), "jid");
 		if (szJid) {
 			if (!strncmp(m_ThreadInfo->fullJID, szJid, _countof(m_ThreadInfo->fullJID)))
 				debugLogA("Result Bind: %s confirmed ", m_ThreadInfo->fullJID);
@@ -351,7 +351,7 @@ void CJabberProto::GroupchatJoinByHContact(MCONTACT hContact, bool autojoin)
 void CJabberProto::OnIqResultGetRoster(const TiXmlElement *iqNode, CJabberIqInfo *pInfo)
 {
 	debugLogA("<iq/> iqIdGetRoster");
-	ptrA szGroupDelimeter((char *)pInfo->GetUserData());
+	ptrA szGroupDelimiter((char *)pInfo->GetUserData());
 	if (pInfo->GetIqType() != JABBER_IQ_TYPE_RESULT)
 		return;
 
@@ -362,8 +362,8 @@ void CJabberProto::OnIqResultGetRoster(const TiXmlElement *iqNode, CJabberIqInfo
 	if (mir_strcmp(queryNode->Attribute("xmlns"), JABBER_FEAT_IQ_ROSTER))
 		return;
 
-	if (!mir_strcmp(szGroupDelimeter, "\\"))
-		szGroupDelimeter = nullptr;
+	if (!mir_strcmp(szGroupDelimiter, "\\"))
+		szGroupDelimiter = nullptr;
 
 	LIST<void> chatRooms(10);
 	OBJLIST<JABBER_HTTP_AVATARS> *httpavatars = new OBJLIST<JABBER_HTTP_AVATARS>(20, JABBER_HTTP_AVATARS::compare);
@@ -402,10 +402,10 @@ void CJabberProto::OnIqResultGetRoster(const TiXmlElement *iqNode, CJabberIqInfo
 		replaceStr(item->group, XmlGetChildText(itemNode, "group"));
 
 		// check group delimiters
-		if (item->group && szGroupDelimeter) {
-			while (char *szPos = strstr(item->group, szGroupDelimeter)) {
+		if (item->group && szGroupDelimiter) {
+			while (char *szPos = strstr(item->group, szGroupDelimiter)) {
 				*szPos = 0;
-				szPos += mir_strlen(szGroupDelimeter);
+				szPos += mir_strlen(szGroupDelimiter);
 				CMStringA szNewGroup(FORMAT, "%s\\%s", item->group, szPos);
 				replaceStr(item->group, szNewGroup.Detach());
 			}
@@ -491,8 +491,8 @@ void CJabberProto::OnIqResultGetRoster(const TiXmlElement *iqNode, CJabberIqInfo
 
 	UI_SAFE_NOTIFY(m_pDlgServiceDiscovery, WM_JABBER_TRANSPORT_REFRESH);
 
-	if (szGroupDelimeter)
-		mir_free(szGroupDelimeter);
+	if (szGroupDelimiter)
+		mir_free(szGroupDelimiter);
 
 	OnProcessLoginRq(m_ThreadInfo, JABBER_LOGIN_ROSTER);
 	RebuildInfoFrame();
@@ -1424,8 +1424,8 @@ void CJabberProto::OnIqResultDiscoBookmarks(const TiXmlElement *iqNode, CJabberI
 						item->name = mir_utf8decodeW(itemNode->Attribute("name"));
 						item->type = mir_strdup("conference");
 						item->bUseResource = true;
-						item->nick = mir_strdup(XPath(itemNode, "nick"));
-						item->password = mir_strdup(XPath(itemNode, "password"));
+						item->nick = mir_strdup(XmlGetChildText(itemNode, "nick"));
+						item->password = mir_strdup(XmlGetChildText(itemNode, "password"));
 
 						const char *autoJ = itemNode->Attribute("autojoin");
 						if (autoJ != nullptr)
@@ -1515,7 +1515,7 @@ void CJabberProto::OnIqResultLastActivity(const TiXmlElement *iqNode, CJabberIqI
 	time_t lastActivity = -1;
 	if (pInfo->m_nIqType == JABBER_IQ_TYPE_RESULT) {
 		if (auto *xmlLast = XmlGetChildByTag(iqNode, "query", "xmlns", JABBER_FEAT_LAST_ACTIVITY)) {
-			int nSeconds = XmlGetChildInt(xmlLast, "seconds");
+			int nSeconds = xmlLast->IntAttribute("seconds");
 			if (nSeconds > 0)
 				lastActivity = time(0) - nSeconds;
 
@@ -1536,24 +1536,27 @@ void CJabberProto::OnIqResultEntityTime(const TiXmlElement *pIqNode, CJabberIqIn
 		return;
 
 	if (pInfo->m_nIqType == JABBER_IQ_TYPE_RESULT) {
-		const char *szTzo = XPathFmt(pIqNode, "time[@xmlns='%s']/tzo", JABBER_FEAT_ENTITY_TIME);
-		if (szTzo && szTzo[0]) {
-			const char *szMin = strchr(szTzo, ':');
-			int nTz = atoi(szTzo) * -2;
-			nTz += (nTz < 0 ? -1 : 1) * (szMin ? atoi(szMin + 1) / 30 : 0);
-
-			TIME_ZONE_INFORMATION tzinfo;
-			if (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT)
-				nTz -= tzinfo.DaylightBias / 30;
-
-			setByte(pInfo->m_hContact, "Timezone", (signed char)nTz);
-
-			const char *szTz = XPathFmt(pIqNode, "time[@xmlns='%s']/tz", JABBER_FEAT_ENTITY_TIME);
-			if (szTz)
-				setUString(pInfo->m_hContact, "TzName", szTz);
-			else
-				delSetting(pInfo->m_hContact, "TzName");
-			return;
+		auto *xmlTime = XmlGetChildByTag(pIqNode, "time", "xmlns", JABBER_FEAT_ENTITY_TIME);
+		if (xmlTime) {
+			const char *szTzo = XmlGetChildText(xmlTime, "tzo");
+			if (szTzo && szTzo[0]) {
+				const char *szMin = strchr(szTzo, ':');
+				int nTz = atoi(szTzo) * -2;
+				nTz += (nTz < 0 ? -1 : 1) * (szMin ? atoi(szMin + 1) / 30 : 0);
+
+				TIME_ZONE_INFORMATION tzinfo;
+				if (GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT)
+					nTz -= tzinfo.DaylightBias / 30;
+
+				setByte(pInfo->m_hContact, "Timezone", (signed char)nTz);
+
+				const char *szTz = XmlGetChildText(xmlTime, "tz");
+				if (szTz)
+					setUString(pInfo->m_hContact, "TzName", szTz);
+				else
+					delSetting(pInfo->m_hContact, "TzName");
+				return;
+			}
 		}
 	}
 	else if (pInfo->m_nIqType == JABBER_IQ_TYPE_ERROR) {
diff --git a/protocols/JabberG/src/jabber_notes.cpp b/protocols/JabberG/src/jabber_notes.cpp
index 67678f107c..3b346ff64c 100644
--- a/protocols/JabberG/src/jabber_notes.cpp
+++ b/protocols/JabberG/src/jabber_notes.cpp
@@ -38,10 +38,10 @@ CNoteItem::CNoteItem()
 CNoteItem::CNoteItem(const TiXmlElement *hXml, const char *szFrom)
 {
 	SetData(
-		XPath(hXml, "title"),
-		szFrom ? szFrom : XPath(hXml, "@from"),
-		Utf2T(XPath(hXml, "text")),
-		XPath(hXml, "@tags"));
+		XmlGetChildText(hXml, "title"),
+		szFrom ? szFrom : hXml->Attribute("from"),
+		Utf2T(XmlGetChildText(hXml, "text")),
+		hXml->Attribute("tags"));
 }
 
 CNoteItem::~CNoteItem()
@@ -107,12 +107,6 @@ int CNoteItem::cmp(const CNoteItem *p1, const CNoteItem *p2)
 	return 0;
 }
 
-void CNoteList::AddNote(TiXmlElement *hXml, const char *szFrom)
-{
-	m_bIsModified = true;
-	insert(new CNoteItem(hXml, szFrom));
-}
-
 void CNoteList::LoadXml(const TiXmlElement *hXml)
 {
 	destroy();
diff --git a/protocols/JabberG/src/jabber_notes.h b/protocols/JabberG/src/jabber_notes.h
index c2867970b3..3f0613a044 100644
--- a/protocols/JabberG/src/jabber_notes.h
+++ b/protocols/JabberG/src/jabber_notes.h
@@ -77,7 +77,6 @@ public:
 		OBJLIST<CNoteItem>::remove(p);
 	}
 
-	void AddNote(TiXmlElement *hXml, const char *szFrom = nullptr);
 	void LoadXml(const TiXmlElement *hXml);
 	void SaveXml(TiXmlElement *hXmlParent);
 
diff --git a/protocols/JabberG/src/jabber_strm_mgmt.cpp b/protocols/JabberG/src/jabber_strm_mgmt.cpp
index 9a61f66c63..b7682364aa 100755
--- a/protocols/JabberG/src/jabber_strm_mgmt.cpp
+++ b/protocols/JabberG/src/jabber_strm_mgmt.cpp
@@ -272,7 +272,7 @@ void strm_mgmt::FinishLoginProcess(ThreadData *info)
 	if (info->auth) { //We are already logged-in
 		info->send(
 			XmlNodeIq(proto->AddIQ(&CJabberProto::OnIqResultBind, JABBER_IQ_TYPE_SET))
-			<< XCHILDNS("bind", "urn:ietf:params:xml:ns:xmpp-bind")
+			<< XCHILDNS("bind", JABBER_FEAT_BIND)
 			<< XCHILD("resource", info->resource));
 
 		if (proto->m_AuthMechs.isSessionAvailable)
diff --git a/protocols/JabberG/src/jabber_thread.cpp b/protocols/JabberG/src/jabber_thread.cpp
index 439ed33d69..fa9ea84c14 100755
--- a/protocols/JabberG/src/jabber_thread.cpp
+++ b/protocols/JabberG/src/jabber_thread.cpp
@@ -760,7 +760,7 @@ void CJabberProto::OnProcessFeatures(const TiXmlElement *node, ThreadData *info)
 		if (info->auth) { //We are already logged-in
 			info->send(
 				XmlNodeIq(AddIQ(&CJabberProto::OnIqResultBind, JABBER_IQ_TYPE_SET))
-				<< XCHILDNS("bind", "urn:ietf:params:xml:ns:xmpp-bind")
+				<< XCHILDNS("bind", JABBER_FEAT_BIND)
 				<< XCHILD("resource", info->resource));
 
 			if (m_AuthMechs.isSessionAvailable)
@@ -989,10 +989,10 @@ void CJabberProto::OnProcessPubsubEvent(const TiXmlElement *node)
 		if (!tuneNode)
 			return;
 
-		const char *szArtist = XPath(tuneNode, "artist");
-		const char *szSource = XPath(tuneNode, "source");
-		const char *szTitle = XPath(tuneNode, "title");
-		const char *szTrack = XPath(tuneNode, "track");
+		const char *szArtist = XmlGetChildText(tuneNode, "artist");
+		const char *szSource = XmlGetChildText(tuneNode, "source");
+		const char *szTitle = XmlGetChildText(tuneNode, "title");
+		const char *szTrack = XmlGetChildText(tuneNode, "track");
 
 		wchar_t szLengthInTime[20];
 		int nLength = XmlGetChildInt(tuneNode, "length");
diff --git a/protocols/JabberG/src/jabber_xml.cpp b/protocols/JabberG/src/jabber_xml.cpp
index bd0fbe08eb..f2336123c7 100644
--- a/protocols/JabberG/src/jabber_xml.cpp
+++ b/protocols/JabberG/src/jabber_xml.cpp
@@ -199,191 +199,3 @@ int XmlGetChildCount(const TiXmlElement *hXml)
 	}
 	return iCount;
 }
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
-void XPath::ProcessPath(LookupInfo &info)
-{
-	if (!info.nodeName) return;
-
-	char *nodeName = (char *)alloca(sizeof(char) * (info.nodeName.length+1));
-	mir_strncpy(nodeName, info.nodeName.p, info.nodeName.length+1);
-
-	if (info.attrName && info.attrValue) {
-		char *attrName = (char *)alloca(sizeof(char)* (info.attrName.length + 1));
-		mir_strncpy(attrName, info.attrName.p, info.attrName.length + 1);
-		char *attrValue = (char *)alloca(sizeof(char)* (info.attrValue.length + 1));
-		mir_strncpy(attrValue, info.attrValue.p, info.attrValue.length + 1);
-		m_hXml = XmlGetChildByTag(m_hXml, nodeName, attrName, attrValue);
-	}
-	else m_hXml = m_hXml->FirstChildElement(nodeName);
-
-	info.Reset();
-}
-
-XPath::PathType XPath::LookupImpl()
-{
-	LookupState state = S_START;
-	LookupInfo info = {};
-
-	for (const char *p = m_szPath; state < S_FINAL; ++p) {
-		switch (state) {
-		case S_START:
-			ProcessPath(info);
-			if (!m_hXml) {
-				state = S_FINAL_ERROR;
-				break;
-			}
-
-			switch (*p) {
-			case 0:
-				state = S_FINAL_ERROR;
-				break;
-			case '@':
-				info.attrName.Begin(p + 1);
-				state = S_ATTR_STEP;
-				break;
-			case '/':
-				break;
-			default:
-				info.nodeName.Begin(p);
-				state = S_NODE_NAME;
-				break;
-			};
-			break;
-
-		case S_ATTR_STEP:
-			switch (*p) {
-			case 0:
-				info.attrName.End(p);
-				state = S_FINAL_ATTR;
-				break;
-			default:
-				break;
-			};
-			break;
-
-		case S_NODE_NAME:
-			switch (*p) {
-			case 0:
-				info.nodeName.End(p);
-				state = S_FINAL_NODESET;
-				break;
-			case '[':
-				info.nodeName.End(p);
-				state = S_NODE_OPENBRACKET;
-				break;
-			case '/':
-				info.nodeName.End(p);
-				state = S_START;
-				break;
-			default:
-				break;
-			};
-			break;
-
-		case S_NODE_OPENBRACKET:
-			switch (*p) {
-			case 0:
-				state = S_FINAL_ERROR;
-				break;
-			case '@':
-				info.attrName.Begin(p + 1);
-				state = S_NODE_ATTRNAME;
-				break;
-			default:
-				state = S_FINAL_ERROR;
-				break;
-			};
-			break;
-
-		case S_NODE_ATTRNAME:
-			switch (*p) {
-			case 0:
-				state = S_FINAL_ERROR;
-				break;
-			case '=':
-				info.attrName.End(p);
-				state = S_NODE_ATTREQUALS;
-				break;
-			default:
-				break;
-			};
-			break;
-
-		case S_NODE_ATTREQUALS:
-			switch (*p) {
-			case 0:
-				state = S_FINAL_ERROR;
-				break;
-			case '\'':
-				info.attrValue.Begin(p + 1);
-				state = S_NODE_ATTRVALUE;
-				break;
-			default:
-				state = S_FINAL_ERROR;
-				break;
-			};
-			break;
-
-		case S_NODE_ATTRVALUE:
-			switch (*p) {
-			case 0:
-				state = S_FINAL_ERROR;
-				break;
-			case '\'':
-				info.attrValue.End(p);
-				state = S_NODE_ATTRCLOSEVALUE;
-				break;
-			default:
-				break;
-			};
-			break;
-
-		case S_NODE_ATTRCLOSEVALUE:
-			switch (*p) {
-			case 0:
-				state = S_FINAL_ERROR;
-				break;
-			case ']':
-				state = S_NODE_CLOSEBRACKET;
-				break;
-			default:
-				state = S_FINAL_ERROR;
-				break;
-			};
-			break;
-
-		case S_NODE_CLOSEBRACKET:
-			switch (*p) {
-			case 0:
-				state = S_FINAL_NODE;
-				break;
-			case '/':
-				state = S_START;
-				break;
-			default:
-				state = S_FINAL_ERROR;
-				break;
-			};
-			break;
-		}
-
-		if (!*p && (state < S_FINAL))
-			state = S_FINAL_ERROR;
-	}
-
-	switch (state) {
-	case S_FINAL_ATTR:
-		m_szParam = info.attrName.p;
-		return T_ATTRIBUTE;
-	case S_FINAL_NODE:
-		ProcessPath(info);
-		return T_NODE;
-	case S_FINAL_NODESET:
-		m_szParam = info.nodeName.p;
-		return T_NODESET;
-	}
-
-	return T_ERROR;
-}
diff --git a/protocols/JabberG/src/jabber_xml.h b/protocols/JabberG/src/jabber_xml.h
index 84be255ba5..098e8ae346 100644
--- a/protocols/JabberG/src/jabber_xml.h
+++ b/protocols/JabberG/src/jabber_xml.h
@@ -188,137 +188,4 @@ struct XQUERY
 
 TiXmlElement* __fastcall operator<<(TiXmlElement *node, const XQUERY& child);
 
-/////////////////////////////////////////////////////////////////////////////////////////
-// Limited XPath support
-//     path should look like: "node-spec/node-spec/.../result-spec"
-//     where "node-spec" can be "node-name", "node-name[@attr-name='attr-value']" or "node-name[node-index]"
-//     result may be either "node-spec", or "@attr-name"
-//
-// Samples:
-//    const char *s = XPath(node, "child/subchild[@attr='value']");          // get node text
-//    XPath(node, "child/subchild[@name='test']/@attr") = L"Hello";   // create path if needed and set attribute value
-//
-//    XPath(node, "child/subchild[@name='test']") = L"Hello";         // TODO: create path if needed and set node text
-
-class XPath
-{
-public:
-	__forceinline XPath(const TiXmlElement *hXml, char *path):
-		m_type(T_UNKNOWN),
-		m_szPath(path),
-		m_hXml(hXml),
-		m_szParam(nullptr)
-	{}
-
-	// Read data
-	operator const TiXmlElement*()
-	{
-		switch (Lookup())
-		{
-			case T_NODE:    return m_hXml;
-			case T_NODESET: return m_hXml->FirstChildElement(m_szParam);
-		}
-		return nullptr;
-	}
-	operator LPCSTR()
-	{
-		switch (Lookup())
-		{
-			case T_ATTRIBUTE: return m_hXml->Attribute(m_szParam);
-			case T_NODE:      return m_hXml->GetText();
-			case T_NODESET:   return (m_hXml->FirstChildElement()) ? m_hXml->FirstChildElement()->GetText() : 0;
-		}
-		return nullptr;
-	}
-	__forceinline bool operator== (char *str)
-	{
-		return !mir_strcmp(*this, str);
-	}
-	__forceinline bool operator!= (char *str)
-	{
-		return mir_strcmp(*this, str) ? true : false;
-	}
-
-private:
-	enum PathType
-	{
-		T_UNKNOWN,
-		T_ERROR,
-		T_NODE,
-		T_ATTRIBUTE,
-		T_NODESET
-	};
-
-	__forceinline PathType Lookup()
-	{
-		return (m_type == T_UNKNOWN) ? LookupImpl() : m_type;
-	}
-
-	enum LookupState
-	{
-		S_START,
-		S_ATTR_STEP,
-		S_NODE_NAME,
-		S_NODE_OPENBRACKET,
-		S_NODE_ATTRNAME,
-		S_NODE_ATTREQUALS,
-		S_NODE_ATTRVALUE,
-		S_NODE_ATTRCLOSEVALUE,
-		S_NODE_CLOSEBRACKET,
-
-		S_FINAL,
-		S_FINAL_ERROR,
-		S_FINAL_ATTR,
-		S_FINAL_NODESET,
-		S_FINAL_NODE
-	};
-
-	struct LookupString
-	{
-		void Begin(const char *p_) { p = p_; }
-		void End(const char *p_) { length = p_ - p; }
-		operator bool() { return p ? true : false; }
-
-		const char *p;
-		int length;
-
-	};
-
-	struct LookupInfo
-	{
-		void Reset() { memset(this, 0, sizeof(*this)); }
-		LookupString nodeName;
-		LookupString attrName;
-		LookupString attrValue;
-	};
-
-	void ProcessPath(LookupInfo &info);
-	PathType LookupImpl();
-
-	PathType m_type;
-	const TiXmlElement *m_hXml;
-	const char *m_szPath;
-	const char *m_szParam;
-};
-
-class XPathFmt: public XPath
-{
-public:
-	enum { BUFSIZE = 512 };
-	XPathFmt(const TiXmlElement *hXml, char *path, ...): XPath(hXml, m_buf)
-	{
-		*m_buf = 0;
-		char buf[BUFSIZE];
-
-		va_list args;
-		va_start(args, path);
-		mir_vsnprintf(buf, BUFSIZE, path, args);
-		buf[BUFSIZE-1] = 0;
-		va_end(args);
-	}
-
-private:
-	char m_buf[BUFSIZE];
-};
-
 #endif
diff --git a/protocols/JabberG/src/jabber_xstatus.cpp b/protocols/JabberG/src/jabber_xstatus.cpp
index 70fad9c365..7909374f13 100644
--- a/protocols/JabberG/src/jabber_xstatus.cpp
+++ b/protocols/JabberG/src/jabber_xstatus.cpp
@@ -1003,7 +1003,7 @@ void CPepActivity::ProcessItems(const char *from, const TiXmlElement *itemsNode)
 	if (!actNode)
 		return;
 
-	const char *szText = XPath(actNode, "text");
+	const char *szText = XmlGetChildText(actNode, "text");
 	const char *szFirstNode = nullptr, *szSecondNode = nullptr;
 
 	for (auto *n : TiXmlFilter(actNode, "text")) {
-- 
cgit v1.2.3