From 09ce7489b73f12d120c785965309d13c199e7a83 Mon Sep 17 00:00:00 2001 From: Tony Olagbaiye Date: Sun, 4 Jul 2021 06:39:30 +0100 Subject: [PATCH] show enters and leaves --- README.org | 9 +- channel.c | 62 +++++++++++++ channel.h | 4 + connection.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++---- user.c | 20 +++++ user.h | 4 + 6 files changed, 328 insertions(+), 19 deletions(-) diff --git a/README.org b/README.org index 325b96b..6a17567 100644 --- a/README.org +++ b/README.org @@ -47,7 +47,7 @@ - glib (dynamic, dependency of libomemo) - sqlite (dynamic, dependency of libomemo/axc) - libsignal-protocol-c (dynamic, dependency of axc) - - weechat (>= v2.5) + - weechat (>= v3.0) .. or just use the guix spec in .envrc @@ -101,13 +101,15 @@ * [X] Sending * [ ] [#B] Edits * [ ] [#B] Handle errors gracefully - * [ ] [#B] Presence/nicklist + * [X] [#B] Presence/nicklist * [X] [#B] Enters - * [ ] [#B] Leaves + * [X] [#B] Leaves * [X] [#B] Tracking * [X] [#B] Set/show topic * [ ] OMEMO (libsignal-protocol-c / axc) * [ ] Presence + * [X] Disco + * [ ] Disco response * [ ] Messages * [ ] MUC PMs * [ ] Send typing notifications @@ -117,6 +119,7 @@ * [ ] PGP (libgpgme) ** TODO [#C] Implement completion engine (milestone v0.3) ** TODO [#D] Close all issues (milestone v1.0) + * [ ] Absorb libomemo / axc, and drop glib * Contributing diff --git a/channel.c b/channel.c index 0379285..4732b98 100644 --- a/channel.c +++ b/channel.c @@ -601,6 +601,68 @@ struct t_channel_member *channel__add_member(struct t_account *account, if (user) user__nicklist_add(account, channel, user); + char *jid_bare = xmpp_jid_bare(account->context, user->id); + char *jid_resource = xmpp_jid_resource(account->context, user->id); + if (weechat_strcasecmp(jid_bare, channel->id) == 0 + && channel->type == CHANNEL_TYPE_MUC) + weechat_printf(channel->buffer, "%s%s entered", + weechat_prefix("join"), + jid_resource); + else + weechat_printf(channel->buffer, "%s%s (%s) entered", + weechat_prefix("join"), + xmpp_jid_bare(account->context, user->id), + xmpp_jid_resource(account->context, user->id)); + + return member; +} + +struct t_channel_member *channel__member_search(struct t_channel *channel, + const char *id) +{ + struct t_channel_member *ptr_member; + + if (!channel || !id) + return NULL; + + for (ptr_member = channel->members; ptr_member; + ptr_member = ptr_member->next_member) + { + if (weechat_strcasecmp(ptr_member->id, id) == 0) + return ptr_member; + } + + return NULL; +} + +struct t_channel_member *channel__remove_member(struct t_account *account, + struct t_channel *channel, + const char *id) +{ + struct t_channel_member *member; + struct t_user *user; + + user = user__search(account, id); + if (user) + user__nicklist_remove(account, channel, user); + + member = channel__member_search(channel, id); + if (member) + channel__member_free(channel, member); + + char *jid_bare = xmpp_jid_bare(account->context, user->id); + char *jid_resource = xmpp_jid_resource(account->context, user->id); + if (weechat_strcasecmp(jid_bare, channel->id) == 0 + && channel->type == CHANNEL_TYPE_MUC) + weechat_printf(channel->buffer, "%s%s left", + weechat_prefix("quit"), + jid_resource); + else + weechat_printf(channel->buffer, "%s%s (%s) left", + weechat_prefix("quit"), + xmpp_jid_bare(account->context, user->id), + xmpp_jid_resource(account->context, user->id)); + return member; } diff --git a/channel.h b/channel.h index 4877714..ed9ca23 100644 --- a/channel.h +++ b/channel.h @@ -120,6 +120,10 @@ struct t_channel_member *channel__add_member(struct t_account *account, struct t_channel *channel, const char *id); +struct t_channel_member *channel__remove_member(struct t_account *account, + struct t_channel *channel, + const char *id); + void channel__send_message(struct t_account *account, struct t_channel *channel, const char *to, const char *body); diff --git a/connection.c b/connection.c index 62190d1..0dac172 100644 --- a/connection.c +++ b/connection.c @@ -21,7 +21,7 @@ void connection__init() xmpp_initialize(); } -int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) +int connection__version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) { xmpp_stanza_t *reply, *query, *name, *version, *text; const char *ns; @@ -72,37 +72,51 @@ int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) return 1; } -int presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) +int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) { (void) conn; struct t_account *account = (struct t_account *)userdata; struct t_user *user; struct t_channel *channel; - const char *from, *from_bare; + xmpp_stanza_t *iq__x, *iq__x__item; + const char *from, *from_bare, *role = NULL, *affiliation = NULL; from = xmpp_stanza_get_from(stanza); if (from == NULL) return 1; from_bare = xmpp_jid_bare(account->context, from); + iq__x = xmpp_stanza_get_child_by_name_and_ns( + stanza, "x", "http://jabber.org/protocol/muc#user"); + if (iq__x) + { + iq__x__item = xmpp_stanza_get_child_by_name(iq__x, "item"); + role = xmpp_stanza_get_attribute(iq__x__item, "role"); + affiliation = xmpp_stanza_get_attribute(iq__x__item, "affiliation"); + } user = user__search(account, from); if (!user) user = user__new(account, from, from); channel = channel__search(account, from_bare); - if (!channel) - channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare); - channel__add_member(account, channel, from); - - weechat_printf(account->buffer, "%s%s (%s) presence", - weechat_prefix("action"), - user->name, user->profile.display_name); + if (!iq__x) + { + if (!channel) + channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare); + channel__add_member(account, channel, from); + } + else if (channel) + { + if (weechat_strcasecmp(role, "none") == 0) + channel__remove_member(account, channel, from); + channel__add_member(account, channel, from); + } return 1; } -int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) +int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) { (void) conn; @@ -184,6 +198,203 @@ int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) return 1; } +#include +int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) +{ + (void) conn; + + struct t_account *account = (struct t_account *)userdata; + xmpp_stanza_t *reply, *query, *identity, *feature, *x, *field, *value, *text; + char client_name[64], *node; + static struct utsname osinfo; + + 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"); + snprintf(client_name, sizeof(client_name), + "weechat %s", weechat_info_get("version", NULL)); + 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"); +#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"); + + if (uname(&osinfo) >= 0) + { + osinfo.machine; + } + + { + 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"); + + 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); + } + + { + 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"); + + 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); + + 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); + + xmpp_stanza_add_child(field, value); + xmpp_stanza_release(value); + + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } + + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "os"); + + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "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(field, value); + xmpp_stanza_release(value); + + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } + + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "os_version"); + + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "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(field, value); + xmpp_stanza_release(value); + + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } + + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "software"); + + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "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(field, value); + xmpp_stanza_release(value); + + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } + + { + field = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(field, "field"); + xmpp_stanza_set_attribute(field, "var", "software_version"); + + value = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(value, "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(field, value); + xmpp_stanza_release(value); + + xmpp_stanza_add_child(x, field); + xmpp_stanza_release(field); + } + + xmpp_stanza_add_child(query, x); + xmpp_stanza_release(x); + + xmpp_stanza_add_child(reply, query); + + xmpp_send(conn, reply); + xmpp_stanza_release(reply); + + return 1; +} + void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, int error, xmpp_stream_error_t *stream_error, void *userdata) @@ -197,9 +408,14 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, xmpp_stanza_t *pres, *pres__c, *pres__status, *pres__status__text; char cap_hash[28+1] = {0}; - xmpp_handler_add(conn, version_handler, "jabber:iq:version", "iq", NULL, account); - xmpp_handler_add(conn, presence_handler, NULL, "presence", NULL, account); - xmpp_handler_add(conn, message_handler, NULL, "message", /*type*/ NULL, account); + xmpp_handler_add(conn, connection__version_handler, + "jabber:iq:version", "iq", NULL, account); + xmpp_handler_add(conn, connection__presence_handler, + NULL, "presence", NULL, account); + xmpp_handler_add(conn, connection__message_handler, + NULL, "message", /*type*/ NULL, account); + xmpp_handler_add(conn, connection__iq_handler, + NULL, "iq", "get", account); /* Send initial so that we appear online to contacts */ pres = xmpp_presence_new(account->context); @@ -240,7 +456,7 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, } } -char* rand_string(int length) +char* connection__rand_string(int length) { char *string = malloc(length); srand(time(NULL)); @@ -262,7 +478,7 @@ int connection__connect(struct t_account *account, xmpp_conn_t **connection, const char *resource = account_resource(account); if (!(resource && strlen(resource))) { - char *const rand = rand_string(8); + char *const rand = connection__rand_string(8); char ident[64] = {0}; snprintf(ident, sizeof(ident), "weechat.%s", rand); free(rand); diff --git a/user.c b/user.c index b6d740c..4a9a654 100644 --- a/user.c +++ b/user.c @@ -115,6 +115,26 @@ void user__nicklist_add(struct t_account *account, 1); } +void user__nicklist_remove(struct t_account *account, + struct t_channel *channel, + struct t_user *user) +{ + struct t_gui_nick_group *ptr_group; + struct t_gui_buffer *ptr_buffer; + char *name = user->profile.display_name; + if (channel && weechat_strcasecmp(xmpp_jid_bare(account->context, name), + channel->id) == 0) + name = xmpp_jid_resource(account->context, name); + + ptr_buffer = channel ? channel->buffer : account->buffer; + + ptr_group = weechat_nicklist_search_group(ptr_buffer, NULL, + user->is_away ? + "+" : "..."); + weechat_nicklist_remove_nick(ptr_buffer, + weechat_nicklist_search_nick(ptr_buffer, ptr_group, name)); +} + struct t_user *user__new(struct t_account *account, const char *id, const char *display_name) { diff --git a/user.h b/user.h index 624aaf2..f90e535 100644 --- a/user.h +++ b/user.h @@ -75,4 +75,8 @@ void user__nicklist_add(struct t_account *account, struct t_channel *channel, struct t_user *user); +void user__nicklist_remove(struct t_account *account, + struct t_channel *channel, + struct t_user *user); + #endif /*WEECHAT_XMPP_USER_H*/