forked from blue/squawk
feat(OMEMO): add Device, DeviceList, PreKey, Bundle + XML
This commit is contained in:
parent
a6254d88b3
commit
b22a4c8ca3
@ -1 +1,6 @@
|
||||
target_sources(squawk PRIVATE signal.h signal.cpp)
|
||||
target_sources(squawk PRIVATE
|
||||
signal.h
|
||||
signal.cpp
|
||||
qomemo.cpp
|
||||
qomemo.h
|
||||
)
|
243
qomemo/qomemo.cpp
Normal file
243
qomemo/qomemo.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
56
qomemo/qomemo.h
Normal file
56
qomemo/qomemo.h
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Created by victoria on 2021-05-12.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QXmppPubSubIq.h>
|
||||
|
||||
namespace QOmemo {
|
||||
|
||||
class Device {
|
||||
public:
|
||||
[[nodiscard]] QXmppElement toXml() const;
|
||||
void fromXml(const QXmppElement &element);
|
||||
|
||||
int id;
|
||||
QString label;
|
||||
};
|
||||
|
||||
class DeviceList {
|
||||
public:
|
||||
[[nodiscard]] QXmppElement toXml() const;
|
||||
[[nodiscard]] QXmppIq toIq() const;
|
||||
/// Expects a urn:xmpp:omemo:1:devices node
|
||||
void fromXml(const QXmppElement &element);
|
||||
|
||||
private:
|
||||
QList<Device> devices;
|
||||
};
|
||||
|
||||
class PreKey {
|
||||
public:
|
||||
[[nodiscard]] QXmppElement toXml() const;
|
||||
/// Expects a <pk>
|
||||
void fromXml(const QXmppElement &element);
|
||||
|
||||
int id;
|
||||
QString data;
|
||||
};
|
||||
|
||||
class Bundle {
|
||||
public:
|
||||
[[nodiscard]] QXmppElement toXml() const;
|
||||
[[nodiscard]] QXmppIq toIq() const;
|
||||
void fromXml(const QXmppElement &element);
|
||||
|
||||
int deviceId;
|
||||
|
||||
QString spk;
|
||||
int spkId;
|
||||
QString spks;
|
||||
QString ik;
|
||||
QList<PreKey> prekeys;
|
||||
};
|
||||
|
||||
} // namespace QOmemo
|
Loading…
Reference in New Issue
Block a user