forked from blue/squawk
269 lines
6.5 KiB
C++
269 lines
6.5 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("list", "eu.siacs.conversations.axolotl");
|
|
|
|
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.appendChild(toXml());
|
|
|
|
auto publish = createElement("publish");
|
|
publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist");
|
|
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, "list", "eu.siacs.conversations.axolotl"))
|
|
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));
|
|
|
|
return result;
|
|
}
|
|
|
|
void QXmpp::Omemo::Device::fromXml(const QXmppElement &element) {
|
|
if (!elementMatches(element, "device"))
|
|
return;
|
|
|
|
id = element.attribute("id").toInt();
|
|
}
|
|
|
|
QXmppElement QXmpp::Omemo::PreKey::toXml() const {
|
|
auto pk = createElement("preKeyPublic");
|
|
pk.setAttribute("preKeyId", QString::number(id));
|
|
// TODO: Base64
|
|
pk.setValue(data);
|
|
|
|
return pk;
|
|
}
|
|
|
|
void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) {
|
|
if (!elementMatches(element, "preKeyPublic"))
|
|
return;
|
|
|
|
id = element.attribute("preKeyId").toInt();
|
|
// TODO: Base64
|
|
data = element.value();
|
|
}
|
|
|
|
QXmppElement QXmpp::Omemo::Bundle::toXml() const {
|
|
auto spkNode = createElement("signedPreKeyPublic");
|
|
spkNode.setAttribute("signedPreKeyId", QString::number(spkId));
|
|
spkNode.setValue(spk);
|
|
|
|
auto spksNode = createElement("signedPreKeySignature");
|
|
spksNode.setValue(spks);
|
|
|
|
auto ikNode = createElement("identityKey");
|
|
ikNode.setValue(ik);
|
|
|
|
auto prekeysNode = createElement("prekeys");
|
|
for (const auto &pk : prekeys) {
|
|
prekeysNode.appendChild(pk.toXml());
|
|
}
|
|
|
|
auto result = createElement("bundle", "eu.siacs.conversations.axolotl");
|
|
result.appendChild(spkNode);
|
|
result.appendChild(spksNode);
|
|
result.appendChild(ikNode);
|
|
result.appendChild(prekeysNode);
|
|
|
|
return result;
|
|
}
|
|
|
|
QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const {
|
|
QXmppIq iq{};
|
|
|
|
iq.setType(QXmppIq::Set);
|
|
|
|
auto item = createElement("item");
|
|
item.appendChild(toXml());
|
|
|
|
auto publish = createElement("publish");
|
|
publish.setAttribute("node", QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId));
|
|
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::Bundle::fromXml(const QXmppElement &element) {
|
|
if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl"))
|
|
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(QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId));
|
|
|
|
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));
|
|
|
|
auto ivNode = createElement("iv");
|
|
ivNode.setValue(iv);
|
|
|
|
for (const auto &key : keys) {
|
|
result.appendChild(key.toXml());
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const {
|
|
auto result = createElement("encrypted", "eu.siacs.conversations.axolotl");
|
|
|
|
result.appendChild(header());
|
|
// TODO: Payload is optional
|
|
result.appendChild(payload());
|
|
|
|
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");
|
|
|
|
result.setAttribute("rid", QString::number(receivingDeviceId));
|
|
if (prekey)
|
|
result.setAttribute("prekey", "true");
|
|
|
|
result.setValue(key);
|
|
|
|
return result;
|
|
}
|