From ee16eaaa7acc75dcd7bc94304177cc018a062c4e Mon Sep 17 00:00:00 2001 From: Tony Olagbaiye Date: Wed, 7 Jul 2021 18:27:06 +0100 Subject: [PATCH] fetch devicelist --- .envrc | 1 + Makefile | 9 +- README.org | 2 + account.c | 153 +++++++++++++----- account.h | 25 ++- buffer.c | 2 +- channel.c | 57 ++++++- channel.h | 11 ++ connection.c | 420 +++++++++++++++++++++++++++++++------------------- omemo.c | 53 ++++++- omemo.h | 28 +++- xmpp/iq.c | 139 ++++++++++++++++- xmpp/stanza.h | 61 +++++++- 13 files changed, 740 insertions(+), 221 deletions(-) diff --git a/.envrc b/.envrc index 1cb1c12..f61f48c 100644 --- a/.envrc +++ b/.envrc @@ -38,6 +38,7 @@ use_guix() make cmake gcc-toolchain pkg-config patchelf weechat libxml2 libstrophe glib libsignal-protocol-c sqlite + minixml ) # Thanks diff --git a/Makefile b/Makefile index 8023aa7..2f7b713 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ FIND=find INCLUDES=-Ilibstrophe $(shell xml2-config --cflags) $(shell pkg-config --cflags glib-2.0) $(shell pkg-config --cflags libsignal-protocol-c) CFLAGS+=$(DBGCFLAGS) -fno-omit-frame-pointer -fPIC -std=gnu99 -g -Wall -Wextra -Werror-implicit-function-declaration -Wno-missing-field-initializers -D_XOPEN_SOURCE=700 $(INCLUDES) LDFLAGS+=$(DBGLDFLAGS) -shared -g $(DBGCFLAGS) -LDLIBS=-lstrophe -lpthread $(shell xml2-config --libs) $(shell pkg-config --libs glib-2.0) $(shell pkg-config --libs libsignal-protocol-c) +LDLIBS=-lstrophe -lpthread $(shell xml2-config --libs) $(shell pkg-config --libs glib-2.0) $(shell pkg-config --libs libsignal-protocol-c) -lmxml PREFIX ?= /usr/local LIBDIR ?= $(PREFIX)/lib @@ -27,7 +27,7 @@ SRCS=plugin.c \ xmpp/presence.c \ xmpp/iq.c \ -DEPS=axc/build/libaxc.a +DEPS=omemo/build/libomemo-conversations.a axc/build/libaxc.a OBJS=$(subst .c,.o,$(SRCS)) all: weechat-xmpp @@ -39,6 +39,10 @@ xmpp.so: $(OBJS) $(DEPS) patchelf --set-rpath $(LIBRARY_PATH):$(shell realpath $(shell dirname $(shell gcc --print-libgcc-file-name))/../../../) xmpp.so && \ patchelf --shrink-rpath xmpp.so || true +omemo/build/libomemo-conversations.a: + $(MAKE) -C omemo +omemo: omemo/build/libomemo-conversations.a + axc/build/libaxc.a: $(MAKE) -C axc axc: axc/build/libaxc.a @@ -62,6 +66,7 @@ tidy: clean: $(RM) -f $(OBJS) + $(MAKE) -C omemo clean || true $(MAKE) -C axc clean || true git submodule foreach --recursive git clean -xfd || true git submodule foreach --recursive git reset --hard || true diff --git a/README.org b/README.org index 8d1fc0b..a6770c9 100644 --- a/README.org +++ b/README.org @@ -122,10 +122,12 @@ * [ ] [#A] Send typing notifications * [ ] [#A] Recv typing notifications * [ ] [#C] Read receipts + * [ ] Message Carbons * [ ] Service Disco * [ ] Bookmarks / Roster * [ ] OTR (libotr) * [ ] PGP (libgpgme) + * [ ] Room Explorer (https://search.jabber.network/docs/api) ** TODO [#C] Implement completion engine (milestone v0.3) ** TODO [#D] Close all issues (milestone v1.0) * [ ] Absorb libomemo / axc, and drop glib diff --git a/account.c b/account.c index daf0e3d..9186b61 100644 --- a/account.c +++ b/account.c @@ -13,6 +13,7 @@ #include "plugin.h" #include "config.h" #include "input.h" +#include "omemo.h" #include "account.h" #include "connection.h" #include "user.h" @@ -86,6 +87,81 @@ int account__search_option(const char *option_name) return -1; } +struct t_device *account__search_device(struct t_account *account, int id) +{ + struct t_device *ptr_device; + + if (!account) + return NULL; + + for (ptr_device = account->devices; ptr_device; + ptr_device = ptr_device->next_device) + { + if (ptr_device->id == id) + return ptr_device; + } + + return NULL; +} + +void account__add_device(struct t_account *account, + struct t_device *device) +{ + struct t_device *new_device; + + new_device = account__search_device(account, device->id); + if (!new_device) + { + new_device = malloc(sizeof(*new_device)); + new_device->id = device->id; + new_device->name = strdup(device->name); + + new_device->prev_device = account->last_device; + new_device->next_device = NULL; + if (account->last_device) + (account->last_device)->next_device = new_device; + else + account->devices = new_device; + account->last_device = new_device; + } +} + +void account__free_device(struct t_account *account, struct t_device *device) +{ + struct t_device *new_devices; + + if (!account || !device) + return; + + /* remove device from devices list */ + if (account->last_device == device) + account->last_device = device->prev_device; + if (device->prev_device) + { + (device->prev_device)->next_device = device->next_device; + new_devices = account->devices; + } + else + new_devices = device->next_device; + + if (device->next_device) + (device->next_device)->prev_device = device->prev_device; + + /* free device data */ + if (device->name) + free(device->name); + + free(device); + + account->devices = new_devices; +} + +void account__free_device_all(struct t_account *account) +{ + while (account->devices) + account__free_device(account, account->devices); +} + void account__log_emit_weechat(void *const userdata, const xmpp_log_level_t level, const char *const area, const char *const msg) { @@ -204,6 +280,10 @@ struct t_account *account__alloc(const char *name) new_account->is_connected = 0; new_account->disconnected = 0; + new_account->current_retry = 0; + new_account->reconnect_delay = 0; + new_account->reconnect_start = 0; + new_account->logger.handler = &account__log_emit_weechat; new_account->logger.userdata = new_account; new_account->context = xmpp_ctx_new(NULL, &new_account->logger); @@ -211,6 +291,11 @@ struct t_account *account__alloc(const char *name) new_account->buffer = NULL; new_account->buffer_as_string = NULL; + + new_account->omemo = NULL; + + new_account->devices = NULL; + new_account->last_device = NULL; new_account->users = NULL; new_account->last_user = NULL; new_account->channels = NULL; @@ -300,6 +385,9 @@ void account__free_data(struct t_account *account) if (account->buffer_as_string) free(account->buffer_as_string); + if (account->omemo) + omemo__free(account->omemo); + //channel__free_all(account); //user__free_all(account); } @@ -384,58 +472,34 @@ void account__disconnect(struct t_account *account, int reconnect) weechat_prefix ("network"), WEECHAT_XMPP_PLUGIN_NAME); } - /* - account->current_retry = 0; - - if (switch_address) - account__switch_address(account, 0); - else - account__set_index_current_address(account, 0); - - if (account->nick_modes) + if (reconnect) { - free (account->nick_modes); - account->nick_modes = NULL; - weechat_bar_item_update ("input_prompt"); - weechat_bar_item_update ("xmpp_nick_modes"); + if (account->current_retry++ == 0) + { + account->reconnect_delay = 5; + account->reconnect_start = time(NULL) + account->reconnect_delay; + } + account->current_retry %= 5; } - account->cap_away_notify = 0; - account->cap_account_notify = 0; - account->cap_extended_join = 0; - account->is_away = 0; - account->away_time = 0; - account->lag = 0; - account->lag_displayed = -1; - account->lag_check_time.tv_sec = 0; - account->lag_check_time.tv_usec = 0; - account->lag_next_check = time (NULL) + - weechat_config_integer (xmpp_config_network_lag_check); - account->lag_last_refresh = 0; - account__set_lag (account); - account->monitor = 0; - account->monitor_time = 0; - */ - - /* - if (reconnect - && IRC_SERVER_OPTION_BOOLEAN(account, IRC_SERVER_OPTION_AUTORECONNECT)) - account__reconnect_schedule(account); else { + account->current_retry = 0; account->reconnect_delay = 0; account->reconnect_start = 0; } - */ - - /* discard current nick if no reconnection asked */ - /* - if (!reconnect && account->nick) - account__set_nick(account, NULL); - account__set_buffer_title(account); + /* + account->lag = 0; + account->lag_displayed = -1; + account->lag_check_time.tv_sec = 0; + account->lag_check_time.tv_usec = 0; + account->lag_next_check = time(NULL) + + weechat_config_integer(xmpp_config_network_lag_check); + account->lag_last_refresh = 0; + account__set_lag(account); + */ // lag based on xmpp ping account->disconnected = 1; - */ /* send signal "account_disconnected" with account name */ (void) weechat_hook_signal_send("xmpp_account_disconnected", @@ -536,6 +600,11 @@ int account__timer_cb(const void *pointer, void *data, int remaining_calls) && (xmpp_conn_is_connecting(ptr_account->connection) || xmpp_conn_is_connected(ptr_account->connection))) connection__process(ptr_account->context, ptr_account->connection, 10); + else if (ptr_account->reconnect_start > 0 + && ptr_account->reconnect_start < time(NULL)) + { + account__connect(ptr_account); + } } return WEECHAT_RC_OK; diff --git a/account.h b/account.h index dd196be..7f1015b 100644 --- a/account.h +++ b/account.h @@ -58,6 +58,15 @@ enum t_account_option #define account_autojoin(account) \ weechat_config_string(account->options[ACCOUNT_OPTION_AUTOJOIN]) +struct t_device +{ + int id; + char *name; + + struct t_device *prev_device; + struct t_device *next_device; +}; + struct t_account { char *name; @@ -68,18 +77,26 @@ struct t_account int is_connected; int disconnected; + int current_retry; + int reconnect_delay; + int reconnect_start; + xmpp_log_t logger; xmpp_ctx_t *context; xmpp_conn_t *connection; struct t_gui_buffer *buffer; char *buffer_as_string; - //struct t_device *devices; - //struct t_device *last_device; + + struct t_omemo *omemo; + + struct t_device *devices; + struct t_device *last_device; struct t_user *users; struct t_user *last_user; struct t_channel *channels; struct t_channel *last_channel; + struct t_account *prev_account; struct t_account *next_account; }; @@ -89,6 +106,10 @@ extern char *account_options[][2]; struct t_account *account__search(const char *account_name); struct t_account *account__casesearch (const char *account_name); int account__search_option(const char *option_name); +struct t_device *account__search_device(struct t_account *account, int id); +void account__add_device(struct t_account *account, struct t_device *device); +void account__free_device(struct t_account *account, struct t_device *device); +void account__free_device_all(struct t_account *account); struct t_account *account__alloc(const char *name); void account__free_data(struct t_account *account); void account__free(struct t_account *account); diff --git a/buffer.c b/buffer.c index 11ed14d..25fa3f6 100644 --- a/buffer.c +++ b/buffer.c @@ -2,8 +2,8 @@ // 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 #include +#include #include #include "plugin.h" diff --git a/channel.c b/channel.c index 5803a7e..8c7f51e 100644 --- a/channel.c +++ b/channel.c @@ -10,6 +10,7 @@ #include #include "plugin.h" +#include "omemo.h" #include "account.h" #include "user.h" #include "channel.h" @@ -489,6 +490,10 @@ void channel__member_free(struct t_channel *channel, /* free member data */ if (member->id) free(member->id); + if (member->role) + free(member->role); + if (member->affiliation) + free(member->affiliation); free(member); @@ -589,6 +594,9 @@ struct t_channel_member *channel__add_member(struct t_account *account, member = malloc(sizeof(struct t_channel_member)); member->id = strdup(id); + member->role = NULL; + member->affiliation = NULL; + member->prev_member = channel->last_member; member->next_member = NULL; if (channel->last_member) @@ -607,12 +615,13 @@ struct t_channel_member *channel__add_member(struct t_account *account, && channel->type == CHANNEL_TYPE_MUC) weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,enter,log4", "%s%s entered", weechat_prefix("join"), - jid_resource); + user__as_prefix_raw(account, jid_resource)); else weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,enter,log4", "%s%s (%s) entered", weechat_prefix("join"), xmpp_jid_bare(account->context, user->id), - xmpp_jid_resource(account->context, user->id)); + user__as_prefix_raw(account, + xmpp_jid_resource(account->context, user->id))); return member; } @@ -635,6 +644,46 @@ struct t_channel_member *channel__member_search(struct t_channel *channel, return NULL; } +int channel__set_member_role(struct t_account *account, + struct t_channel *channel, + const char *id, const char *role) +{ + struct t_channel_member *member; + struct t_user *user; + + user = user__search(account, id); + if (!user) + return 0; + + member = channel__member_search(channel, id); + if (!member) + return 0; + + member->role = strdup(role); + + return 1; +} + +int channel__set_member_affiliation(struct t_account *account, + struct t_channel *channel, + const char *id, const char *affiliation) +{ + struct t_channel_member *member; + struct t_user *user; + + user = user__search(account, id); + if (!user) + return 0; + + member = channel__member_search(channel, id); + if (!member) + return 0; + + member->affiliation = strdup(affiliation); + + return 1; +} + struct t_channel_member *channel__remove_member(struct t_account *account, struct t_channel *channel, const char *id) @@ -643,8 +692,8 @@ struct t_channel_member *channel__remove_member(struct t_account *account, struct t_user *user; user = user__search(account, id); - if (user) - user__nicklist_remove(account, channel, user); + //if (user) + // user__nicklist_remove(account, channel, user); member = channel__member_search(channel, id); if (member) diff --git a/channel.h b/channel.h index ed9ca23..3c2924d 100644 --- a/channel.h +++ b/channel.h @@ -27,6 +27,9 @@ struct t_channel_member { char *id; + char *role; + char *affiliation; + struct t_channel_member *prev_member; struct t_channel_member *next_member; }; @@ -120,6 +123,14 @@ struct t_channel_member *channel__add_member(struct t_account *account, struct t_channel *channel, const char *id); +int channel__set_member_role(struct t_account *account, + struct t_channel *channel, + const char *id, const char *role); + +int channel__set_member_affiliation(struct t_account *account, + struct t_channel *channel, + const char *id, const char *affiliation); + struct t_channel_member *channel__remove_member(struct t_account *account, struct t_channel *channel, const char *id); diff --git a/connection.c b/connection.c index 8e31a18..a433ce0 100644 --- a/connection.c +++ b/connection.c @@ -111,6 +111,8 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void } else if (channel) { + channel__set_member_role(account, channel, from, role); + channel__set_member_affiliation(account, channel, from, affiliation); if (weechat_strcasecmp(role, "none") == 0) channel__remove_member(account, channel, from); else @@ -127,7 +129,7 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * struct t_account *account = (struct t_account *)userdata; struct t_channel *channel; xmpp_stanza_t *body, *delay, *topic, *replace; - const char *type, *from, *from_bare, *to, *id, *replace_id, *timestamp; + const char *type, *from, *nick, *from_bare, *to, *id, *replace_id, *timestamp; char *intext; struct tm time = {0}; time_t date = 0; @@ -170,9 +172,10 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * if (!channel) channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare); + nick = NULL; if (weechat_strcasecmp(type, "groupchat") == 0) { - from = weechat_strcasecmp(channel->name, + nick = weechat_strcasecmp(channel->name, xmpp_jid_bare(account->context, from)) == 0 ? xmpp_jid_resource(account->context, from) @@ -188,6 +191,14 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * char **dyn_tags = weechat_string_dyn_alloc(1); weechat_string_dyn_concat(dyn_tags, "xmpp_message,message", -1); + { + weechat_string_dyn_concat(dyn_tags, ",nick_", -1); + weechat_string_dyn_concat(dyn_tags, nick, -1); + } + { + weechat_string_dyn_concat(dyn_tags, ",host_", -1); + weechat_string_dyn_concat(dyn_tags, from, -1); + } if (id) { weechat_string_dyn_concat(dyn_tags, ",id_", -1); @@ -213,15 +224,15 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * const char *edit = replace ? "* " : ""; // Losing which message was edited, sadly if (strcmp(to, channel->id) == 0) weechat_printf_date_tags(channel->buffer, date, *dyn_tags, "%s%s[to %s]: %s", - edit, user__as_prefix_raw(account, from), + edit, user__as_prefix_raw(account, nick), to, intext ? intext : ""); else if (weechat_string_match(intext, "/me *", 0)) weechat_printf_date_tags(channel->buffer, date, *dyn_tags, "%s%s%s %s", - edit, weechat_prefix("action"), from, + edit, weechat_prefix("action"), nick, intext ? intext+4 : ""); else weechat_printf_date_tags(channel->buffer, date, *dyn_tags, "%s%s%s", - edit, user__as_prefix_raw(account, from), + edit, user__as_prefix_raw(account, nick), intext ? intext : ""); weechat_string_dyn_free(dyn_tags, 1); @@ -238,196 +249,276 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd struct t_account *account = (struct t_account *)userdata; xmpp_stanza_t *reply, *query, *identity, *feature, *x, *field, *value, *text; - const char *node; + xmpp_stanza_t *pubsub, *items, *item, *list, *device, *children[2]; static struct utsname osinfo; - char *client_name = weechat_string_eval_expression("weechat ${info:version}", - NULL, NULL, NULL); - - reply = xmpp_stanza_reply(stanza); - xmpp_stanza_set_type(reply, "result"); - query = xmpp_stanza_get_child_by_name_and_ns( stanza, "query", "http://jabber.org/protocol/disco#info"); - node = xmpp_stanza_get_attribute(query, "node"); - xmpp_stanza_set_attribute(reply, "id", node); - - identity = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(identity, "identity"); - xmpp_stanza_set_attribute(identity, "category", "client"); - xmpp_stanza_set_attribute(identity, "name", client_name); - xmpp_stanza_set_attribute(identity, "type", "pc"); - xmpp_stanza_add_child(query, identity); - xmpp_stanza_release(identity); - -#define FEATURE(ns) \ - feature = xmpp_stanza_new(account->context); \ - xmpp_stanza_set_name(feature, "feature"); \ - xmpp_stanza_set_attribute(feature, "var", ns); \ - xmpp_stanza_add_child(query, feature); \ - xmpp_stanza_release(feature); - - FEATURE("http://jabber.org/protocol/caps"); - FEATURE("http://jabber.org/protocol/disco#info"); - FEATURE("http://jabber.org/protocol/disco#items"); - FEATURE("http://jabber.org/protocol/muc"); - FEATURE("eu.siacs.conversations.axolotl.devicelist"); - FEATURE("eu.siacs.conversations.axolotl.devicelist+notify"); + if (query) + { + char *client_name; + + reply = xmpp_stanza_reply(stanza); + xmpp_stanza_set_type(reply, "result"); + + client_name = weechat_string_eval_expression("weechat ${info:version}", + NULL, NULL, NULL); + + identity = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(identity, "identity"); + xmpp_stanza_set_attribute(identity, "category", "client"); + xmpp_stanza_set_attribute(identity, "name", client_name); + xmpp_stanza_set_attribute(identity, "type", "pc"); + xmpp_stanza_add_child(query, identity); + xmpp_stanza_release(identity); + +#define FEATURE(ns) \ + feature = xmpp_stanza_new(account->context); \ + xmpp_stanza_set_name(feature, "feature"); \ + xmpp_stanza_set_attribute(feature, "var", ns); \ + xmpp_stanza_add_child(query, feature); \ + xmpp_stanza_release(feature); + + FEATURE("http://jabber.org/protocol/caps"); + FEATURE("http://jabber.org/protocol/disco#info"); + FEATURE("http://jabber.org/protocol/disco#items"); + FEATURE("http://jabber.org/protocol/muc"); + FEATURE("eu.siacs.conversations.axolotl.devicelist"); + FEATURE("eu.siacs.conversations.axolotl.devicelist+notify"); + FEATURE("storage:bookmarks+notify"); #undef FEATURE - x = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(x, "x"); - xmpp_stanza_set_ns(x, "jabber:x:data"); - xmpp_stanza_set_attribute(x, "type", "result"); + x = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(x, "x"); + xmpp_stanza_set_ns(x, "jabber:x:data"); + xmpp_stanza_set_attribute(x, "type", "result"); - if (uname(&osinfo) < 0) - { - *osinfo.sysname = 0; - *osinfo.release = 0; - } + if (uname(&osinfo) < 0) + { + *osinfo.sysname = 0; + *osinfo.release = 0; + } + + // This is utter bullshit, TODO: not this. + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "FORM_TYPE"); + xmpp_stanza_set_attribute(field, "type", "hidden"); - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "FORM_TYPE"); - xmpp_stanza_set_attribute(field, "type", "hidden"); + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "value"); - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); + text = xmpp_stanza_new(account->context); + xmpp_stanza_set_text(text, "urn:xmpp:dataforms:softwareinfo"); + xmpp_stanza_add_child(value, text); + xmpp_stanza_release(text); - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, "urn:xmpp:dataforms:softwareinfo"); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); + xmpp_stanza_add_child(field, value); + xmpp_stanza_release(value); + + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "ip_version"); + xmpp_stanza_set_attribute(field, "type", "text-multi"); + + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "value"); - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } + text = xmpp_stanza_new(account->context); + xmpp_stanza_set_text(text, "ipv4"); + xmpp_stanza_add_child(value, text); + xmpp_stanza_release(text); + + xmpp_stanza_add_child(field, value); + xmpp_stanza_release(value); - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "ip_version"); - xmpp_stanza_set_attribute(field, "type", "text-multi"); + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "value"); - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); + text = xmpp_stanza_new(account->context); + xmpp_stanza_set_text(text, "ipv6"); + xmpp_stanza_add_child(value, text); + xmpp_stanza_release(text); - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, "ipv4"); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); + xmpp_stanza_add_child(field, value); + xmpp_stanza_release(value); - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "os"); - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, "ipv6"); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "value"); - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); + text = xmpp_stanza_new(account->context); + xmpp_stanza_set_text(text, osinfo.sysname); + xmpp_stanza_add_child(value, text); + xmpp_stanza_release(text); - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } + xmpp_stanza_add_child(field, value); + xmpp_stanza_release(value); - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "os"); + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "os_version"); - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, osinfo.sysname); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "value"); - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); + text = xmpp_stanza_new(account->context); + xmpp_stanza_set_text(text, osinfo.release); + xmpp_stanza_add_child(value, text); + xmpp_stanza_release(text); - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } + xmpp_stanza_add_child(field, value); + xmpp_stanza_release(value); - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "os_version"); + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "software"); - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, osinfo.release); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "value"); - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); + text = xmpp_stanza_new(account->context); + xmpp_stanza_set_text(text, "weechat"); + xmpp_stanza_add_child(value, text); + xmpp_stanza_release(text); - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } + xmpp_stanza_add_child(field, value); + xmpp_stanza_release(value); - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "software"); + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "software_version"); - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, "weechat"); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "value"); - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); + text = xmpp_stanza_new(account->context); + xmpp_stanza_set_text(text, weechat_info_get("version", NULL)); + xmpp_stanza_add_child(value, text); + xmpp_stanza_release(text); - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); - } + xmpp_stanza_add_child(field, value); + xmpp_stanza_release(value); - { - field = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(field, "field"); - xmpp_stanza_set_attribute(field, "var", "software_version"); + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } - value = xmpp_stanza_new(account->context); - xmpp_stanza_set_name(value, "value"); + xmpp_stanza_add_child(query, x); + xmpp_stanza_release(x); - text = xmpp_stanza_new(account->context); - xmpp_stanza_set_text(text, weechat_info_get("version", NULL)); - xmpp_stanza_add_child(value, text); - xmpp_stanza_release(text); + xmpp_stanza_add_child(reply, query); - xmpp_stanza_add_child(field, value); - xmpp_stanza_release(value); + xmpp_send(conn, reply); + xmpp_stanza_release(reply); - xmpp_stanza_add_child(x, field); - xmpp_stanza_release(field); + free(client_name); } - xmpp_stanza_add_child(query, x); - xmpp_stanza_release(x); - - xmpp_stanza_add_child(reply, query); - - xmpp_send(conn, reply); - xmpp_stanza_release(reply); - - free(client_name); + pubsub = xmpp_stanza_get_child_by_name_and_ns( + stanza, "pubsub", "http://jabber.org/protocol/pubsub"); + if (pubsub) + { + const char *items_node, *item_id, *device_id, *ns, *node; + + items = xmpp_stanza_get_child_by_name(pubsub, "items"); + if (items) + items_node = xmpp_stanza_get_attribute(items, "node"); + if (items && items_node + && weechat_strcasecmp(items_node, + "eu.siacs.conversations.axolotl.devicelist") == 0) + { + item = xmpp_stanza_get_child_by_name(items, "item"); + if (item) + item_id = xmpp_stanza_get_id(item); + if (item && item_id && weechat_strcasecmp(item_id, "current") == 0) + { + list = xmpp_stanza_get_child_by_name_and_ns( + item, "list", "eu.siacs.conversations.axolotl"); + if (list) + { + account__free_device_all(account); + + struct t_device *dev = malloc(sizeof(dev)); + char id[64] = {0}; + + dev->id = account->omemo->device_id; + snprintf(id, sizeof(id), "%d", dev->id); + dev->name = strdup(id); + account__add_device(account, dev); + + free(dev->name); + free(dev); + + for (device = xmpp_stanza_get_children(list); + device; device = xmpp_stanza_get_next(device)) + { + device_id = xmpp_stanza_get_id(device); + + dev = malloc(sizeof(dev)); + dev->id = atoi(device_id); + dev->name = strdup(device_id); + account__add_device(account, dev); + + free(dev->name); + free(dev); + } + + reply = xmpp_stanza_reply(stanza); + xmpp_stanza_set_type(reply, "result"); + + children[1] = NULL; + children[0] = stanza__iq_pubsub_publish_item_list_device( + account->context, NULL, with_noop(id)); + ns = "http://jabber.org/protocol/pubsub"; + children[0] = stanza__iq_pubsub_publish_item_list( + account->context, NULL, children, with_noop(ns)); + node = "eu.siacs.conversations.axolotl.devicelist"; + children[0] = stanza__iq_pubsub_publish_item( + account->context, NULL, children, with_noop(node)); + children[0] = stanza__iq_pubsub_publish(account->context, + NULL, children, + with_noop(ns)); + children[0] = stanza__iq_pubsub(account->context, NULL, + children, with_noop("")); + reply = stanza__iq(account->context, reply, children, NULL, + NULL, NULL, NULL, NULL); + + xmpp_send(conn, reply); + xmpp_stanza_release(reply); + } + } + } + } return 1; } @@ -441,18 +532,21 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, (void)error; (void)stream_error; - if (status == XMPP_CONN_CONNECT) { + if (status == XMPP_CONN_CONNECT) + { xmpp_stanza_t *pres, *pres__c, *pres__status, *pres__status__text, **children; char cap_hash[28+1] = {0}; - xmpp_handler_add(conn, connection__version_handler, + xmpp_handler_add(conn, &connection__version_handler, "jabber:iq:version", "iq", NULL, account); - xmpp_handler_add(conn, connection__presence_handler, + xmpp_handler_add(conn, &connection__presence_handler, NULL, "presence", NULL, account); - xmpp_handler_add(conn, connection__message_handler, + xmpp_handler_add(conn, &connection__message_handler, NULL, "message", /*type*/ NULL, account); - xmpp_handler_add(conn, connection__iq_handler, - NULL, "iq", "get", account); + //xmpp_handler_add(conn, &connection__iq_handler, + // NULL, "iq", "get", account); + xmpp_handler_add(conn, &connection__iq_handler, + NULL, "iq", NULL, account); /* Send initial so that we appear online to contacts */ children = malloc(sizeof(*children) * (2 + 1)); @@ -495,7 +589,7 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, strdup("eu.siacs.conversations.axolotl.devicelist")); children[0] = stanza__iq_pubsub(account->context, NULL, children, - strdup("http://jabber.org/protocol/pubsub")); + with_noop("http://jabber.org/protocol/pubsub")); children[0] = stanza__iq(account->context, NULL, children, NULL, strdup("fetch1"), strdup(account_jid(account)), strdup(account_jid(account)), @@ -504,8 +598,10 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, xmpp_send(conn, children[0]); xmpp_stanza_release(children[0]); - omemo__init(account); - } else { + omemo__init(&account->omemo, 0, NULL); + } + else + { account__disconnect(account, 1); //xmpp_stop(account->context); //keep context? } diff --git a/omemo.c b/omemo.c index affb2b0..6ce67d7 100644 --- a/omemo.c +++ b/omemo.c @@ -4,13 +4,58 @@ #include -#include "omemo/src/libomemo.h" -#include "axc/src/axc.h" - #include "omemo.h" -void omemo__init() +void omemo__init(struct t_omemo **omemo, uint32_t device, + struct t_identity *identity) { const char* ns_devicelist = "eu.siacs.conversations.axolotl.devicelist"; const char* ft_devicelist = "eu.siacs.conversations.axolotl.devicelist+notify"; + int rc; + + srandom(time(NULL)); + + omemo_bundle* bundle; + rc = omemo_bundle_create(&bundle); + if (rc) + return; + + if (!device) + device = random(); + omemo_bundle_set_device_id(bundle, device); + + if (identity) + omemo_bundle_set_identity_key(bundle, identity->key, identity->length); + else + { + identity = malloc(sizeof(*identity)); + identity->length = 4; + identity->key = malloc(sizeof(*identity->key) * identity->length); + + identity->key[0] = random(); + identity->key[1] = random(); + identity->key[2] = random(); + identity->key[3] = random(); + + omemo_bundle_set_identity_key(bundle, identity->key, identity->length); + + free(identity->key); + free(identity); + } + + *omemo = malloc(sizeof(**omemo)); + (*omemo)->provider.random_bytes_func = omemo_default_crypto_random_bytes; + (*omemo)->provider.aes_gcm_encrypt_func = omemo_default_crypto_aes_gcm_encrypt; + (*omemo)->provider.aes_gcm_decrypt_func = omemo_default_crypto_aes_gcm_decrypt; + (*omemo)->provider.user_data_p = (void *)(*omemo); + (*omemo)->bundle = bundle; + (*omemo)->device_id = omemo_bundle_get_device_id(bundle); + omemo_bundle_get_identity_key(bundle, &(*omemo)->identity.key, &(*omemo)->identity.length); + + omemo_devicelist *devicelist; +} + +void omemo__free(struct t_omemo *omemo) +{ + free(omemo); } diff --git a/omemo.h b/omemo.h index 7fb146c..faccd8e 100644 --- a/omemo.h +++ b/omemo.h @@ -5,6 +5,32 @@ #ifndef _WEECHAT_XMPP_OMEMO_H_ #define _WEECHAT_XMPP_OMEMO_H_ -void omemo__init(); +#include "axc/src/axc.h" +#include "omemo/src/libomemo.h" +#include "omemo/src/libomemo_crypto.h" + +struct t_identity +{ + uint8_t *key; + size_t length; +}; + +struct t_omemo +{ + omemo_crypto_provider provider; + + omemo_devicelist *devicelist; + + omemo_bundle *bundle; + + struct t_identity identity; + + uint32_t device_id; +}; + +void omemo__init(struct t_omemo **omemo, uint32_t device, + struct t_identity *identity); + +void omemo__free(struct t_omemo *omemo); #endif /*WEECHAT_XMPP_OMEMO_H*/ diff --git a/xmpp/iq.c b/xmpp/iq.c index c31941d..c5c8277 100644 --- a/xmpp/iq.c +++ b/xmpp/iq.c @@ -5,6 +5,8 @@ #include #include +#include "stanza.h" + xmpp_stanza_t *stanza__iq(xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, char *ns, char *id, char *from, char *to, char *type) @@ -54,7 +56,7 @@ xmpp_stanza_t *stanza__iq(xmpp_ctx_t *context, xmpp_stanza_t *base, } xmpp_stanza_t *stanza__iq_pubsub(xmpp_ctx_t *context, xmpp_stanza_t *base, - xmpp_stanza_t **children, char *ns) + xmpp_stanza_t **children, struct t_string *ns) { xmpp_stanza_t *parent = base; xmpp_stanza_t **child = children; @@ -67,7 +69,8 @@ xmpp_stanza_t *stanza__iq_pubsub(xmpp_ctx_t *context, xmpp_stanza_t *base, if (ns) { - xmpp_stanza_set_ns(parent, ns); + xmpp_stanza_set_ns(parent, ns->value); + ns->finalize(ns); free(ns); } @@ -100,3 +103,135 @@ xmpp_stanza_t *stanza__iq_pubsub_items(xmpp_ctx_t *context, xmpp_stanza_t *base, return parent; } + +xmpp_stanza_t *stanza__iq_pubsub_publish(xmpp_ctx_t *context, xmpp_stanza_t *base, + xmpp_stanza_t **children, struct t_string *node) +{ + xmpp_stanza_t *parent = base; + xmpp_stanza_t **child = children; + + if (!parent) + { + parent = xmpp_stanza_new(context); + xmpp_stanza_set_name(parent, "publish"); + } + + if (node) + { + xmpp_stanza_set_attribute(parent, "node", node->value); + node->finalize(node); + free(node); + } + + while (*child) + { + xmpp_stanza_add_child(parent, *child); + xmpp_stanza_release(*child); + + ++child; + } + + return parent; +} + +xmpp_stanza_t *stanza__iq_pubsub_publish_item(xmpp_ctx_t *context, xmpp_stanza_t *base, + xmpp_stanza_t **children, struct t_string *id) +{ + xmpp_stanza_t *parent = base; + xmpp_stanza_t **child = children; + + if (!parent) + { + parent = xmpp_stanza_new(context); + xmpp_stanza_set_name(parent, "item"); + } + + if (id) + { + xmpp_stanza_set_id(parent, id->value); + id->finalize(id); + free(id); + } + + while (*child) + { + xmpp_stanza_add_child(parent, *child); + xmpp_stanza_release(*child); + + ++child; + } + + return parent; +} + +xmpp_stanza_t *stanza__iq_pubsub_publish_item_list(xmpp_ctx_t *context, xmpp_stanza_t *base, + xmpp_stanza_t **children, struct t_string *ns) +{ + xmpp_stanza_t *parent = base; + xmpp_stanza_t **child = children; + + if (!parent) + { + parent = xmpp_stanza_new(context); + xmpp_stanza_set_name(parent, "list"); + } + + if (ns) + { + xmpp_stanza_set_ns(parent, ns->value); + ns->finalize(ns); + free(ns); + } + + while (*child) + { + xmpp_stanza_add_child(parent, *child); + xmpp_stanza_release(*child); + + ++child; + } + + return parent; +} + +xmpp_stanza_t *stanza__iq_pubsub_publish_item_list_device(xmpp_ctx_t *context, xmpp_stanza_t *base, + struct t_string *id) +{ + xmpp_stanza_t *parent = base; + + if (!parent) + { + parent = xmpp_stanza_new(context); + xmpp_stanza_set_name(parent, "device"); + } + + if (id) + { + xmpp_stanza_set_id(parent, id->value); + id->finalize(id); + free(id); + } + + return parent; +} + +xmpp_stanza_t *stanza__iq_ping(xmpp_ctx_t *context, xmpp_stanza_t *base, + struct t_string *ns) +{ + xmpp_stanza_t *parent = base; + + if (!parent) + { + parent = xmpp_stanza_new(context); + xmpp_stanza_set_name(parent, "ping"); + } + + if (ns) + { + xmpp_stanza_set_ns(parent, ns->value); + ns->finalize(ns); + free(ns); + } + + return parent; +} diff --git a/xmpp/stanza.h b/xmpp/stanza.h index 56f3d8f..38aa459 100644 --- a/xmpp/stanza.h +++ b/xmpp/stanza.h @@ -5,6 +5,50 @@ #ifndef _WEECHAT_XMPP_STANZA_H_ #define _WEECHAT_XMPP_STANZA_H_ +struct t_string +{ + char *value; + + void (*finalize)(struct t_string *); + void *pointer; +}; + +static void t_string_noop(struct t_string *string) +{ (void)string; } + +static void t_string_free(struct t_string *string) +{ free(string->value); } + +static void t_string_xmpp_free(struct t_string *string) +{ xmpp_free(string->pointer, string->value); } + +static inline struct t_string *with_noop(const char *const value) +{ + struct t_string *string = malloc(sizeof(struct t_string)); + string->value = (char*)value; + string->finalize = &t_string_noop; + string->pointer = NULL; + return string; +} + +static inline struct t_string *with_free(char *value) +{ + struct t_string *string = malloc(sizeof(struct t_string)); + string->value = value; + string->finalize = &t_string_free; + string->pointer = NULL; + return string; +} + +static inline struct t_string *with_xmpp_free(char *value, xmpp_ctx_t *pointer) +{ + struct t_string *string = malloc(sizeof(struct t_string)); + string->value = value; + string->finalize = &t_string_xmpp_free; + string->pointer = pointer; + return string; +} + xmpp_stanza_t *stanza__presence(xmpp_ctx_t *context, xmpp_stanza_t *base, xmpp_stanza_t **children, const char *ns, char *from, char *to, const char *type); @@ -14,8 +58,23 @@ xmpp_stanza_t *stanza__iq(xmpp_ctx_t *context, xmpp_stanza_t *base, char *from, char *to, char *type); xmpp_stanza_t *stanza__iq_pubsub(xmpp_ctx_t *context, xmpp_stanza_t *base, - xmpp_stanza_t **children, char *ns); + xmpp_stanza_t **children, struct t_string *ns); xmpp_stanza_t *stanza__iq_pubsub_items(xmpp_ctx_t *context, xmpp_stanza_t *base, char *node); +xmpp_stanza_t *stanza__iq_pubsub_publish(xmpp_ctx_t *context, xmpp_stanza_t *base, + xmpp_stanza_t **children, struct t_string *node); + +xmpp_stanza_t *stanza__iq_pubsub_publish_item(xmpp_ctx_t *context, xmpp_stanza_t *base, + xmpp_stanza_t **children, struct t_string *id); + +xmpp_stanza_t *stanza__iq_pubsub_publish_item_list(xmpp_ctx_t *context, xmpp_stanza_t *base, + xmpp_stanza_t **children, struct t_string *ns); + +xmpp_stanza_t *stanza__iq_pubsub_publish_item_list_device(xmpp_ctx_t *context, xmpp_stanza_t *base, + struct t_string *id); + +xmpp_stanza_t *stanza__iq_ping(xmpp_ctx_t *context, xmpp_stanza_t *base, + struct t_string *ns); + #endif /*WEECHAT_XMPP_STANZA_H*/