something something inheritance

master
Tony Olagbaiye 3 years ago
parent 0199da610e
commit 134bd388f4
No known key found for this signature in database
GPG Key ID: 9E2FF3BDEBDFC910

@ -30,6 +30,7 @@ PACKAGES=(
libsignal-protocol-c -l libomemo.scm # Dep (libsignal) libsignal-protocol-c -l libomemo.scm # Dep (libsignal)
lmdb lmdbxx # Dep (lmdb) lmdb lmdbxx # Dep (lmdb)
rnp # Dep (rnpgp) rnp # Dep (rnpgp)
xsd # Generate xml schema code
) )
echo direnv: fetching source - weechat echo direnv: fetching source - weechat

@ -16,11 +16,11 @@
[[https://github.com/bqv/weechat-xmpp/issues?q=is:issue+is:closed][file:https://img.shields.io/github/issues-closed/bqv/weechat-xmpp.svg]] [[https://github.com/bqv/weechat-xmpp/issues?q=is:issue+is:closed][file:https://img.shields.io/github/issues-closed/bqv/weechat-xmpp.svg]]
[[https://github.com/bqv/weechat-xmpp/blob/master/LICENSE][file:https://img.shields.io/github/license/bqv/weechat-xmpp.svg]] [[https://github.com/bqv/weechat-xmpp/blob/master/LICENSE][file:https://img.shields.io/github/license/bqv/weechat-xmpp.svg]]
[[https://github.com/bqv/weechat-extras/][file:https://img.shields.io/badge/weechat--extras-xmpp-blue.svg]] [[https://github.com/bqv/weechat-extras/][file:https://img.shields.io/badge/weechat--extras-xmpp-blue.svg]]
[[https://inverse.chat/#converse/room?jid=weechat@muc.xa0.uk][file:https://inverse.chat/badge.svg?room=weechat@muc.xa0.uk]] [[https://inverse.chat/#converse/room?jid=weechat@muc.xa0.uk][file:https://img.shields.io/badge/xmpp-weechat%40muc.xa0.uk-yellow]]
| Status: | XMPP for power users and digital masochists | | Status: | XMPP for power users and digital masochists |
| Location: | [[http://github.com/bqv/weechat-xmpp]] | | Location: | [[http://github.com/bqv/weechat-xmpp]] |
| Version: | 0.1.1 | | Version: | 0.2.0 |
| Disclaimer: | I'm lazy and unashamedly clinically insane | | Disclaimer: | I'm lazy and unashamedly clinically insane |
* Description * Description

@ -1048,7 +1048,7 @@ int channel__send_message(struct t_account *account, struct t_channel *channel,
if (!encrypted) if (!encrypted)
{ {
weechat_printf_date_tags(channel->buffer, 0, "notify_none", "%s%s", weechat_printf_date_tags(channel->buffer, 0, "notify_none", "%s%s",
weechat_prefix("error"), "OMEMO Error"); weechat_prefix("error"), "OMEMO Encryption Error");
channel__set_transport(channel, CHANNEL_TRANSPORT_PLAIN, 1); channel__set_transport(channel, CHANNEL_TRANSPORT_PLAIN, 1);
xmpp_stanza_release(message); xmpp_stanza_release(message);
return WEECHAT_RC_ERROR; return WEECHAT_RC_ERROR;

@ -12,6 +12,7 @@
#include <weechat/weechat-plugin.h> #include <weechat/weechat-plugin.h>
#include "plugin.hh" #include "plugin.hh"
#include "xmpp/node.hh"
#include "xmpp/stanza.hh" #include "xmpp/stanza.hh"
#include "config.hh" #include "config.hh"
#include "account.hh" #include "account.hh"
@ -84,8 +85,6 @@ int connection__version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *
int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{ {
(void) conn;
struct t_account *account = (struct t_account *)userdata; struct t_account *account = (struct t_account *)userdata;
struct t_user *user; struct t_user *user;
struct t_channel *channel; struct t_channel *channel;
@ -94,6 +93,7 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void
const char *show__text = NULL, *idle__since = NULL, *certificate = NULL, *node = NULL, *ver = NULL; const char *show__text = NULL, *idle__since = NULL, *certificate = NULL, *node = NULL, *ver = NULL;
char *clientid = NULL, *status; char *clientid = NULL, *status;
auto binding = xml::presence(account->context, stanza);
to = xmpp_stanza_get_to(stanza); to = xmpp_stanza_get_to(stanza);
from = xmpp_stanza_get_from(stanza); from = xmpp_stanza_get_from(stanza);
if (from == NULL) if (from == NULL)
@ -272,8 +272,6 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void
int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata)
{ {
(void) conn;
struct t_account *account = (struct t_account *)userdata; struct t_account *account = (struct t_account *)userdata;
struct t_channel *channel, *parent_channel; struct t_channel *channel, *parent_channel;
xmpp_stanza_t *x, *body, *delay, *topic, *replace, *request, *markable, *composing, *sent, *received, *result, *forwarded, *event, *items, *item, *list, *device, *encrypted, **children; xmpp_stanza_t *x, *body, *delay, *topic, *replace, *request, *markable, *composing, *sent, *received, *result, *forwarded, *event, *items, *item, *list, *device, *encrypted, **children;
@ -282,6 +280,7 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *
struct tm time = {0}; struct tm time = {0};
time_t date = 0; time_t date = 0;
auto binding = xml::message(account->context, stanza);
body = xmpp_stanza_get_child_by_name(stanza, "body"); body = xmpp_stanza_get_child_by_name(stanza, "body");
if (body == NULL) if (body == NULL)
{ {
@ -531,12 +530,18 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *
encrypted = xmpp_stanza_get_child_by_name_and_ns(stanza, "encrypted", encrypted = xmpp_stanza_get_child_by_name_and_ns(stanza, "encrypted",
"eu.siacs.conversations.axolotl"); "eu.siacs.conversations.axolotl");
x = xmpp_stanza_get_child_by_name_and_ns(stanza, "x", "jabber:x:encrypted");
intext = xmpp_stanza_get_text(body);
if (encrypted && account->omemo) if (encrypted && account->omemo)
{ {
cleartext = account->omemo.decode(account, from_bare, encrypted); cleartext = account->omemo.decode(account, from_bare, encrypted);
if (!cleartext)
{
weechat_printf_date_tags(channel->buffer, 0, "notify_none", "%s%s (%s)",
weechat_prefix("error"), "OMEMO Decryption Error", from);
return 1;
}
} }
x = xmpp_stanza_get_child_by_name_and_ns(stanza, "x", "jabber:x:encrypted");
intext = xmpp_stanza_get_text(body);
if (x) if (x)
{ {
char *ciphertext = xmpp_stanza_get_text(x); char *ciphertext = xmpp_stanza_get_text(x);
@ -936,6 +941,7 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd
xmpp_stanza_t *pubsub, *items, *item, *list, *bundle, *device; xmpp_stanza_t *pubsub, *items, *item, *list, *bundle, *device;
xmpp_stanza_t *storage, *conference, *nick; xmpp_stanza_t *storage, *conference, *nick;
auto binding = xml::iq(account->context, stanza);
const char *id = xmpp_stanza_get_id(stanza); const char *id = xmpp_stanza_get_id(stanza);
const char *from = xmpp_stanza_get_from(stanza); const char *from = xmpp_stanza_get_from(stanza);
const char *to = xmpp_stanza_get_to(stanza); const char *to = xmpp_stanza_get_to(stanza);

@ -6,8 +6,26 @@
#include <memory> #include <memory>
#include <functional> #include <functional>
#include <stdexcept>
#include <gcrypt.h> #include <gcrypt.h>
namespace gcrypt { namespace gcrypt {
class version_error : public std::runtime_error {
private:
const char *const message = "GCrypt: library version mismatch";
public:
version_error() noexcept : runtime_error(GCRYPT_VERSION) {
}
virtual const char* what() const noexcept {
return message;
}
};
void check_version() {
if (!gcry_check_version(GCRYPT_VERSION))
throw gcrypt::version_error();
}
} }

@ -13,16 +13,17 @@ INCLUDES=-Ilibstrophe -Ideps -Ideps/fmt/include \
CFLAGS+=$(DBGCFLAGS) \ CFLAGS+=$(DBGCFLAGS) \
-fno-omit-frame-pointer -fPIC \ -fno-omit-frame-pointer -fPIC \
-fvisibility=hidden -fvisibility-inlines-hidden \ -fvisibility=hidden -fvisibility-inlines-hidden \
-std=gnu99 -gdwarf-4 \ -fdebug-prefix-map=.=$(shell readlink -f .) \
-std=gnu99 -gdwarf-4 -fkeep-inline-functions \
-Wall -Wextra -pedantic \ -Wall -Wextra -pedantic \
-Werror-implicit-function-declaration \ -Werror-implicit-function-declaration \
-Wno-missing-field-initializers \ -Wno-missing-field-initializers \
-D_XOPEN_SOURCE=700 \ -D_XOPEN_SOURCE=700 \
$(INCLUDES) $(INCLUDES)
CPPFLAGS+=$(DBGCFLAGS) \ CPPFLAGS+=$(DBGCFLAGS) -O0 \
-fno-omit-frame-pointer -fPIC \ -fno-omit-frame-pointer -fPIC \
-fvisibility=hidden -fvisibility-inlines-hidden \ -fvisibility=hidden -fvisibility-inlines-hidden \
-std=c++20 -gdwarf-4 \ -std=c++20 -gdwarf-4 -fkeep-inline-functions \
-Wall -Wextra -pedantic \ -Wall -Wextra -pedantic \
-Wno-missing-field-initializers \ -Wno-missing-field-initializers \
$(INCLUDES) $(INCLUDES)
@ -56,6 +57,8 @@ HDRS=plugin.hh \
user.hh \ user.hh \
util.hh \ util.hh \
xmpp/stanza.hh \ xmpp/stanza.hh \
xmpp/ns.hh \
xmpp/node.hh \
SRCS=plugin.cpp \ SRCS=plugin.cpp \
account.cpp \ account.cpp \
@ -73,6 +76,7 @@ SRCS=plugin.cpp \
util.cpp \ util.cpp \
xmpp/presence.cpp \ xmpp/presence.cpp \
xmpp/iq.cpp \ xmpp/iq.cpp \
xmpp/node.cpp \
DEPS=deps/diff/libdiff.a \ DEPS=deps/diff/libdiff.a \
deps/fmt/libfmt.a \ deps/fmt/libfmt.a \
@ -99,13 +103,13 @@ xmpp.so: $(OBJS) $(DEPS) $(HDRS)
$(CXX) $(CPPFLAGS) -c $< -o $@ $(CXX) $(CPPFLAGS) -c $< -o $@
.%.cov.o: %.cpp .%.cov.o: %.cpp
@$(CXX) --coverage -O0 $(CPPFLAGS) -c $< -o $@ @$(CXX) --coverage $(CPPFLAGS) -O0 -c $< -o $@
xmpp/.%.o: xmpp/%.cpp xmpp/.%.o: xmpp/%.cpp
$(CXX) $(CPPFLAGS) -c $< -o $@ $(CXX) $(CPPFLAGS) -c $< -o $@
xmpp/.%.cov.o: xmpp/%.cpp xmpp/.%.cov.o: xmpp/%.cpp
@$(CXX) --coverage -O0 $(CPPFLAGS) -c $< -o $@ @$(CXX) --coverage $(CPPFLAGS) -O0 -c $< -o $@
deps/diff/libdiff.a: deps/diff/libdiff.a:
git submodule update --init --recursive git submodule update --init --recursive

@ -4,14 +4,18 @@
#include <fmt/core.h> #include <fmt/core.h>
#include <memory> #include <memory>
#include <stdexcept> #include <numeric>
#include <optional>
#include <ranges>
#include <sstream>
#include <string>
#include <string_view>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <sys/param.h> #include <sys/param.h>
#include <time.h> #include <time.h>
#include <math.h> #include <math.h>
#include <limits.h> #include <limits.h>
#include <gcrypt.h>
#include <strophe.h> #include <strophe.h>
#include <weechat/weechat-plugin.h> #include <weechat/weechat-plugin.h>
@ -19,6 +23,7 @@
#include "xmpp/stanza.hh" #include "xmpp/stanza.hh"
#include "account.hh" #include "account.hh"
#include "omemo.hh" #include "omemo.hh"
#include "gcrypt.hh"
#include "util.hh" #include "util.hh"
using namespace weechat::xmpp; using namespace weechat::xmpp;
@ -520,7 +525,7 @@ int iks_get_identity_key_pair(struct signal_buffer **public_data, signal_buffer
*public_data = signal_buffer_create((const uint8_t*)v_local_public_key.mv_data, *public_data = signal_buffer_create((const uint8_t*)v_local_public_key.mv_data,
v_local_public_key.mv_size); v_local_public_key.mv_size);
omemo->identity = identity; omemo->identity = std::move(identity);
} }
return 0; return 0;
@ -1558,54 +1563,34 @@ void sks_destroy_func(void *user_data)
int dls_store_devicelist(const char *jid, signal_int_list *devicelist, t_omemo *omemo) int dls_store_devicelist(const char *jid, signal_int_list *devicelist, t_omemo *omemo)
{ {
MDB_txn *transaction = NULL; auto transaction = lmdb::txn::begin(omemo->db_env);
MDB_val k_devicelist = { std::string k_devicelist = fmt::format("devicelist_{}", jid);
.mv_size = strlen("devicelist_") + strlen(jid), std::string v_devicelist;
.mv_data = NULL,
};
MDB_val v_devicelist;
k_devicelist.mv_data = malloc(sizeof(char) * (
k_devicelist.mv_size + 1));
k_devicelist.mv_size =
snprintf((char*)k_devicelist.mv_data, k_devicelist.mv_size + 1,
"devicelist_%s", jid);
char *devices[128] = {0};
for (size_t i = 0; i < signal_int_list_size(devicelist); i++) for (size_t i = 0; i < signal_int_list_size(devicelist); i++)
{ {
int device = signal_int_list_at(devicelist, i); int device = signal_int_list_at(devicelist, i);
devices[i] = (char*)malloc(sizeof(*devices) * (10 + 1)); std::string device_id = std::to_string(device);
devices[i+1] = NULL; if (v_devicelist.size() > 0)
snprintf(devices[i], 10 + 1, "%u", device); v_devicelist += ";";
} v_devicelist += device_id;
v_devicelist.mv_data = weechat_string_build_with_split_string( }
(const char **)devices, ";");
v_devicelist.mv_size = strlen((const char*)v_devicelist.mv_data); omemo->dbi.omemo.put(transaction, k_devicelist, v_devicelist);
for (char **device = (char **)devices; *device; device++) free(*device); //omemo->dbi.omemo.put(wtxn, "fullname", std::string_view("J. Random Hacker"));
//{
if (mdb_txn_begin(omemo->db_env, NULL, 0, &transaction)) { // auto cursor = lmdb::cursor::open(rtxn, dbi);
weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", // std::string_view key, value;
weechat_prefix("error")); // if (cursor.get(key, value, MDB_FIRST)) {
return -1; // do {
} // std::cout << "key: " << key << " value: " << value << std::endl;
// } while (cursor.get(key, value, MDB_NEXT));
// }
//}
if (mdb_put(transaction, omemo->dbi.omemo, &k_devicelist, transaction.commit();
&v_devicelist, 0)) {
weechat_printf(NULL, "%sxmpp: failed to write lmdb value",
weechat_prefix("error"));
goto cleanup;
};
if (mdb_txn_commit(transaction)) {
weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction",
weechat_prefix("error"));
goto cleanup;
};
return 0; return 0;
cleanup:
mdb_txn_abort(transaction);
return -1;
} }
int dls_load_devicelist(signal_int_list **devicelist, const char *jid, t_omemo *omemo) int dls_load_devicelist(signal_int_list **devicelist, const char *jid, t_omemo *omemo)
@ -1615,16 +1600,17 @@ int dls_load_devicelist(signal_int_list **devicelist, const char *jid, t_omemo *
std::string_view v_devicelist; std::string_view v_devicelist;
omemo->dbi.omemo.get(transaction, k_devicelist, v_devicelist); omemo->dbi.omemo.get(transaction, k_devicelist, v_devicelist);
int devices_len = 0; auto devices = v_devicelist
char **devices = weechat_string_split(v_devicelist.data(), ";", NULL, 0, 0, &devices_len); | std::ranges::views::split(';')
| std::ranges::views::transform([](auto&& str) {
return std::stoul(std::string(&*str.begin(), std::ranges::distance(str)));
});
*devicelist = signal_int_list_alloc(); *devicelist = signal_int_list_alloc();
for (int i = 0; i < devices_len; i++) for (uint32_t&& device_id : devices)
{ {
char* device_id = devices[i]; signal_int_list_push_back(*devicelist, device_id);
signal_int_list_push_back(*devicelist, strtol(device_id, NULL, 10));
} }
weechat_string_free_split(devices);
transaction.commit(); transaction.commit();
@ -1637,28 +1623,18 @@ int bks_store_bundle(struct signal_protocol_address *address,
{ {
size_t n_pre_keys = -1; size_t n_pre_keys = -1;
while (pre_keys[++n_pre_keys] != NULL); while (pre_keys[++n_pre_keys] != NULL);
char **pre_key_buffers = (char**)malloc(sizeof(char*) * (n_pre_keys + 1)); auto pre_key_buffers = std::vector<std::string>(n_pre_keys);
for (size_t i = 0; i < n_pre_keys; i++) for (auto pre_key : std::vector<struct t_pre_key*>(pre_keys, pre_keys + n_pre_keys))
{ {
struct t_pre_key *pre_key = pre_keys[i]; pre_key_buffers.push_back(fmt::format("{}.{}", pre_key->id, pre_key->public_key));
size_t keylen = 10 + strlen(pre_key->public_key) + 1; }
pre_key_buffers[i] = (char*)malloc(sizeof(char) * keylen);
pre_key_buffers[i+1] = NULL; n_pre_keys = -1;
snprintf(pre_key_buffers[i], keylen, while (signed_pre_keys[++n_pre_keys] != NULL);
"%s.%s", pre_key->id, pre_key->public_key); auto signed_pre_key_buffers = std::vector<std::string>(n_pre_keys);
} for (auto signed_pre_key : std::vector<struct t_pre_key*>(signed_pre_keys, signed_pre_keys + n_pre_keys))
size_t n_signed_pre_keys = -1;
while (signed_pre_keys[++n_signed_pre_keys] != NULL);
char **signed_pre_key_buffers = (char**)malloc(sizeof(char*) * (n_signed_pre_keys + 1));
for (size_t i = 0; i < n_signed_pre_keys; i++)
{ {
struct t_pre_key *signed_pre_key = signed_pre_keys[i]; signed_pre_key_buffers.push_back(fmt::format("{}.{}", signed_pre_key->id, signed_pre_key->public_key));
size_t keylen = 10 + 1 + strlen(signed_pre_key->public_key);
signed_pre_key_buffers[i] = (char*)malloc(sizeof(char) * (keylen + 1));
signed_pre_key_buffers[i+1] = NULL;
snprintf(signed_pre_key_buffers[i], keylen + 1,
"%s.%s", signed_pre_key->id, signed_pre_key->public_key);
uint8_t *signing_key_buf; uint8_t *signing_key_buf;
size_t signing_key_len = base64_decode(identity_key, size_t signing_key_len = base64_decode(identity_key,
@ -1681,214 +1657,108 @@ int bks_store_bundle(struct signal_protocol_address *address,
} }
} }
MDB_txn *transaction = NULL; std::string k_bundle_pk = fmt::format("bundle_pk_{}_{}", address->name, address->device_id);
const char *jid = address->name; std::string k_bundle_sk = fmt::format("bundle_sk_{}_{}", address->name, address->device_id);
uint32_t device_id = address->device_id; std::string k_bundle_sg = fmt::format("bundle_sg_{}_{}", address->name, address->device_id);
size_t keylen = strlen("bundle_??_") + strlen(jid) + 1 + 10 + 1; std::string k_bundle_ik = fmt::format("bundle_ik_{}_{}", address->name, address->device_id);
MDB_val k_bundle_pk = {
.mv_size = 0,
.mv_data = malloc(sizeof(char) * (keylen + 1)),
};
k_bundle_pk.mv_size = snprintf((char*)k_bundle_pk.mv_data, keylen,
"bundle_pk_%s_%u", jid, device_id);
MDB_val k_bundle_sk = {
.mv_size = 0,
.mv_data = malloc(sizeof(char) * (keylen + 1)),
};
k_bundle_sk.mv_size = snprintf((char*)k_bundle_sk.mv_data, keylen,
"bundle_sk_%s_%u", jid, device_id);
MDB_val k_bundle_sg = {
.mv_size = 0,
.mv_data = malloc(sizeof(char) * (keylen + 1)),
};
k_bundle_sg.mv_size = snprintf((char*)k_bundle_sg.mv_data, keylen,
"bundle_sg_%s_%u", jid, device_id);
MDB_val k_bundle_ik = {
.mv_size = 0,
.mv_data = malloc(sizeof(char) * (keylen + 1)),
};
k_bundle_ik.mv_size = snprintf((char*)k_bundle_ik.mv_data, keylen,
"bundle_ik_%s_%u", jid, device_id);
MDB_val v_bundle_pk = {
.mv_size = 0,
.mv_data = weechat_string_build_with_split_string(
(const char **)pre_key_buffers, ";"),
};
v_bundle_pk.mv_size = strlen((const char*)v_bundle_pk.mv_data) + 1;
MDB_val v_bundle_sk = {
.mv_size = 0,
.mv_data = weechat_string_build_with_split_string(
(const char **)signed_pre_key_buffers, ";"),
};
v_bundle_sk.mv_size = strlen((const char*)v_bundle_sk.mv_data) + 1;
MDB_val v_bundle_sg = {
.mv_size = strlen(signature),
.mv_data = (char*)signature,
};
MDB_val v_bundle_ik = {
.mv_size = strlen(identity_key),
.mv_data = (char*)identity_key,
};
if (mdb_txn_begin(omemo->db_env, NULL, 0, &transaction)) { std::string v_bundle_pk = std::accumulate(pre_key_buffers.begin(), pre_key_buffers.end(), std::string(";"));
weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", std::string v_bundle_sk = std::accumulate(signed_pre_key_buffers.begin(), signed_pre_key_buffers.end(), std::string(";"));
weechat_prefix("error")); std::string_view v_bundle_sg = signature;
return -1; std::string_view v_bundle_ik = identity_key;
}
int ret; auto transaction = lmdb::txn::begin(omemo->db_env);
if ((ret = mdb_put(transaction, omemo->dbi.omemo, &k_bundle_pk,
&v_bundle_pk, 0)) ||
(ret = mdb_put(transaction, omemo->dbi.omemo, &k_bundle_sk,
&v_bundle_sk, 0)) ||
(ret = mdb_put(transaction, omemo->dbi.omemo, &k_bundle_sg,
&v_bundle_sg, 0)) ||
(ret = mdb_put(transaction, omemo->dbi.omemo, &k_bundle_ik,
&v_bundle_ik, 0))) {
weechat_printf(NULL, "%sxmpp: failed to write lmdb value '%s'@%u: %s",
weechat_prefix("error"), v_bundle_pk.mv_data, v_bundle_pk.mv_size, mdb_strerror(ret));
goto cleanup;
};
if ((ret = mdb_txn_commit(transaction))) { omemo->dbi.omemo.put(transaction, k_bundle_pk, v_bundle_pk);
weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", omemo->dbi.omemo.put(transaction, k_bundle_sk, v_bundle_sk);
weechat_prefix("error")); omemo->dbi.omemo.put(transaction, k_bundle_sg, v_bundle_sg);
goto cleanup; omemo->dbi.omemo.put(transaction, k_bundle_ik, v_bundle_ik);
};
transaction.commit();
return 0; return 0;
cleanup:
mdb_txn_abort(transaction);
return -1;
} }
int bks_load_bundle(session_pre_key_bundle **bundle, struct signal_protocol_address *address, t_omemo *omemo) std::optional<libsignal::pre_key_bundle> bks_load_bundle(struct signal_protocol_address *address, t_omemo *omemo)
{ {
MDB_txn *transaction = NULL; std::string k_bundle_pk = fmt::format("bundle_pk_{}_{}", address->name, address->device_id);
const char *jid = address->name; std::string k_bundle_sk = fmt::format("bundle_sk_{}_{}", address->name, address->device_id);
uint32_t device_id = address->device_id; std::string k_bundle_sg = fmt::format("bundle_sg_{}_{}", address->name, address->device_id);
size_t keylen = strlen("bundle_??_") + address->name_len + 1 + 10 + 1; std::string k_bundle_ik = fmt::format("bundle_ik_{}_{}", address->name, address->device_id);
MDB_val k_bundle_pk = {
.mv_size = 0,
.mv_data = malloc(sizeof(char) * (keylen + 1)),
};
k_bundle_pk.mv_size = snprintf((char*)k_bundle_pk.mv_data, keylen,
"bundle_pk_%s_%u", jid, device_id);
MDB_val k_bundle_sk = {
.mv_size = 0,
.mv_data = malloc(sizeof(char) * (keylen + 1)),
};
k_bundle_sk.mv_size = snprintf((char*)k_bundle_sk.mv_data, keylen,
"bundle_sk_%s_%u", jid, device_id);
MDB_val k_bundle_sg = {
.mv_size = 0,
.mv_data = malloc(sizeof(char) * (keylen + 1)),
};
k_bundle_sg.mv_size = snprintf((char*)k_bundle_sg.mv_data, keylen,
"bundle_sg_%s_%u", jid, device_id);
MDB_val k_bundle_ik = {
.mv_size = 0,
.mv_data = malloc(sizeof(char) * (keylen + 1)),
};
k_bundle_ik.mv_size = snprintf((char*)k_bundle_ik.mv_data, keylen,
"bundle_ik_%s_%u", jid, device_id);
MDB_val v_bundle_pk;
MDB_val v_bundle_sk;
MDB_val v_bundle_sg;
MDB_val v_bundle_ik;
int ret; std::string_view v_bundle_pk;
if ((ret = mdb_txn_begin(omemo->db_env, NULL, MDB_RDONLY, &transaction))) { std::string_view v_bundle_sk;
weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", std::string_view v_bundle_sg;
weechat_prefix("error")); std::string_view v_bundle_ik;
return -1;
} auto transaction = lmdb::txn::begin(omemo->db_env);
int bundle_pk_len; uint32_t pre_key_id = 0;
int bundle_sk_len; uint32_t signed_pre_key_id = 0;
char **bundle_pks;
char **bundle_sks;
ec_public_key *pre_key;
ec_public_key *signed_pre_key;
ec_public_key *identity_key;
uint32_t pre_key_id;
uint32_t signed_pre_key_id;
uint8_t *sig_buf; size_t sig_len; uint8_t *sig_buf; size_t sig_len;
struct signal_buffer *signature; struct signal_buffer *signature;
uint8_t *key_buf; size_t key_len; uint8_t *key_buf; size_t key_len;
if ((ret = mdb_get(transaction, omemo->dbi.omemo, omemo->dbi.omemo.get(transaction, k_bundle_pk, v_bundle_pk);
&k_bundle_pk, &v_bundle_pk)) || omemo->dbi.omemo.get(transaction, k_bundle_sk, v_bundle_sk);
(ret = mdb_get(transaction, omemo->dbi.omemo, omemo->dbi.omemo.get(transaction, k_bundle_sg, v_bundle_sg);
&k_bundle_sk, &v_bundle_sk)) || omemo->dbi.omemo.get(transaction, k_bundle_ik, v_bundle_ik);
(ret = mdb_get(transaction, omemo->dbi.omemo,
&k_bundle_sg, &v_bundle_sg)) || auto r_bundle_pks = v_bundle_pk
(ret = mdb_get(transaction, omemo->dbi.omemo, | std::ranges::views::split(';')
&k_bundle_ik, &v_bundle_ik))) | std::ranges::views::transform([](auto&& str) {
return std::string_view(&*str.begin(), std::ranges::distance(str));
});
auto bundle_pks = std::vector<std::string>{r_bundle_pks.begin(), r_bundle_pks.begin()};
if (bundle_pks.size() > 0)
{ {
goto cleanup; std::istringstream iss(bundle_pks[rand() % bundle_pks.size()]);
iss >> pre_key_id;
char delim;
iss.get(delim);
if (delim != ',') throw std::runtime_error("Bundle parse failure");
std::string key_data;
iss >> key_data;
key_len = base64_decode(key_data.data(), key_data.size(), &key_buf);
} }
else
bundle_pk_len = 0; return {};
bundle_pks = weechat_string_split((const char*)v_bundle_pk.mv_data, ";", NULL, 0, 0, &bundle_pk_len); libsignal::public_key pre_key(key_buf, key_len, omemo->context);
pre_key_id = 0;
{ auto r_bundle_sks = v_bundle_sk
int i = rand() % bundle_pk_len; | std::ranges::views::split(';')
char *bundle_pk = bundle_pks[i]; | std::ranges::views::transform([](auto&& str) {
pre_key_id = strtol(bundle_pk, NULL, 10); return std::string_view(&*str.begin(), std::ranges::distance(str));
char *key_data = (char *)memchr(bundle_pk, '.', 10 + 1) + 1; });
uint8_t *key_buf; auto bundle_sks = std::vector<std::string>{r_bundle_sks.begin(), r_bundle_sks.begin()};
size_t key_len = base64_decode(key_data, strlen(key_data), &key_buf); if (bundle_sks.size() > 0)
if ((ret = curve_decode_point(&pre_key, key_buf, key_len, omemo->context))) {
weechat_printf(NULL, "%sxmpp: failed to decode ED25519 prekey",
weechat_prefix("error"));
goto cleanup;
};
}
bundle_sks = weechat_string_split((const char*)v_bundle_sk.mv_data, ";", NULL, 0, 0, &bundle_sk_len);
{ {
int i = rand() % bundle_sk_len; std::istringstream iss(bundle_sks[rand() % bundle_sks.size()]);
char *bundle_sk = bundle_sks[i]; iss >> signed_pre_key_id;
signed_pre_key_id = strtol(bundle_sk, NULL, 10); char delim;
char *key_data = (char *)memchr(bundle_sk, '.', 10 + 1) + 1; iss.get(delim);
uint8_t *key_buf; if (delim != ',') throw std::runtime_error("Bundle parse failure");
size_t key_len = base64_decode(key_data, strlen(key_data), &key_buf); std::string key_data;
if ((ret = curve_decode_point(&signed_pre_key, key_buf, key_len, omemo->context))) { iss >> key_data;
weechat_printf(NULL, "%sxmpp: failed to decode ED25519 signed prekey", key_len = base64_decode(key_data.data(), key_data.size(), &key_buf);
weechat_prefix("error"));
goto cleanup;
};
} }
sig_len = base64_decode((const char*)v_bundle_sg.mv_data, v_bundle_sg.mv_size, &sig_buf); else
return {};
libsignal::public_key signed_pre_key(key_buf, key_len, omemo->context);
sig_len = base64_decode(v_bundle_sg.data(), v_bundle_sg.size(), &sig_buf);
signature = signal_buffer_create(sig_buf, sig_len); signature = signal_buffer_create(sig_buf, sig_len);
key_len = base64_decode((const char*)v_bundle_ik.mv_data, v_bundle_ik.mv_size, &key_buf); key_len = base64_decode(v_bundle_ik.data(), v_bundle_ik.size(), &key_buf);
if ((ret = curve_decode_point(&identity_key, key_buf, key_len, omemo->context))) { libsignal::public_key identity_key(key_buf, key_len, omemo->context);
weechat_printf(NULL, "%sxmpp: failed to decode ED25519 identity key",
weechat_prefix("error"));
goto cleanup;
};
if ((ret = session_pre_key_bundle_create(bundle, device_id, device_id/*?*/, pre_key_id, pre_key, signed_pre_key_id, signed_pre_key, signal_buffer_data(signature), signal_buffer_len(signature), identity_key))) { libsignal::pre_key_bundle bundle((uint32_t)address->device_id, (int)address->device_id,
weechat_printf(NULL, "%sxmpp: failed to create OMEMO prekey bundle", (uint32_t)pre_key_id, *pre_key, (uint32_t)signed_pre_key_id, *signed_pre_key,
weechat_prefix("error")); (const uint8_t*)signal_buffer_data(signature), (size_t)signal_buffer_len(signature),
goto cleanup; *identity_key);
};
if ((ret = mdb_txn_commit(transaction))) { transaction.commit();
weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction",
weechat_prefix("error"));
goto cleanup;
};
return 0; return bundle;
cleanup:
/*
void session_pre_key_bundle_destroy(signal_type_base *type);
*/
mdb_txn_abort(transaction);
return -1;
} }
extern "C" extern "C"
@ -2016,10 +1886,9 @@ xmpp_stanza_t *omemo::get_bundle(xmpp_ctx_t *context, char *from, char *to)
void omemo::init(struct t_gui_buffer *buffer, const char *account_name) void omemo::init(struct t_gui_buffer *buffer, const char *account_name)
{ {
if (!gcry_check_version(GCRYPT_VERSION)) gcrypt::check_version();
throw std::runtime_error("GCrypt: library version mismatch");
auto omemo = this; const auto omemo = this;
omemo->context.create(buffer); omemo->context.create(buffer);
omemo->context.set_log_function(&log_emit_weechat); omemo->context.set_log_function(&log_emit_weechat);
@ -2030,11 +1899,10 @@ void omemo::init(struct t_gui_buffer *buffer, const char *account_name)
NULL, NULL, NULL), NULL, NULL, NULL),
&free).get(); &free).get();
lmdb::env &env = omemo->db_env; omemo->db_env = lmdb::env::create();
env = lmdb::env::create(); omemo->db_env.set_max_dbs(50);
env.set_max_dbs(50); omemo->db_env.set_mapsize((size_t)1048576 * 8000); // 8000MB map for valgrind
env.set_mapsize((size_t)1048576 * 8000); // 8000MB map for valgrind omemo->db_env.open(omemo->db_path.data(), MDB_NOSUBDIR, 0664);
env.open(omemo->db_path.data(), MDB_NOSUBDIR, 0664);
lmdb::txn parentTransaction{nullptr}; lmdb::txn parentTransaction{nullptr};
lmdb::txn transaction = lmdb::txn::begin(omemo->db_env, parentTransaction); lmdb::txn transaction = lmdb::txn::begin(omemo->db_env, parentTransaction);
@ -2043,10 +1911,10 @@ void omemo::init(struct t_gui_buffer *buffer, const char *account_name)
omemo->dbi.omemo = lmdb::dbi::open(transaction, db_name.data(), MDB_CREATE); omemo->dbi.omemo = lmdb::dbi::open(transaction, db_name.data(), MDB_CREATE);
transaction.commit(); transaction.commit();
} catch (const std::exception& ex) { } catch (const lmdb::error& ex) {
auto format = fmt::format("%sxmpp: lmdb failure - {}", ex.what()); auto format = fmt::format("%sxmpp: lmdb failure - {}", ex.what());
weechat_printf(NULL, format.data(), weechat_prefix("error")); weechat_printf(NULL, format.data(), weechat_prefix("error"));
return; throw;
} }
struct signal_crypto_provider crypto_provider = { struct signal_crypto_provider crypto_provider = {
@ -2431,7 +2299,6 @@ xmpp_stanza_t *omemo::encode(struct t_account *account, const char *jid,
for (size_t i = 0; i < signal_int_list_size(devicelist); i++) for (size_t i = 0; i < signal_int_list_size(devicelist); i++)
{ {
uint32_t device_id = signal_int_list_at(devicelist, i); uint32_t device_id = signal_int_list_at(devicelist, i);
weechat_printf(NULL, "omemo: trying device %u for %s", device_id, jid);
if (!device_id) continue; if (!device_id) continue;
struct signal_protocol_address address = { struct signal_protocol_address address = {
.name = target, .name_len = strlen(target), .device_id = (int32_t)device_id}; .name = target, .name_len = strlen(target), .device_id = (int32_t)device_id};
@ -2442,14 +2309,18 @@ xmpp_stanza_t *omemo::encode(struct t_account *account, const char *jid,
snprintf(device_id_str, 10+1, "%u", device_id); snprintf(device_id_str, 10+1, "%u", device_id);
xmpp_stanza_set_attribute(header__key, "rid", device_id_str); xmpp_stanza_set_attribute(header__key, "rid", device_id_str);
struct session_builder *builder = NULL;
if (((ret = ss_contains_session_func(&address, omemo))) <= 0) if (((ret = ss_contains_session_func(&address, omemo))) <= 0)
{ {
struct session_pre_key_bundle *bundle; try {
if ((ret = bks_load_bundle(&bundle, &address, omemo))) continue; auto bundle = bks_load_bundle(&address, omemo);
if (!bundle) throw std::runtime_error(fmt::format("No bundle for {}", target));
if ((ret = session_builder_create(&builder, omemo->store_context, &address, omemo->context))) continue; libsignal::session_builder builder(omemo->store_context, &address, omemo->context);
if ((ret = session_builder_process_pre_key_bundle(builder, bundle))) continue; builder.process_pre_key_bundle(*bundle);
}
catch (const std::exception& ex) {
continue;
}
} }
struct session_cipher *cipher; struct session_cipher *cipher;
@ -2478,8 +2349,6 @@ xmpp_stanza_t *omemo::encode(struct t_account *account, const char *jid,
signal_buffer_free(record); signal_buffer_free(record);
//SIGNAL_UNREF(signal_message); //SIGNAL_UNREF(signal_message);
session_cipher_free(cipher); session_cipher_free(cipher);
if (builder)
session_builder_free(builder);
} }
signal_int_list_free(devicelist); signal_int_list_free(devicelist);
target = account_jid(account); target = account_jid(account);

@ -24,8 +24,8 @@ struct t_hook *weechat_xmpp_process_timer = NULL;
struct t_gui_bar_item *weechat_xmpp_typing_bar_item = NULL; struct t_gui_bar_item *weechat_xmpp_typing_bar_item = NULL;
extern "C" {
#pragma GCC visibility push(default) #pragma GCC visibility push(default)
extern "C" {
WEECHAT_PLUGIN_NAME(WEECHAT_XMPP_PLUGIN_NAME); WEECHAT_PLUGIN_NAME(WEECHAT_XMPP_PLUGIN_NAME);
WEECHAT_PLUGIN_DESCRIPTION(N_("XMPP client protocol")); WEECHAT_PLUGIN_DESCRIPTION(N_("XMPP client protocol"));
WEECHAT_PLUGIN_AUTHOR("bqv <weechat@fron.io>"); WEECHAT_PLUGIN_AUTHOR("bqv <weechat@fron.io>");

@ -32,9 +32,28 @@ namespace libsignal {
private: private:
T *_ptr; T *_ptr;
public: protected:
typedef T* pointer_type; typedef T* pointer_type;
inline type(T *ptr) : _ptr(ptr) {
}
template<typename Fun, Fun &func, int success = 0, typename... Args,
typename = std::enable_if_t<std::is_same_v<int, std::invoke_result_t<Fun, pointer_type, Args...>>>>
inline void call(Args&&... args) {
int ret = func(*this, std::forward<Args>(args)...);
if (ret != success) throw std::runtime_error(
fmt::format("Signal Error: expected {}, was {}", success, ret));
}
template<typename Fun, Fun &func, typename... Args,
typename = std::enable_if_t<!std::is_same_v<int, std::invoke_result_t<Fun, pointer_type, Args...>>>>
inline std::invoke_result<Fun, pointer_type, Args...>::type
call(Args&&... args) {
return func(*this, std::forward<Args>(args)...);
}
public:
inline explicit type() : _ptr(nullptr) { inline explicit type() : _ptr(nullptr) {
} }
@ -49,6 +68,9 @@ namespace libsignal {
_ptr = nullptr; _ptr = nullptr;
} }
type(const type &other) = delete; /* no copy construction */
type(type &&other) = default;
template<typename... Args> template<typename... Args>
inline void create(Args&&... args) { inline void create(Args&&... args) {
if (_ptr) if (_ptr)
@ -57,31 +79,16 @@ namespace libsignal {
f_create(&_ptr, std::forward<Args>(args)...); f_create(&_ptr, std::forward<Args>(args)...);
} }
type& operator =(const type &other) = delete; /* no copy assignment */
type& operator =(type &&other) = default;
inline operator bool() const { return _ptr; } inline operator bool() const { return _ptr; }
inline T* operator *() { return _ptr; }
inline operator T*() { return _ptr; } inline operator T*() { return _ptr; }
inline operator const T*() const { return _ptr; } inline operator const T*() const { return _ptr; }
protected:
inline type(T *ptr) {
_ptr = ptr;
}
template<typename Fun, Fun &func, int success = 0, typename... Args,
typename = std::enable_if_t<std::is_same_v<int, std::invoke_result_t<Fun, pointer_type, Args...>>>>
inline void call(Args&&... args) {
int ret = func(*this, std::forward<Args>(args)...);
if (ret != success) throw std::runtime_error(
fmt::format("Signal Error: expected {}, was {}", success, ret));
}
template<typename Fun, Fun &func, typename... Args,
typename = std::enable_if_t<!std::is_same_v<int, std::invoke_result_t<Fun, pointer_type, Args...>>>>
inline std::invoke_result<Fun, pointer_type, Args...>::type
call(Args&&... args) {
return func(*this, std::forward<Args>(args)...);
}
}; };
typedef type<struct signal_context, typedef type<struct signal_context,
@ -91,9 +98,6 @@ namespace libsignal {
public: public:
using context_type::context_type; using context_type::context_type;
context& operator =(const context &other) = delete;
context& operator =(context &&other) = default;
inline auto set_log_function(auto &&...args) { inline auto set_log_function(auto &&...args) {
return call<decltype(signal_context_set_log_function), return call<decltype(signal_context_set_log_function),
signal_context_set_log_function>(args...); signal_context_set_log_function>(args...);
@ -220,4 +224,30 @@ namespace libsignal {
} }
}; };
typedef type<struct session_pre_key_bundle,
decltype(session_pre_key_bundle_create),
decltype(session_pre_key_bundle_destroy),
session_pre_key_bundle_create,
session_pre_key_bundle_destroy,
signal_type_base> pre_key_bundle_type;
class pre_key_bundle : public pre_key_bundle_type {
public:
using pre_key_bundle_type::pre_key_bundle_type;
};
typedef type<struct session_builder,
decltype(session_builder_create),
decltype(session_builder_free),
session_builder_create,
session_builder_free> session_builder_type;
class session_builder : public session_builder_type {
public:
using session_builder_type::session_builder_type;
inline auto process_pre_key_bundle(auto &&...args) {
return call<decltype(session_builder_process_pre_key_bundle),
session_builder_process_pre_key_bundle>(args...);
}
};
} }

@ -1,13 +0,0 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#pragma once
#include <memory>
#include <functional>
#include <strophe.h>
namespace strophe {
}

@ -0,0 +1,166 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include "node.hh"
#pragma GCC visibility push(default)
#include "ns.hh"
#pragma GCC visibility pop
std::string get_name(xmpp_stanza_t *stanza) {
const char *result = NULL;
result = xmpp_stanza_get_name(stanza);
if (result)
return result;
else
return {};
}
std::optional<std::string> get_attribute(xmpp_stanza_t *stanza, const char *name) {
const char *result = NULL;
result = xmpp_stanza_get_attribute(stanza, name);
if (result)
return result;
else
return {};
}
std::string get_text(xmpp_stanza_t *stanza) {
const char *result = NULL;
result = xmpp_stanza_get_text_ptr(stanza);
if (result)
return result;
else
return {};
}
std::chrono::system_clock::time_point get_time(const std::string& text) {
std::tm tm = {};
std::istringstream ss(text);
//ss.imbue(std::locale("en_GB.utf-8"));
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S%z");
if (ss.fail()) {
throw std::invalid_argument("Bad time format");
} else {
return std::chrono::system_clock::from_time_t(std::mktime(&tm));
}
}
jid::jid(xmpp_ctx_t *context, std::string s) {
char *result = NULL;
result = xmpp_jid_node(context, s.data());
if (result)
{
local = result;
xmpp_free(context, result);
result = NULL;
}
result = xmpp_jid_domain(context, s.data());
if (result)
{
domain = result;
xmpp_free(context, result);
result = NULL;
}
else
throw std::invalid_argument("Invalid JID");
result = xmpp_jid_resource(context, s.data());
if (result)
{
resource = result;
xmpp_free(context, result);
result = NULL;
}
}
std::string jid::full() const {
return fmt::format("{}{}{}{}{}", *local, local ? "@" : "", domain,
resource ? "/" : "", *resource);
}
std::string jid::bare() const {
return fmt::format("{}{}{}", *local, local ? "@" : "", domain);
}
bool jid::is_bare() const {
return !resource;
}
xml::node::node() {}
void xml::node::bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) {
name = get_name(stanza);
id = get_attribute(stanza, "id");
ns = get_attribute(stanza, "xmlns");
int count = xmpp_stanza_get_attribute_count(stanza);
std::vector<const char*> attrvec(count * 2, nullptr);
const char **attrs = attrvec.data();
xmpp_stanza_get_attributes(stanza, attrs, count * 2);
for (int i = 0; i < count; i++) {
const char *key = attrs[(2*i)];
const char *value = attrs[(2*i)+1];
attributes.emplace(key, value);
}
text = get_text(stanza);
for (xmpp_stanza_t *child = xmpp_stanza_get_children(stanza);
child; child = xmpp_stanza_get_next(child)) {
if (xmpp_stanza_is_text(child))
text += get_text(child);
else
children.emplace_back(context, child);
}
}
void xml::message::bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) {
auto result = get_attribute(stanza, "from");
if (result)
from = jid(context, *result);
result = get_attribute(stanza, "to");
if (result)
to = jid(context, *result);
type = get_attribute(stanza, "type");
node::bind(context, stanza);
}
std::optional<std::string> xml::presence::show() {
auto child = get_children("show");
if (child.size() > 0)
return child.front().get().text;
return {};
}
std::optional<std::string> xml::presence::status() {
auto child = get_children("status");
if (child.size() > 0)
return child.front().get().text;
return {};
}
void xml::presence::bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) {
auto result = get_attribute(stanza, "from");
if (result)
from = jid(context, *result);
result = get_attribute(stanza, "to");
if (result)
to = jid(context, *result);
type = get_attribute(stanza, "type");
node::bind(context, stanza);
}
void xml::iq::bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) {
auto result = get_attribute(stanza, "from");
if (result)
from = jid(context, *result);
result = get_attribute(stanza, "to");
if (result)
to = jid(context, *result);
type = get_attribute(stanza, "type");
node::bind(context, stanza);
}

@ -0,0 +1,138 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#pragma once
#include <map>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
#include <chrono>
#include <sstream>
#include <locale>
#include <iomanip>
#include <fmt/core.h>
#include <strophe.h>
std::string get_name(xmpp_stanza_t *stanza);
std::optional<std::string> get_attribute(xmpp_stanza_t *stanza, const char *name);
std::string get_text(xmpp_stanza_t *stanza);
std::chrono::system_clock::time_point get_time(const std::string& text);
class jid {
public:
std::optional<std::string> local;
std::string domain;
std::optional<std::string> resource;
jid(xmpp_ctx_t *context, std::string s);
std::string full() const;
std::string bare() const;
bool is_bare() const;
};
namespace xml {
class node {
protected:
explicit node();
public:
inline node(xmpp_ctx_t *context, xmpp_stanza_t *stanza) {
bind(context, stanza);
}
std::optional<std::string> name;
std::optional<std::string> id;
std::optional<std::string> ns;
std::map<std::string, std::string> attributes;
std::vector<node> children;
std::string text;
virtual void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza);
template<typename xmlns>
inline std::vector<std::reference_wrapper<node>>
get_children(std::string_view name) {
std::vector<std::reference_wrapper<node>> list;
std::copy_if(children.begin(), children.end(),
std::back_inserter(list),
[&](node& x) {
return x.name == name
&& x.ns == std::string_view(xmlns());
});
return list;
}
inline std::vector<std::reference_wrapper<node>>
get_children(std::string_view name) {
std::vector<std::reference_wrapper<node>> list;
std::copy_if(children.begin(), children.end(),
std::back_inserter(list),
[&](node& x) {
return x.name == name;
});
return list;
}
};
}
#include "xep-0027.inl"
#include "xep-0045.inl"
#include "xep-0115.inl"
#include "xep-0319.inl"
namespace xml {
class message : virtual public node, public xep0027 {
public:
using node::node;
std::optional<jid> from;
std::optional<jid> to;
std::optional<std::string> type;
void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override;
};
class presence : virtual public node, public xep0045, public xep0115, public xep0319 {
public:
using node::node;
std::optional<jid> from;
std::optional<jid> to;
std::optional<std::string> type;
std::optional<std::string> show();
std::optional<std::string> status();
void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override;
};
class iq : virtual public node {
public:
using node::node;
std::optional<jid> from;
std::optional<jid> to;
std::optional<std::string> type;
void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override;
};
}

@ -0,0 +1,276 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#pragma once
#include <string_view>
class xmlns {
private:
const char *const _uri;
protected:
inline xmlns(const char *uri) : _uri(uri) {}
public:
inline const char *ns() { return _uri; }
inline operator const char *() { return _uri; }
};
struct etherx_jabber_org {
struct streams : public xmlns { streams() : xmlns("http://etherx.jabber.org/streams") {} };
};
struct jabber_org {
struct features {
struct amp : public xmlns { amp() : xmlns("http://jabber.org/features/amp") {} };
struct compress : public xmlns { compress() : xmlns("http://jabber.org/features/compress") {} };
};
struct protocol {
struct activity : public xmlns { activity() : xmlns("http://jabber.org/protocol/activity") {} };
struct address : public xmlns { address() : xmlns("http://jabber.org/protocol/address") {} };
struct amp : public xmlns { amp() : xmlns("http://jabber.org/protocol/amp") {}
struct errors : public xmlns { errors() : xmlns("http://jabber.org/protocol/amp#errors") {} };
};
struct bytestreams : public xmlns { bytestreams() : xmlns("http://jabber.org/protocol/bytestreams") {} };
struct caps : public xmlns { caps() : xmlns("http://jabber.org/protocol/caps") {} };
struct chatstates : public xmlns { chatstates() : xmlns("http://jabber.org/protocol/chatstates") {} };
struct commands : public xmlns { commands() : xmlns("http://jabber.org/protocol/commands") {} };
struct compress : public xmlns { compress() : xmlns("http://jabber.org/protocol/compress") {}
struct exi : public xmlns { exi() : xmlns("http://jabber.org/protocol/compress/exi") {} };
};
struct disco {
struct info : public xmlns { info() : xmlns("http://jabber.org/protocol/disco#info") {} };
struct items : public xmlns { items() : xmlns("http://jabber.org/protocol/disco#items") {} };
};
struct feature_neg : public xmlns { feature_neg() : xmlns("http://jabber.org/protocol/feature-neg") {} };
struct files : public xmlns { files() : xmlns("http://jabber.org/protocol/files") {} };
struct geoloc : public xmlns { geoloc() : xmlns("http://jabber.org/protocol/geoloc") {} };
struct http_auth : public xmlns { http_auth() : xmlns("http://jabber.org/protocol/http-auth") {} };
struct httpbind : public xmlns { httpbind() : xmlns("http://jabber.org/protocol/httpbind") {} };
struct ibb : public xmlns { ibb() : xmlns("http://jabber.org/protocol/ibb") {} };
struct jinglenodes : public xmlns { jinglenodes() : xmlns("http://jabber.org/protocol/jinglenodes") {} };
struct mood : public xmlns { mood() : xmlns("http://jabber.org/protocol/mood") {} };
struct muc : public xmlns { muc() : xmlns("http://jabber.org/protocol/muc") {}
struct admin : public xmlns { admin() : xmlns("http://jabber.org/protocol/muc#admin") {} };
struct owner : public xmlns { owner() : xmlns("http://jabber.org/protocol/muc#owner") {} };
struct unique : public xmlns { unique() : xmlns("http://jabber.org/protocol/muc#unique") {} };
struct user : public xmlns { user() : xmlns("http://jabber.org/protocol/muc#user") {} };
};
struct nick : public xmlns { nick() : xmlns("http://jabber.org/protocol/nick") {} };
struct offline : public xmlns { offline() : xmlns("http://jabber.org/protocol/offline") {} };
struct physloc : public xmlns { physloc() : xmlns("http://jabber.org/protocol/physloc") {} };
struct poke : public xmlns { poke() : xmlns("http://jabber.org/protocol/poke") {} };
struct pubsub : public xmlns { pubsub() : xmlns("http://jabber.org/protocol/pubsub") {}
struct errors : public xmlns { errors() : xmlns("http://jabber.org/protocol/pubsub#errors") {} };
struct event : public xmlns { event() : xmlns("http://jabber.org/protocol/pubsub#event") {} };
struct owner : public xmlns { owner() : xmlns("http://jabber.org/protocol/pubsub#owner") {} };
};
struct rosterx : public xmlns { rosterx() : xmlns("http://jabber.org/protocol/rosterx") {} };
struct rsm : public xmlns { rsm() : xmlns("http://jabber.org/protocol/rsm") {} };
struct shim : public xmlns { shim() : xmlns("http://jabber.org/protocol/shim") {} };
struct si : public xmlns { si() : xmlns("http://jabber.org/protocol/si") {}
struct profile {
struct file_transfer : public xmlns { file_transfer() : xmlns("http://jabber.org/protocol/si/profile/file-transfer") {} };
};
};
struct sipub : public xmlns { sipub() : xmlns("http://jabber.org/protocol/sipub") {} };
struct soap {
struct fault : public xmlns { fault() : xmlns("http://jabber.org/protocol/soap#fault") {} };
};
struct tune : public xmlns { tune() : xmlns("http://jabber.org/protocol/tune") {} };
struct waitinglist : public xmlns { waitinglist() : xmlns("http://jabber.org/protocol/waitinglist") {} };
struct workgroup : public xmlns { workgroup() : xmlns("http://jabber.org/protocol/workgroup") {} };
struct xdata_layout : public xmlns { xdata_layout() : xmlns("http://jabber.org/protocol/xdata-layout") {} };
struct xdata_validate : public xmlns { xdata_validate() : xmlns("http://jabber.org/protocol/xdata-validate") {} };
struct xhtml_im : public xmlns { xhtml_im() : xmlns("http://jabber.org/protocol/xhtml-im") {} };
};
};
struct jabber {
struct client : public xmlns { client() : xmlns("jabber:client") {} };
struct component {
struct accept : public xmlns { accept() : xmlns("jabber:component:accept") {} };
struct connect : public xmlns { connect() : xmlns("jabber:component:connect") {} };
};
struct iq {
struct auth : public xmlns { auth() : xmlns("jabber:iq:auth") {} };
struct gateway : public xmlns { gateway() : xmlns("jabber:iq:gateway") {} };
struct last : public xmlns { last() : xmlns("jabber:iq:last") {} };
struct oob : public xmlns { oob() : xmlns("jabber:iq:oob") {} };
struct pass : public xmlns { pass() : xmlns("jabber:iq:pass") {} };
struct privacy : public xmlns { privacy() : xmlns("jabber:iq:privacy") {} };
struct private_ : public xmlns { private_() : xmlns("jabber:iq:private") {} };
struct register_ : public xmlns { register_() : xmlns("jabber:iq:register") {} };
struct roster : public xmlns { roster() : xmlns("jabber:iq:roster") {} };
struct rpc : public xmlns { rpc() : xmlns("jabber:iq:rpc") {} };
struct search : public xmlns { search() : xmlns("jabber:iq:search") {} };
struct time : public xmlns { time() : xmlns("jabber:iq:time") {} };
struct version : public xmlns { version() : xmlns("jabber:iq:version") {} };
};
struct server : public xmlns { server() : xmlns("jabber:server") {}
struct dialback : public xmlns { dialback() : xmlns("jabber:server:dialback") {} };
};
struct x {
struct conference : public xmlns { conference() : xmlns("jabber:x:conference") {} };
struct data : public xmlns { data() : xmlns("jabber:x:data") {} };
struct delay : public xmlns { delay() : xmlns("jabber:x:delay") {} };
struct encrypted : public xmlns { encrypted() : xmlns("jabber:x:encrypted") {} };
struct event : public xmlns { event() : xmlns("jabber:x:event") {} };
struct expire : public xmlns { expire() : xmlns("jabber:x:expire") {} };
struct oob : public xmlns { oob() : xmlns("jabber:x:oob") {} };
struct roster : public xmlns { roster() : xmlns("jabber:x:roster") {} };
struct signed_ : public xmlns { signed_() : xmlns("jabber:x:signed") {} };
};
};
struct roster {
struct delimiter : public xmlns { delimiter() : xmlns("roster:delimiter") {} };
};
struct storage {
struct bookmarks : public xmlns { bookmarks() : xmlns("storage:bookmarks") {} };
struct metacontacts : public xmlns { metacontacts() : xmlns("storage:metacontacts") {} };
struct pubsubs : public xmlns { pubsubs() : xmlns("storage:pubsubs") {} };
struct rosternotes : public xmlns { rosternotes() : xmlns("storage:rosternotes") {} };
};
struct urn {
struct ietf {
struct params {
struct xml {
struct ns {
struct xmpp_bind : public xmlns { xmpp_bind() : xmlns("urn:ietf:params:xml:ns:xmpp-bind") {} };
struct xmpp_e2e : public xmlns { xmpp_e2e() : xmlns("urn:ietf:params:xml:ns:xmpp-e2e") {} };
struct xmpp_sasl : public xmlns { xmpp_sasl() : xmlns("urn:ietf:params:xml:ns:xmpp-sasl") {} };
struct xmpp_session : public xmlns { xmpp_session() : xmlns("urn:ietf:params:xml:ns:xmpp-session") {} };
struct xmpp_stanzas : public xmlns { xmpp_stanzas() : xmlns("urn:ietf:params:xml:ns:xmpp-stanzas") {} };
struct xmpp_streams : public xmlns { xmpp_streams() : xmlns("urn:ietf:params:xml:ns:xmpp-streams") {} };
struct xmpp_tls : public xmlns { xmpp_tls() : xmlns("urn:ietf:params:xml:ns:xmpp-tls") {} };
};
};
};
};
struct xmpp {
struct ago { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:ago:0") {} }; };
struct archive : public xmlns { archive() : xmlns("urn:xmpp:archive") {} };
struct attention { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:attention:0") {} }; };
struct avatar {
struct data : public xmlns { data() : xmlns("urn:xmpp:avatar:data") {} };
struct metadata : public xmlns { metadata() : xmlns("urn:xmpp:avatar:metadata") {} };
};
struct bidi : public xmlns { bidi() : xmlns("urn:xmpp:bidi") {} };
struct blocking : public xmlns { blocking() : xmlns("urn:xmpp:blocking") {}
struct errors : public xmlns { errors() : xmlns("urn:xmpp:blocking:errors") {} };
};
struct bob : public xmlns { bob() : xmlns("urn:xmpp:bob") {} };
struct bookmarks { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:bookmarks:1") {} }; };
struct browsing { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:browsing:0") {} }; };
struct bxmpp : public xmlns { bxmpp() : xmlns("urn:xmpp:bxmpp") {} };
struct caps : public xmlns { caps() : xmlns("urn:xmpp:caps") {} };
struct captcha : public xmlns { captcha() : xmlns("urn:xmpp:captcha") {} };
struct carbons { struct _2 : public xmlns { _2() : xmlns("urn:xmpp:carbons:2") {} }; };
struct chatting { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:chatting:0") {} }; };
struct cmr { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:cmr:0") {} }; };
struct component { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:component:0") {} }; };
struct decloak { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:decloak:0") {} }; };
struct delay : public xmlns { delay() : xmlns("urn:xmpp:delay") {} };
struct delegation { struct _2 : public xmlns { _2() : xmlns("urn:xmpp:delegation:2") {} }; };
struct domain_based_name { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:domain-based-name:1") {} }; };
struct dox { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:dox:0") {} }; };
struct eventlog : public xmlns { eventlog() : xmlns("urn:xmpp:eventlog") {} };
struct extdisco { struct _2 : public xmlns { _2() : xmlns("urn:xmpp:extdisco:2") {} }; };
struct features {
struct rosterver : public xmlns { rosterver() : xmlns("urn:xmpp:features:rosterver") {} };
};
struct forward { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:forward:0") {} }; };
struct gaming { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:gaming:0") {} }; };
struct hashes { struct _2 : public xmlns { _2() : xmlns("urn:xmpp:hashes:2") {} }; };
struct http : public xmlns { http() : xmlns("urn:xmpp:http") {}
struct upload { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:http:upload:0") {} }; };
};
struct idle { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:idle:1") {} }; };
struct incident { struct _2 : public xmlns { _2() : xmlns("urn:xmpp:incident:2") {} }; };
struct invisible { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:invisible:1") {} }; };
struct iot {
struct concentrators : public xmlns { concentrators() : xmlns("urn:xmpp:iot:concentrators") {} };
struct control : public xmlns { control() : xmlns("urn:xmpp:iot:control") {} };
struct discovery : public xmlns { discovery() : xmlns("urn:xmpp:iot:discovery") {} };
struct provisioning : public xmlns { provisioning() : xmlns("urn:xmpp:iot:provisioning") {} };
struct sensordata : public xmlns { sensordata() : xmlns("urn:xmpp:iot:sensordata") {} };
};
struct jid { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jid:0") {} }; };
struct jingle { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:1") {} };
struct apps {
struct dtls { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:apps:dtls:0") {} }; };
struct rtp { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:apps:rtp:1") {} };
struct errors { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:apps:rtp:errors:1") {} }; };
struct info { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:apps:rtp:info:1") {} }; };
struct rtcp_fb { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:apps:rtp:rtcp-fb:0") {} }; };
struct rtp_hdrext { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:apps:rtp:rtp-hdrext:0") {} }; };
struct ssma { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:apps:rtp:ssma:0") {} }; };
struct zrtp { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:apps:rtp:zrtp:1") {} }; };
};
struct xmlstream { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:apps:xmlstream:0") {} }; };
};
struct dtmf { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:dtmf:0") {} }; };
struct errors { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:errors:1") {} }; };
struct transfer { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:transfer:0") {} }; };
struct transports {
struct dtls_sctp { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:transports:dtls-sctp:1") {} }; };
struct ibb { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:transports:ibb:1") {} }; };
struct ice { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:transports:ice:0") {} }; };
struct ice_udp { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:transports:ice-udp:1") {} }; };
struct raw_udp { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:transports:raw-udp:1") {} }; };
struct s5b { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle:transports:s5b:1") {} }; };
struct webrtc_datachannel { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:jingle:transports:webrtc-datachannel:0") {} }; };
};
};
struct jingle_message { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jingle-message:1") {} }; };
struct jinglepub { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:jinglepub:1") {} }; };
struct json { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:json:0") {} }; };
struct keepalive { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:keepalive:0") {} }; };
struct langtrans : public xmlns { langtrans() : xmlns("urn:xmpp:langtrans") {}
struct items : public xmlns { items() : xmlns("urn:xmpp:langtrans:items") {} };
};
struct locationquery { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:locationquery:0") {} }; };
struct media_element : public xmlns { media_element() : xmlns("urn:xmpp:media-element") {} };
struct message_attaching { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:message-attaching:1") {} }; };
struct message_correct { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:message-correct:0") {} }; };
struct message_moderate { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:message-moderate:0") {} }; };
struct message_retract { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:message-retract:0") {} }; };
struct muc {
struct conditions { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:muc:conditions:1") {} }; };
};
struct omemo { struct _2 : public xmlns { _2() : xmlns("urn:xmpp:omemo:2") {} }; };
struct order_by { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:order-by:1") {} }; };
struct pie { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:pie:0") {} }; };
struct ping : public xmlns { ping() : xmlns("urn:xmpp:ping") {} };
struct privilege { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:privilege:1") {} }; };
struct reach { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:reach:0") {} }; };
struct receipts : public xmlns { receipts() : xmlns("urn:xmpp:receipts") {} };
struct reputation { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:reputation:0") {} }; };
struct sec_label { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:sec-label:0") {} };
struct catalog { struct _2 : public xmlns { _2() : xmlns("urn:xmpp:sec-label:catalog:2") {} }; };
struct ess { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:sec-label:ess:0") {} }; };
};
struct sic { struct _1 : public xmlns { _1() : xmlns("urn:xmpp:sic:1") {} }; };
struct sift { struct _2 : public xmlns { _2() : xmlns("urn:xmpp:sift:2") {} }; };
struct sm { struct _3 : public xmlns { _3() : xmlns("urn:xmpp:sm:3") {} }; };
struct spoiler { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:spoiler:0") {} }; };
struct thumbs { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:thumbs:0") {} }; };
struct time : public xmlns { time() : xmlns("urn:xmpp:time") {} };
struct tmp {
struct abuse : public xmlns { abuse() : xmlns("urn:xmpp:tmp:abuse") {} };
struct io_data : public xmlns { io_data() : xmlns("urn:xmpp:tmp:io-data") {} };
struct mine { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:tmp:mine:0") {} }; };
struct profile : public xmlns { profile() : xmlns("urn:xmpp:tmp:profile") {} };
struct roster_management { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:tmp:roster-management:0") {} }; };
};
struct viewing { struct _0 : public xmlns { _0() : xmlns("urn:xmpp:viewing:0") {} }; };
struct xbosh : public xmlns { xbosh() : xmlns("urn:xmpp:xbosh") {} };
struct xdata {
struct dynamic : public xmlns { dynamic() : xmlns("urn:xmpp:xdata:dynamic") {} };
};
};
};
struct vcard_temp_filter : public xmlns { vcard_temp_filter() : xmlns("vcard-temp-filter") {} };
struct vcard_temp {
struct x {
struct update : public xmlns { update() : xmlns("vcard-temp:x:update") {} };
};
};

@ -0,0 +1,34 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#pragma once
#include <optional>
#include <string>
#include "node.hh"
#pragma GCC visibility push(default)
#include "ns.hh"
#pragma GCC visibility pop
namespace xml {
class xep0027 : virtual public node {
public:
std::optional<std::string> signature() {
auto child = get_children<jabber::x::signed_>("x");
if (child.size() > 0)
return child.front().get().text;
return {};
}
std::optional<std::string> encrypted() {
auto child = get_children<jabber::x::encrypted>("x");
if (child.size() > 0)
return child.front().get().text;
return {};
}
};
}

@ -0,0 +1,62 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#pragma once
#include <optional>
#include <string>
#include "node.hh"
#pragma GCC visibility push(default)
#include "ns.hh"
#pragma GCC visibility pop
namespace xml {
class xep0045 : virtual public node {
public:
class x {
private:
struct decline {
std::string reason;
std::optional<jid> from;
std::optional<jid> to;
};
struct destroy {
std::string reason;
std::optional<jid> target;
};
struct invite {
std::string reason;
std::optional<jid> from;
std::optional<jid> to;
};
struct item {
std::string reason;
};
public:
x(const node& node) {
}
std::vector<decline> declines;
std::vector<destroy> destroys;
std::vector<invite> invites;
std::vector<item> items;
std::vector<std::string> passwords;
std::vector<int> statuses;
};
std::optional<x> muc_user() {
auto child = get_children<jabber_org::protocol::muc::user>("x");
if (child.size() > 0)
return child.front().get();
return {};
}
};
}

@ -0,0 +1,27 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#pragma once
#include <optional>
#include <string>
#include "node.hh"
#pragma GCC visibility push(default)
#include "ns.hh"
#pragma GCC visibility pop
namespace xml{
class xep0115 : virtual public node {
public:
std::optional<node> caps() {
auto child = get_children<jabber_org::protocol::caps>("c");
if (child.size() > 0)
return child.front().get();
return {};
}
};
}

@ -0,0 +1,38 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, version 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#pragma once
#include <chrono>
#include <optional>
#include <stdexcept>
#include <string>
#include "node.hh"
#pragma GCC visibility push(default)
#include "ns.hh"
#pragma GCC visibility pop
namespace xml {
class xep0319 : virtual public node {
public:
std::optional<std::chrono::system_clock::time_point> idle_since() {
auto children = get_children<urn::xmpp::idle::_1>("idle");
if (children.size() <= 0)
return {};
auto child = children.front().get();
auto since = child.attributes.find("since");
if (since == child.attributes.end())
return {};
try {
return get_time(since->second);
}
catch (const std::invalid_argument& ex) {
return {};
}
}
};
}
Loading…
Cancel
Save