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*/