/* * Created by victoria on 2021-05-12. */ #include "qomemo.h" #include "sce.h" #include #include #include #include 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; }