From 2b42e8cd2ab4873146dfd8181542a662055719d1 Mon Sep 17 00:00:00 2001 From: Tony Olagbaiye Date: Wed, 30 Jun 2021 07:26:06 +0100 Subject: [PATCH] buffers! hooray. --- .envrc | 8 +- Makefile | 34 ++- account.c | 495 +++++++++++++++++++++++++++++++++++++++ account.h | 64 +++++ buffer.c | 165 +++++++++++++ buffer.h | 27 +++ channel.c | 643 +++++++++++++++++++++++++++++++++++++++++++++++++++ channel.h | 147 ++++++++++++ command.c | 438 +++++++++++++++++------------------ command.h | 2 +- config.c | 388 ++++++++++++++++--------------- config.h | 88 ++++--- connection.c | 162 ++++--------- connection.h | 17 +- input.c | 64 +++++ input.h | 12 + message.c | 249 ++++++++++++++++++++ message.h | 13 ++ plugin.c | 63 +++-- user.c | 242 +++++++++++++++++++ user.h | 75 ++++++ 21 files changed, 2762 insertions(+), 634 deletions(-) create mode 100644 account.c create mode 100644 account.h create mode 100644 buffer.c create mode 100644 buffer.h create mode 100644 channel.c create mode 100644 channel.h create mode 100644 input.c create mode 100644 input.h create mode 100644 message.c create mode 100644 message.h create mode 100644 user.c create mode 100644 user.h diff --git a/.envrc b/.envrc index ec1a23e..989115f 100644 --- a/.envrc +++ b/.envrc @@ -18,12 +18,6 @@ export_function() use_guix() { - # Set GitHub token. - export GUIX_GITHUB_TOKEN="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" - - # Unset 'GUIX_PACKAGE_PATH'. - export GUIX_PACKAGE_PATH="" - # Recreate a garbage collector root. gcroots="$HOME/.config/guix/gcroots" mkdir -p "$gcroots" @@ -51,4 +45,4 @@ use_guix() export CC=gcc } -use guix +use guix --with-debug-info=weechat --with-debug-info=libstrophe diff --git a/Makefile b/Makefile index 3ef02a1..657cd60 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ ifdef DEBUG - DBGCFLAGS=-fsanitize=address -fsanitize=leak -fsanitize=undefined - DBGLDFLAGS=-static-libasan -static-liblsan -static-libubsan + DBGCFLAGS=-fsanitize=address -fsanitize=undefined -fsanitize=leak + DBGLDFLAGS=-lasan -lubsan -llsan endif RM=rm -f FIND=find INCLUDES=-Ilibstrophe CFLAGS+=$(DBGCFLAGS) -fno-omit-frame-pointer -fPIC -std=gnu99 -g -Wall -Wextra -Werror-implicit-function-declaration -Wno-missing-field-initializers $(INCLUDES) -LDFLAGS+=-shared -g $(DBGCFLAGS) $(DBGLDFLAGS) +LDFLAGS+=$(DBGLDFLAGS) -shared -g $(DBGCFLAGS) LDLIBS=-lstrophe -lpthread PREFIX ?= /usr/local @@ -14,19 +14,35 @@ LIBDIR ?= $(PREFIX)/lib INSTALL ?= /usr/bin/install SRCS=plugin.c \ - command.c \ - config.c \ - connection.c \ + account.c \ + buffer.c \ + channel.c \ + command.c \ + config.c \ + connection.c \ + input.c \ + message.c \ + user.c \ DEPS= OBJS=$(subst .c,.o,$(SRCS)) -all: $(DEPS) weechat-xmpp +all: weechat-xmpp +weechat-xmpp: $(DEPS) xmpp.so -weechat-xmpp: $(OBJS) +xmpp.so: $(OBJS) $(CC) $(LDFLAGS) -o xmpp.so $(OBJS) $(LDLIBS) which patchelf >/dev/null && \ - patchelf --set-rpath $(LIBRARY_PATH):$(shell patchelf --print-rpath xmpp.so) xmpp.so || true + patchelf --set-rpath $(LIBRARY_PATH):$(shell realpath $(shell dirname $(shell gcc --print-libgcc-file-name))/../../../) xmpp.so && \ + patchelf --shrink-rpath xmpp.so || true + +test: xmpp.so + env LD_PRELOAD=$(DEBUG) \ + weechat -a -P buflist -r '/plugin load ./xmpp.so' + +debug: xmpp.so + gdb -ex "handle SIGPIPE nostop noprint pass" --args \ + weechat -a -r '/plugin load ./xmpp.so' depend: .depend diff --git a/account.c b/account.c new file mode 100644 index 0000000..e70c2cc --- /dev/null +++ b/account.c @@ -0,0 +1,495 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include + +#include "plugin.h" +#include "config.h" +#include "input.h" +#include "account.h" +#include "connection.h" +//#include "xmpp-api.h" +//#include "xmpp-request.h" +#include "user.h" +#include "channel.h" +#include "buffer.h" + +struct t_account *accounts = NULL; +struct t_account *last_account = NULL; + +char *account_options[ACCOUNT_NUM_OPTIONS][2] = +{ { "jid", "" }, + { "password", "" }, + { "tls", "normal" }, + { "nickname", "" }, +}; + +struct t_account *account__search(const char *name) +{ + struct t_account *ptr_account; + + if (!name) + return NULL; + + for (ptr_account = accounts; ptr_account; + ptr_account = ptr_account->next_account) + { + if (strcmp(ptr_account->name, name) == 0) + return ptr_account; + } + + /* account not found */ + return NULL; +} + +struct t_account *account__casesearch (const char *name) +{ + struct t_account *ptr_account; + + if (!name) + return NULL; + + for (ptr_account = accounts; ptr_account; + ptr_account = ptr_account->next_account) + { + if (weechat_strcasecmp (ptr_account->name, name) == 0) + return ptr_account; + } + + /* account not found */ + return NULL; +} + +int account__search_option(const char *option_name) +{ + int i; + + if (!option_name) + return -1; + + for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++) + { + if (weechat_strcasecmp(account_options[i][0], option_name) == 0) + return i; + } + + /* account option not found */ + return -1; +} + +void log_emit_weechat(void *const userdata, const xmpp_log_level_t level, + const char *const area, const char *const msg) +{ + struct t_account *account = (struct t_account*)userdata; + + static const char *log_level_name[4] = {"debug", "info", "warn", "error"}; + + time_t date = time(NULL); + const char *timestamp = weechat_util_get_time_string(&date); + + weechat_printf( + account ? account->buffer : NULL, + _("%s%s (%s): %s"), + weechat_prefix("error"), area, + log_level_name[level], msg); +} + +struct t_account *account__alloc(const char *name) +{ + struct t_account *new_account; + int i, length; + char *option_name; + + if (account__casesearch(name)) + return NULL; + + /* alloc memory for new account */ + new_account = malloc(sizeof(*new_account)); + if (!new_account) + { + weechat_printf(NULL, + _("%s%s: error when allocating new account"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + return NULL; + } + + /* add new account to queue */ + new_account->prev_account = last_account; + new_account->next_account = NULL; + if (last_account) + last_account->next_account = new_account; + else + accounts = new_account; + last_account = new_account; + + /* set name */ + new_account->name = strdup(name); + + /* set properties */ + new_account->jid = NULL; + new_account->password = NULL; + new_account->tls = 1; + + /* internal vars */ + new_account->reloading_from_config = 0; + + new_account->is_connected = 0; + new_account->disconnected = 0; + + new_account->logger.handler = &log_emit_weechat; + new_account->logger.userdata = new_account; + new_account->context = xmpp_ctx_new(NULL, &new_account->logger); + new_account->connection = NULL; + + new_account->nickname = NULL; + + new_account->buffer = NULL; + new_account->buffer_as_string = NULL; + new_account->users = NULL; + new_account->last_user = NULL; + new_account->channels = NULL; + new_account->last_channel = NULL; + + /* create options with null value */ + for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++) + { + new_account->options[i] = NULL; + + length = strlen(new_account->name) + 1 + + strlen(account_options[i][0]) + + 512 + /* inherited option name(xmpp.account_default.xxx) */ + 1; + option_name = malloc(length); + if (option_name) + { + snprintf(option_name, length, "%s.%s << xmpp.account_default.%s", + new_account->name, + account_options[i][0], + account_options[i][0]); + new_account->options[i] = config__account_new_option( + config_file, + config_section_account, + i, + option_name, + account_options[i][1], + account_options[i][1], + 0, + &config__account_check_value_cb, + account_options[i][0], + NULL, + &config__account_change_cb, + account_options[i][0], + NULL); + config__account_change_cb(account_options[i][0], NULL, + new_account->options[i]); + free(option_name); + } + } + + return new_account; +} + +void account__free_data(struct t_account *account) +{ + int i; + + if (!account) + return; + + /* free linked lists */ + /* + for (i = 0; i < IRC_SERVER_NUM_OUTQUEUES_PRIO; i++) + { + account__outqueue_free_all(account, i); + } + xmpp_redirect_free_all(account); + xmpp_notify_free_all(account); + */ + channel__free_all(account); + user__free_all(account); + + /* free hashtables */ + /* + weechat_hashtable_free(account->join_manual); + weechat_hashtable_free(account->join_channel_key); + weechat_hashtable_free(account->join_noswitch); + */ + + /* close xmpp context */ + if (account->connection) + xmpp_conn_release(account->connection); + if (account->context) + xmpp_ctx_free(account->context); + + /* free account data */ + //for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++) + //{ + // if (account->options[i]) + // weechat_config_option_free(account->options[i]); + //} + + if (account->name) + free(account->name); + if (account->jid) + free(account->jid); + if (account->password) + free(account->password); + + if (account->nickname) + free(account->nickname); + + if (account->buffer_as_string) + free(account->buffer_as_string); + + channel__free_all(account); + user__free_all(account); +} + +void account__free(struct t_account *account) +{ + struct t_account *new_accounts; + + if (!account) + return; + + /* + * close account buffer (and all channels/privates) + * (only if we are not in a /upgrade, because during upgrade we want to + * keep connections and closing account buffer would disconnect from account) + */ + if (account->buffer) + weechat_buffer_close(account->buffer); + + /* remove account from queue */ + if (last_account == account) + last_account = account->prev_account; + if (account->prev_account) + { + (account->prev_account)->next_account = account->next_account; + new_accounts = accounts; + } + else + new_accounts = account->next_account; + + if (account->next_account) + (account->next_account)->prev_account = account->prev_account; + + account__free_data(account); + free(account); + accounts = new_accounts; +} + +void account__free_all() +{ + /* for each account in memory, remove it */ + while (accounts) + { + account__free(accounts); + } +} + +void account__disconnect(struct t_account *account, int reconnect) +{ + (void) reconnect; + + struct t_channel *ptr_channel; + (void) ptr_channel; + + if (account->is_connected) + { + /* + * remove all nicks and write disconnection message on each + * channel/private buffer + */ + user__free_all(account); + weechat_nicklist_remove_all(account->buffer); + for (ptr_channel = account->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + weechat_nicklist_remove_all(ptr_channel->buffer); + weechat_printf( + ptr_channel->buffer, + _("%s%s: disconnected from account"), + weechat_prefix("network"), WEECHAT_XMPP_PLUGIN_NAME); + } + /* remove away status on account buffer */ + //weechat_buffer_set(account->buffer, "localvar_del_away", ""); + } + + account__close_connection(account); + + if (account->buffer) + { + weechat_printf( + account->buffer, + _("%s%s: disconnected from account"), + 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) + { + free (account->nick_modes); + account->nick_modes = NULL; + weechat_bar_item_update ("input_prompt"); + weechat_bar_item_update ("xmpp_nick_modes"); + } + 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->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->disconnected = 1; + */ + + /* send signal "account_disconnected" with account name */ + /* + (void) weechat_hook_signal_send("account_disconnected", + WEECHAT_HOOK_SIGNAL_STRING, account->name); + */ +} + +void account__disconnect_all() +{ + struct t_account *ptr_account; + + for (ptr_account = accounts; ptr_account; + ptr_account = ptr_account->next_account) + { + account__disconnect(ptr_account, 0); + } +} + +struct t_gui_buffer *account__create_buffer(struct t_account *account) +{ + char buffer_name[256], charset_modifier[256]; + + snprintf(buffer_name, sizeof(buffer_name), + "account.%s", account->name); + account->buffer = weechat_buffer_new(buffer_name, + &input__data_cb, NULL, NULL, + &buffer__close_cb, NULL, NULL); + if (!account->buffer) + return NULL; + + if (!weechat_buffer_get_integer(account->buffer, "short_name_is_set")) + weechat_buffer_set(account->buffer, "short_name", account->name); + weechat_buffer_set(account->buffer, "localvar_set_type", "server"); + weechat_buffer_set(account->buffer, "localvar_set_server", account->name); + weechat_buffer_set(account->buffer, "localvar_set_channel", account->name); + snprintf(charset_modifier, sizeof (charset_modifier), + "account.%s", account->name); + weechat_buffer_set(account->buffer, "localvar_set_charset_modifier", + charset_modifier); + weechat_buffer_set(account->buffer, "title", + (account->name) ? account->name : ""); + + weechat_buffer_set(account->buffer, "nicklist", "1"); + weechat_buffer_set(account->buffer, "nicklist_display_groups", "0"); + weechat_buffer_set_pointer(account->buffer, "nicklist_callback", + &buffer__nickcmp_cb); + weechat_buffer_set_pointer(account->buffer, "nicklist_callback_pointer", + account); + + return account->buffer; +} + +void account__close_connection(struct t_account *account) +{ + if (account->connection) + { + if (xmpp_conn_is_connected(account->connection)) + xmpp_disconnect(account->connection); + } + + account->is_connected = 0; +} + +int account__connect(struct t_account *account) +{ + account->disconnected = 0; + + if (!account->buffer) + { + if (!account__create_buffer(account)) + return 0; + weechat_buffer_set(account->buffer, "display", "auto"); + } + + account__close_connection(account); + + account->jid = !account->options[ACCOUNT_OPTION_JID] ? NULL : + weechat_config_string(account->options[ACCOUNT_OPTION_JID]); + account->password = !account->options[ACCOUNT_OPTION_PASSWORD] ? NULL : + weechat_config_string(account->options[ACCOUNT_OPTION_PASSWORD]); + account->tls = !account->options[ACCOUNT_OPTION_TLS] ? NULL : + weechat_config_integer(account->options[ACCOUNT_OPTION_TLS]); + account->nickname = !account->options[ACCOUNT_OPTION_NICKNAME] ? NULL : + weechat_config_string(account->options[ACCOUNT_OPTION_NICKNAME]); + + account->is_connected = + connection__connect(account->context, &account->connection, + &account->logger, account->jid, + account->password, account->tls); + + return account->is_connected; +} + +int account__timer_cb(const void *pointer, void *data, int remaining_calls) +{ + (void) pointer; + (void) data; + (void) remaining_calls; + + struct t_account *ptr_account; + + for (ptr_account = accounts; ptr_account; + ptr_account = ptr_account->next_account) + { + if (ptr_account->is_connected + && (xmpp_conn_is_connecting(ptr_account->connection) + || xmpp_conn_is_connected(ptr_account->connection))) + connection__process(ptr_account->context, ptr_account->connection, 10); + } +} diff --git a/account.h b/account.h new file mode 100644 index 0000000..c963bde --- /dev/null +++ b/account.h @@ -0,0 +1,64 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef _ACCOUNT_H_ +#define _ACCOUNT_H_ + +extern struct t_account *accounts; +extern struct t_account *last_account; + +enum t_account_option +{ + ACCOUNT_OPTION_JID, + ACCOUNT_OPTION_PASSWORD, + ACCOUNT_OPTION_TLS, + ACCOUNT_OPTION_NICKNAME, + ACCOUNT_NUM_OPTIONS, +}; + +struct t_account +{ + const char *name; + const char *jid; + const char *password; + int tls; + struct t_config_option *options[ACCOUNT_NUM_OPTIONS]; + + int reloading_from_config; + + int is_connected; + int disconnected; + + xmpp_log_t logger; + struct xmpp_ctx_t *context; + struct xmpp_conn_t *connection; + + char *nickname; + + struct t_gui_buffer *buffer; + char *buffer_as_string; + 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; +}; + +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_account *account__alloc(const char *name); +void account__free_data(struct t_account *account); +void account__free(struct t_account *account); +void account__free_all(); +void account__disconnect(struct t_account *account, int reconnect); +void account__disconnect_all(); +void account__close_connection(struct t_account *account); +int account__connect(struct t_account *account); +int account__timer_cb(const void *pointer, void *data, int remaining_calls); + +#endif /*ACCOUNT_H*/ diff --git a/buffer.c b/buffer.c new file mode 100644 index 0000000..48232b3 --- /dev/null +++ b/buffer.c @@ -0,0 +1,165 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include + +#include "plugin.h" +#include "account.h" +#include "channel.h" +#include "buffer.h" + +void buffer__get_account_and_channel(struct t_gui_buffer *buffer, + struct t_account **account, + struct t_channel **channel) +{ + struct t_account *ptr_account; + struct t_channel *ptr_channel; + + if (!buffer) + return; + + /* look for a account or channel using this buffer */ + for (ptr_account = accounts; ptr_account; + ptr_account = ptr_account->next_account) + { + if (ptr_account->buffer == buffer) + { + if (account) + *account = ptr_account; + return; + } + + for (ptr_channel = ptr_account->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (ptr_channel->buffer == buffer) + { + if (account) + *account = ptr_account; + if (channel) + *channel = ptr_channel; + return; + } + } + } + + /* no account or channel found */ +} + +char *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_channel_typing *ptr_typing; + struct t_account *account; + struct t_channel *channel; + char notification[256]; + unsigned typecount; + + (void) pointer; + (void) data; + (void) item; + (void) window; + (void) extra_info; + + account = NULL; + channel = NULL; + + buffer__get_account_and_channel(buffer, &account, &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 buffer__nickcmp_cb(const void *pointer, void *data, + struct t_gui_buffer *buffer, + const char *nick1, + const char *nick2) +{ + struct t_account *account; + + (void) data; + + if (pointer) + account = (struct t_account *)pointer; + else + buffer__get_account_and_channel(buffer, &account, NULL); + + if (account) + { + return weechat_strcasecmp(nick1, nick2); + } + else + { + return weechat_strcasecmp(nick1, nick2); + } +} + +int buffer__close_cb(const void *pointer, void *data, + struct t_gui_buffer *buffer) +{ + struct t_weechat_plugin *buffer_plugin = NULL; + struct t_account *ptr_account = NULL; + struct t_channel *ptr_channel = NULL; + + buffer_plugin = weechat_buffer_get_pointer(buffer, "plugin"); + if (buffer_plugin == weechat_plugin) + buffer__get_account_and_channel(buffer, + &ptr_account, &ptr_channel); + + (void) pointer; + (void) data; + (void) buffer; + + if (ptr_account) + { + if (!ptr_account->disconnected) + { + //command_quit_account(ptr_account, NULL); + account__disconnect(ptr_account, 0); + } + + ptr_account->buffer = NULL; + } + + return WEECHAT_RC_OK; +} diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..b126d9d --- /dev/null +++ b/buffer.h @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef _WEECHAT_XMPP_BUFFER_H_ +#define _WEECHAT_XMPP_BUFFER_H_ + +void buffer__get_account_and_channel(struct t_gui_buffer *buffer, + struct t_account **account, + struct t_channel **channel); + +char *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 buffer__nickcmp_cb(const void *pointer, void *data, + struct t_gui_buffer *buffer, + const char *nick1, + const char *nick2); + +int buffer__close_cb(const void *pointer, void *data, + struct t_gui_buffer *buffer); + +#endif /*WEECHAT_XMPP_BUFFER_H*/ diff --git a/channel.c b/channel.c new file mode 100644 index 0000000..a2dd60c --- /dev/null +++ b/channel.c @@ -0,0 +1,643 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include + +#include "plugin.h" +#include "account.h" +#include "user.h" +#include "channel.h" +#include "input.h" +#include "buffer.h" + +struct t_channel *channel__search(struct t_account *account, + const char *id) +{ + struct t_channel *ptr_channel; + + if (!account || !id) + return NULL; + + for (ptr_channel = account->channels; ptr_channel; + ptr_channel = ptr_channel->next_channel) + { + if (weechat_strcasecmp(ptr_channel->id, id) == 0) + return ptr_channel; + } + + return NULL; +} + +struct t_gui_buffer *channel__search_buffer(struct t_account *account, + enum t_channel_type type, + const char *name) +{ + struct t_hdata *hdata_buffer; + struct t_gui_buffer *ptr_buffer; + const char *ptr_type, *ptr_account_name, *ptr_channel_name; + + hdata_buffer = weechat_hdata_get("buffer"); + ptr_buffer = weechat_hdata_get_list(hdata_buffer, "gui_buffers"); + + while (ptr_buffer) + { + if (weechat_buffer_get_pointer(ptr_buffer, "plugin") == weechat_plugin) + { + ptr_type = weechat_buffer_get_string(ptr_buffer, "localvar_type"); + ptr_account_name = weechat_buffer_get_string(ptr_buffer, + "localvar_server"); + ptr_channel_name = weechat_buffer_get_string(ptr_buffer, + "localvar_channel"); + if (ptr_type && ptr_type[0] + && ptr_account_name && ptr_account_name[0] + && ptr_channel_name && ptr_channel_name[0] + && ( (( (type == CHANNEL_TYPE_CHANNEL) + || (type == CHANNEL_TYPE_GROUP)) + && (strcmp(ptr_type, "channel") == 0)) + || (( (type == CHANNEL_TYPE_MPIM) + || (type == CHANNEL_TYPE_IM)) + && (strcmp(ptr_type, "private") == 0))) + && (strcmp(ptr_account_name, account->name) == 0) + && (weechat_strcasecmp(ptr_channel_name, name) == 0)) + { + return ptr_buffer; + } + } + ptr_buffer = weechat_hdata_move(hdata_buffer, ptr_buffer, 1); + } + + return NULL; +} + +struct t_gui_buffer *channel__create_buffer(struct t_account *account, + enum t_channel_type type, + const char *name) +{ + struct t_gui_buffer *ptr_buffer; + int buffer_created; + const char *short_name, *localvar_channel; + char buffer_name[256]; + + buffer_created = 0; + + snprintf(buffer_name, sizeof(buffer_name), + "%s.%s", account->name, name); + + ptr_buffer = channel__search_buffer(account, type, name); + if (ptr_buffer) + { + weechat_nicklist_remove_all(ptr_buffer); + } + else + { + ptr_buffer = weechat_buffer_new(buffer_name, + &input__data_cb, NULL, NULL, + &buffer__close_cb, NULL, NULL); + if (!ptr_buffer) + return NULL; + + buffer_created = 1; + } + + if (buffer_created) + { + if (!weechat_buffer_get_integer(ptr_buffer, "short_name_is_set")) + weechat_buffer_set(ptr_buffer, "short_name", name); + } + else + { + short_name = weechat_buffer_get_string (ptr_buffer, "short_name"); + localvar_channel = weechat_buffer_get_string (ptr_buffer, + "localvar_channel"); + + if (!short_name || + (localvar_channel && (strcmp(localvar_channel, short_name) == 0))) + { + weechat_buffer_set (ptr_buffer, "short_name", name); + } + } + + weechat_buffer_set(ptr_buffer, "name", buffer_name); + weechat_buffer_set(ptr_buffer, "localvar_set_type", + (type == CHANNEL_TYPE_IM || + type == CHANNEL_TYPE_MPIM) ? "private" : "channel"); + weechat_buffer_set(ptr_buffer, "localvar_set_nick", account->nickname); + weechat_buffer_set(ptr_buffer, "localvar_set_server", account->name); + weechat_buffer_set(ptr_buffer, "localvar_set_channel", name); + + if (buffer_created) + { + (void) weechat_hook_signal_send ("logger_backlog", + WEECHAT_HOOK_SIGNAL_POINTER, + ptr_buffer); + weechat_buffer_set(ptr_buffer, "input_get_unknown_commands", "1"); + if (type != CHANNEL_TYPE_IM) + { + weechat_buffer_set(ptr_buffer, "nicklist", "1"); + weechat_buffer_set(ptr_buffer, "nicklist_display_groups", "0"); + weechat_buffer_set_pointer(ptr_buffer, "nicklist_callback", + &buffer__nickcmp_cb); + weechat_buffer_set_pointer(ptr_buffer, "nicklist_callback_pointer", + account); + } + + weechat_buffer_set(ptr_buffer, "highlight_words_add", + account->nickname); + weechat_buffer_set(ptr_buffer, "highlight_tags_restrict", + "message"); + } + + return ptr_buffer; +} + +void channel__add_nicklist_groups(struct t_account *account, + struct t_channel *channel) +{ + struct t_gui_buffer *ptr_buffer; + char str_group[32]; + + if (channel && channel->type == CHANNEL_TYPE_MPIM) + return; + if (channel && channel->type == CHANNEL_TYPE_IM) + return; + + ptr_buffer = channel ? channel->buffer : account->buffer; + + snprintf(str_group, sizeof(str_group), "%03d|%s", + 000, "+"); + weechat_nicklist_add_group(ptr_buffer, NULL, str_group, + "weechat.color.nicklist_group", 1); + snprintf(str_group, sizeof(str_group), "%03d|%s", + 999, "..."); + weechat_nicklist_add_group(ptr_buffer, NULL, str_group, + "weechat.color.nicklist_group", 1); +} + +struct t_channel *channel__new(struct t_account *account, + enum t_channel_type type, + const char *id, const char *name) +{ + struct t_channel *new_channel, *ptr_channel; + struct t_gui_buffer *ptr_buffer; + struct t_hook *typing_timer; + char buffer_name[CHANNEL_NAME_MAX_LEN + 2]; + + if (!account || !id || !name || !name[0]) + return NULL; + + ptr_channel = channel__search(account, id); + if (ptr_channel) + { + return ptr_channel; + } + + buffer_name[0] = '#'; + strncpy(&buffer_name[1], name, CHANNEL_NAME_MAX_LEN + 1); + + ptr_buffer = channel__create_buffer(account, type, buffer_name); + if (!ptr_buffer) + return NULL; + + if ((new_channel = malloc(sizeof(*new_channel))) == NULL) + return NULL; + + typing_timer = weechat_hook_timer(1 * 1000, 0, 0, + &channel__typing_cb, + new_channel, NULL); + + new_channel->type = type; + new_channel->id = strdup(id); + new_channel->name = strdup(name); + new_channel->created = 0; + + new_channel->is_general = 0; + new_channel->name_normalized = NULL; + new_channel->is_shared = 0; + new_channel->is_org_shared = 0; + new_channel->is_member = 0; + + new_channel->topic.value = NULL; + new_channel->topic.creator = NULL; + new_channel->topic.last_set = 0; + new_channel->purpose.value = NULL; + new_channel->purpose.creator = NULL; + new_channel->purpose.last_set = 0; + new_channel->is_archived = 0; + + new_channel->creator = NULL; + new_channel->last_read = 0.0; + new_channel->unread_count = 0; + new_channel->unread_count_display = 0; + + new_channel->is_user_deleted = 0; + + new_channel->typing_hook_timer = typing_timer; + new_channel->members_speaking[0] = NULL; + new_channel->members_speaking[1] = NULL; + new_channel->typings = NULL; + new_channel->last_typing = NULL; + new_channel->members = NULL; + new_channel->last_member = NULL; + new_channel->buffer = ptr_buffer; + new_channel->buffer_as_string = NULL; + + new_channel->prev_channel = account->last_channel; + new_channel->next_channel = NULL; + if (account->last_channel) + (account->last_channel)->next_channel = new_channel; + else + account->channels = new_channel; + account->last_channel = new_channel; + + return new_channel; +} + +void channel__member_speaking_add_to_list(struct t_channel *channel, + const char *nick, + int highlight) +{ + int size, to_remove, i; + struct t_weelist_item *ptr_item; + + /* create list if it does not exist */ + if (!channel->members_speaking[highlight]) + channel->members_speaking[highlight] = weechat_list_new(); + + /* remove item if it was already in list */ + ptr_item = weechat_list_casesearch(channel->members_speaking[highlight], nick); + if (ptr_item) + weechat_list_remove(channel->members_speaking[highlight], ptr_item); + + /* add nick in list */ + weechat_list_add(channel->members_speaking[highlight], nick, + WEECHAT_LIST_POS_END, NULL); + + /* reduce list size if it's too big */ + size = weechat_list_size(channel->members_speaking[highlight]); + if (size > CHANNEL_MEMBERS_SPEAKING_LIMIT) + { + to_remove = size - CHANNEL_MEMBERS_SPEAKING_LIMIT; + for (i = 0; i < to_remove; i++) + { + weechat_list_remove( + channel->members_speaking[highlight], + weechat_list_get(channel->members_speaking[highlight], 0)); + } + } +} + +void channel__member_speaking_add(struct t_channel *channel, + const char *nick, int highlight) +{ + if (highlight < 0) + highlight = 0; + if (highlight > 1) + highlight = 1; + if (highlight) + channel__member_speaking_add_to_list(channel, nick, 1); + + channel__member_speaking_add_to_list(channel, nick, 0); +} + +void channel__member_speaking_rename(struct t_channel *channel, + const char *old_nick, + const char *new_nick) +{ + struct t_weelist_item *ptr_item; + int i; + + for (i = 0; i < 2; i++) + { + if (channel->members_speaking[i]) + { + ptr_item = weechat_list_search(channel->members_speaking[i], old_nick); + if (ptr_item) + weechat_list_set(ptr_item, new_nick); + } + } +} + +void channel__member_speaking_rename_if_present(struct t_account *account, + struct t_channel *channel, + const char *nick) +{ + struct t_weelist_item *ptr_item; + int i, j, list_size; + + (void) account; + + for (i = 0; i < 2; i++) + { + if (channel->members_speaking[i]) + { + list_size = weechat_list_size(channel->members_speaking[i]); + for (j = 0; j < list_size; j++) + { + ptr_item = weechat_list_get (channel->members_speaking[i], j); + if (ptr_item && (strcasecmp(weechat_list_string(ptr_item), nick) == 0)) + weechat_list_set(ptr_item, nick); + } + } + } +} + +void channel__typing_free(struct t_channel *channel, + struct t_channel_typing *typing) +{ + struct t_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 channel__typing_free_all(struct t_channel *channel) +{ + while (channel->typings) + channel__typing_free(channel, channel->typings); +} + +int channel__typing_cb(const void *pointer, + void *data, + int remaining_calls) +{ + struct t_channel_typing *ptr_typing, *next_typing; + struct t_channel *channel; + const char *localvar; + unsigned typecount; + time_t now; + + (void) data; + (void) remaining_calls; + + if (!pointer) + return WEECHAT_RC_ERROR; + + channel = (struct t_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) + { + channel__typing_free(channel, ptr_typing); + ptr_typing = next_typing; + if (ptr_typing) + next_typing = ptr_typing->next_typing; + } + + if (!ptr_typing) + break; + + typecount++; + } + + localvar = weechat_buffer_get_string(channel->buffer, "localvar_typing"); + if (!localvar || strncmp(localvar, typecount > 0 ? "1" : "0", 1) != 0) + weechat_buffer_set(channel->buffer, + "localvar_set_typing", + typecount > 0 ? "1" : "0"); + weechat_bar_item_update("typing"); + + return WEECHAT_RC_OK; +} + +struct t_channel_typing *channel__typing_search( + struct t_channel *channel, + const char *id) +{ + struct t_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 channel__add_typing(struct t_channel *channel, + struct t_user *user) +{ + struct t_channel_typing *new_typing; + + new_typing = 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); + + channel__typing_cb(channel, NULL, 0); +} + +void channel__member_free(struct t_channel *channel, + struct t_channel_member *member) +{ + struct t_channel_member *new_members; + + if (!channel || !member) + return; + + /* remove member from members list */ + if (channel->last_member == member) + channel->last_member = member->prev_member; + if (member->prev_member) + { + (member->prev_member)->next_member = member->next_member; + new_members = channel->members; + } + else + new_members = member->next_member; + + if (member->next_member) + (member->next_member)->prev_member = member->prev_member; + + /* free member data */ + if (member->id) + free(member->id); + + free(member); + + channel->members = new_members; +} + +void channel__member_free_all(struct t_channel *channel) +{ + while (channel->members) + channel__member_free(channel, channel->members); +} + +void channel__free(struct t_account *account, + struct t_channel *channel) +{ + struct t_channel *new_channels; + + if (!account || !channel) + return; + + /* remove channel from channels list */ + if (account->last_channel == channel) + account->last_channel = channel->prev_channel; + if (channel->prev_channel) + { + (channel->prev_channel)->next_channel = channel->next_channel; + new_channels = account->channels; + } + else + new_channels = channel->next_channel; + + 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 */ + channel__typing_free_all(channel); + channel__member_free_all(channel); + + /* free channel data */ + if (channel->id) + free(channel->id); + if (channel->name) + 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->creator) + free(channel->creator); + if (channel->members_speaking[0]) + weechat_list_free(channel->members_speaking[0]); + if (channel->members_speaking[1]) + weechat_list_free(channel->members_speaking[1]); + if (channel->buffer_as_string) + free(channel->buffer_as_string); + + free(channel); + + account->channels = new_channels; +} + +void channel__free_all(struct t_account *account) +{ + while (account->channels) + channel__free(account, account->channels); +} + +void channel__update_topic(struct t_channel *channel, + const char* topic, + const char* creator, + int last_set) +{ + if (channel->topic.value) + free(channel->topic.value); + if (channel->topic.creator) + free(channel->topic.creator); + channel->topic.value = (topic) ? strdup(topic) : NULL; + channel->topic.creator = (creator) ? strdup(creator) : NULL; + channel->topic.last_set = last_set; + + if (channel->topic.value) + weechat_buffer_set(channel->buffer, "title", topic); + else + weechat_buffer_set(channel->buffer, "title", ""); +} + +void channel__update_purpose(struct t_channel *channel, + const char* purpose, + const char* creator, + int last_set) +{ + if (channel->purpose.value) + free(channel->purpose.value); + if (channel->purpose.creator) + free(channel->purpose.creator); + channel->purpose.value = (purpose) ? strdup(purpose) : NULL; + channel->purpose.creator = (creator) ? strdup(creator) : NULL; + channel->purpose.last_set = last_set; +} + +struct t_channel_member *channel__add_member( + struct t_account *account, + struct t_channel *channel, + const char *id) +{ + struct t_channel_member *member; + struct t_user *user; + + member = malloc(sizeof(struct t_channel_member)); + member->id = strdup(id); + + member->prev_member = channel->last_member; + member->next_member = NULL; + if (channel->last_member) + (channel->last_member)->next_member = member; + else + channel->members = member; + channel->last_member = member; + + user = user__search(account, id); + if (user) + user__nicklist_add(account, channel, user); + + return member; +} diff --git a/channel.h b/channel.h new file mode 100644 index 0000000..5b79c79 --- /dev/null +++ b/channel.h @@ -0,0 +1,147 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef _WEECHAT_XMPP_CHANNEL_H_ +#define _WEECHAT_XMPP_CHANNEL_H_ + +#define CHANNEL_MEMBERS_SPEAKING_LIMIT 128 + +#define CHANNEL_NAME_MAX_LEN 22 + +enum t_channel_type +{ + CHANNEL_TYPE_CHANNEL, + CHANNEL_TYPE_GROUP, + CHANNEL_TYPE_MPIM, + CHANNEL_TYPE_IM, +}; + +struct t_channel_typing +{ + char *id; + char *name; + time_t ts; + + struct t_channel_typing *prev_typing; + struct t_channel_typing *next_typing; +}; + +struct t_channel_member +{ + char *id; + + struct t_channel_member *prev_member; + struct t_channel_member *next_member; +}; + +struct t_channel_topic +{ + char *value; + char *creator; + time_t last_set; +}; + +struct t_channel_purpose +{ + char *value; + char *creator; + time_t last_set; +}; + +struct t_channel +{ + enum t_channel_type type; + char *id; + char *name; + time_t created; + + /* channel */ + int is_general; + char *name_normalized; + int is_shared; + int is_org_shared; + int is_member; + + /* group */ + struct t_channel_topic topic; + struct t_channel_purpose purpose; + int is_archived; + + /* mpim */ + char *creator; + double last_read; + int unread_count; + int unread_count_display; + + /* im */ + int is_user_deleted; + + struct t_hook *typing_hook_timer; + struct t_weelist *members_speaking[2]; + struct t_channel_typing *typings; + struct t_channel_typing *last_typing; + struct t_channel_member *members; + struct t_channel_member *last_member; + struct t_gui_buffer *buffer; + char *buffer_as_string; + + struct t_channel *prev_channel; + struct t_channel *next_channel; +}; + +struct t_channel *channel__search(struct t_account *account, + const char *id); + +void channel__add_nicklist_groups(struct t_account *account, + struct t_channel *channel); + +struct t_channel *channel__new(struct t_account *account, + enum t_channel_type type, + const char *id, const char *name); + +void channel__member_speaking_add(struct t_channel *channel, + const char *nick, int highlight); + +void channel__member_speaking_rename(struct t_channel *channel, + const char *old_nick, + const char *new_nick); + +void channel__member_speaking_rename_if_present(struct t_account *account, + struct t_channel *channel, + const char *nick); + +void channel__typing_free(struct t_channel *channel, + struct t_channel_typing *typing); + +void channel__typing_free_all(struct t_channel *channel); + +int channel__typing_cb(const void *pointer, + void *data, + int remaining_calls); + +struct t_channel_typing *channel__typing_search( + struct t_channel *channel, + const char *id); + +void channel__add_typing(struct t_channel *channel, + struct t_user *user); + +void channel__free_all(struct t_account *account); + +void channel__update_topic(struct t_channel *channel, + const char* title, + const char* creator, + int last_set); + +void channel__update_purpose(struct t_channel *channel, + const char* purpose, + const char* creator, + int last_set); + +struct t_channel_member *channel__add_member( + struct t_account *account, + struct t_channel *channel, + const char *id); + +#endif /*WEECHAT_XMPP_CHANNEL_H*/ diff --git a/command.c b/command.c index ff62b1a..fe39db0 100644 --- a/command.c +++ b/command.c @@ -8,39 +8,35 @@ #include #include "plugin.h" -//#include "xmpp-oauth.h" -//#include "xmpp-teaminfo.h" -//#include "xmpp-workspace.h" -//#include "xmpp-channel.h" -//#include "xmpp-buffer.h" -//#include "xmpp-message.h" +//#include "oauth.h" +//#include "teaminfo.h" +#include "account.h" +#include "channel.h" +#include "buffer.h" +#include "message.h" #include "command.h" -//#include "request/xmpp-request-chat-memessage.h" -/* -void xmpp_command_display_workspace(xmpp_conn_t *workspace) +void command__display_account(struct t_account *account) { int num_channels, num_pv; - if (workspace->is_connected) + if (account->is_connected) { - num_channels = 0;//xmpp_workspace_get_channel_count(workspace); - num_pv = 0;//xmpp_workspace_get_pv_count(workspace); + num_channels = 0;//xmpp_account_get_channel_count(account); + num_pv = 0;//xmpp_account_get_pv_count(account); weechat_printf( NULL, - " %s %s%s%s.xmpp.com %s(%s%s%s) [%s%s%s]%s, %d %s, %d pv", - (workspace->is_connected) ? "*" : " ", + " %s %s%s%s %s(%s%s%s) [%s%s%s]%s, %d %s, %d pv", + (account->is_connected) ? "*" : " ", weechat_color("chat_server"), - workspace->domain, + account->name, weechat_color("reset"), weechat_color("chat_delimiters"), weechat_color("chat_server"), - (workspace->name) ? - workspace->name : "???", + (account->jid) ? account->jid : "???", weechat_color("chat_delimiters"), weechat_color("reset"), - (workspace->is_connected) ? - _("connected") : _("not connected"), + (account->is_connected) ? _("connected") : _("not connected"), weechat_color("chat_delimiters"), weechat_color("reset"), num_channels, @@ -51,177 +47,166 @@ void xmpp_command_display_workspace(xmpp_conn_t *workspace) { weechat_printf( NULL, - " %s%s%s.xmpp.com %s(%s%s%s)%s", + " %s%s%s %s(%s%s%s)%s", weechat_color("chat_server"), - workspace->domain, + account->name, weechat_color("reset"), weechat_color("chat_delimiters"), weechat_color("chat_server"), - (workspace->name) ? - workspace->name : "???", + (account->jid) ? account->jid : "???", weechat_color("chat_delimiters"), weechat_color("reset")); } } -void xmpp_command_workspace_list(int argc, char **argv) +void command__account_list(int argc, char **argv) { - int i, one_workspace_found; - xmpp_conn_t *ptr_workspace2; - char *workspace_name = NULL; + int i, one_account_found; + struct t_account *ptr_account2; + char *account_name = NULL; for (i = 2; i < argc; i++) { - if (!workspace_name) - workspace_name = argv[i]; + if (!account_name) + account_name = argv[i]; } - if (!workspace_name) + if (!account_name) { - if (xmpp_workspaces) + if (accounts) { weechat_printf(NULL, ""); - weechat_printf(NULL, _("All workspaces:")); - for (ptr_workspace2 = xmpp_workspaces; ptr_workspace2; - ptr_workspace2 = ptr_workspace2->next_workspace) + weechat_printf(NULL, _("All accounts:")); + for (ptr_account2 = accounts; ptr_account2; + ptr_account2 = ptr_account2->next_account) { - xmpp_command_display_workspace(ptr_workspace2); + command__display_account(ptr_account2); } } else - weechat_printf(NULL, _("No workspace")); + weechat_printf(NULL, _("No account")); } else { - one_workspace_found = 0; - for (ptr_workspace2 = xmpp_workspaces; ptr_workspace2; - ptr_workspace2 = ptr_workspace2->next_workspace) + one_account_found = 0; + for (ptr_account2 = accounts; ptr_account2; + ptr_account2 = ptr_account2->next_account) { - if (weechat_strcasestr(ptr_workspace2->name, workspace_name)) + if (weechat_strcasestr(ptr_account2->name, account_name)) { - if (!one_workspace_found) + if (!one_account_found) { weechat_printf(NULL, ""); weechat_printf(NULL, _("Servers with \"%s\":"), - workspace_name); + account_name); } - one_workspace_found = 1; - xmpp_command_display_workspace(ptr_workspace2); + one_account_found = 1; + command__display_account(ptr_account2); } } - if (!one_workspace_found) + if (!one_account_found) weechat_printf(NULL, - _("No workspace found with \"%s\""), - workspace_name); + _("No account found with \"%s\""), + account_name); } } -void xmpp_command_add_workspace(struct t_xmpp_teaminfo *xmpp_teaminfo) +void command__add_account(const char *name, const char *jid, const char *password) { - xmpp_conn_t *workspace; + struct t_account *account; - workspace = xmpp_workspace_casesearch(xmpp_teaminfo->domain); - if (workspace) + account = account__casesearch(name); + if (account) { weechat_printf( NULL, - _("%s%s: workspace \"%s\" already exists, can't add it!"), - weechat_prefix("error"), XMPP_PLUGIN_NAME, - xmpp_teaminfo->domain); + _("%s%s: account \"%s\" already exists, can't add it!"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, + name); return; } - workspace = xmpp_workspace_alloc(xmpp_teaminfo->domain); - if (!workspace) + account = account__alloc(name); + if (!account) { weechat_printf( NULL, - _("%s%s: unable to add workspace"), - weechat_prefix("error"), XMPP_PLUGIN_NAME); + _("%s%s: unable to add account"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); return; } - workspace->id = strdup(xmpp_teaminfo->id); - workspace->name = strdup(xmpp_teaminfo->name); - weechat_config_option_set(workspace->options[XMPP_WORKSPACE_OPTION_TOKEN], - xmpp_teaminfo->token, 1); + account->name = strdup(name); + if (jid) + account->jid = strdup(jid); + if (password) + account->password = strdup(password); + weechat_config_option_set(account->options[ACCOUNT_OPTION_JID], + account->jid, 1); + weechat_config_option_set(account->options[ACCOUNT_OPTION_PASSWORD], + account->password, 1); + weechat_config_option_set(account->options[ACCOUNT_OPTION_NICKNAME], + account->jid ? xmpp_jid_node(account->context, + account->jid) + : NULL, 1); weechat_printf ( NULL, - _("%s: workspace %s%s%s.xmpp.com %s(%s%s%s)%s added"), - XMPP_PLUGIN_NAME, + _("%s: account %s%s%s %s(%s%s%s)%s added"), + WEECHAT_XMPP_PLUGIN_NAME, weechat_color("chat_server"), - workspace->domain, + account->name, weechat_color("reset"), weechat_color("chat_delimiters"), weechat_color("chat_server"), - workspace->name, + account->jid ? account->jid : "???", weechat_color("chat_delimiters"), weechat_color("reset")); - - free_teaminfo(xmpp_teaminfo); -} - -void xmpp_command_fetch_workspace(char *token) -{ - xmpp_teaminfo_fetch(token, &xmpp_command_add_workspace); - - free(token); } -void xmpp_command_workspace_register(int argc, char **argv) +void command__account_add(int argc, char **argv) { - char *code; + char *name, *jid = NULL, *password = NULL; - if (argc > 2) - { - code = argv[2]; - - if (strncmp("xoxp", code, 4) == 0) - { - xmpp_command_fetch_workspace(strdup(code)); - } - else - { - xmpp_oauth_request_token(code, &xmpp_command_fetch_workspace); - } - } - else + switch (argc) { - weechat_printf(NULL, - _("\n#### Retrieving a Xmpp token via OAUTH ####\n" - "1) Paste this into a browser: https://xmpp.com/oauth/authorize?client_id=%s&scope=client\n" - "2) Select the team you wish to access from weechat in your browser.\n" - "3) Click \"Authorize\" in the browser **IMPORTANT: the redirect will fail, this is expected**\n" - "4) Copy the \"code\" portion of the URL to your clipboard\n" - "5) Return to weechat and run `/xmpp register [code]`\n"), - XMPP_CLIENT_ID); + case 5: + password = argv[4]; + case 4: + jid = argv[3]; + case 3: + name = argv[2]; + command__add_account(name, jid, password); + break; + default: + weechat_printf(NULL, _("account add: wrong number of arguments")); + break; } } -int xmpp_command_connect_workspace(xmpp_conn_t *workspace) +int command__connect_account(struct t_account *account) { - if (!workspace) + if (!account) return 0; - if (workspace->is_connected) + if (account->is_connected) { weechat_printf( NULL, - _("%s%s: already connected to workspace \"%s\"!"), - weechat_prefix("error"), XMPP_PLUGIN_NAME, - workspace->domain); + _("%s%s: already connected to account \"%s\"!"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, + account->name); } - xmpp_workspace_connect(workspace); + account__connect(account); return 1; } -int xmpp_command_workspace_connect(int argc, char **argv) +int command__account_connect(int argc, char **argv) { int i, nb_connect, connect_ok; - xmpp_conn_t *ptr_workspace; + struct t_account *ptr_account; (void) argc; (void) argv; @@ -232,10 +217,10 @@ int xmpp_command_workspace_connect(int argc, char **argv) for (i = 2; i < argc; i++) { nb_connect++; - ptr_workspace = xmpp_workspace_search(argv[i]); - if (ptr_workspace) + ptr_account = account__search(argv[i]); + if (ptr_account) { - if (!xmpp_command_connect_workspace(ptr_workspace)) + if (!command__connect_account(ptr_account)) { connect_ok = 0; } @@ -244,9 +229,9 @@ int xmpp_command_workspace_connect(int argc, char **argv) { weechat_printf( NULL, - _("%s%s: workspace not found \"%s\" " - "(register first with: /xmpp register)"), - weechat_prefix("error"), XMPP_PLUGIN_NAME, + _("%s%s: account not found \"%s\" " + "(add one first with: /account add)"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, argv[i]); } } @@ -254,10 +239,10 @@ int xmpp_command_workspace_connect(int argc, char **argv) return (connect_ok) ? WEECHAT_RC_OK : WEECHAT_RC_ERROR; } -void xmpp_command_workspace_delete(int argc, char **argv) +void command__account_delete(int argc, char **argv) { - xmpp_conn_t *workspace; - char *workspace_domain; + struct t_account *account; + char *account_name; if (argc < 3) { @@ -270,88 +255,87 @@ void xmpp_command_workspace_delete(int argc, char **argv) return; } - workspace = xmpp_workspace_search(argv[2]); - if (!workspace) + account = account__search(argv[2]); + if (!account) { weechat_printf( NULL, - _("%s%s: workspace \"%s\" not found for \"%s\" command"), - weechat_prefix("error"), XMPP_PLUGIN_NAME, + _("%s%s: account \"%s\" not found for \"%s\" command"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, argv[2], "xmpp delete"); return; } - if (workspace->is_connected) + if (account->is_connected) { weechat_printf( NULL, - _("%s%s: you cannot delete workspace \"%s\" because you" + _("%s%s: you cannot delete account \"%s\" because you" "are connected. Try \"/xmpp disconnect %s\" first."), - weechat_prefix("error"), XMPP_PLUGIN_NAME, + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, argv[2], argv[2]); return; } - workspace_domain = strdup(workspace->domain); - xmpp_workspace_free(workspace); + account_name = strdup(account->name); + account__free(account); weechat_printf ( NULL, - _("%s: workspace %s%s%s has been deleted"), - XMPP_PLUGIN_NAME, + _("%s: account %s%s%s has been deleted"), + WEECHAT_XMPP_PLUGIN_NAME, weechat_color("chat_server"), - (workspace_domain) ? workspace_domain : "???", + (account_name) ? account_name : "???", weechat_color("reset")); - if (workspace_domain) - free(workspace_domain); + if (account_name) + free(account_name); } -*/ -int xmpp_command_xmpp(const void *pointer, void *data, - struct t_gui_buffer *buffer, int argc, - char **argv, char **argv_eol) +int command__account(const void *pointer, void *data, + struct t_gui_buffer *buffer, int argc, + char **argv, char **argv_eol) { (void) pointer; (void) data; (void) buffer; - //if (argc <= 1 || weechat_strcasecmp(argv[1], "list") == 0) - //{ - // xmpp_command_workspace_list(argc, argv); - // return WEECHAT_RC_OK; - //} - - //if (argc > 1) - //{ - // if (weechat_strcasecmp(argv[1], "register") == 0) - // { - // xmpp_command_workspace_register(argc, argv); - // return WEECHAT_RC_OK; - // } - - // if (weechat_strcasecmp(argv[1], "connect") == 0) - // { - // xmpp_command_workspace_connect(argc, argv); - // return WEECHAT_RC_OK; - // } - - // if (weechat_strcasecmp(argv[1], "delete") == 0) - // { - // xmpp_command_workspace_delete(argc, argv); - // return WEECHAT_RC_OK; - // } - - // WEECHAT_COMMAND_ERROR; - //} + if (argc <= 1 || weechat_strcasecmp(argv[1], "list") == 0) + { + command__account_list(argc, argv); + return WEECHAT_RC_OK; + } + + if (argc > 1) + { + if (weechat_strcasecmp(argv[1], "add") == 0) + { + command__account_add(argc, argv); + return WEECHAT_RC_OK; + } + + if (weechat_strcasecmp(argv[1], "connect") == 0) + { + command__account_connect(argc, argv); + return WEECHAT_RC_OK; + } + + if (weechat_strcasecmp(argv[1], "delete") == 0) + { + command__account_delete(argc, argv); + return WEECHAT_RC_OK; + } + + WEECHAT_COMMAND_ERROR; + } return WEECHAT_RC_OK; } -int xmpp_command_me(const void *pointer, void *data, - struct t_gui_buffer *buffer, int argc, - char **argv, char **argv_eol) +int command__me(const void *pointer, void *data, + struct t_gui_buffer *buffer, int argc, + char **argv, char **argv_eol) { - xmpp_conn_t *ptr_workspace = NULL; - //struct t_xmpp_channel *ptr_channel = NULL; + struct t_account *ptr_account = NULL; + struct t_xmpp_channel *ptr_channel = NULL; //struct t_xmpp_request *request; char *text; @@ -360,76 +344,72 @@ int xmpp_command_me(const void *pointer, void *data, (void) buffer; (void) argv; - //xmpp_buffer_get_workspace_and_channel(buffer, &ptr_workspace, &ptr_channel); - - //if (!ptr_workspace) - // return WEECHAT_RC_ERROR; - - //if (!ptr_channel) - //{ - // weechat_printf ( - // ptr_workspace->buffer, - // _("%s%s: \"%s\" command can not be executed on a workspace buffer"), - // weechat_prefix("error"), XMPP_PLUGIN_NAME, "me"); - // return WEECHAT_RC_OK; - //} - - //if (!ptr_workspace->is_connected) - //{ - // weechat_printf(buffer, - // _("%s%s: you are not connected to server"), - // weechat_prefix("error"), XMPP_PLUGIN_NAME); - // return WEECHAT_RC_OK; - //} - - //if (argc > 1) - //{ - // text = malloc(XMPP_MESSAGE_MAX_LENGTH); - // if (!text) - // { - // weechat_printf(buffer, - // _("%s%s: error allocating string"), - // weechat_prefix("error"), XMPP_PLUGIN_NAME); - // return WEECHAT_RC_ERROR; - // } - // lws_urlencode(text, argv_eol[1], XMPP_MESSAGE_MAX_LENGTH); - - // request = xmpp_request_chat_memessage(ptr_workspace, - // weechat_config_string( - // ptr_workspace->options[XMPP_WORKSPACE_OPTION_TOKEN]), - // ptr_channel->id, text); - // if (request) - // xmpp_workspace_register_request(ptr_workspace, request); - - // free(text); - //} + buffer__get_account_and_channel(buffer, &ptr_account, &ptr_channel); + + if (!ptr_account) + return WEECHAT_RC_ERROR; + + if (!ptr_channel) + { + weechat_printf ( + ptr_account->buffer, + _("%s%s: \"%s\" command can not be executed on a account buffer"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, "me"); + return WEECHAT_RC_OK; + } + + if (!ptr_account->is_connected) + { + weechat_printf(buffer, + _("%s%s: you are not connected to server"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + return WEECHAT_RC_OK; + } + + if (argc > 1) + { + text = argv_eol[1]; + + //request = xmpp_request_chat_memessage(ptr_account, + // weechat_config_string( + // ptr_account->options[XMPP_ACCOUNT_OPTION_TOKEN]), + // ptr_channel->id, text); + //if (request) + // xmpp_account_register_request(ptr_account, request); + } return WEECHAT_RC_OK; } -void xmpp_command_init() +void command__init() { - weechat_hook_command( - "xmpp", - N_("xmpp control"), + struct t_hook *hook; + + hook = weechat_hook_command( + "account", + N_("handle xmpp accounts"), N_("list" - " || register [token]" - " || connect " - " || delete "), - N_(" list: list workspaces\n" - "register: add a xmpp workspace\n" - " connect: connect to a xmpp workspace\n" - " delete: delete a xmpp workspace\n"), + " || add " + " || connect " + " || delete "), + N_(" list: list accounts\n" + " add: add a xmpp account\n" + "connect: connect to a xmpp account\n" + " delete: delete a xmpp account\n"), "list" - " || register %(xmpp_token)" - " || connect %(xmpp_workspace)" - " || delete %(xmpp_workspace)", - &xmpp_command_xmpp, NULL, NULL); - - weechat_hook_command( + " || add %(xmpp_account)" + " || connect %(xmpp_account)" + " || delete %(xmpp_account)", + &command__account, NULL, NULL); + if (!hook) + weechat_printf(NULL, "Failed to setup command /account"); + + hook = weechat_hook_command( "me", N_("send a xmpp action to the current channel"), N_(""), N_("message: message to send"), - NULL, &xmpp_command_me, NULL, NULL); + NULL, &command__me, NULL, NULL); + if (!hook) + weechat_printf(NULL, "Failed to setup command /me"); } diff --git a/command.h b/command.h index 1e79a41..f8af048 100644 --- a/command.h +++ b/command.h @@ -5,6 +5,6 @@ #ifndef _WEECHAT_XMPP_COMMAND_H_ #define _WEECHAT_XMPP_COMMAND_H_ -extern void xmpp_command_init(); +void command__init(); #endif /*WEECHAT_XMPP_COMMAND_H*/ diff --git a/config.c b/config.c index cc5789b..dcc1c2c 100644 --- a/config.c +++ b/config.c @@ -8,41 +8,40 @@ #include #include "plugin.h" +#include "account.h" #include "config.h" -struct t_config_file *xmpp_config_file; +struct t_config_file *config_file; -//struct t_config_section *xmpp_config_section_workspace_default; -//struct t_config_section *xmpp_config_section_workspace; +struct t_config_section *config_section_account_default; +struct t_config_section *config_section_account; -struct t_config_option *xmpp_config_server_jid; -struct t_config_option *xmpp_config_server_password; -struct t_config_option *xmpp_config_look_nick_completion_smart; +struct t_config_option *config_look_nick_completion_smart; -/* -struct t_config_option *xmpp_config_workspace_default[XMPP_WORKSPACE_NUM_OPTIONS]; +struct t_config_option *config_account_default[ACCOUNT_NUM_OPTIONS]; -int xmpp_config_workspace_check_value_cb(const void *pointer, void *data, - struct t_config_option *option, - const char *value) +int config__account_check_value_cb(const void *pointer, void *data, + struct t_config_option *option, + const char *value) { (void) pointer; (void) data; (void) option; (void) value; - return 1; + + return 1; } -void xmpp_config_workspace_change_cb(const void *pointer, void *data, - struct t_config_option *option) +void config__account_change_cb(const void *pointer, void *data, + struct t_config_option *option) { (void) pointer; (void) data; (void) option; } -void xmpp_config_workspace_default_change_cb(const void *pointer, void *data, - struct t_config_option *option) +void config__account_default_change_cb(const void *pointer, void *data, + struct t_config_option *option) { (void) pointer; (void) data; @@ -50,36 +49,84 @@ void xmpp_config_workspace_default_change_cb(const void *pointer, void *data, } struct t_config_option * -xmpp_config_workspace_new_option (struct t_config_file *config_file, - struct t_config_section *section, - int index_option, - const char *option_name, - const char *default_value, - const char *value, - int null_value_allowed, - int (*callback_check_value)(const void *pointer, - void *data, - struct t_config_option *option, - const char *value), - const void *callback_check_value_pointer, - void *callback_check_value_data, - void (*callback_change)(const void *pointer, - void *data, - struct t_config_option *option), - const void *callback_change_pointer, - void *callback_change_data) +config__account_new_option (struct t_config_file *config_file, + struct t_config_section *section, + int index_option, + const char *option_name, + const char *default_value, + const char *value, + int null_value_allowed, + int (*callback_check_value)(const void *pointer, + void *data, + struct t_config_option *option, + const char *value), + const void *callback_check_value_pointer, + void *callback_check_value_data, + void (*callback_change)(const void *pointer, + void *data, + struct t_config_option *option), + const void *callback_change_pointer, + void *callback_change_data) { - struct t_config_option *new_option; + struct t_config_option *new_option; new_option = NULL; switch (index_option) { - case XMPP_WORKSPACE_OPTION_TOKEN: + case ACCOUNT_OPTION_JID: + new_option = weechat_config_new_option ( + config_file, section, + option_name, "string", + N_("XMPP Account JID"), + NULL, 0, 0, + default_value, value, + null_value_allowed, + callback_check_value, + callback_check_value_pointer, + callback_check_value_data, + callback_change, + callback_change_pointer, + callback_change_data, + NULL, NULL, NULL); + break; + case ACCOUNT_OPTION_PASSWORD: + new_option = weechat_config_new_option ( + config_file, section, + option_name, "string", + N_("XMPP Account Password"), + NULL, 0, 0, + default_value, value, + null_value_allowed, + callback_check_value, + callback_check_value_pointer, + callback_check_value_data, + callback_change, + callback_change_pointer, + callback_change_data, + NULL, NULL, NULL); + break; + case ACCOUNT_OPTION_TLS: + new_option = weechat_config_new_option ( + config_file, section, + option_name, "integer", + N_("XMPP Server TLS Policy"), + "disable|normal|trust", 0, 0, + default_value, value, + null_value_allowed, + callback_check_value, + callback_check_value_pointer, + callback_check_value_data, + callback_change, + callback_change_pointer, + callback_change_data, + NULL, NULL, NULL); + break; + case ACCOUNT_OPTION_NICKNAME: new_option = weechat_config_new_option ( config_file, section, option_name, "string", - N_("xmpp api token"), + N_("XMPP Server JID"), NULL, 0, 0, default_value, value, null_value_allowed, @@ -91,46 +138,46 @@ xmpp_config_workspace_new_option (struct t_config_file *config_file, callback_change_data, NULL, NULL, NULL); break; - case XMPP_WORKSPACE_NUM_OPTIONS: + case ACCOUNT_NUM_OPTIONS: break; } return new_option; } -void xmpp_config_workspace_create_default_options(struct t_config_section *section) +void config__account_create_default_options(struct t_config_section *section) { int i; - for (i = 0; i < XMPP_WORKSPACE_NUM_OPTIONS; i++) - { - xmpp_config_workspace_default[i] = xmpp_config_workspace_new_option( - xmpp_config_file, - section, - i, - xmpp_workspace_options[i][0], - xmpp_workspace_options[i][1], - xmpp_workspace_options[i][1], - 0, - &xmpp_config_workspace_check_value_cb, - xmpp_workspace_options[i][0], - NULL, - &xmpp_config_workspace_default_change_cb, - xmpp_workspace_options[i][0], - NULL); - } + for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++) + { + config_account_default[i] = config__account_new_option( + config_file, + section, + i, + account_options[i][0], + account_options[i][1], + account_options[i][1], + 0, + &config__account_check_value_cb, + account_options[i][0], + NULL, + &config__account_default_change_cb, + account_options[i][0], + NULL); + } } -int xmpp_config_workspace_read_cb (const void *pointer, void *data, - struct t_config_file *config_file, - struct t_config_section *section, - const char *option_name, const char *value) +int config__account_read_cb (const void *pointer, void *data, + struct t_config_file *config_file, + struct t_config_section *section, + const char *option_name, const char *value) { - struct t_xmpp_workspace *ptr_workspace; + struct t_account *ptr_account; int index_option, rc, i; - char *pos_option, *workspace_domain; + char *pos_option, *account_name; (void) pointer; (void) data; @@ -144,42 +191,42 @@ int xmpp_config_workspace_read_cb (const void *pointer, void *data, pos_option = strrchr(option_name, '.'); if (pos_option) { - workspace_domain = weechat_strndup(option_name, - pos_option - option_name); + account_name = weechat_strndup(option_name, + pos_option - option_name); pos_option++; - if (workspace_domain) + if (account_name) { - index_option = xmpp_workspace_search_option(pos_option); + index_option = account__search_option(pos_option); if (index_option >= 0) { - ptr_workspace = xmpp_workspace_search(workspace_domain); - if (!ptr_workspace) - ptr_workspace = xmpp_workspace_alloc(workspace_domain); - if (ptr_workspace) + ptr_account = account__search(account_name); + if (!ptr_account) + ptr_account = account__alloc(account_name); + if (ptr_account) { - if (ptr_workspace->reloading_from_config - && !ptr_workspace->reloaded_from_config) + if (!ptr_account->reloading_from_config++) { - for (i = 0; i < XMPP_WORKSPACE_NUM_OPTIONS; i++) + for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++) { weechat_config_option_set( - ptr_workspace->options[i], NULL, 1); + ptr_account->options[i], NULL, 1); } - ptr_workspace->reloaded_from_config = 1; } + ptr_account->reloading_from_config %= + ACCOUNT_NUM_OPTIONS; rc = weechat_config_option_set( - ptr_workspace->options[index_option], value, 1); + ptr_account->options[index_option], value, 1); } else { weechat_printf( NULL, - _("%s%s: error adding workspace \"%s\""), - weechat_prefix("error"), XMPP_PLUGIN_NAME, - workspace_domain); + _("%s%s: error adding account \"%s\""), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, + account_name); } } - free(workspace_domain); + free(account_name); } } } @@ -188,18 +235,18 @@ int xmpp_config_workspace_read_cb (const void *pointer, void *data, { weechat_printf( NULL, - _("%s%s: error creating workspace option \"%s\""), - weechat_prefix("error"), XMPP_PLUGIN_NAME, option_name); + _("%s%s: error creating account option \"%s\""), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, option_name); } return rc; } -int xmpp_config_workspace_write_cb (const void *pointer, void *data, - struct t_config_file *config_file, - const char *section_name) +int config__account_write_cb (const void *pointer, void *data, + struct t_config_file *config_file, + const char *section_name) { - struct t_xmpp_workspace *ptr_workspace; + struct t_account *ptr_account; int i; (void) pointer; @@ -208,88 +255,61 @@ int xmpp_config_workspace_write_cb (const void *pointer, void *data, if (!weechat_config_write_line(config_file, section_name, NULL)) return WEECHAT_CONFIG_WRITE_ERROR; - for (ptr_workspace = xmpp_workspaces; ptr_workspace; - ptr_workspace = ptr_workspace->next_workspace) + for (ptr_account = accounts; ptr_account; + ptr_account = ptr_account->next_account) { - for (i = 0; i < XMPP_WORKSPACE_NUM_OPTIONS; i++) + for (i = 0; i < ACCOUNT_NUM_OPTIONS; i++) { if (!weechat_config_write_option(config_file, - ptr_workspace->options[i])) + ptr_account->options[i])) return WEECHAT_CONFIG_WRITE_ERROR; } } return WEECHAT_CONFIG_WRITE_OK; } -*/ -int xmpp_config_reload (const void *pointer, void *data, - struct t_config_file *config_file) +int config__reload (const void *pointer, void *data, + struct t_config_file *config_file) { (void) pointer; (void) data; - //weechat_config_section_free_options(xmpp_config_section_workspace_default); - //weechat_config_section_free_options(xmpp_config_section_workspace); - //xmpp_workspace_free_all(); + weechat_config_section_free_options(config_section_account_default); + weechat_config_section_free_options(config_section_account); + account__free_all(); return weechat_config_reload(config_file); } -int xmpp_config_init() +int config__init() { - struct t_config_section *ptr_section_server; - struct t_config_section *ptr_section_look; + struct t_config_section *ptr_section; - xmpp_config_file = weechat_config_new(WEECHAT_XMPP_CONFIG_NAME, - &xmpp_config_reload, NULL, NULL); + config_file = weechat_config_new(WEECHAT_XMPP_CONFIG_NAME, + &config__reload, NULL, NULL); - if(!xmpp_config_file) + if(!config_file) return 0; - ptr_section_server = weechat_config_new_section( - xmpp_config_file, "server", - 0, 0, - NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, NULL, NULL); - - ptr_section_look = weechat_config_new_section( - xmpp_config_file, "look", - 0, 0, - NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, NULL, NULL, - NULL, NULL, NULL); - - if (!ptr_section_server - || !ptr_section_server - || !ptr_section_look) + ptr_section = weechat_config_new_section( + config_file, "look", + 0, 0, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + + if (!ptr_section) { - weechat_config_free(xmpp_config_file); - xmpp_config_file = NULL; + weechat_config_free(config_file); + config_file = NULL; return 0; } - xmpp_config_server_jid = weechat_config_new_option ( - xmpp_config_file, ptr_section_server, - "jid", "string", - N_("XMPP Server JID"), - NULL, 0, 0, "", "", 0, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - - xmpp_config_server_password = weechat_config_new_option ( - xmpp_config_file, ptr_section_server, - "password", "string", - N_("XMPP Server Password"), - NULL, 0, 0, "", "", 0, - NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - - xmpp_config_look_nick_completion_smart = weechat_config_new_option ( - xmpp_config_file, ptr_section_look, + config_look_nick_completion_smart = weechat_config_new_option ( + config_file, ptr_section, "nick_completion_smart", "integer", N_("smart completion for nicks (completes first with last speakers): " "speakers = all speakers (including highlights), " @@ -297,61 +317,61 @@ int xmpp_config_init() "off|speakers|speakers_highlights", 0, 0, "speakers", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - //ptr_section = weechat_config_new_section( - // xmpp_config_file, "workspace_default", - // 0, 0, - // NULL, NULL, NULL, - // NULL, NULL, NULL, - // NULL, NULL, NULL, - // NULL, NULL, NULL, - // NULL, NULL, NULL); - - //if (!ptr_section) - //{ - // weechat_config_free(xmpp_config_file); - // xmpp_config_file = NULL; - // return 0; - //} - - //xmpp_config_section_workspace_default = ptr_section; - - //xmpp_config_workspace_create_default_options(ptr_section); - - // ptr_section = weechat_config_new_section( - // xmpp_config_file, "workspace", - // 0, 0, - // &xmpp_config_workspace_read_cb, NULL, NULL, - // &xmpp_config_workspace_write_cb, NULL, NULL, - // NULL, NULL, NULL, - // NULL, NULL, NULL, - // NULL, NULL, NULL); - - //if (!ptr_section) - //{ - // weechat_config_free(xmpp_config_file); - // xmpp_config_file = NULL; - // return 0; - //} - - //xmpp_config_section_workspace = ptr_section; + ptr_section = weechat_config_new_section( + config_file, "account_default", + 0, 0, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + + if (!ptr_section) + { + weechat_config_free(config_file); + config_file = NULL; + return 0; + } + + config_section_account_default = ptr_section; + + config__account_create_default_options(ptr_section); + + ptr_section = weechat_config_new_section( + config_file, "account", + 0, 0, + &config__account_read_cb, NULL, NULL, + &config__account_write_cb, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + + if (!ptr_section) + { + weechat_config_free(config_file); + config_file = NULL; + return 0; + } + + config_section_account = ptr_section; return 1; } -int xmpp_config_read() +int config__read() { - int rc; + int rc; - rc = weechat_config_read(xmpp_config_file); + rc = weechat_config_read(config_file); return rc; } -int xmpp_config_write() +int config__write() { - return weechat_config_write(xmpp_config_file); + return weechat_config_write(config_file); } -void xmpp_config_free() +void config__free() { } diff --git a/config.h b/config.h index c291cc4..8b2214d 100644 --- a/config.h +++ b/config.h @@ -7,53 +7,51 @@ #define WEECHAT_XMPP_CONFIG_NAME "xmpp" -enum t_xmpp_config_nick_completion +enum t_config_nick_completion { - XMPP_CONFIG_NICK_COMPLETION_SMART_OFF = 0, - XMPP_CONFIG_NICK_COMPLETION_SMART_SPEAKERS, - XMPP_CONFIG_NICK_COMPLETION_SMART_SPEAKERS_HIGHLIGHTS, + CONFIG_NICK_COMPLETION_SMART_OFF = 0, + CONFIG_NICK_COMPLETION_SMART_SPEAKERS, + CONFIG_NICK_COMPLETION_SMART_SPEAKERS_HIGHLIGHTS, }; -extern struct t_config_file *xmpp_config_file; - -//extern struct t_config_section *xmpp_config_section_workspace_default; -//extern struct t_config_section *xmpp_config_section_workspace; - -extern struct t_config_option *xmpp_config_server_jid; -extern struct t_config_option *xmpp_config_server_password; -extern struct t_config_option *xmpp_config_look_nick_completion_smart; - -//extern struct t_config_option *xmpp_config_workspace_default[]; - -//int xmpp_config_workspace_check_value_cb(const void *pointer, void *data, -// struct t_config_option *option, -// const char *value); - -//void xmpp_config_workspace_change_cb(const void *pointer, void *data, -// struct t_config_option *option); - -//struct t_config_option *xmpp_config_workspace_new_option (struct t_config_file *config_file, -// struct t_config_section *section, -// int index_option, -// const char *option_name, -// const char *default_value, -// const char *value, -// int null_value_allowed, -// int (*callback_check_value)(const void *pointer, -// void *data, -// struct t_config_option *option, -// const char *value), -// const void *callback_check_value_pointer, -// void *callback_check_value_data, -// void (*callback_change)(const void *pointer, -// void *data, -// struct t_config_option *option), -// const void *callback_change_pointer, -// void *callback_change_data); - -extern int xmpp_config_init(); -extern int xmpp_config_read(); -extern int xmpp_config_write(); -extern void xmpp_config_free(); +extern struct t_config_file *config_file; + +extern struct t_config_section *config_section_account_default; +extern struct t_config_section *config_section_account; + +extern struct t_config_option *config_look_nick_completion_smart; + +extern struct t_config_option *config_account_default[]; + +int config__account_check_value_cb(const void *pointer, void *data, + struct t_config_option *option, + const char *value); + +void config__account_change_cb(const void *pointer, void *data, + struct t_config_option *option); + +struct t_config_option *config__account_new_option (struct t_config_file *config_file, + struct t_config_section *section, + int index_option, + const char *option_name, + const char *default_value, + const char *value, + int null_value_allowed, + int (*callback_check_value)(const void *pointer, + void *data, + struct t_config_option *option, + const char *value), + const void *callback_check_value_pointer, + void *callback_check_value_data, + void (*callback_change)(const void *pointer, + void *data, + struct t_config_option *option), + const void *callback_change_pointer, + void *callback_change_data); + +extern int config__init(); +extern int config__read(); +extern int config__write(); +extern void config__free(); #endif /*WEECHAT_XMPP_CONFIG_H*/ diff --git a/connection.c b/connection.c index 77ed274..0f0b93e 100644 --- a/connection.c +++ b/connection.c @@ -15,42 +15,13 @@ //#include "api/xmpp-api-message.h" //#include "api/xmpp-api-user-typing.h" -xmpp_conn_t *xmpp_connection; +xmpp_conn_t *connection; -void xmpp_log_emit_weechat(void *const userdata, const xmpp_log_level_t level, const char *const area, const char *const msg) -{ - (void) userdata; - - static const char *log_level_name[4] = {"debug", "info", "warn", "error"}; - - time_t date = time(NULL); - const char *timestamp = weechat_util_get_time_string(&date); - - weechat_printf( - NULL, - _("%s%s/%s (%s): %s"), - weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, area, - log_level_name[level], msg); -} - -xmpp_log_t xmpp_logger = { - &xmpp_log_emit_weechat, - NULL -}; - -void xmpp_connection_init() +void connection__init() { xmpp_initialize(); } -int xmpp_connection_autoconnect(const void *pointer, void *data, int remaining_calls) -{ - xmpp_connection_connect(weechat_config_string(xmpp_config_server_jid), - weechat_config_string(xmpp_config_server_password)); - - return WEECHAT_RC_OK; -} - int version_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) { xmpp_stanza_t *reply, *query, *name, *version, *text; @@ -142,9 +113,9 @@ int message_handler(xmpp_conn_t *conn, xmpp_stanza_t *stanza, void *userdata) return 1; } -void xmpp_connection_on_connected(xmpp_conn_t *conn, xmpp_conn_event_t status, - int error, xmpp_stream_error_t *stream_error, - void *userdata) +void connection__handler(xmpp_conn_t *conn, xmpp_conn_event_t status, + int error, xmpp_stream_error_t *stream_error, + void *userdata) { xmpp_ctx_t *ctx = (xmpp_ctx_t *)userdata; @@ -168,97 +139,60 @@ void xmpp_connection_on_connected(xmpp_conn_t *conn, xmpp_conn_event_t status, } } -void xmpp_connection_connect(const char* jid, const char* password) +int connection__connect(xmpp_ctx_t *context, xmpp_conn_t **connection, + xmpp_log_t *logger, const char* jid, + const char* password, int tls) { - xmpp_ctx_t *xmpp_context = xmpp_ctx_new(NULL, &xmpp_logger); - - xmpp_connection = xmpp_conn_new(xmpp_context); - xmpp_conn_set_jid(xmpp_connection, jid); - xmpp_conn_set_pass(xmpp_connection, password); - auto flags = xmpp_conn_get_flags(xmpp_connection); - //flags |= XMPP_CONN_FLAG_TRUST_TLS; - xmpp_conn_set_flags(xmpp_connection, flags); - xmpp_connect_client(xmpp_connection, NULL, 0, xmpp_connection_on_connected, xmpp_context); - //struct lws_context_creation_info ctxinfo; - //struct lws_client_connect_info ccinfo; - //const char *url_protocol, *url_path; - //char path[512]; - - //memset(&ctxinfo, 0, sizeof(ctxinfo)); - //memset(&ccinfo, 0, sizeof(ccinfo)); - - //ccinfo.port = 443; - - //if (lws_parse_uri(workspace->ws_url, - // &url_protocol, &ccinfo.address, - // &ccinfo.port, &url_path)) - //{ - // weechat_printf( - // workspace->buffer, - // _("%s%s: error connecting to xmpp: bad websocket uri"), - // weechat_prefix("error"), XMPP_PLUGIN_NAME); - // return; - //} - - //path[0] = '/'; - //strncpy(path + 1, url_path, sizeof(path) - 2); - //path[sizeof(path) - 1] = '\0'; - - //ccinfo.path = path; - - //ctxinfo.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - //ctxinfo.port = CONTEXT_PORT_NO_LISTEN; - //ctxinfo.protocols = protocols; - //ctxinfo.uid = -1; - //ctxinfo.gid = -1; + *connection = xmpp_conn_new(context); + xmpp_conn_set_jid(*connection, jid); + xmpp_conn_set_pass(*connection, password); - //workspace->context = lws_create_context(&ctxinfo); - //if (!workspace->context) - //{ - // weechat_printf( - // workspace->buffer, - // _("%s%s: error connecting to xmpp: lws init failed"), - // weechat_prefix("error"), XMPP_PLUGIN_NAME); - // return; - //} - //else - //{ - // weechat_printf( - // workspace->buffer, - // _("%s%s: connecting to %s://%s:%d%s"), - // weechat_prefix("network"), XMPP_PLUGIN_NAME, - // url_protocol, ccinfo.address, ccinfo.port, path); - //} + auto flags = xmpp_conn_get_flags(*connection); + switch (tls) + { + case 0: + flags |= XMPP_CONN_FLAG_DISABLE_TLS; + break; + case 1: + break; + case 2: + flags |= XMPP_CONN_FLAG_TRUST_TLS; + break; + default: + break; + } + xmpp_conn_set_flags(*connection, flags); - //ccinfo.context = workspace->context; - //ccinfo.ssl_connection = LCCSCF_USE_SSL; - //ccinfo.host = ccinfo.address; - //ccinfo.origin = ccinfo.address; - //ccinfo.ietf_version_or_minus_one = -1; - //ccinfo.protocol = protocols[0].name; - //ccinfo.pwsi = &workspace->client_wsi; - //ccinfo.userdata = workspace; + if (xmpp_connect_client(*connection, NULL, 0, &connection__handler, context) + != XMPP_EOK) + { + weechat_printf( + NULL, + _("%s%s: error connecting to %s"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, + jid); + return 0; + } - //lws_client_connect_via_info(&ccinfo); + weechat_printf( + NULL, + _("%s%s: c'necting to %s"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, + jid); + return 1; } -int xmpp_connection_check_events(const void *pointer, void *data, int remaining_calls) +void connection__process(xmpp_ctx_t *context, xmpp_conn_t *connection, + const unsigned long timeout) { - (void) pointer; - (void) data; - (void) remaining_calls; - - if (xmpp_connection) + if (connection) { - xmpp_ctx_t *xmpp_context = xmpp_conn_get_context(xmpp_connection); - - xmpp_run_once(xmpp_context, 10); + xmpp_run_once(context ? context : xmpp_conn_get_context(connection), + timeout); } - - return WEECHAT_RC_OK; } -int xmpp_connection_route_message(xmpp_conn_t *workspace, +int connection__route_message(xmpp_conn_t *workspace, const char *type, void *message) { //struct stringcase key; diff --git a/connection.h b/connection.h index 9507803..1b049ea 100644 --- a/connection.h +++ b/connection.h @@ -5,17 +5,16 @@ #ifndef _WEECHAT_XMPP_CONNECTION_H_ #define _WEECHAT_XMPP_CONNECTION_H_ -extern xmpp_conn_t *xmpp_connection; +void connection__init(); -void xmpp_connection_init(); +int connection__connect(xmpp_ctx_t *context, xmpp_conn_t **connection, + xmpp_log_t *logger, const char* jid, + const char* password, int tls); -int xmpp_connection_autoconnect(const void *pointer, void *data, int remaining_calls); +void connection__process(xmpp_ctx_t *context, xmpp_conn_t *connection, + const unsigned long timeout); -void xmpp_connection_connect(const char* jid, const char* password); - -int xmpp_connection_check_events(const void *pointer, void *data, int remaining_calls); - -int xmpp_connection_route_message(xmpp_conn_t *connection, - const char *type, void *message); +int connection__route_message(xmpp_conn_t *connection, + const char *type, void *message); #endif /*WEECHAT_XMPP_CONNECTION_H*/ diff --git a/input.c b/input.c new file mode 100644 index 0000000..6aebd91 --- /dev/null +++ b/input.c @@ -0,0 +1,64 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include + +#include "plugin.h" +#include "account.h" +#include "channel.h" +#include "buffer.h" +//#include "request.h" +#include "message.h" +#include "input.h" + +int input__data(struct t_gui_buffer *buffer, const char *text) +{ + struct t_account *account = NULL; + struct t_channel *channel = NULL; + struct t_request *request; + + buffer__get_account_and_channel(buffer, &account, &channel); + + if (!account) + return WEECHAT_RC_ERROR; + + if (channel) + { + if (!account->is_connected) + { + weechat_printf(buffer, + _("%s%s: you are not connected to server"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + return WEECHAT_RC_OK; + } + + //TODO: SEND + //request = request_chat_postmessage(account, + // weechat_config_string( + // account->options[ACCOUNT_OPTION_TOKEN]), + // channel->id, text); + //if (request) + // account__register_request(account, request); + } + else + { + weechat_printf(buffer, + _("%s%s: this buffer is not a channel!"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + } + + return WEECHAT_RC_OK; +} + +int input__data_cb(const void *pointer, void *data, + struct t_gui_buffer *buffer, + const char *text) +{ + (void) pointer; + (void) data; + + return input__data(buffer, text); +} diff --git a/input.h b/input.h new file mode 100644 index 0000000..c218506 --- /dev/null +++ b/input.h @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef _WEECHAT_XMPP_INPUT_H_ +#define _WEECHAT_XMPP_INPUT_H_ + +int input__data_cb(const void *pointer, void *data, + struct t_gui_buffer *buffer, + const char *input_data); + +#endif /*WEECHAT_XMPP_INPUT_H*/ diff --git a/message.c b/message.c new file mode 100644 index 0000000..0314d44 --- /dev/null +++ b/message.c @@ -0,0 +1,249 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include +#include + +#include "plugin.h" +#include "account.h" +#include "channel.h" +#include "user.h" +#include "message.h" + +static const char format_regex[] = "<([^>]*?)>"; +static const size_t max_groups = 2; + +char *message__translate_code(struct t_account *account, + const char *code) +{ + struct t_channel *channel; + struct t_user *user; + size_t resultlen; + char *identifier, *alttext, *result, *symbol, *prefix; + + identifier = strdup(code); + alttext = strchr(identifier, '|'); + if (alttext) + *alttext++ = '\0'; + + switch (identifier[0]) + { + case '#': /* channel */ + if (alttext) + { + prefix = "#"; + symbol = strdup(alttext); + } + else + { + channel = channel__search(account, identifier+1); + if (channel) + { + prefix = "#"; + symbol = strdup(channel->name); + } + else + { + prefix = "Channel:"; + symbol = strdup(identifier+1); + } + } + break; + case '@': /* user */ + if (alttext) + { + prefix = "@"; + symbol = strdup(alttext); + } + else + { + user = user__search(account, identifier+1); + if (user) + { + prefix = "@"; + symbol = strdup(user->profile.display_name); + } + else + { + prefix = "User:"; + symbol = strdup(identifier+1); + } + } + break; + case '!': /* special */ + if (alttext) + { + prefix = "@"; + symbol = strdup(alttext); + } + else + { + prefix = "@"; + symbol = strdup(identifier+1); + } + break; + default: /* url */ + prefix = ""; + symbol = strdup(code); + break; + } + + free(identifier); + resultlen = snprintf(NULL, 0, "%s%s%s%s", weechat_color("chat_nick"), prefix, symbol, weechat_color("reset")) + 1; + result = malloc(resultlen); + snprintf(result, resultlen, "%s%s%s%s", weechat_color("chat_nick"), prefix, symbol, weechat_color("reset")); + free(symbol); + + return result; +} + +void message__htmldecode(char *dest, const char *src, size_t n) +{ + size_t i, j; + + for (i = 0, j = 0; i < n; i++, j++) + switch (src[i]) + { + case '\0': + dest[j] = '\0'; + return; + case '&': + if (src[i+1] == 'g' && + src[i+2] == 't' && + src[i+3] == ';') + { + dest[j] = '>'; + i += 3; + break; + } + else if (src[i+1] == 'l' && + src[i+2] == 't' && + src[i+3] == ';') + { + dest[j] = '<'; + i += 3; + break; + } + else if (src[i+1] == 'a' && + src[i+2] == 'm' && + src[i+3] == 'p' && + src[i+4] == ';') + { + dest[j] = '&'; + i += 4; + break; + } + /* fallthrough */ + default: + dest[j] = src[i]; + break; + } + dest[j-1] = '\0'; + return; +} + +char *message__decode(struct t_account *account, + const char *text) +{ + int rc; + regex_t reg; + regmatch_t groups[max_groups]; + char msgbuf[100]; + char *decoded_text; + const char *cursor; + size_t offset; + + if ((rc = regcomp(®, format_regex, REG_EXTENDED))) + { + regerror(rc, ®, msgbuf, sizeof(msgbuf)); + weechat_printf( + account->buffer, + _("%s%s: error compiling message formatting regex: %s"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME, + msgbuf); + return strdup(text); + } + + decoded_text = malloc(MESSAGE_MAX_LENGTH); + if (!decoded_text) + { + regfree(®); + weechat_printf( + account->buffer, + _("%s%s: error allocating space for message"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + return strdup(text); + } + decoded_text[0] = '\0'; + + for (cursor = text; regexec(®, cursor, max_groups, groups, 0) == 0; cursor += offset) + { + offset = groups[0].rm_eo; + + char *copy = strdup(cursor); + if (!copy) + { + regfree(®); + free(decoded_text); + weechat_printf( + account->buffer, + _("%s%s: error allocating space for message"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + return strdup(text); + } + copy[groups[1].rm_eo] = '\0'; + + char *match = strdup(copy + groups[1].rm_so); + if (!match) + { + free(copy); + regfree(®); + free(decoded_text); + weechat_printf( + account->buffer, + _("%s%s: error allocating space for message"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + return strdup(text); + } + copy[groups[0].rm_so] = '\0'; + + char *prematch = strdup(copy); + if (!prematch) + { + free(match); + free(copy); + regfree(®); + free(decoded_text); + weechat_printf( + account->buffer, + _("%s%s: error allocating space for message"), + weechat_prefix("error"), WEECHAT_XMPP_PLUGIN_NAME); + return strdup(text); + } + free(copy); + + strncat(decoded_text, prematch, + MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1); + free(prematch); + + char *replacement = message__translate_code(account, match); + free(match); + + strncat(decoded_text, replacement, + MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1); + free(replacement); + } + strncat(decoded_text, cursor, + MESSAGE_MAX_LENGTH - strlen(decoded_text) - 1); + + message__htmldecode(decoded_text, decoded_text, + MESSAGE_MAX_LENGTH); + + regfree(®); + return decoded_text; +} diff --git a/message.h b/message.h new file mode 100644 index 0000000..fdf7263 --- /dev/null +++ b/message.h @@ -0,0 +1,13 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef _WEECHAT_XMPP_MESSAGE_H_ +#define _WEECHAT_XMPP_MESSAGE_H_ + +#define MESSAGE_MAX_LENGTH 40000 + +char *message__decode(struct t_account *account, + const char *text); + +#endif /*WEECHAT_XMPP_MESSAGE_H*/ diff --git a/plugin.c b/plugin.c index 582a524..fc0c9f2 100644 --- a/plugin.c +++ b/plugin.c @@ -12,13 +12,13 @@ #include "config.h" #include "connection.h" #include "command.h" -//#include "xmpp-workspace.h" -//#include "xmpp-buffer.h" +#include "account.h" +#include "buffer.h" //#include "xmpp-completion.h" WEECHAT_PLUGIN_NAME(WEECHAT_XMPP_PLUGIN_NAME); -WEECHAT_PLUGIN_DESCRIPTION(N_("XMPP protocol")); +WEECHAT_PLUGIN_DESCRIPTION(N_("XMPP client protocol")); WEECHAT_PLUGIN_AUTHOR("bqv "); WEECHAT_PLUGIN_VERSION(WEECHAT_XMPP_PLUGIN_VERSION); WEECHAT_PLUGIN_LICENSE("MPL2"); @@ -26,35 +26,32 @@ WEECHAT_PLUGIN_PRIORITY(5500); struct t_weechat_plugin *weechat_xmpp_plugin = NULL; -struct t_hook *xmpp_hook_timer = NULL; +struct t_hook *weechat_xmpp_autoconnect_timer = NULL; +struct t_hook *weechat_xmpp_process_timer = NULL; -struct t_gui_bar_item *xmpp_typing_bar_item = NULL; +struct t_gui_bar_item *weechat_xmpp_typing_bar_item = NULL; int weechat_plugin_init(struct t_weechat_plugin *plugin, int argc, char *argv[]) { (void) argc; (void) argv; - weechat_plugin = plugin; + weechat_xmpp_plugin = plugin; - if (!xmpp_config_init()) + if (!config__init()) return WEECHAT_RC_ERROR; - xmpp_config_read(); + config__read(); - xmpp_connection_init(); + connection__init(); - xmpp_command_init(); + command__init(); - //xmpp_completion_init(); + //completion__init(); - xmpp_hook_timer = weechat_hook_timer(1 * 1000, 0, 1, - &xmpp_connection_autoconnect, - NULL, NULL); - - xmpp_hook_timer = weechat_hook_timer(0.1 * 1000, 0, 0, - &xmpp_connection_check_events, - NULL, NULL); + weechat_xmpp_process_timer = weechat_hook_timer(0.1 * 1000, 0, 0, + &account__timer_cb, + NULL, NULL); if (!weechat_bar_search("typing")) { @@ -64,9 +61,9 @@ int weechat_plugin_init(struct t_weechat_plugin *plugin, int argc, char *argv[]) "off", "xmpp_typing"); } - //xmpp_typing_bar_item = weechat_bar_item_new("xmpp_typing", - // &xmpp_buffer_typing_bar_cb, - // NULL, NULL); + weechat_xmpp_typing_bar_item = weechat_bar_item_new("xmpp_typing", + &buffer__typing_bar_cb, + NULL, NULL); return WEECHAT_RC_OK; } @@ -76,25 +73,19 @@ int weechat_plugin_end(struct t_weechat_plugin *plugin) // make C compiler happy (void) plugin; - if (xmpp_typing_bar_item) - weechat_bar_item_remove(xmpp_typing_bar_item); - - if (xmpp_hook_timer) - weechat_unhook(xmpp_hook_timer); + if (weechat_xmpp_typing_bar_item) + weechat_bar_item_remove(weechat_xmpp_typing_bar_item); - xmpp_config_write(); + if (weechat_xmpp_autoconnect_timer) + weechat_unhook(weechat_xmpp_autoconnect_timer); + if (weechat_xmpp_process_timer) + weechat_unhook(weechat_xmpp_process_timer); - if (xmpp_connection) - { - xmpp_ctx_t *xmpp_context = xmpp_conn_get_context(xmpp_connection); + config__write(); - if (xmpp_conn_is_connected(xmpp_connection)) - xmpp_disconnect(xmpp_connection); + account__disconnect_all(); - xmpp_conn_release(xmpp_connection); - - xmpp_ctx_free(xmpp_context); - } + account__free_all(); xmpp_shutdown(); diff --git a/user.c b/user.c new file mode 100644 index 0000000..2469af3 --- /dev/null +++ b/user.c @@ -0,0 +1,242 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include +#include +#include +#include +#include + +#include "plugin.h" +#include "account.h" +#include "user.h" +#include "channel.h" + +const char *user__get_colour(struct t_user *user) +{ + return weechat_info_get("nick_color", user->profile.display_name); +} + +const char *user__get_colour_for_nicklist(struct t_user *user) +{ + return weechat_info_get("nick_color_name", user->profile.display_name); +} + +const char *user__as_prefix(struct t_account *account, + struct t_user *user, + const char *name) +{ + static char result[256]; + + (void) account; + + snprintf(result, sizeof(result), "%s%s\t", + user__get_colour(user), + name ? name : user->profile.display_name); + + return result; +} + +struct t_user *user__bot_search(struct t_account *account, + const char *bot_id) +{ + struct t_user *ptr_user; + + if (!account || !bot_id) + return NULL; + + for (ptr_user = account->users; ptr_user; + ptr_user = ptr_user->next_user) + { + if (ptr_user->profile.bot_id && + weechat_strcasecmp(ptr_user->profile.bot_id, bot_id) == 0) + return ptr_user; + } + + return NULL; +} + +struct t_user *user__search(struct t_account *account, + const char *id) +{ + struct t_user *ptr_user; + + if (!account || !id) + return NULL; + + for (ptr_user = account->users; ptr_user; + ptr_user = ptr_user->next_user) + { + if (weechat_strcasecmp(ptr_user->id, id) == 0) + return ptr_user; + } + + return NULL; +} + +void user__nicklist_add(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; + + ptr_buffer = channel ? channel->buffer : account->buffer; + + ptr_group = weechat_nicklist_search_group(ptr_buffer, NULL, + user->is_away ? + "+" : "..."); + weechat_nicklist_add_nick(ptr_buffer, ptr_group, + user->profile.display_name, + user->is_away ? + "weechat.color.nicklist_away" : + user__get_colour_for_nicklist(user), + user->is_away ? "+" : "", + "bar_fg", + 1); +} + +struct t_user *user__new(struct t_account *account, + const char *id, const char *display_name) +{ + struct t_user *new_user, *ptr_user; + + if (!account || !id || !display_name) + { + return NULL; + } + + if (!display_name[0] && strcmp("USLACKBOT", id) == 0) + return NULL; + + if (!account->users) + channel__add_nicklist_groups(account, NULL); + + ptr_user = user__search(account, id); + if (ptr_user) + { + user__nicklist_add(account, NULL, ptr_user); + return ptr_user; + } + + if ((new_user = malloc(sizeof(*new_user))) == NULL) + { + return NULL; + } + + new_user->prev_user = account->last_user; + new_user->next_user = NULL; + if (account->last_user) + (account->last_user)->next_user = new_user; + else + account->users = new_user; + account->last_user = new_user; + + new_user->id = strdup(id); + new_user->name = NULL; + new_user->team_id = NULL; + new_user->real_name = NULL; + new_user->colour = NULL; + new_user->deleted = 0; + + new_user->tz = NULL; + new_user->tz_label = NULL; + new_user->tz_offset = 0; + new_user->locale = NULL; + + new_user->profile.avatar_hash = NULL; + new_user->profile.status_text = NULL; + new_user->profile.status_emoji = NULL; + new_user->profile.real_name = NULL; + new_user->profile.display_name = display_name[0] ? + strdup(display_name) : + strdup("xmppbot"); + new_user->profile.real_name_normalized = NULL; + new_user->profile.email = NULL; + new_user->profile.team = NULL; + new_user->profile.bot_id = NULL; + new_user->updated = 0; + new_user->is_away = 0; + + new_user->is_admin = 0; + new_user->is_owner = 0; + new_user->is_primary_owner = 0; + new_user->is_restricted = 0; + new_user->is_ultra_restricted = 0; + new_user->is_bot = 0; + new_user->is_stranger = 0; + new_user->is_app_user = 0; + new_user->has_2fa = 0; + + user__nicklist_add(account, NULL, new_user); + + return new_user; +} + +void user__free(struct t_account *account, + struct t_user *user) +{ + struct t_user *new_users; + + if (!account || !user) + return; + + /* remove user from users list */ + if (account->last_user == user) + account->last_user = user->prev_user; + if (user->prev_user) + { + (user->prev_user)->next_user = user->next_user; + new_users = account->users; + } + else + new_users = user->next_user; + + if (user->next_user) + (user->next_user)->prev_user = user->prev_user; + + /* free user data */ + if (user->id) + free(user->id); + if (user->name) + free(user->name); + if (user->team_id) + free(user->team_id); + if (user->real_name) + free(user->real_name); + if (user->colour) + free(user->colour); + if (user->tz) + free(user->tz); + if (user->tz_label) + 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); + + free(user); + + account->users = new_users; +} + +void user__free_all(struct t_account *account) +{ + while (account->users) + user__free(account, account->users); +} diff --git a/user.h b/user.h new file mode 100644 index 0000000..88aaf96 --- /dev/null +++ b/user.h @@ -0,0 +1,75 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, version 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef _WEECHAT_XMPP_USER_H_ +#define _WEECHAT_XMPP_USER_H_ + +struct t_user_profile +{ + char *avatar_hash; + char *status_text; + char *status_emoji; + char *real_name; + char *display_name; + char *real_name_normalized; + char *email; + char *team; + char *bot_id; +}; + +struct t_user +{ + char *id; + char *name; + char *team_id; + char *real_name; + char *colour; + + int deleted; + char *tz; + char *tz_label; + int tz_offset; + char *locale; + + struct t_user_profile profile; + int updated; + int is_away; + + int is_admin; + int is_owner; + int is_primary_owner; + int is_restricted; + int is_ultra_restricted; + int is_bot; + int is_stranger; + int is_app_user; + int has_2fa; + + struct t_user *prev_user; + struct t_user *next_user; +}; +struct t_channel; + +const char *user__get_colour(struct t_user *user); + +const char *user__as_prefix(struct t_account *account, + struct t_user *user, + const char *name); + +struct t_user *user__bot_search(struct t_account *account, + const char *bot_id); + +struct t_user *user__search(struct t_account *account, + const char *id); + +struct t_user *user__new(struct t_account *account, + const char *id, const char *display_name); + +void user__free_all(struct t_account *account); + +void user__nicklist_add(struct t_account *account, + struct t_channel *channel, + struct t_user *user); + +#endif /*WEECHAT_XMPP_USER_H*/