diff --git a/.envrc b/.envrc index dc64a39..b4ec2e7 100644 --- a/.envrc +++ b/.envrc @@ -30,6 +30,7 @@ PACKAGES=( libsignal-protocol-c -l libomemo.scm # Dep (libsignal) lmdb lmdbxx # Dep (lmdb) rnp # Dep (rnpgp) + xsd # Generate xml schema code ) echo direnv: fetching source - weechat diff --git a/README.org b/README.org index 84aa336..2635814 100644 --- a/README.org +++ b/README.org @@ -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/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://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 | - | Location: | [[http://github.com/bqv/weechat-xmpp]] | - | Version: | 0.1.1 | + | Location: | [[http://github.com/bqv/weechat-xmpp]] | + | Version: | 0.2.0 | | Disclaimer: | I'm lazy and unashamedly clinically insane | * Description diff --git a/channel.cpp b/channel.cpp index 6625e5f..e4139a7 100644 --- a/channel.cpp +++ b/channel.cpp @@ -1048,7 +1048,7 @@ int channel__send_message(struct t_account *account, struct t_channel *channel, if (!encrypted) { 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); xmpp_stanza_release(message); return WEECHAT_RC_ERROR; diff --git a/connection.cpp b/connection.cpp index 8aaebc2..11f90fb 100644 --- a/connection.cpp +++ b/connection.cpp @@ -12,6 +12,7 @@ #include #include "plugin.hh" +#include "xmpp/node.hh" #include "xmpp/stanza.hh" #include "config.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) { - (void) conn; - struct t_account *account = (struct t_account *)userdata; struct t_user *user; 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; char *clientid = NULL, *status; + auto binding = xml::presence(account->context, stanza); to = xmpp_stanza_get_to(stanza); from = xmpp_stanza_get_from(stanza); 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) { - (void) conn; - struct t_account *account = (struct t_account *)userdata; 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; @@ -282,6 +280,7 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * struct tm time = {0}; time_t date = 0; + auto binding = xml::message(account->context, stanza); body = xmpp_stanza_get_child_by_name(stanza, "body"); 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", "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) { 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) { 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 *storage, *conference, *nick; + auto binding = xml::iq(account->context, stanza); const char *id = xmpp_stanza_get_id(stanza); const char *from = xmpp_stanza_get_from(stanza); const char *to = xmpp_stanza_get_to(stanza); diff --git a/gcrypt.hh b/gcrypt.hh index 916b613..4aab32d 100644 --- a/gcrypt.hh +++ b/gcrypt.hh @@ -6,8 +6,26 @@ #include #include +#include #include 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(); + } + } diff --git a/makefile b/makefile index 82c74f5..496bd3f 100644 --- a/makefile +++ b/makefile @@ -13,16 +13,17 @@ INCLUDES=-Ilibstrophe -Ideps -Ideps/fmt/include \ CFLAGS+=$(DBGCFLAGS) \ -fno-omit-frame-pointer -fPIC \ -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 \ -Werror-implicit-function-declaration \ -Wno-missing-field-initializers \ -D_XOPEN_SOURCE=700 \ $(INCLUDES) -CPPFLAGS+=$(DBGCFLAGS) \ +CPPFLAGS+=$(DBGCFLAGS) -O0 \ -fno-omit-frame-pointer -fPIC \ -fvisibility=hidden -fvisibility-inlines-hidden \ - -std=c++20 -gdwarf-4 \ + -std=c++20 -gdwarf-4 -fkeep-inline-functions \ -Wall -Wextra -pedantic \ -Wno-missing-field-initializers \ $(INCLUDES) @@ -56,6 +57,8 @@ HDRS=plugin.hh \ user.hh \ util.hh \ xmpp/stanza.hh \ + xmpp/ns.hh \ + xmpp/node.hh \ SRCS=plugin.cpp \ account.cpp \ @@ -73,6 +76,7 @@ SRCS=plugin.cpp \ util.cpp \ xmpp/presence.cpp \ xmpp/iq.cpp \ + xmpp/node.cpp \ DEPS=deps/diff/libdiff.a \ deps/fmt/libfmt.a \ @@ -99,13 +103,13 @@ xmpp.so: $(OBJS) $(DEPS) $(HDRS) $(CXX) $(CPPFLAGS) -c $< -o $@ .%.cov.o: %.cpp - @$(CXX) --coverage -O0 $(CPPFLAGS) -c $< -o $@ + @$(CXX) --coverage $(CPPFLAGS) -O0 -c $< -o $@ xmpp/.%.o: xmpp/%.cpp $(CXX) $(CPPFLAGS) -c $< -o $@ xmpp/.%.cov.o: xmpp/%.cpp - @$(CXX) --coverage -O0 $(CPPFLAGS) -c $< -o $@ + @$(CXX) --coverage $(CPPFLAGS) -O0 -c $< -o $@ deps/diff/libdiff.a: git submodule update --init --recursive diff --git a/omemo.cpp b/omemo.cpp index 6b9982f..14a7a57 100644 --- a/omemo.cpp +++ b/omemo.cpp @@ -4,14 +4,18 @@ #include #include -#include +#include +#include +#include +#include +#include +#include #include #include #include #include #include #include -#include #include #include @@ -19,6 +23,7 @@ #include "xmpp/stanza.hh" #include "account.hh" #include "omemo.hh" +#include "gcrypt.hh" #include "util.hh" 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, v_local_public_key.mv_size); - omemo->identity = identity; + omemo->identity = std::move(identity); } 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) { - MDB_txn *transaction = NULL; - MDB_val k_devicelist = { - .mv_size = strlen("devicelist_") + strlen(jid), - .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}; + auto transaction = lmdb::txn::begin(omemo->db_env); + std::string k_devicelist = fmt::format("devicelist_{}", jid); + std::string v_devicelist; + for (size_t i = 0; i < signal_int_list_size(devicelist); i++) { int device = signal_int_list_at(devicelist, i); - devices[i] = (char*)malloc(sizeof(*devices) * (10 + 1)); - devices[i+1] = NULL; - snprintf(devices[i], 10 + 1, "%u", device); - } - v_devicelist.mv_data = weechat_string_build_with_split_string( - (const char **)devices, ";"); - v_devicelist.mv_size = strlen((const char*)v_devicelist.mv_data); - for (char **device = (char **)devices; *device; device++) free(*device); - - if (mdb_txn_begin(omemo->db_env, NULL, 0, &transaction)) { - weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", - weechat_prefix("error")); - return -1; - } + std::string device_id = std::to_string(device); + if (v_devicelist.size() > 0) + v_devicelist += ";"; + v_devicelist += device_id; + } + + omemo->dbi.omemo.put(transaction, k_devicelist, v_devicelist); + //omemo->dbi.omemo.put(wtxn, "fullname", std::string_view("J. Random Hacker")); + //{ + // auto cursor = lmdb::cursor::open(rtxn, dbi); + // std::string_view key, value; + // if (cursor.get(key, value, MDB_FIRST)) { + // 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, - &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; - }; + transaction.commit(); return 0; -cleanup: - mdb_txn_abort(transaction); - return -1; } 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; omemo->dbi.omemo.get(transaction, k_devicelist, v_devicelist); - int devices_len = 0; - char **devices = weechat_string_split(v_devicelist.data(), ";", NULL, 0, 0, &devices_len); + auto devices = v_devicelist + | 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(); - 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, strtol(device_id, NULL, 10)); + signal_int_list_push_back(*devicelist, device_id); } - weechat_string_free_split(devices); transaction.commit(); @@ -1637,28 +1623,18 @@ int bks_store_bundle(struct signal_protocol_address *address, { size_t n_pre_keys = -1; while (pre_keys[++n_pre_keys] != NULL); - char **pre_key_buffers = (char**)malloc(sizeof(char*) * (n_pre_keys + 1)); - for (size_t i = 0; i < n_pre_keys; i++) + auto pre_key_buffers = std::vector(n_pre_keys); + for (auto pre_key : std::vector(pre_keys, pre_keys + n_pre_keys)) { - struct t_pre_key *pre_key = pre_keys[i]; - 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; - snprintf(pre_key_buffers[i], keylen, - "%s.%s", pre_key->id, pre_key->public_key); - } - - 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++) + pre_key_buffers.push_back(fmt::format("{}.{}", pre_key->id, pre_key->public_key)); + } + + n_pre_keys = -1; + while (signed_pre_keys[++n_pre_keys] != NULL); + auto signed_pre_key_buffers = std::vector(n_pre_keys); + for (auto signed_pre_key : std::vector(signed_pre_keys, signed_pre_keys + n_pre_keys)) { - struct t_pre_key *signed_pre_key = signed_pre_keys[i]; - 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); + signed_pre_key_buffers.push_back(fmt::format("{}.{}", signed_pre_key->id, signed_pre_key->public_key)); uint8_t *signing_key_buf; 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; - const char *jid = address->name; - uint32_t device_id = address->device_id; - size_t keylen = strlen("bundle_??_") + strlen(jid) + 1 + 10 + 1; - 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); + std::string k_bundle_pk = fmt::format("bundle_pk_{}_{}", address->name, address->device_id); + std::string k_bundle_sk = fmt::format("bundle_sk_{}_{}", address->name, address->device_id); + std::string k_bundle_sg = fmt::format("bundle_sg_{}_{}", address->name, address->device_id); + std::string k_bundle_ik = fmt::format("bundle_ik_{}_{}", address->name, address->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, - }; + std::string v_bundle_pk = std::accumulate(pre_key_buffers.begin(), pre_key_buffers.end(), std::string(";")); + std::string v_bundle_sk = std::accumulate(signed_pre_key_buffers.begin(), signed_pre_key_buffers.end(), std::string(";")); + std::string_view v_bundle_sg = signature; + std::string_view v_bundle_ik = identity_key; - if (mdb_txn_begin(omemo->db_env, NULL, 0, &transaction)) { - weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", - weechat_prefix("error")); - return -1; - } + auto transaction = lmdb::txn::begin(omemo->db_env); - int ret; - 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; - }; + omemo->dbi.omemo.put(transaction, k_bundle_pk, v_bundle_pk); + omemo->dbi.omemo.put(transaction, k_bundle_sk, v_bundle_sk); + omemo->dbi.omemo.put(transaction, k_bundle_sg, v_bundle_sg); + omemo->dbi.omemo.put(transaction, k_bundle_ik, v_bundle_ik); - if ((ret = mdb_txn_commit(transaction))) { - weechat_printf(NULL, "%sxmpp: failed to write lmdb transaction", - weechat_prefix("error")); - goto cleanup; - }; + transaction.commit(); 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 bks_load_bundle(struct signal_protocol_address *address, t_omemo *omemo) { - MDB_txn *transaction = NULL; - const char *jid = address->name; - uint32_t device_id = address->device_id; - size_t keylen = strlen("bundle_??_") + address->name_len + 1 + 10 + 1; - 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); + std::string k_bundle_pk = fmt::format("bundle_pk_{}_{}", address->name, address->device_id); + std::string k_bundle_sk = fmt::format("bundle_sk_{}_{}", address->name, address->device_id); + std::string k_bundle_sg = fmt::format("bundle_sg_{}_{}", address->name, address->device_id); + std::string k_bundle_ik = fmt::format("bundle_ik_{}_{}", address->name, address->device_id); - MDB_val v_bundle_pk; - MDB_val v_bundle_sk; - MDB_val v_bundle_sg; - MDB_val v_bundle_ik; + std::string_view v_bundle_pk; + std::string_view v_bundle_sk; + std::string_view v_bundle_sg; + std::string_view v_bundle_ik; - int ret; - if ((ret = mdb_txn_begin(omemo->db_env, NULL, MDB_RDONLY, &transaction))) { - weechat_printf(NULL, "%sxmpp: failed to open lmdb transaction", - weechat_prefix("error")); - return -1; - } + auto transaction = lmdb::txn::begin(omemo->db_env); - int bundle_pk_len; - int bundle_sk_len; - 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; + uint32_t pre_key_id = 0; + uint32_t signed_pre_key_id = 0; uint8_t *sig_buf; size_t sig_len; struct signal_buffer *signature; uint8_t *key_buf; size_t key_len; - if ((ret = mdb_get(transaction, omemo->dbi.omemo, - &k_bundle_pk, &v_bundle_pk)) || - (ret = mdb_get(transaction, omemo->dbi.omemo, - &k_bundle_sk, &v_bundle_sk)) || - (ret = mdb_get(transaction, omemo->dbi.omemo, - &k_bundle_sg, &v_bundle_sg)) || - (ret = mdb_get(transaction, omemo->dbi.omemo, - &k_bundle_ik, &v_bundle_ik))) - { - goto cleanup; - } - - bundle_pk_len = 0; - bundle_pks = weechat_string_split((const char*)v_bundle_pk.mv_data, ";", NULL, 0, 0, &bundle_pk_len); - pre_key_id = 0; + omemo->dbi.omemo.get(transaction, k_bundle_pk, v_bundle_pk); + omemo->dbi.omemo.get(transaction, k_bundle_sk, v_bundle_sk); + omemo->dbi.omemo.get(transaction, k_bundle_sg, v_bundle_sg); + omemo->dbi.omemo.get(transaction, k_bundle_ik, v_bundle_ik); + + auto r_bundle_pks = v_bundle_pk + | std::ranges::views::split(';') + | std::ranges::views::transform([](auto&& str) { + return std::string_view(&*str.begin(), std::ranges::distance(str)); + }); + auto bundle_pks = std::vector{r_bundle_pks.begin(), r_bundle_pks.begin()}; + if (bundle_pks.size() > 0) { - int i = rand() % bundle_pk_len; - char *bundle_pk = bundle_pks[i]; - pre_key_id = strtol(bundle_pk, NULL, 10); - char *key_data = (char *)memchr(bundle_pk, '.', 10 + 1) + 1; - uint8_t *key_buf; - size_t key_len = base64_decode(key_data, strlen(key_data), &key_buf); - 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; - }; + 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); } - bundle_sks = weechat_string_split((const char*)v_bundle_sk.mv_data, ";", NULL, 0, 0, &bundle_sk_len); + else + return {}; + libsignal::public_key pre_key(key_buf, key_len, omemo->context); + + auto r_bundle_sks = v_bundle_sk + | std::ranges::views::split(';') + | std::ranges::views::transform([](auto&& str) { + return std::string_view(&*str.begin(), std::ranges::distance(str)); + }); + auto bundle_sks = std::vector{r_bundle_sks.begin(), r_bundle_sks.begin()}; + if (bundle_sks.size() > 0) { - int i = rand() % bundle_sk_len; - char *bundle_sk = bundle_sks[i]; - signed_pre_key_id = strtol(bundle_sk, NULL, 10); - char *key_data = (char *)memchr(bundle_sk, '.', 10 + 1) + 1; - uint8_t *key_buf; - size_t key_len = base64_decode(key_data, strlen(key_data), &key_buf); - if ((ret = curve_decode_point(&signed_pre_key, key_buf, key_len, omemo->context))) { - weechat_printf(NULL, "%sxmpp: failed to decode ED25519 signed prekey", - weechat_prefix("error")); - goto cleanup; - }; + std::istringstream iss(bundle_sks[rand() % bundle_sks.size()]); + iss >> signed_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); } - 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); - key_len = base64_decode((const char*)v_bundle_ik.mv_data, v_bundle_ik.mv_size, &key_buf); - if ((ret = curve_decode_point(&identity_key, key_buf, key_len, omemo->context))) { - weechat_printf(NULL, "%sxmpp: failed to decode ED25519 identity key", - weechat_prefix("error")); - goto cleanup; - }; + key_len = base64_decode(v_bundle_ik.data(), v_bundle_ik.size(), &key_buf); + libsignal::public_key identity_key(key_buf, key_len, omemo->context); - 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))) { - weechat_printf(NULL, "%sxmpp: failed to create OMEMO prekey bundle", - weechat_prefix("error")); - goto cleanup; - }; + libsignal::pre_key_bundle bundle((uint32_t)address->device_id, (int)address->device_id, + (uint32_t)pre_key_id, *pre_key, (uint32_t)signed_pre_key_id, *signed_pre_key, + (const uint8_t*)signal_buffer_data(signature), (size_t)signal_buffer_len(signature), + *identity_key); - if ((ret = mdb_txn_commit(transaction))) { - weechat_printf(NULL, "%sxmpp: failed to close lmdb transaction", - weechat_prefix("error")); - goto cleanup; - }; + transaction.commit(); - return 0; -cleanup: - /* - void session_pre_key_bundle_destroy(signal_type_base *type); - */ - mdb_txn_abort(transaction); - return -1; + return bundle; } 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) { - if (!gcry_check_version(GCRYPT_VERSION)) - throw std::runtime_error("GCrypt: library version mismatch"); + gcrypt::check_version(); - auto omemo = this; + const auto omemo = this; omemo->context.create(buffer); 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), &free).get(); - lmdb::env &env = omemo->db_env; - env = lmdb::env::create(); - env.set_max_dbs(50); - env.set_mapsize((size_t)1048576 * 8000); // 8000MB map for valgrind - env.open(omemo->db_path.data(), MDB_NOSUBDIR, 0664); + omemo->db_env = lmdb::env::create(); + omemo->db_env.set_max_dbs(50); + omemo->db_env.set_mapsize((size_t)1048576 * 8000); // 8000MB map for valgrind + omemo->db_env.open(omemo->db_path.data(), MDB_NOSUBDIR, 0664); lmdb::txn parentTransaction{nullptr}; 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); transaction.commit(); - } catch (const std::exception& ex) { + } catch (const lmdb::error& ex) { auto format = fmt::format("%sxmpp: lmdb failure - {}", ex.what()); weechat_printf(NULL, format.data(), weechat_prefix("error")); - return; + throw; } 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++) { 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; struct signal_protocol_address address = { .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); xmpp_stanza_set_attribute(header__key, "rid", device_id_str); - struct session_builder *builder = NULL; if (((ret = ss_contains_session_func(&address, omemo))) <= 0) { - struct session_pre_key_bundle *bundle; - if ((ret = bks_load_bundle(&bundle, &address, omemo))) continue; - - if ((ret = session_builder_create(&builder, omemo->store_context, &address, omemo->context))) continue; - if ((ret = session_builder_process_pre_key_bundle(builder, bundle))) continue; + try { + auto bundle = bks_load_bundle(&address, omemo); + if (!bundle) throw std::runtime_error(fmt::format("No bundle for {}", target)); + + libsignal::session_builder builder(omemo->store_context, &address, omemo->context); + builder.process_pre_key_bundle(*bundle); + } + catch (const std::exception& ex) { + continue; + } } 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_UNREF(signal_message); session_cipher_free(cipher); - if (builder) - session_builder_free(builder); } signal_int_list_free(devicelist); target = account_jid(account); diff --git a/plugin.cpp b/plugin.cpp index f5f35ca..9c371ab 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -24,8 +24,8 @@ struct t_hook *weechat_xmpp_process_timer = NULL; struct t_gui_bar_item *weechat_xmpp_typing_bar_item = NULL; -extern "C" { #pragma GCC visibility push(default) +extern "C" { WEECHAT_PLUGIN_NAME(WEECHAT_XMPP_PLUGIN_NAME); WEECHAT_PLUGIN_DESCRIPTION(N_("XMPP client protocol")); WEECHAT_PLUGIN_AUTHOR("bqv "); diff --git a/signal.hh b/signal.hh index 80e486b..1f5e383 100644 --- a/signal.hh +++ b/signal.hh @@ -32,9 +32,28 @@ namespace libsignal { private: T *_ptr; - public: + protected: typedef T* pointer_type; + inline type(T *ptr) : _ptr(ptr) { + } + + template>>> + inline void call(Args&&... args) { + int ret = func(*this, std::forward(args)...); + if (ret != success) throw std::runtime_error( + fmt::format("Signal Error: expected {}, was {}", success, ret)); + } + + template>>> + inline std::invoke_result::type + call(Args&&... args) { + return func(*this, std::forward(args)...); + } + + public: inline explicit type() : _ptr(nullptr) { } @@ -49,6 +68,9 @@ namespace libsignal { _ptr = nullptr; } + type(const type &other) = delete; /* no copy construction */ + type(type &&other) = default; + template inline void create(Args&&... args) { if (_ptr) @@ -57,31 +79,16 @@ namespace libsignal { f_create(&_ptr, std::forward(args)...); } + type& operator =(const type &other) = delete; /* no copy assignment */ + type& operator =(type &&other) = default; + inline operator bool() const { return _ptr; } + inline T* operator *() { return _ptr; } + inline operator T*() { return _ptr; } inline operator const T*() const { return _ptr; } - - protected: - inline type(T *ptr) { - _ptr = ptr; - } - - template>>> - inline void call(Args&&... args) { - int ret = func(*this, std::forward(args)...); - if (ret != success) throw std::runtime_error( - fmt::format("Signal Error: expected {}, was {}", success, ret)); - } - - template>>> - inline std::invoke_result::type - call(Args&&... args) { - return func(*this, std::forward(args)...); - } }; typedef type(args...); @@ -220,4 +224,30 @@ namespace libsignal { } }; + typedef type 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 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(args...); + } + }; + } diff --git a/strophe.hh b/strophe.hh deleted file mode 100644 index bb11c73..0000000 --- a/strophe.hh +++ /dev/null @@ -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 -#include -#include - -namespace strophe { - -} diff --git a/xmpp/node.cpp b/xmpp/node.cpp new file mode 100644 index 0000000..6a12754 --- /dev/null +++ b/xmpp/node.cpp @@ -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 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 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 xml::presence::show() { + auto child = get_children("show"); + if (child.size() > 0) + return child.front().get().text; + return {}; +} + +std::optional 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); +} diff --git a/xmpp/node.hh b/xmpp/node.hh new file mode 100644 index 0000000..05a2681 --- /dev/null +++ b/xmpp/node.hh @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::string get_name(xmpp_stanza_t *stanza); + +std::optional 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 local; + std::string domain; + std::optional 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 name; + + std::optional id; + std::optional ns; + + std::map attributes; + std::vector children; + + std::string text; + + virtual void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza); + + template + inline std::vector> + get_children(std::string_view name) { + std::vector> 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> + get_children(std::string_view name) { + std::vector> 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 from; + std::optional to; + + std::optional 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 from; + std::optional to; + + std::optional type; + + std::optional show(); + std::optional status(); + + void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override; + }; + + class iq : virtual public node { + public: + using node::node; + + std::optional from; + std::optional to; + + std::optional type; + + void bind(xmpp_ctx_t *context, xmpp_stanza_t *stanza) override; + }; + +} diff --git a/xmpp/ns.hh b/xmpp/ns.hh new file mode 100644 index 0000000..5716dd4 --- /dev/null +++ b/xmpp/ns.hh @@ -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 + +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") {} }; + }; +}; diff --git a/xmpp/xep-0027.inl b/xmpp/xep-0027.inl new file mode 100644 index 0000000..504f829 --- /dev/null +++ b/xmpp/xep-0027.inl @@ -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 +#include + +#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 signature() { + auto child = get_children("x"); + if (child.size() > 0) + return child.front().get().text; + return {}; + } + + std::optional encrypted() { + auto child = get_children("x"); + if (child.size() > 0) + return child.front().get().text; + return {}; + } + }; + +} diff --git a/xmpp/xep-0045.inl b/xmpp/xep-0045.inl new file mode 100644 index 0000000..ae192de --- /dev/null +++ b/xmpp/xep-0045.inl @@ -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 +#include + +#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 from; + std::optional to; + }; + + struct destroy { + std::string reason; + std::optional target; + }; + + struct invite { + std::string reason; + std::optional from; + std::optional to; + }; + + struct item { + std::string reason; + }; + + public: + x(const node& node) { + } + + std::vector declines; + std::vector destroys; + std::vector invites; + std::vector items; + std::vector passwords; + std::vector statuses; + }; + + std::optional muc_user() { + auto child = get_children("x"); + if (child.size() > 0) + return child.front().get(); + return {}; + } + }; + +} diff --git a/xmpp/xep-0115.inl b/xmpp/xep-0115.inl new file mode 100644 index 0000000..43dbb36 --- /dev/null +++ b/xmpp/xep-0115.inl @@ -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 +#include + +#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 caps() { + auto child = get_children("c"); + if (child.size() > 0) + return child.front().get(); + return {}; + } + }; + +} diff --git a/xmpp/xep-0319.inl b/xmpp/xep-0319.inl new file mode 100644 index 0000000..ddb961d --- /dev/null +++ b/xmpp/xep-0319.inl @@ -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 +#include +#include +#include + +#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 idle_since() { + auto children = get_children("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 {}; + } + } + }; + +}