/* * Created by victoria on 2021-05-13. */ #include "conversations.h" #include "qomemo/bundle.h" #include "qomemo/device.h" #include "shared/qxmppfactories.h" #include #include #include using namespace QXmpp::Omemo; using namespace QXmpp::Factories; QXmppElement Variant::Conversations::deviceToXml(const Device &device) { auto result = createElement("device"); result.setAttribute("id", QString::number(device.id)); return result; } Device Variant::Conversations::deviceFromXml(const QXmppElement &xml) { Device result{}; if (!elementMatches(xml, "device")) return result; result.id = xml.attribute("id").toInt(); return result; } QXmppElement Variant::Conversations::deviceListToXml(const DeviceList &deviceList) { auto element = createElement("list", "eu.siacs.conversations.axolotl"); for (const auto &device : deviceList.devices) { element.appendChild(deviceToXml(device)); } return element; } std::optional Variant::Conversations::latestDeviceListFromPubSubNode(const QXmppElement &items) { auto item = items.firstChildElement("item"); if (item.isNull()) return std::nullopt; auto list = item.firstChildElement("list"); if (list.isNull()) return std::nullopt; if (!elementMatches(list, "list", "eu.siacs.conversations.axolotl")) return std::nullopt; DeviceList result{}; auto deviceElement = list.firstChildElement("device"); while (!deviceElement.isNull()) { result.devices.push_back(deviceFromXml(deviceElement)); deviceElement = deviceElement.nextSiblingElement("device"); } return result; } QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) { QXmppIq iq{}; iq.setType(QXmppIq::Set); auto item = createElement("item"); item.appendChild(deviceListToXml(deviceList)); auto publish = createElement("publish"); publish.setAttribute("node", getDeviceListNode()); publish.appendChild(item); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); pubSub.appendChild(publish); pubSub.appendChild(createOpenPublishOptions()); iq.setExtensions({ pubSub }); return iq; } QString Variant::Conversations::getDeviceListNode() const { return "eu.siacs.conversations.axolotl.devicelist"; } QXmppIq Variant::Conversations::bundleSetIq(int deviceId, const Bundle &bundle) { QXmppIq iq{}; iq.setType(QXmppIq::Set); auto item = createElement("item"); item.appendChild(bundleToXml(bundle)); auto publish = createElement("publish"); publish.setAttribute( "node", QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); publish.appendChild(item); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); pubSub.appendChild(publish); pubSub.appendChild(createOpenPublishOptions()); iq.setExtensions({ pubSub }); return iq; } QXmppElement Variant::Conversations::bundleToXml(const Bundle &bundle) { auto spkNode = createElement("signedPreKeyPublic"); spkNode.setAttribute("signedPreKeyId", QString::number(bundle.spkId)); spkNode.setValue(bundle.spk.toBase64()); auto spksNode = createElement("signedPreKeySignature"); spksNode.setValue(bundle.spks.toBase64()); auto ikNode = createElement("identityKey"); ikNode.setValue(bundle.ik.toBase64()); auto prekeysNode = createElement("prekeys"); for (const auto &pk : bundle.prekeys) { prekeysNode.appendChild(preKeyToXml(pk)); } auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); result.appendChild(spkNode); result.appendChild(spksNode); result.appendChild(ikNode); result.appendChild(prekeysNode); return result; } QXmppElement Variant::Conversations::preKeyToXml(const PreKey &pk) { auto x = createElement("preKeyPublic"); x.setAttribute("preKeyId", QString::number(pk.id)); x.setValue(pk.data.toBase64()); return x; } std::optional Variant::Conversations::preKeyFromXml(const QXmppElement &xml) { if (!elementMatches(xml, "preKeyPublic")) return std::nullopt; auto pk = PreKey(); pk.id = xml.attribute("preKeyId").toInt(); pk.data = QByteArray::fromBase64Encoding(xml.value().toUtf8()).decoded; return pk; } std::optional Variant::Conversations::bundleFromXml(const QXmppElement &xml) { if (!elementMatches(xml, "bundle", "eu.siacs.conversations.axolotl")) return std::nullopt; auto spkNode = xml.firstChildElement("signedPreKeyPublic"); if (spkNode.isNull()) { qWarning() << "'bundle': missing 'signedPreKeyPublic'"; return std::nullopt; } Bundle result{}; result.spk = QByteArray::fromBase64Encoding(spkNode.value().toUtf8()).decoded; result.spkId = spkNode.attribute("signedPreKeyId").toInt(); auto spksNode = xml.firstChildElement("signedPreKeySignature"); if (spksNode.isNull()) { qWarning() << "'bundle': missing 'signedPreKeySignature'"; return std::nullopt; } result.spks = QByteArray::fromBase64Encoding(spksNode.value().toUtf8()).decoded; auto ikNode = xml.firstChildElement("identityKey"); if (ikNode.isNull()) { qWarning() << "'bundle': missing 'identityKey'"; return std::nullopt; } result.ik = QByteArray::fromBase64Encoding(ikNode.value().toUtf8()).decoded; auto prekeysNode = xml.firstChildElement("prekeys"); auto pkNode = prekeysNode.firstChildElement("preKeyPublic"); result.prekeys.clear(); while (!pkNode.isNull()) { auto maybePk = preKeyFromXml(pkNode); if (maybePk) result.prekeys.push_back(*maybePk); pkNode = pkNode.nextSiblingElement("preKeyPublic"); } return result; }