squawk/qomemo/qomemo.cpp

281 lines
6.4 KiB
C++

/*
* Created by victoria on 2021-05-12.
*/
#include "qomemo.h"
#include "sce.h"
#include <shared/qxmppfactories.h>
#include <QBuffer>
#include <QDebug>
#include <QDomDocument>
using namespace QXmpp::Factories;
QXmppElement QXmpp::Omemo::DeviceList::toXml() const {
auto element = createElement("devices", "urn:xmpp:omemo:1");
for (const auto &d : devices) {
element.appendChild(d.toXml());
}
return element;
}
QXmppIq QXmpp::Omemo::DeviceList::toIq() const {
QXmppIq iq{};
iq.setType(QXmppIq::Set);
auto item = createElement("item");
item.setAttribute("id", "current");
item.appendChild(toXml());
auto publish = createElement("publish");
publish.setAttribute("node", "urn:xmpp:omemo:1:devices");
publish.appendChild(item);
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
pubSub.appendChild(publish);
pubSub.appendChild(createOpenPublishOptions());
iq.extensions().push_back(pubSub);
return iq;
}
void QXmpp::Omemo::DeviceList::fromXml(const QXmppElement &element) {
if (!elementMatches(element, "devices", "urn:xmpp:omemo:1"))
return;
devices.clear();
auto deviceElement = element.firstChildElement("device");
while (!deviceElement.isNull()) {
Device device{};
device.fromXml(deviceElement);
devices.push_back(device);
deviceElement = deviceElement.nextSiblingElement("device");
}
}
QXmppElement QXmpp::Omemo::Device::toXml() const {
auto result = createElement("device");
result.setAttribute("id", QString::number(id));
if (!label.isEmpty()) {
result.setAttribute("label", label);
}
return result;
}
void QXmpp::Omemo::Device::fromXml(const QXmppElement &element) {
if (!elementMatches(element, "device"))
return;
id = element.attribute("id").toInt();
label = element.attribute("label");
}
QXmppElement QXmpp::Omemo::PreKey::toXml() const {
auto pk = createElement("pk");
pk.setAttribute("id", QString::number(id));
// TODO: Base64
pk.setValue(data);
return pk;
}
void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) {
if (!elementMatches(element, "pk"))
return;
id = element.attribute("id").toInt();
// TODO: Base64
data = element.value();
}
QXmppElement QXmpp::Omemo::Bundle::toXml() const {
auto spkNode = createElement("spk");
spkNode.setAttribute("id", QString::number(spkId));
spkNode.setValue(spk);
auto spksNode = createElement("spks");
spksNode.setValue(spks);
auto ikNode = createElement("ik");
ikNode.setValue(ik);
auto prekeysNode = createElement("prekeys");
for (const auto &pk : prekeys) {
prekeysNode.appendChild(pk.toXml());
}
auto result = createElement("bundle", "urn:xmpp:omemo:1");
result.appendChild(spkNode);
result.appendChild(spksNode);
result.appendChild(ikNode);
result.appendChild(prekeysNode);
return result;
}
QXmppIq QXmpp::Omemo::Bundle::toIq() const {
QXmppIq iq{};
iq.setType(QXmppIq::Set);
auto item = createElement("item");
item.setAttribute("id", QString::number(deviceId));
item.appendChild(toXml());
auto publish = createElement("publish");
publish.setAttribute("node", "urn:xmpp:omemo:1:bundles");
publish.appendChild(item);
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
pubSub.appendChild(publish);
pubSub.appendChild(createOpenPublishOptions("max"));
iq.extensions().push_back(pubSub);
return iq;
}
void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) {
if (!elementMatches(element, "bundle", "urn:xmpp:omemo:1"))
return;
auto spkNode = element.firstChildElement("spk");
if (spkNode.isNull()) {
qWarning() << "'bundle': missing 'spk'";
return;
}
spk = spkNode.value();
spkId = spkNode.attribute("id").toInt();
auto spksNode = element.firstChildElement("spks");
if (spksNode.isNull()) {
qWarning() << "'bundle': missing 'spks'";
return;
}
spks = spksNode.value();
auto ikNode = element.firstChildElement("ik");
if (ikNode.isNull()) {
qWarning() << "'bundle': missing 'ik'";
return;
}
ik = ikNode.value();
auto prekeysNode = element.firstChildElement("prekeys");
auto pkNode = prekeysNode.firstChildElement("pk");
prekeys.clear();
while (!pkNode.isNull()) {
PreKey pk{};
pk.fromXml(pkNode);
prekeys.push_back(pk);
}
}
QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) {
QXmppPubSubIq iq{};
iq.setType(QXmppIq::Get);
iq.setQueryNode("urn:xmpp:omemo:1:bundles");
QXmppPubSubItem item{};
item.setId(QString::number(deviceId));
iq.setItems({item});
return iq;
}
QXmppElement QXmpp::Omemo::EncryptedMessage::header() const {
auto result = createElement("header");
result.setAttribute("sid", QString::number(fromDeviceId));
return result;
}
QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const {
auto result = createElement("encrypted", "urn:xmpp:omemo:1");
result.appendChild(header());
for (const auto &key : keys) {
result.appendChild(key.toXml());
}
return result;
}
QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const {
QBuffer buffer;
buffer.open(QIODevice::ReadWrite);
QXmlStreamWriter writer(&buffer);
message.toXml(&writer);
QDomDocument doc;
doc.setContent(buffer.data(), true);
QXmppElement root(doc.documentElement());
root.setTagName("payload");
return root;
}
QXmppElement QXmpp::Omemo::EncryptedMessage::content() const {
auto envelope = createElement("content", "urn:xmpp:sce:0");
envelope.appendChild(payload());
if (!from.isEmpty()) {
auto fromNode = createElement("from");
fromNode.setAttribute("jid", from);
envelope.appendChild(fromNode);
}
if (!to.isEmpty()) {
auto toNode = createElement("to");
toNode.setAttribute("jid", to);
envelope.appendChild(toNode);
}
if (!timestamp.isNull()) {
auto timeNode = createElement("time");
timeNode.setAttribute("stamp", timestamp.toString(Qt::DateFormat::ISODate));
envelope.appendChild(timeNode);
}
auto rpad = createElement("rpad");
rpad.setValue(QXmpp::Sce::generatePadding());
envelope.appendChild(rpad);
return envelope;
}
QXmppElement QXmpp::Omemo::MessageKey::toXml() const {
auto result = createElement("key");
if (kex)
result.setAttribute("kex", "true");
result.setValue(key);
return result;
}
QXmppElement QXmpp::Omemo::HeaderKeys::toXml() const {
auto result = createElement("keys");
result.setAttribute("jid", jid);
for (const auto &key : keys) {
result.appendChild(key.toXml());
}
return result;
}