squawk/qomemo/variant/conversations.cpp

206 lines
5.8 KiB
C++

/*
* Created by victoria on 2021-05-13.
*/
#include "conversations.h"
#include "qomemo/bundle.h"
#include "qomemo/device.h"
#include "shared/qxmppfactories.h"
#include <QDebug>
#include <QXmppIq.h>
#include <QXmppPubSubIq.h>
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<DeviceList> 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<PreKey> 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<Bundle> 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;
}