diff --git a/.gitmodules b/.gitmodules index 56502e4..6d435d0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "omemo"] path = omemo url = https://github.com/gkdr/libomemo +[submodule "diff"] + path = diff + url = https://github.com/kristapsdz/libdiff diff --git a/Makefile b/Makefile index 2f7b713..160c79c 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ SRCS=plugin.c \ xmpp/presence.c \ xmpp/iq.c \ -DEPS=omemo/build/libomemo-conversations.a axc/build/libaxc.a +DEPS=omemo/build/libomemo-conversations.a axc/build/libaxc.a diff/libdiff.a OBJS=$(subst .c,.o,$(SRCS)) all: weechat-xmpp @@ -47,6 +47,11 @@ axc/build/libaxc.a: $(MAKE) -C axc axc: axc/build/libaxc.a +diff/libdiff.a: + cd diff && ./configure + $(MAKE) -C diff CFLAGS=-fPIC +diff: diff/libdiff.a + test: xmpp.so env LD_PRELOAD=$(DEBUG) \ weechat -a -P 'alias,buflist,irc' -r '/plugin load ./xmpp.so' @@ -68,6 +73,7 @@ clean: $(RM) -f $(OBJS) $(MAKE) -C omemo clean || true $(MAKE) -C axc clean || true + $(MAKE) -C diff clean || true git submodule foreach --recursive git clean -xfd || true git submodule foreach --recursive git reset --hard || true git submodule update --init --recursive || true diff --git a/README.org b/README.org index 37728fd..19d9269 100644 --- a/README.org +++ b/README.org @@ -106,6 +106,7 @@ * [X] [#B] Displaying * [X] [#B] Tagging * [ ] [#B] Making + * [X] [#C] Diff highlighting * [ ] [#B] Handle errors gracefully * [X] [#B] Presence/nicklist * [X] [#B] Enters diff --git a/account.c b/account.c index 4943e83..a9583db 100644 --- a/account.c +++ b/account.c @@ -168,6 +168,8 @@ void account__log_emit_weechat(void *const userdata, const xmpp_log_level_t leve static const char *log_level_name[4] = {"debug", "info", "warn", "error"}; + const char *tags = level > XMPP_LEVEL_DEBUG ? "no_log" : NULL; + char *xml; if ((level == XMPP_LEVEL_DEBUG) && ((xml = strchr(msg, '<')) != NULL)) { @@ -219,14 +221,16 @@ void account__log_emit_weechat(void *const userdata, const xmpp_log_level_t leve 0, 0, &size); if (lines[size-1][0] == 0) lines[--size] = 0; - weechat_printf( + weechat_printf_date_tags( account ? account->buffer : NULL, + 0, tags, _("%s%s (%s): %s"), weechat_prefix("network"), area, log_level_name[level], header); for (int i = 1; i < size; i++) - weechat_printf( + weechat_printf_date_tags( account ? account->buffer : NULL, + 0, tags, _("%s%s"), colour, lines[i]); weechat_string_free_split(lines); @@ -234,8 +238,9 @@ void account__log_emit_weechat(void *const userdata, const xmpp_log_level_t leve } else { - weechat_printf( + weechat_printf_date_tags( account ? account->buffer : NULL, + 0, tags, _("%s%s (%s): %s"), weechat_prefix("network"), area, log_level_name[level], msg); diff --git a/channel.c b/channel.c index 8d8aa0d..bebed0d 100644 --- a/channel.c +++ b/channel.c @@ -86,7 +86,7 @@ struct t_gui_buffer *channel__create_buffer(struct t_account *account, buffer_created = 0; snprintf(buffer_name, sizeof(buffer_name), - "%s.%s", account->name, name); + "xmpp.%s.%s", account->name, name); ptr_buffer = channel__search_buffer(account, type, name); if (ptr_buffer) @@ -586,7 +586,8 @@ void channel__update_topic(struct t_channel *channel, struct t_channel_member *channel__add_member(struct t_account *account, struct t_channel *channel, - const char *id) + const char *id, const char *client, + const char *status) { struct t_channel_member *member; struct t_user *user; @@ -611,23 +612,35 @@ struct t_channel_member *channel__add_member(struct t_account *account, 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 + if (weechat_strcasecmp(user->id, channel->id) == 0 && channel->type == CHANNEL_TYPE_MUC) - weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,enter,log4", "%s%s %sentered%s %s", + weechat_printf_date_tags(channel->buffer, 0, "log2", "%sMUC: %s", + weechat_prefix("network"), + user->id); + else if (weechat_strcasecmp(jid_bare, channel->id) == 0 + && channel->type == CHANNEL_TYPE_MUC) + weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,enter,log4", "%s%s %s%s%s %sentered%s %s %s%s%s", weechat_prefix("join"), user__as_prefix_raw(account, jid_resource), + client ? "(" : "", client, client ? ")" : "", weechat_color("irc.color.message_join"), weechat_color("reset"), - channel->id); + channel->id, + status ? "[" : "", + status ? status : "", + status ? "]" : ""); else - weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,enter,log4", "%s%s (%s) %sentered%s %s", + weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,enter,log4", "%s%s (%s) %sentered%s %s %s%s%s", weechat_prefix("join"), user__as_prefix_raw(account, xmpp_jid_bare(account->context, user->id)), xmpp_jid_resource(account->context, user->id), weechat_color("irc.color.message_join"), weechat_color("reset"), - channel->id); + channel->id, + status ? "[" : "", + status ? status : "", + status ? "]" : ""); return member; } @@ -692,7 +705,7 @@ int channel__set_member_affiliation(struct t_account *account, struct t_channel_member *channel__remove_member(struct t_account *account, struct t_channel *channel, - const char *id) + const char *id, const char *status) { struct t_channel_member *member; struct t_user *user; @@ -709,20 +722,26 @@ struct t_channel_member *channel__remove_member(struct t_account *account, 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_date_tags(channel->buffer, 0, "xmpp_presence,leave,log4", "%s%s %sleft%s %s", + weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,leave,log4", "%s%s %sleft%s %s %s%s%s", weechat_prefix("quit"), jid_resource, weechat_color("irc.color.message_quit"), weechat_color("reset"), - channel->id); + channel->id, + status ? "[" : "", + status ? status : "", + status ? "]" : ""); else - weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,leave,log4", "%s%s (%s) %sleft%s %s", + weechat_printf_date_tags(channel->buffer, 0, "xmpp_presence,leave,log4", "%s%s (%s) %sleft%s %s %s%s%s", weechat_prefix("quit"), xmpp_jid_bare(account->context, user->id), xmpp_jid_resource(account->context, user->id), weechat_color("irc.color.message_quit"), weechat_color("reset"), - channel->id); + channel->id, + status ? "[" : "", + status ? status : "", + status ? "]" : ""); return member; } @@ -761,7 +780,9 @@ void channel__send_message(struct t_account *account, struct t_channel *channel, xmpp_send(account->connection, message); xmpp_stanza_release(message); if (channel->type != CHANNEL_TYPE_MUC) - weechat_printf(channel->buffer, "%s\t%s", - user__as_prefix_raw(account, account_jid(account)), - body); + weechat_printf_date_tags(channel->buffer, 0, + "xmpp_message,message,notify_none,self_msg,log1", + "%s\t%s", + user__as_prefix_raw(account, account_jid(account)), + body); } diff --git a/channel.h b/channel.h index 3c2924d..591d87e 100644 --- a/channel.h +++ b/channel.h @@ -121,7 +121,8 @@ void channel__update_purpose(struct t_channel *channel, struct t_channel_member *channel__add_member(struct t_account *account, struct t_channel *channel, - const char *id); + const char *id, const char *client, + const char *status); int channel__set_member_role(struct t_account *account, struct t_channel *channel, @@ -133,7 +134,7 @@ int channel__set_member_affiliation(struct t_account *account, struct t_channel_member *channel__remove_member(struct t_account *account, struct t_channel *channel, - const char *id); + const char *id, const char *status); 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 d534ce2..40fad43 100644 --- a/connection.c +++ b/connection.c @@ -11,6 +11,7 @@ #include #include "plugin.h" +#include "diff/diff.h" #include "xmpp/stanza.h" #include "config.h" #include "account.h" @@ -82,8 +83,10 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void struct t_account *account = (struct t_account *)userdata; struct t_user *user; struct t_channel *channel; - xmpp_stanza_t *iq__x, *iq__x__item; - const char *from, *from_bare, *role = NULL, *affiliation = NULL; + xmpp_stanza_t *iq__x, *iq__x__item, *iq__c, *iq__status; + const char *from, *from_bare, *role = NULL, *affiliation = NULL, *jid = NULL; + const char *node = NULL, *ver = NULL; + char *clientid = NULL, *status; from = xmpp_stanza_get_from(stanza); if (from == NULL) @@ -96,7 +99,25 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void 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"); + jid = xmpp_stanza_get_attribute(iq__x__item, "jid"); } + iq__c = xmpp_stanza_get_child_by_name_and_ns( + stanza, "c", "http://jabber.org/protocol/caps"); + if (iq__c) + { + node = xmpp_stanza_get_attribute(iq__c, "node"); + ver = xmpp_stanza_get_attribute(iq__c, "ver"); + if (jid) + clientid = strdup(jid); + else if (node && ver) + { + int len = strlen(node)+1+strlen(ver); + clientid = malloc(sizeof(char)*len); + snprintf(clientid, len, "%s#%s", node, ver); + } + } + iq__status = xmpp_stanza_get_child_by_name(stanza, "status"); + status = iq__status ? xmpp_stanza_get_text(iq__status) : NULL; user = user__search(account, from); if (!user) @@ -107,18 +128,21 @@ int connection__presence_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void { if (!channel) channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare); - channel__add_member(account, channel, from); + channel__add_member(account, channel, from, clientid, status); } 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); + channel__remove_member(account, channel, from, status); else - channel__add_member(account, channel, from); + channel__add_member(account, channel, from, clientid, status); } + if (clientid) + free(clientid); + return 1; } @@ -130,7 +154,7 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * struct t_channel *channel; xmpp_stanza_t *body, *delay, *topic, *replace; const char *type, *from, *nick, *from_bare, *to, *id, *replace_id, *timestamp; - char *intext; + char *intext, *difftext = NULL; struct tm time = {0}; time_t date = 0; @@ -171,6 +195,80 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * channel = channel__search(account, from_bare); if (!channel) channel = channel__new(account, CHANNEL_TYPE_PM, from_bare, from_bare); + if (replace) + { + const char *orig = NULL; + void *lines = weechat_hdata_pointer(weechat_hdata_get("buffer"), + channel->buffer, "lines"); + if (lines) + { + void *last_line = weechat_hdata_pointer(weechat_hdata_get("lines"), + lines, "last_line"); + while (last_line && !orig) + { + void *line_data = weechat_hdata_pointer(weechat_hdata_get("line"), + last_line, "data"); + if (line_data) + { + int tags_count = weechat_hdata_integer(weechat_hdata_get("line_data"), + line_data, "tags_count"); + char str_tag[20] = {0}; + for (int n_tag = 0; n_tag < tags_count; n_tag++) + { + snprintf(str_tag, sizeof(str_tag), "%d|tags_array", n_tag); + const char *tag = weechat_hdata_string(weechat_hdata_get("line_data"), + line_data, str_tag); + if (strlen(tag) > strlen("id_") && + weechat_strcasecmp(tag+strlen("id_"), replace_id) == 0) + { + orig = weechat_hdata_string(weechat_hdata_get("line_data"), + line_data, "message"); + break; + } + } + } + + last_line = weechat_hdata_pointer(weechat_hdata_get("line"), + last_line, "prev_line"); + } + } + + if (orig) + { + struct diff result; + if (diff(&result, char_cmp, 1, orig, strlen(orig), intext, strlen(intext)) > 0) + { + char **visual = weechat_string_dyn_alloc(256); + char ch[2] = {0}; + + for (size_t i = 0; i < result.sessz; i++) + switch (result.ses[i].type) + { + case DIFF_ADD: + weechat_string_dyn_concat(visual, weechat_color("green"), -1); + *ch = *(const char *)result.ses[i].e; + weechat_string_dyn_concat(visual, ch, -1); + break; + case DIFF_DELETE: + weechat_string_dyn_concat(visual, weechat_color("red"), -1); + *ch = *(const char *)result.ses[i].e; + weechat_string_dyn_concat(visual, ch, -1); + break; + case DIFF_COMMON: + default: + weechat_string_dyn_concat(visual, weechat_color("resetcolor"), -1); + *ch = *(const char *)result.ses[i].e; + weechat_string_dyn_concat(visual, ch, -1); + break; + } + free(result.ses); + free(result.lcs); + + difftext = strdup(*visual); + weechat_string_dyn_free(visual, 1); + } + } + } nick = NULL; if (weechat_strcasecmp(type, "groupchat") == 0) @@ -225,20 +323,22 @@ int connection__message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void * if (strcmp(to, channel->id) == 0) weechat_printf_date_tags(channel->buffer, date, *dyn_tags, "%s%s\t[to %s]: %s", edit, user__as_prefix_raw(account, nick), - to, intext ? intext : ""); + to, difftext ? difftext : intext ? intext : ""); else if (weechat_string_match(intext, "/me *", 0)) weechat_printf_date_tags(channel->buffer, date, *dyn_tags, "%s%s\t%s %s", edit, weechat_prefix("action"), nick, - intext ? intext+4 : ""); + difftext ? difftext+4 : intext ? intext+4 : ""); else weechat_printf_date_tags(channel->buffer, date, *dyn_tags, "%s%s\t%s", edit, user__as_prefix_raw(account, nick), - intext ? intext : ""); + difftext ? difftext : intext ? intext : ""); weechat_string_dyn_free(dyn_tags, 1); if (intext) xmpp_free(account->context, intext); + if (difftext) + free(difftext); return 1; } @@ -248,7 +348,7 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd (void) conn; struct t_account *account = (struct t_account *)userdata; - xmpp_stanza_t *reply, *query, *identity, *feature, *x, *field, *value, *text; + xmpp_stanza_t *reply, *query, *identity, *feature, *enable, *x, *field, *value, *text; xmpp_stanza_t *pubsub, *items, *item, *list, *device, **children; xmpp_stanza_t *storage, *conference, *nick; static struct utsname osinfo; @@ -315,6 +415,12 @@ int connection__iq_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userd FEATURE("urn:xmpp:time"); #undef FEATURE + enable = xmpp_stanza_new(account->context); + xmpp_stanza_set_name(enable, "enable"); + xmpp_stanza_set_ns(enable, "urn:xmpp:carbons:2"); + xmpp_stanza_add_child(query, enable); + xmpp_stanza_release(enable); + x = xmpp_stanza_new(account->context); xmpp_stanza_set_name(x, "x"); xmpp_stanza_set_ns(x, "jabber:x:data"); @@ -710,15 +816,15 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, NULL, variables, NULL); weechat_hashtable_free(variables); uint32_t dev_id = dev_str[0] ? atoi(dev_str) : 0; - uint8_t identity[128]; - if (b64_id) + uint8_t identity[128] = {0}; + if (b64_id && *b64_id) weechat_string_base_decode(64, b64_id, (char*)identity); struct t_identity id_key = { - .key = b64_id ? identity : NULL, + .key = identity, .length = 4, }; - omemo__init(&account->omemo, dev_id, &id_key); + omemo__init(&account->omemo, dev_id, b64_id && *b64_id ? &id_key : NULL); char account_id[64] = {0}; snprintf(account_id, sizeof(account_id), "%d", account->omemo->device_id); @@ -735,16 +841,15 @@ void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, } char account_key[64] = {0}; weechat_string_base_encode(64, (char*)account->omemo->identity.key, - account->omemo->identity.length, account_key); - if (memcmp(identity, account->omemo->identity.key, - account->omemo->identity.length) != 0) + account->omemo->identity.length, account_key); + if (weechat_strcasecmp(b64_id, account_key) != 0) { char **command = weechat_string_dyn_alloc(256); weechat_string_dyn_concat(command, "/secure set ", -1); weechat_string_dyn_concat(command, "xmpp_identity_", -1); weechat_string_dyn_concat(command, account->name, -1); weechat_string_dyn_concat(command, " ", -1); - weechat_string_dyn_concat(command, account_id, -1); + weechat_string_dyn_concat(command, account_key, -1); weechat_command(account->buffer, *command); weechat_string_dyn_free(command, 1); } diff --git a/connection.h b/connection.h index 54eb1dc..e84c419 100644 --- a/connection.h +++ b/connection.h @@ -13,4 +13,10 @@ int connection__connect(struct t_account *account, xmpp_conn_t **connection, void connection__process(xmpp_ctx_t *context, xmpp_conn_t *connection, const unsigned long timeout); +static inline int +char_cmp(const void *p1, const void *p2) +{ + return *(const char *)p1 == *(const char *)p2; +} + #endif /*WEECHAT_XMPP_CONNECTION_H*/ diff --git a/diff b/diff new file mode 160000 index 0000000..aadb3d7 --- /dev/null +++ b/diff @@ -0,0 +1 @@ +Subproject commit aadb3d7fe4dcb4b212c77e4fc6c2599826aeb50a