diff --git a/.gitignore b/.gitignore index 771d19f..8498b9d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Prerequisites +compile_commands.json cscope* .depend *.d diff --git a/Makefile b/Makefile index ee1280a..9b4bc76 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,10 @@ CC=clang -CXX=clang++ +CXX=g++ RM=rm -f -CFLAGS=-fsanitize=address -fno-omit-frame-pointer -fPIC -std=gnu99 -g -Wall -Wextra -Werror-implicit-function-declaration -Wno-missing-field-initializers -I libwebsockets/include -I json-c -LDFLAGS=-shared -g -fsanitize=address +SANCFLAGS=-fsanitize=address -fsanitize=leak +SANLDFLAGS=-static-libasan -static-liblsan +CFLAGS=$(SANCFLAGS) -fno-omit-frame-pointer -fPIC -std=gnu99 -g -Wall -Wextra -Werror-implicit-function-declaration -Wno-missing-field-initializers -I libwebsockets/include -I json-c +LDFLAGS=-shared -g $(SANCFLAGS) $(SANLDFLAGS) LDLIBS=-lgnutls SRCS=slack.c \ @@ -21,6 +23,7 @@ SRCS=slack.c \ api/slack-api-hello.c \ api/slack-api-error.c \ api/slack-api-message.c \ + api/slack-api-user-typing.c \ api/message/slack-api-message-unimplemented.c \ request/slack-request-chat-postmessage.c \ request/slack-request-channels-list.c \ @@ -30,7 +33,7 @@ OBJS=$(subst .c,.o,$(SRCS)) libwebsockets/lib/libwebsockets.a json-c/libjson-c.a all: libwebsockets/lib/libwebsockets.a json-c/libjson-c.a weechat-slack weechat-slack: $(OBJS) - $(CC) $(LDFLAGS) -o slack.so $(OBJS) $(LDLIBS) + $(CXX) $(LDFLAGS) -o slack.so $(OBJS) $(LDLIBS) libwebsockets/lib/libwebsockets.a: cd libwebsockets && cmake -DLWS_STATIC_PIC=ON -DLWS_WITH_SHARED=OFF -DLWS_WITHOUT_TESTAPPS=ON -DLWS_WITH_LIBEV=OFF -DLWS_WITH_LIBUV=OFF -DLWS_WITH_LIBEVENT=OFF -DCMAKE_BUILD_TYPE=DEBUG . diff --git a/api/slack-api-message.c b/api/slack-api-message.c index 3ea0514..ae6010c 100644 --- a/api/slack-api-message.c +++ b/api/slack-api-message.c @@ -82,6 +82,7 @@ int slack_api_message_message_handle(struct t_slack_workspace *workspace, { struct t_slack_channel *ptr_channel; struct t_slack_user *ptr_user; + struct t_slack_channel_typing *ptr_typing; ptr_channel = slack_channel_search(workspace, channel); if (!ptr_channel) @@ -100,6 +101,14 @@ int slack_api_message_message_handle(struct t_slack_workspace *workspace, message); free(message); + ptr_typing = slack_channel_typing_search(ptr_channel, + ptr_user->profile.display_name); + if (ptr_typing) + { + slack_channel_typing_free(ptr_channel, ptr_typing); + slack_channel_typing_cb(ptr_channel, NULL, 0); + } + return 1; } diff --git a/api/slack-api-user-typing.c b/api/slack-api-user-typing.c new file mode 100644 index 0000000..75eaf65 --- /dev/null +++ b/api/slack-api-user-typing.c @@ -0,0 +1,63 @@ +#include + +#include "../weechat-plugin.h" +#include "../slack.h" +#include "../slack-workspace.h" +#include "../slack-user.h" +#include "../slack-channel.h" +#include "../slack-api.h" +#include "slack-api-user-typing.h" + +static const char *type = "user_typing"; + +static inline int json_valid(json_object *object, struct t_slack_workspace *workspace) +{ + if (!object) + { + weechat_printf( + workspace->buffer, + _("%s%s: error handling websocket %s%s%s message: " + "unexpected response from server"), + weechat_prefix("error"), SLACK_PLUGIN_NAME, + weechat_color("chat_value"), type, weechat_color("reset")); + return 0; + } + + return 1; +} + +int slack_api_user_typing_handle(struct t_slack_workspace *workspace, + const char *channel, const char *user) +{ + struct t_slack_channel *ptr_channel; + struct t_slack_user *ptr_user; + + ptr_channel = slack_channel_search(workspace, channel); + if (!ptr_channel) + return 1; /* silently ignore if channel hasn't been loaded yet */ + ptr_user = slack_user_search(workspace, user); + if (!ptr_user) + return 1; /* silently ignore if user hasn't been loaded yet */ + + slack_channel_add_typing(ptr_channel, ptr_user); + + return 1; +} + +int slack_api_user_typing(struct t_slack_workspace *workspace, + json_object *message) +{ + json_object *channel, *user; + + channel = json_object_object_get(message, "channel"); + if (!json_valid(channel, workspace)) + return 0; + + user = json_object_object_get(message, "user"); + if (!json_valid(user, workspace)) + return 0; + + return slack_api_user_typing_handle(workspace, + json_object_get_string(channel), + json_object_get_string(user)); +} diff --git a/api/slack-api-user-typing.h b/api/slack-api-user-typing.h new file mode 100644 index 0000000..3aea802 --- /dev/null +++ b/api/slack-api-user-typing.h @@ -0,0 +1,7 @@ +#ifndef _SLACK_API_USER_TYPING_H_ +#define _SLACK_API_USER_TYPING_H_ + +int slack_api_user_typing(struct t_slack_workspace *workspace, + json_object *message); + +#endif /*SLACK_API_USER_TYPING_H*/ diff --git a/request/slack-request-channels-list.c b/request/slack-request-channels-list.c index 6227875..2b50e6d 100644 --- a/request/slack-request-channels-list.c +++ b/request/slack-request-channels-list.c @@ -245,6 +245,7 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, json_object_put(response); free(json_string); } + /* fallthrough */ case LWS_CALLBACK_CLOSED_CLIENT_HTTP: request->client_wsi = NULL; /* Does not doing this cause a leak? diff --git a/request/slack-request-chat-postmessage.c b/request/slack-request-chat-postmessage.c index d795a9c..4ca8a35 100644 --- a/request/slack-request-chat-postmessage.c +++ b/request/slack-request-chat-postmessage.c @@ -165,6 +165,7 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, json_object_put(response); free(json_string); } + /* fallthrough */ case LWS_CALLBACK_CLOSED_CLIENT_HTTP: request->client_wsi = NULL; /* Does not doing this cause a leak? diff --git a/request/slack-request-users-list.c b/request/slack-request-users-list.c index d0e051a..85814bb 100644 --- a/request/slack-request-users-list.c +++ b/request/slack-request-users-list.c @@ -251,6 +251,7 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, json_object_put(response); free(json_string); } + /* fallthrough */ case LWS_CALLBACK_CLOSED_CLIENT_HTTP: request->client_wsi = NULL; /* Does not doing this cause a leak? diff --git a/slack-api.c b/slack-api.c index 2314951..e5d9af3 100644 --- a/slack-api.c +++ b/slack-api.c @@ -10,6 +10,7 @@ #include "api/slack-api-hello.h" #include "api/slack-api-error.h" #include "api/slack-api-message.h" +#include "api/slack-api-user-typing.h" struct stringcase { @@ -22,6 +23,7 @@ static struct stringcase cases[] = { { "hello", &slack_api_hello } , { "error", &slack_api_error } , { "message", &slack_api_message } +, { "user_typing", &slack_api_user_typing } }; static int stringcase_cmp(const void *p1, const void *p2) @@ -71,49 +73,95 @@ static int callback_ws(struct lws* wsi, enum lws_callback_reasons reason, weechat_prefix("network"), SLACK_PLUGIN_NAME, (const char *)in); { + int data_size; char *json_string; json_object *response, *type; + struct t_json_chunk *new_chunk, *last_chunk, *chunk_ptr; - json_string = strdup((const char *)in); + new_chunk = malloc(sizeof(*new_chunk)); + new_chunk->data = malloc(((int)len) + 1); + new_chunk->data[0] = '\0'; + new_chunk->next = NULL; + + strncat(new_chunk->data, in, (int)len); + + if (workspace->json_chunks) + { + for (last_chunk = workspace->json_chunks; last_chunk->next; + last_chunk = last_chunk->next); + last_chunk->next = new_chunk; + } + else + { + workspace->json_chunks = new_chunk; + } + + data_size = 0; + for (chunk_ptr = workspace->json_chunks; chunk_ptr; chunk_ptr = chunk_ptr->next) + { + data_size += strlen(chunk_ptr->data); + } + + json_string = malloc(data_size + 1); + json_string[0] = '\0'; + + for (chunk_ptr = workspace->json_chunks; chunk_ptr; chunk_ptr = chunk_ptr->next) + { + strcat(json_string, chunk_ptr->data); + } response = json_tokener_parse(json_string); - type = json_object_object_get(response, "type"); - if (!type) + if (response) { - weechat_printf( - workspace->buffer, - _("%s%s: unexpected data received from websocket: closing"), - weechat_prefix("error"), SLACK_PLUGIN_NAME); + for (chunk_ptr = workspace->json_chunks; chunk_ptr; workspace->json_chunks = chunk_ptr) + { + chunk_ptr = chunk_ptr->next; + free(workspace->json_chunks->data); + free(workspace->json_chunks); + } - slack_workspace_disconnect(workspace, 0); + type = json_object_object_get(response, "type"); + if (!type) + { + weechat_printf( + workspace->buffer, + _("%s%s: unexpected data received from websocket: closing"), + weechat_prefix("error"), SLACK_PLUGIN_NAME); + + slack_workspace_disconnect(workspace, 0); + + json_object_put(response); + free(json_string); + return -1; + } + + if (!slack_api_route_message(workspace, + json_object_get_string(type), response)) + { + weechat_printf( + workspace->buffer, + _("%s%s: error while handling message: %s"), + weechat_prefix("error"), SLACK_PLUGIN_NAME, + json_string); + weechat_printf( + workspace->buffer, + _("%s%s: closing connection."), + weechat_prefix("error"), SLACK_PLUGIN_NAME); + + slack_workspace_disconnect(workspace, 0); + + json_object_put(response); + free(json_string); + return -1; + } json_object_put(response); free(json_string); - return -1; } - - if (!slack_api_route_message(workspace, - json_object_get_string(type), response)) + else { - weechat_printf( - workspace->buffer, - _("%s%s: error while handling message: %s"), - weechat_prefix("error"), SLACK_PLUGIN_NAME, - json_string); - weechat_printf( - workspace->buffer, - _("%s%s: closing connection."), - weechat_prefix("error"), SLACK_PLUGIN_NAME); - - slack_workspace_disconnect(workspace, 0); - - json_object_put(response); free(json_string); - return -1; } - - json_object_put(response); - free(json_string); } return 0; /* don't passthru */ diff --git a/slack-buffer.c b/slack-buffer.c index d50816d..36a3d2a 100644 --- a/slack-buffer.c +++ b/slack-buffer.c @@ -1,3 +1,5 @@ +#include + #include "weechat-plugin.h" #include "slack.h" #include "slack-workspace.h" @@ -42,6 +44,67 @@ void slack_buffer_get_workspace_and_channel(struct t_gui_buffer *buffer, /* no workspace or channel found */ } +char *slack_buffer_typing_bar_cb(const void *pointer, + void *data, + struct t_gui_bar_item *item, + struct t_gui_window *window, + struct t_gui_buffer *buffer, + struct t_hashtable *extra_info) +{ + struct t_slack_channel_typing *ptr_typing; + struct t_slack_workspace *workspace; + struct t_slack_channel *channel; + char notification[256]; + unsigned typecount; + + (void) pointer; + (void) data; + (void) item; + (void) window; + (void) extra_info; + + workspace = NULL; + channel = NULL; + + slack_buffer_get_workspace_and_channel(buffer, &workspace, &channel); + + if (!channel) + return strdup(""); + + typecount = 0; + + for (ptr_typing = channel->typings; ptr_typing; + ptr_typing = ptr_typing->next_typing) + { + switch (++typecount) + { + case 1: + strcpy(notification, ptr_typing->name); + break; + case 2: + strcat(notification, ", "); + strcat(notification, ptr_typing->name); + break; + case 3: + default: + strcpy(notification, "Several people"); + break; + } + } + + if (typecount) + { + strcat(notification, NG_(" is typing...", + " are typing...", + typecount)); + return strdup(notification); + } + else + { + return strdup(""); + } +} + int slack_buffer_nickcmp_cb(const void *pointer, void *data, struct t_gui_buffer *buffer, const char *nick1, diff --git a/slack-buffer.h b/slack-buffer.h index 649c813..4ed21fa 100644 --- a/slack-buffer.h +++ b/slack-buffer.h @@ -5,6 +5,13 @@ void slack_buffer_get_workspace_and_channel(struct t_gui_buffer *buffer, struct t_slack_workspace **workspace, struct t_slack_channel **channel); +char *slack_buffer_typing_bar_cb(const void *pointer, + void *data, + struct t_gui_bar_item *item, + struct t_gui_window *window, + struct t_gui_buffer *buffer, + struct t_hashtable *extra_info); + int slack_buffer_nickcmp_cb(const void *pointer, void *data, struct t_gui_buffer *buffer, const char *nick1, diff --git a/slack-channel.c b/slack-channel.c index d6255e2..a3b99d6 100644 --- a/slack-channel.c +++ b/slack-channel.c @@ -1,10 +1,12 @@ #include #include #include +#include #include "weechat-plugin.h" #include "slack.h" #include "slack-workspace.h" +#include "slack-user.h" #include "slack-channel.h" #include "slack-input.h" #include "slack-buffer.h" @@ -73,7 +75,7 @@ struct t_gui_buffer *slack_channel_create_buffer(struct t_slack_workspace *works const char *name) { struct t_gui_buffer *ptr_buffer; - int buffer_created, current_buffer_number; + int buffer_created; const char *short_name, *localvar_channel; char buffer_name[256]; @@ -89,9 +91,6 @@ struct t_gui_buffer *slack_channel_create_buffer(struct t_slack_workspace *works } else { - current_buffer_number = weechat_buffer_get_integer( - weechat_current_buffer(), "number"); - ptr_buffer = weechat_buffer_new(buffer_name, &slack_input_data_cb, NULL, NULL, &slack_buffer_close_cb, NULL, NULL); @@ -181,6 +180,7 @@ struct t_slack_channel *slack_channel_new(struct t_slack_workspace *workspace, { struct t_slack_channel *new_channel, *ptr_channel; struct t_gui_buffer *ptr_buffer; + struct t_hook *typing_timer; char buffer_name[SLACK_CHANNEL_NAME_MAX_LEN + 2]; if (!workspace || !id || !name || !name[0]) @@ -202,6 +202,10 @@ struct t_slack_channel *slack_channel_new(struct t_slack_workspace *workspace, if ((new_channel = malloc(sizeof(*new_channel))) == NULL) return NULL; + typing_timer = weechat_hook_timer(1 * 1000, 0, 0, + &slack_channel_typing_cb, + new_channel, NULL); + new_channel->type = type; new_channel->id = strdup(id); new_channel->name = strdup(name); @@ -228,6 +232,9 @@ struct t_slack_channel *slack_channel_new(struct t_slack_workspace *workspace, new_channel->is_user_deleted = 0; + new_channel->typing_hook_timer = typing_timer; + new_channel->typings = NULL; + new_channel->last_typing = NULL; new_channel->members = NULL; new_channel->last_member = NULL; new_channel->buffer = ptr_buffer; @@ -244,6 +251,137 @@ struct t_slack_channel *slack_channel_new(struct t_slack_workspace *workspace, return new_channel; } +void slack_channel_typing_free(struct t_slack_channel *channel, + struct t_slack_channel_typing *typing) +{ + struct t_slack_channel_typing *new_typings; + + if (!channel || !typing) + return; + + /* remove typing from typings list */ + if (channel->last_typing == typing) + channel->last_typing = typing->prev_typing; + if (typing->prev_typing) + { + (typing->prev_typing)->next_typing = typing->next_typing; + new_typings = channel->typings; + } + else + new_typings = typing->next_typing; + + if (typing->next_typing) + (typing->next_typing)->prev_typing = typing->prev_typing; + + /* free typing data */ + if (typing->id) + free(typing->id); + if (typing->name) + free(typing->name); + + free(typing); + + channel->typings = new_typings; +} + +void slack_channel_typing_free_all(struct t_slack_channel *channel) +{ + while (channel->typings) + slack_channel_typing_free(channel, channel->typings); +} + +int slack_channel_typing_cb(const void *pointer, + void *data, + int remaining_calls) +{ + struct t_slack_channel_typing *ptr_typing, *next_typing; + struct t_slack_channel *channel; + unsigned typecount; + time_t now; + + (void) data; + (void) remaining_calls; + + if (!pointer) + return WEECHAT_RC_ERROR; + + channel = (struct t_slack_channel *)pointer; + + now = time(NULL); + + typecount = 0; + + for (ptr_typing = channel->typings; ptr_typing; + ptr_typing = ptr_typing->next_typing) + { + next_typing = ptr_typing->next_typing; + + while (ptr_typing && now - ptr_typing->ts > 5) + { + slack_channel_typing_free(channel, ptr_typing); + ptr_typing = next_typing; + if (ptr_typing) + next_typing = ptr_typing->next_typing; + } + + if (!ptr_typing) + break; + + typecount++; + } + + weechat_buffer_set(channel->buffer, + "localvar_set_typing", + typecount > 0 ? "1" : "0"); + weechat_bar_item_update("slack_typing"); + + return WEECHAT_RC_OK; +} + +struct t_slack_channel_typing *slack_channel_typing_search( + struct t_slack_channel *channel, + const char *id) +{ + struct t_slack_channel_typing *ptr_typing; + + if (!channel || !id) + return NULL; + + for (ptr_typing = channel->typings; ptr_typing; + ptr_typing = ptr_typing->next_typing) + { + if (weechat_strcasecmp(ptr_typing->id, id) == 0) + return ptr_typing; + } + + return NULL; +} + +void slack_channel_add_typing(struct t_slack_channel *channel, + struct t_slack_user *user) +{ + struct t_slack_channel_typing *new_typing; + + new_typing = slack_channel_typing_search(channel, user->id); + if (!new_typing) + { + new_typing = malloc(sizeof(*new_typing)); + new_typing->id = strdup(user->id); + new_typing->name = strdup(user->profile.display_name); + + new_typing->prev_typing = channel->last_typing; + new_typing->next_typing = NULL; + if (channel->last_typing) + (channel->last_typing)->next_typing = new_typing; + else + channel->typings = new_typing; + channel->last_typing = new_typing; + } + new_typing->ts = time(NULL); + + slack_channel_typing_cb(channel, NULL, 0); +} + void slack_channel_member_free(struct t_slack_channel *channel, struct t_slack_channel_member *member) { @@ -252,7 +390,7 @@ void slack_channel_member_free(struct t_slack_channel *channel, if (!channel || !member) return; - /* remove member from members list */ + /* remove member from members list */ if (channel->last_member == member) channel->last_member = member->prev_member; if (member->prev_member) @@ -289,7 +427,7 @@ void slack_channel_free(struct t_slack_workspace *workspace, if (!workspace || !channel) return; - /* remove channel from channels list */ + /* remove channel from channels list */ if (workspace->last_channel == channel) workspace->last_channel = channel->prev_channel; if (channel->prev_channel) @@ -303,7 +441,12 @@ void slack_channel_free(struct t_slack_workspace *workspace, if (channel->next_channel) (channel->next_channel)->prev_channel = channel->prev_channel; + /* free hooks */ + if (channel->typing_hook_timer) + weechat_unhook(channel->typing_hook_timer); + /* free linked lists */ + slack_channel_typing_free_all(channel); slack_channel_member_free_all(channel); /* free channel data */ @@ -313,18 +456,18 @@ void slack_channel_free(struct t_slack_workspace *workspace, free(channel->name); if (channel->name_normalized) free(channel->name_normalized); - if (channel->topic.value) - free(channel->topic.value); - if (channel->topic.creator) - free(channel->topic.creator); - if (channel->purpose.value) - free(channel->purpose.value); - if (channel->purpose.creator) - free(channel->purpose.creator); + if (channel->topic.value) + free(channel->topic.value); + if (channel->topic.creator) + free(channel->topic.creator); + if (channel->purpose.value) + free(channel->purpose.value); + if (channel->purpose.creator) + free(channel->purpose.creator); if (channel->creator) free(channel->creator); if (channel->buffer_as_string) - free (channel->buffer_as_string); + free(channel->buffer_as_string); free(channel); diff --git a/slack-channel.h b/slack-channel.h index f5892be..715c4b2 100644 --- a/slack-channel.h +++ b/slack-channel.h @@ -11,6 +11,16 @@ enum t_slack_channel_type SLACK_CHANNEL_TYPE_IM, }; +struct t_slack_channel_typing +{ + char *id; + char *name; + time_t ts; + + struct t_slack_channel_typing *prev_typing; + struct t_slack_channel_typing *next_typing; +}; + struct t_slack_channel_member { char *id; @@ -61,6 +71,9 @@ struct t_slack_channel /* im */ int is_user_deleted; + struct t_hook *typing_hook_timer; + struct t_slack_channel_typing *typings; + struct t_slack_channel_typing *last_typing; struct t_slack_channel_member *members; struct t_slack_channel_member *last_member; struct t_gui_buffer *buffer; @@ -80,6 +93,22 @@ struct t_slack_channel *slack_channel_new(struct t_slack_workspace *workspace, enum t_slack_channel_type type, const char *id, const char *name); +void slack_channel_typing_free(struct t_slack_channel *channel, + struct t_slack_channel_typing *typing); + +void slack_channel_typing_free_all(struct t_slack_channel *channel); + +int slack_channel_typing_cb(const void *pointer, + void *data, + int remaining_calls); + +struct t_slack_channel_typing *slack_channel_typing_search( + struct t_slack_channel *channel, + const char *id); + +void slack_channel_add_typing(struct t_slack_channel *channel, + struct t_slack_user *user); + void slack_channel_free_all(struct t_slack_workspace *workspace); #endif /*SLACK_CHANNEL_H*/ diff --git a/slack-message.c b/slack-message.c index 4aa6ef1..2cfa71e 100644 --- a/slack-message.c +++ b/slack-message.c @@ -1,5 +1,6 @@ #include #include +#include #include #include "../weechat-plugin.h" @@ -9,7 +10,7 @@ #include "../slack-user.h" #include "../slack-message.h" -static const char format_regex[] = "<(.*?)>"; +static const char format_regex[] = "<([^>]*?)>"; static const size_t max_groups = 2; char *slack_message_translate_code(struct t_slack_workspace *workspace, @@ -30,7 +31,12 @@ char *slack_message_translate_code(struct t_slack_workspace *workspace, case '@': /* user */ user = slack_user_search(workspace, code+1); if (user) - return strdup(user->profile.display_name); + { + size_t nicklen = snprintf(NULL, 0, "%s@%s%s", weechat_color("chat_nick"), user->profile.display_name, weechat_color("reset")) + 1; + char *tag = malloc(nicklen); + snprintf(tag, nicklen, "%s@%s%s", weechat_color("chat_nick"), user->profile.display_name, weechat_color("reset")); + return tag; + } else return strdup(code); case '!': /* special */ @@ -49,7 +55,7 @@ char *slack_message_decode(struct t_slack_workspace *workspace, regex_t reg; regmatch_t groups[max_groups]; char msgbuf[100]; - char *decoded_text, *pos; + char *decoded_text; const char *cursor; size_t offset; @@ -74,8 +80,7 @@ char *slack_message_decode(struct t_slack_workspace *workspace, weechat_prefix("error"), SLACK_PLUGIN_NAME); return strdup(text); } - pos = decoded_text; - pos[0] = '\0'; + decoded_text[0] = '\0'; for (cursor = text; regexec(®, cursor, max_groups, groups, 0) == 0; cursor += offset) { @@ -85,6 +90,7 @@ char *slack_message_decode(struct t_slack_workspace *workspace, if (!copy) { regfree(®); + free(decoded_text); weechat_printf( workspace->buffer, _("%s%s: error allocating space for message"), @@ -98,6 +104,7 @@ char *slack_message_decode(struct t_slack_workspace *workspace, { free(copy); regfree(®); + free(decoded_text); weechat_printf( workspace->buffer, _("%s%s: error allocating space for message"), @@ -112,6 +119,7 @@ char *slack_message_decode(struct t_slack_workspace *workspace, free(match); free(copy); regfree(®); + free(decoded_text); weechat_printf( workspace->buffer, _("%s%s: error allocating space for message"), @@ -120,18 +128,18 @@ char *slack_message_decode(struct t_slack_workspace *workspace, } free(copy); - pos = strncat(decoded_text, prematch, + strncat(decoded_text, prematch, SLACK_MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1); free(prematch); char *replacement = slack_message_translate_code(workspace, match); free(match); - pos = strncat(decoded_text, replacement, + strncat(decoded_text, replacement, SLACK_MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1); free(replacement); } - pos = strncat(decoded_text, cursor, + strncat(decoded_text, cursor, SLACK_MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1); regfree(®); diff --git a/slack-teaminfo.c b/slack-teaminfo.c index ba1347e..192ca0b 100644 --- a/slack-teaminfo.c +++ b/slack-teaminfo.c @@ -228,6 +228,7 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, json_object_put(response); free(json_string); } + /* fallthrough */ case LWS_CALLBACK_CLOSED_CLIENT_HTTP: client_wsi = NULL; /* Does not doing this cause a leak? diff --git a/slack-user.c b/slack-user.c index 3b0d0fd..c26aca5 100644 --- a/slack-user.c +++ b/slack-user.c @@ -142,7 +142,7 @@ void slack_user_free(struct t_slack_workspace *workspace, if (!workspace || !user) return; - /* remove user from users list */ + /* remove user from users list */ if (workspace->last_user == user) workspace->last_user = user->prev_user; if (user->prev_user) @@ -173,22 +173,22 @@ void slack_user_free(struct t_slack_workspace *workspace, free(user->tz_label); if (user->locale) free(user->locale); - if (user->profile.avatar_hash) - free(user->profile.avatar_hash); - if (user->profile.status_text) - free(user->profile.status_text); - if (user->profile.status_emoji) - free(user->profile.status_emoji); - if (user->profile.real_name) - free(user->profile.real_name); - if (user->profile.display_name) - free(user->profile.display_name); - if (user->profile.real_name_normalized) - free(user->profile.real_name_normalized); - if (user->profile.email) - free(user->profile.email); - if (user->profile.team) - free(user->profile.team); + if (user->profile.avatar_hash) + free(user->profile.avatar_hash); + if (user->profile.status_text) + free(user->profile.status_text); + if (user->profile.status_emoji) + free(user->profile.status_emoji); + if (user->profile.real_name) + free(user->profile.real_name); + if (user->profile.display_name) + free(user->profile.display_name); + if (user->profile.real_name_normalized) + free(user->profile.real_name_normalized); + if (user->profile.email) + free(user->profile.email); + if (user->profile.team) + free(user->profile.team); free(user); diff --git a/slack-workspace.c b/slack-workspace.c index 2d8fc6d..6b0a4af 100644 --- a/slack-workspace.c +++ b/slack-workspace.c @@ -247,6 +247,7 @@ static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, json_object_put(response); free(json_string); } + /* fallthrough */ case LWS_CALLBACK_CLOSED_CLIENT_HTTP: workspace->client_wsi = NULL; /* Does not doing this cause a leak? diff --git a/slack-workspace.h b/slack-workspace.h index 9861ab9..997ff23 100644 --- a/slack-workspace.h +++ b/slack-workspace.h @@ -6,7 +6,7 @@ extern struct t_slack_workspace *last_slack_workspace; enum t_slack_workspace_option { - SLACK_WORKSPACE_OPTION_TOKEN = 0, + SLACK_WORKSPACE_OPTION_TOKEN, SLACK_WORKSPACE_NUM_OPTIONS, }; diff --git a/slack.c b/slack.c index 4b59065..0a81640 100644 --- a/slack.c +++ b/slack.c @@ -10,6 +10,7 @@ #include "slack-command.h" #include "slack-workspace.h" #include "slack-api.h" +#include "slack-buffer.h" WEECHAT_PLUGIN_NAME(SLACK_PLUGIN_NAME); @@ -23,6 +24,8 @@ struct t_weechat_plugin *weechat_slack_plugin = NULL; struct t_hook *slack_hook_timer = NULL; +struct t_gui_bar_item *slack_typing_bar_item = NULL; + void slack_lwsl_emit_weechat(int level, const char *line) { char buf[50]; @@ -60,6 +63,18 @@ int weechat_plugin_init(struct t_weechat_plugin *plugin, int argc, char *argv[]) &slack_workspace_timer_cb, NULL, NULL); + if (!weechat_bar_search("typing")) + { + weechat_bar_new("typing", "off", "400", "window", "${typing}", + "bottom", "horizontal", "vertical", + "1", "1", "default", "default", "default", + "off", "slack_typing"); + } + + slack_typing_bar_item = weechat_bar_item_new("slack_typing", + &slack_buffer_typing_bar_cb, + NULL, NULL); + return WEECHAT_RC_OK; } @@ -68,6 +83,9 @@ int weechat_plugin_end(struct t_weechat_plugin *plugin) /* make C compiler happy */ (void) plugin; + if (slack_typing_bar_item) + weechat_bar_item_remove(slack_typing_bar_item); + if (slack_hook_timer) weechat_unhook(slack_hook_timer);