forked from blue/squawk
244 lines
5.9 KiB
C++
244 lines
5.9 KiB
C++
/*
|
|
* Created by victoria on 2021-05-12.
|
|
*/
|
|
|
|
#include "qomemo.h"
|
|
|
|
#include <QDebug>
|
|
|
|
static bool elementMatches(const QXmppElement &element, const QString &tagName,
|
|
const QString &xmlns = QStringLiteral("")) {
|
|
if (element.tagName() != tagName) {
|
|
qWarning() << "tag name: expected = " << tagName
|
|
<< ", got = " << element.tagName();
|
|
return false;
|
|
}
|
|
|
|
if (!xmlns.isEmpty() && element.attribute("xmlns") != xmlns) {
|
|
qWarning() << "xmlns: expected = " << xmlns
|
|
<< ", got = " << element.attribute("xmlns");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static QXmppElement createElement(const QString &tagName,
|
|
const QString &xmlns = QStringLiteral("")) {
|
|
QXmppElement el{};
|
|
el.setTagName(tagName);
|
|
|
|
if (!xmlns.isEmpty())
|
|
el.setAttribute("xmlns", xmlns);
|
|
|
|
return el;
|
|
}
|
|
|
|
static QXmppElement createValue(const QString &value) {
|
|
auto el = createElement("value");
|
|
el.setValue(value);
|
|
|
|
return el;
|
|
}
|
|
|
|
static QXmppElement createField(const QString &key, const QString &value,
|
|
bool hidden = false) {
|
|
auto field = createElement("field");
|
|
field.setAttribute("var", key);
|
|
if (hidden)
|
|
field.setAttribute("type", "hidden");
|
|
field.appendChild(createValue(value));
|
|
|
|
return field;
|
|
}
|
|
|
|
static QXmppElement
|
|
createOpenPublishOptions(const QString &maxItems = QStringLiteral("")) {
|
|
auto formType = createField(
|
|
"FORM_TYPE", "http://jabber.org/protocol/pubsub#publish-options", true);
|
|
auto accessModel = createField("pubsub#access_model", "open");
|
|
|
|
auto x = createElement("x", "jabber:x:data");
|
|
x.setAttribute("type", "submit");
|
|
|
|
x.appendChild(formType);
|
|
x.appendChild(accessModel);
|
|
|
|
if (!maxItems.isEmpty())
|
|
x.appendChild(createField("pubsub#max_items", maxItems));
|
|
|
|
auto publishOptions = createElement("publish-options");
|
|
publishOptions.appendChild(x);
|
|
|
|
return publishOptions;
|
|
}
|
|
|
|
QXmppElement QOmemo::DeviceList::toXml() const {
|
|
auto element = createElement("devices", "urn:xmpp:omemo:1");
|
|
|
|
for (const auto &d : devices) {
|
|
element.appendChild(d.toXml());
|
|
}
|
|
|
|
return element;
|
|
}
|
|
|
|
QXmppIq QOmemo::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 QOmemo::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 QOmemo::Device::toXml() const {
|
|
auto result = createElement("device");
|
|
result.setAttribute("id", QString::number(id));
|
|
|
|
if (!label.isEmpty()) {
|
|
result.setAttribute("label", label);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void QOmemo::Device::fromXml(const QXmppElement &element) {
|
|
if (!elementMatches(element, "device"))
|
|
return;
|
|
|
|
id = element.attribute("id").toInt();
|
|
label = element.attribute("label");
|
|
}
|
|
|
|
QXmppElement QOmemo::PreKey::toXml() const {
|
|
auto pk = createElement("pk");
|
|
pk.setAttribute("id", QString::number(id));
|
|
// TODO: Base64
|
|
pk.setValue(data);
|
|
|
|
return pk;
|
|
}
|
|
|
|
void QOmemo::PreKey::fromXml(const QXmppElement &element) {
|
|
if (!elementMatches(element, "pk"))
|
|
return;
|
|
|
|
id = element.attribute("id").toInt();
|
|
// TODO: Base64
|
|
data = element.value();
|
|
}
|
|
|
|
QXmppElement QOmemo::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 QOmemo::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 QOmemo::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);
|
|
}
|
|
}
|