diff options
25 files changed, 1487 insertions, 99 deletions
diff --git a/ChangeLog.adoc b/ChangeLog.adoc index be23f30a3..687fd203c 100644 --- a/ChangeLog.adoc +++ b/ChangeLog.adoc @@ -41,6 +41,7 @@ New features:: * irc: add server option "autojoin_delay" (delay before autojoin), use option "command_delay" before execution of the command (issue #862) * relay: add "api" protocol (HTTP REST API), add option relay.look.display_clients, change option type relay.look.auto_open_buffer to string, rename option relay.weechat.commands to relay.network.commands, add option relay.network.time_window (issue #2066) * relay: add support of websocket extension "permessage-deflate" (issue #1549) + * relay: add command `/remote` to manage remote WeeChat relay servers and connect to them (issue #2066) * script: add option `enable` in command `/script` * script: add info "script_loaded" diff --git a/doc/en/weechat_dev.en.adoc b/doc/en/weechat_dev.en.adoc index 9271aa558..c9bf13695 100644 --- a/doc/en/weechat_dev.en.adoc +++ b/doc/en/weechat_dev.en.adoc @@ -335,6 +335,7 @@ WeeChat "core" is located in following directories: | relay-info.c | Relay info/infolists/hdata. | relay-network.c | Network functions for relay. | relay-raw.c | Relay raw buffer. +| relay-remote.c | Relay remote. | relay-server.c | Relay server. | relay-upgrade.c | Save/restore of relay data when upgrading WeeChat. | relay-websocket.c | WebSocket server functions (RFC 6455). diff --git a/doc/fr/weechat_dev.fr.adoc b/doc/fr/weechat_dev.fr.adoc index 2e34f1073..f7a21cdbf 100644 --- a/doc/fr/weechat_dev.fr.adoc +++ b/doc/fr/weechat_dev.fr.adoc @@ -337,6 +337,7 @@ Le cœur de WeeChat est situé dans les répertoires suivants : | relay-info.c | Info/infolists/hdata pour Relay. | relay-network.c | Fonctions de réseau pour Relay. | relay-raw.c | Tampon des données brutes de Relay. +| relay-remote.c | Relai distant. | relay-server.c | Serveur Relay. | relay-upgrade.c | Sauvegarde/restauration des données Relay lors de la mise à jour de WeeChat. | relay-websocket.c | Fonctions pour le serveur WebSocket (RFC 6455). diff --git a/doc/ja/weechat_dev.ja.adoc b/doc/ja/weechat_dev.ja.adoc index 85c5fb4ba..65f820d67 100644 --- a/doc/ja/weechat_dev.ja.adoc +++ b/doc/ja/weechat_dev.ja.adoc @@ -357,6 +357,8 @@ WeeChat "core" は以下のディレクトリに配置されています: | relay-info.c | relay の情報/インフォリスト/hdata | relay-network.c | relay 用のネットワーク関数 | relay-raw.c | relay 生バッファ +// TRANSLATION MISSING +| relay-remote.c | Relay remote. | relay-server.c | relay サーバ | relay-upgrade.c | WeeChat をアップグレードする際にデータを保存/回復 | relay-websocket.c | リレー用の websocket サーバ関数 (RFC 6455) diff --git a/doc/sr/weechat_dev.sr.adoc b/doc/sr/weechat_dev.sr.adoc index 745a38118..630ebf3c2 100644 --- a/doc/sr/weechat_dev.sr.adoc +++ b/doc/sr/weechat_dev.sr.adoc @@ -337,6 +337,8 @@ WeeChat „језгро” се налази у следећим директо | relay-info.c | Релеј info/infolists/hdata. | relay-network.c | Мрежне функције за релеј. | relay-raw.c | Релеј сирови бафер. +// TRANSLATION MISSING +| relay-remote.c | Relay remote. | relay-server.c | Релеј сервер. | relay-upgrade.c | Save/restore of relay data when upgrading WeeChat. | relay-websocket.c | WebSocket сервер функције (RFC 6455). diff --git a/po/srcfiles.cmake b/po/srcfiles.cmake index 60db4f36e..3b68e623a 100644 --- a/po/srcfiles.cmake +++ b/po/srcfiles.cmake @@ -374,6 +374,8 @@ SET(WEECHAT_SOURCES ./src/plugins/relay/relay-network.h ./src/plugins/relay/relay-raw.c ./src/plugins/relay/relay-raw.h +./src/plugins/relay/relay-remote.c +./src/plugins/relay/relay-remote.h ./src/plugins/relay/relay-server.c ./src/plugins/relay/relay-server.h ./src/plugins/relay/relay-upgrade.c diff --git a/src/plugins/relay/CMakeLists.txt b/src/plugins/relay/CMakeLists.txt index e467c384d..724191947 100644 --- a/src/plugins/relay/CMakeLists.txt +++ b/src/plugins/relay/CMakeLists.txt @@ -29,6 +29,7 @@ set(RELAY_SRC relay-info.c relay-info.h relay-network.c relay-network.h relay-raw.c relay-raw.h + relay-remote.c relay-remote.h relay-server.c relay-server.h relay-upgrade.c relay-upgrade.h relay-websocket.c relay-websocket.h diff --git a/src/plugins/relay/api/relay-api-protocol.c b/src/plugins/relay/api/relay-api-protocol.c index ac3b8f13e..265e0df3b 100644 --- a/src/plugins/relay/api/relay-api-protocol.c +++ b/src/plugins/relay/api/relay-api-protocol.c @@ -808,7 +808,7 @@ relay_api_protocol_recv_http (struct t_relay_client *client) { NULL, NULL, 0, 0, 0, NULL }, }; - if (!client->http_req || RELAY_CLIENT_HAS_ENDED(client)) + if (!client->http_req || RELAY_STATUS_HAS_ENDED(client->status)) return; /* display debug message */ diff --git a/src/plugins/relay/api/relay-api.c b/src/plugins/relay/api/relay-api.c index 054ddbf29..e20e147f7 100644 --- a/src/plugins/relay/api/relay-api.c +++ b/src/plugins/relay/api/relay-api.c @@ -208,14 +208,16 @@ relay_api_alloc_with_infolist (struct t_relay_client *client, RELAY_API_DATA(client, sync_colors) = weechat_infolist_integer ( infolist, "sync_colors"); - if (!RELAY_CLIENT_HAS_ENDED(client) && RELAY_API_DATA(client, sync_enabled)) + if (!RELAY_STATUS_HAS_ENDED(client->status) + && RELAY_API_DATA(client, sync_enabled)) + { relay_api_hook_signals (client); + } } /* - * Returns the client initial status: it is always "waiting_auth" for API - * protocol because we always expect the "init" command, even without any - * password. + * Returns the client initial status: it is always "authenticating" for API + * protocol because we always expect the client to authenticate. */ enum t_relay_status @@ -224,7 +226,7 @@ relay_api_get_initial_status (struct t_relay_client *client) /* make C compiler happy */ (void) client; - return RELAY_STATUS_WAITING_AUTH; + return RELAY_STATUS_AUTHENTICATING; } /* diff --git a/src/plugins/relay/irc/relay-irc.c b/src/plugins/relay/irc/relay-irc.c index 0c406488b..c0885511c 100644 --- a/src/plugins/relay/irc/relay-irc.c +++ b/src/plugins/relay/irc/relay-irc.c @@ -2298,7 +2298,7 @@ relay_irc_alloc_with_infolist (struct t_relay_client *client, } /* - * Returns the client initial status: it can be "waiting_auth" or "connected", + * Returns the client initial status: it can be "authenticating" or "connected", * depending if a password is expected or not. */ @@ -2306,7 +2306,7 @@ enum t_relay_status relay_irc_get_initial_status (struct t_relay_client *client) { return (RELAY_IRC_DATA(client, password_ok)) ? - RELAY_STATUS_CONNECTED : RELAY_STATUS_WAITING_AUTH; + RELAY_STATUS_CONNECTED : RELAY_STATUS_AUTHENTICATING; } /* @@ -2360,7 +2360,7 @@ relay_irc_add_to_infolist (struct t_infolist_item *item, if (!item || !client) return 0; - if (!RELAY_CLIENT_HAS_ENDED(client) && force_disconnected_state) + if (!RELAY_STATUS_HAS_ENDED(client->status) && force_disconnected_state) { if (!weechat_infolist_new_var_integer (item, "connected", 0)) return 0; diff --git a/src/plugins/relay/relay-buffer.c b/src/plugins/relay/relay-buffer.c index 1cb3c1349..8476504ef 100644 --- a/src/plugins/relay/relay-buffer.c +++ b/src/plugins/relay/relay-buffer.c @@ -77,11 +77,11 @@ relay_buffer_refresh (const char *hotlist) weechat_color ("lightgreen"), /* disconnect */ (client_selected - && !RELAY_CLIENT_HAS_ENDED(client_selected)) ? + && !RELAY_STATUS_HAS_ENDED(client_selected->status)) ? _(" [D] Disconnect") : "", /* remove */ (client_selected - && RELAY_CLIENT_HAS_ENDED(client_selected)) ? + && RELAY_STATUS_HAS_ENDED(client_selected->status)) ? _(" [R] Remove") : "", /* purge old */ _(" [P] Purge finished"), @@ -98,7 +98,7 @@ relay_buffer_refresh (const char *hotlist) weechat_config_string (relay_config_color_text_bg)); snprintf (str_status, sizeof (str_status), - "%s", _(relay_client_status_string[ptr_client->status])); + "%s", _(relay_status_string[ptr_client->status])); length = weechat_utf8_strlen_screen (str_status); if (length < 20) { @@ -191,7 +191,7 @@ relay_buffer_input_cb (const void *pointer, void *data, /* disconnect client */ if (weechat_strcmp (input_data, "d") == 0) { - if (client && !RELAY_CLIENT_HAS_ENDED(client)) + if (client && !RELAY_STATUS_HAS_ENDED(client->status)) { relay_client_disconnect (client); relay_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); @@ -204,7 +204,7 @@ relay_buffer_input_cb (const void *pointer, void *data, while (ptr_client) { next_client = ptr_client->next_client; - if (RELAY_CLIENT_HAS_ENDED(ptr_client)) + if (RELAY_STATUS_HAS_ENDED(ptr_client->status)) relay_client_free (ptr_client); ptr_client = next_client; } @@ -218,7 +218,7 @@ relay_buffer_input_cb (const void *pointer, void *data, /* remove client */ else if (weechat_strcmp (input_data, "r") == 0) { - if (client && RELAY_CLIENT_HAS_ENDED(client)) + if (client && RELAY_STATUS_HAS_ENDED(client->status)) { relay_client_free (client); relay_buffer_refresh (WEECHAT_HOTLIST_MESSAGE); diff --git a/src/plugins/relay/relay-client.c b/src/plugins/relay/relay-client.c index a5b8eac0b..312b67404 100644 --- a/src/plugins/relay/relay-client.c +++ b/src/plugins/relay/relay-client.c @@ -49,15 +49,6 @@ #include "weechat/relay-weechat.h" -char *relay_client_status_string[] = /* status strings for display */ -{ N_("connecting"), N_("waiting auth"), - N_("connected"), N_("auth failed"), N_("disconnected") -}; -char *relay_client_status_name[] = /* name of status (for signal/info) */ -{ "connecting", "waiting_auth", - "connected", "auth_failed", "disconnected" -}; - char *relay_client_data_type_string[] = /* strings for data types */ { "text", "binary", "http" }; @@ -144,30 +135,6 @@ relay_client_search_by_id (int id) } /* - * Searches for a client status. - * - * Returns index of status in enum t_relay_status, -1 if status is not found. - */ - -int -relay_client_status_search (const char *name) -{ - int i; - - if (!name) - return -1; - - for (i = 0; i < RELAY_NUM_STATUS; i++) - { - if (strcmp (relay_client_status_name[i], name) == 0) - return i; - } - - /* status not found */ - return -1; -} - -/* * Returns the number of active clients (connecting or connected) on a given * server port. */ @@ -183,7 +150,7 @@ relay_client_count_active_by_port (int server_port) ptr_client = ptr_client->next_client) { if ((ptr_client->server_port == server_port) - && !RELAY_CLIENT_HAS_ENDED(ptr_client)) + && !RELAY_STATUS_HAS_ENDED(ptr_client->status)) { count++; } @@ -203,7 +170,7 @@ relay_client_send_signal (struct t_relay_client *client) snprintf (signal, sizeof (signal), "relay_client_%s", - relay_client_status_name[client->status]); + relay_status_name[client->status]); weechat_hook_signal_send (signal, WEECHAT_HOOK_SIGNAL_POINTER, client); } @@ -761,7 +728,7 @@ relay_client_recv_cb (const void *pointer, void *data, int fd) * data can be received only during authentication * or if connected (authentication was OK) */ - if ((client->status != RELAY_STATUS_WAITING_AUTH) + if ((client->status != RELAY_STATUS_AUTHENTICATING) && (client->status != RELAY_STATUS_CONNECTED)) { return WEECHAT_RC_OK; @@ -1361,7 +1328,7 @@ relay_client_timer_cb (const void *pointer, void *data, int remaining_calls) { ptr_next_client = ptr_client->next_client; - if (RELAY_CLIENT_HAS_ENDED(ptr_client)) + if (RELAY_STATUS_HAS_ENDED(ptr_client->status)) { if ((purge_delay >= 0) && (current_time >= ptr_client->end_time + (purge_delay * 60))) @@ -1377,7 +1344,7 @@ relay_client_timer_cb (const void *pointer, void *data, int remaining_calls) /* disconnect clients not authenticated */ if ((auth_timeout > 0) - && (ptr_client->status == RELAY_STATUS_WAITING_AUTH)) + && (ptr_client->status == RELAY_STATUS_AUTHENTICATING)) { if (current_time - ptr_client->start_time > auth_timeout) { @@ -1576,7 +1543,7 @@ relay_client_new (int sock, const char *address, struct t_relay_server *server) RELAY_COLOR_CHAT_CLIENT, new_client->desc, RELAY_COLOR_CHAT, - _(relay_client_status_string[new_client->status])); + _(relay_status_string[new_client->status])); } else { @@ -1588,7 +1555,7 @@ relay_client_new (int sock, const char *address, struct t_relay_server *server) RELAY_COLOR_CHAT_CLIENT, new_client->desc, RELAY_COLOR_CHAT, - _(relay_client_status_string[new_client->status])); + _(relay_status_string[new_client->status])); } } @@ -1818,7 +1785,7 @@ relay_client_set_status (struct t_relay_client *client, client->desc, RELAY_COLOR_CHAT); } - else if (RELAY_CLIENT_HAS_ENDED(client)) + else if (RELAY_STATUS_HAS_ENDED(client->status)) { client->end_time = time (NULL); @@ -2069,7 +2036,7 @@ relay_client_add_to_infolist (struct t_infolist *infolist, return 0; if (!weechat_infolist_new_var_string (ptr_item, "desc", client->desc)) return 0; - if (!RELAY_CLIENT_HAS_ENDED(client) && force_disconnected_state) + if (!RELAY_STATUS_HAS_ENDED(client->status) && force_disconnected_state) { if (!weechat_infolist_new_var_integer (ptr_item, "sock", -1)) return 0; @@ -2156,7 +2123,7 @@ relay_client_add_to_infolist (struct t_infolist *infolist, return 0; if (!weechat_infolist_new_var_string (ptr_item, "real_ip", client->real_ip)) return 0; - if (!weechat_infolist_new_var_string (ptr_item, "status_string", relay_client_status_string[client->status])) + if (!weechat_infolist_new_var_string (ptr_item, "status_string", relay_status_string[client->status])) return 0; if (!weechat_infolist_new_var_integer (ptr_item, "protocol", client->protocol)) return 0; @@ -2244,7 +2211,7 @@ relay_client_print_log () weechat_log_printf (" real_ip . . . . . . . . . : '%s'", ptr_client->real_ip); weechat_log_printf (" status. . . . . . . . . . : %d (%s)", ptr_client->status, - relay_client_status_string[ptr_client->status]); + relay_status_string[ptr_client->status]); weechat_log_printf (" protocol. . . . . . . . . : %d (%s)", ptr_client->protocol, relay_protocol_string[ptr_client->protocol]); diff --git a/src/plugins/relay/relay-client.h b/src/plugins/relay/relay-client.h index 5978181fd..b2b5a7661 100644 --- a/src/plugins/relay/relay-client.h +++ b/src/plugins/relay/relay-client.h @@ -27,19 +27,6 @@ struct t_relay_server; struct t_relay_http_request; -/* relay status */ - -enum t_relay_status -{ - RELAY_STATUS_CONNECTING = 0, /* connecting to client */ - RELAY_STATUS_WAITING_AUTH, /* waiting AUTH from client */ - RELAY_STATUS_CONNECTED, /* connected to client */ - RELAY_STATUS_AUTH_FAILED, /* AUTH failed with client */ - RELAY_STATUS_DISCONNECTED, /* disconnected from client */ - /* number of relay status */ - RELAY_NUM_STATUS, -}; - /* type of data exchanged with client */ enum t_relay_client_data_type @@ -75,12 +62,6 @@ enum t_relay_client_msg_type RELAY_NUM_CLIENT_MSG_TYPES, }; -/* macros for status */ - -#define RELAY_CLIENT_HAS_ENDED(client) \ - ((client->status == RELAY_STATUS_AUTH_FAILED) || \ - (client->status == RELAY_STATUS_DISCONNECTED)) - /* fake send function (for tests) */ typedef void (t_relay_fake_send_func)(void *client, @@ -146,8 +127,6 @@ struct t_relay_client struct t_relay_client *next_client;/* link to next client */ }; -extern char *relay_client_status_string[]; -extern char *relay_client_status_name[]; extern char *relay_client_msg_type_string[]; extern struct t_relay_client *relay_clients; extern struct t_relay_client *last_relay_client; @@ -156,7 +135,6 @@ extern int relay_client_count; extern int relay_client_valid (struct t_relay_client *client); extern struct t_relay_client *relay_client_search_by_number (int number); extern struct t_relay_client *relay_client_search_by_id (int id); -extern int relay_client_status_search (const char *name); extern int relay_client_count_active_by_port (int server_port); extern void relay_client_set_desc (struct t_relay_client *client); extern void relay_client_recv_buffer (struct t_relay_client *client, diff --git a/src/plugins/relay/relay-command.c b/src/plugins/relay/relay-command.c index ce3142c91..edcbdffab 100644 --- a/src/plugins/relay/relay-command.c +++ b/src/plugins/relay/relay-command.c @@ -1,5 +1,5 @@ /* - * relay-command.c - relay command + * relay-command.c - relay commands * * Copyright (C) 2003-2024 Sébastien Helleu <flashcode@flashtux.org> * @@ -21,6 +21,7 @@ #include <stdlib.h> #include <string.h> +#include <limits.h> #include <time.h> #include "../weechat-plugin.h" @@ -30,6 +31,7 @@ #include "relay-config.h" #include "relay-network.h" #include "relay-raw.h" +#include "relay-remote.h" #include "relay-server.h" @@ -49,7 +51,7 @@ relay_command_client_list (int full) for (ptr_client = relay_clients; ptr_client; ptr_client = ptr_client->next_client) { - if (!full && RELAY_CLIENT_HAS_ENDED(ptr_client)) + if (!full && RELAY_STATUS_HAS_ENDED(ptr_client->status)) continue; if (num_found == 0) @@ -89,7 +91,7 @@ relay_command_client_list (int full) ptr_client->desc, RELAY_COLOR_CHAT, weechat_color (weechat_config_string (relay_config_color_status[ptr_client->status])), - relay_client_status_string[ptr_client->status], + relay_status_string[ptr_client->status], RELAY_COLOR_CHAT, date_start, date_activity, @@ -104,7 +106,7 @@ relay_command_client_list (int full) ptr_client->desc, RELAY_COLOR_CHAT, weechat_color (weechat_config_string (relay_config_color_status[ptr_client->status])), - relay_client_status_string[ptr_client->status], + relay_status_string[ptr_client->status], RELAY_COLOR_CHAT, date_start); } @@ -387,6 +389,309 @@ relay_command_relay (const void *pointer, void *data, } /* + * Displays a relay remote. + */ + +void +relay_command_display_remote (struct t_relay_remote *remote, int with_detail) +{ + if (with_detail) + { + weechat_printf (NULL, ""); + weechat_printf (NULL, _("Remote: %s"), remote->name); + weechat_printf (NULL, " url. . . . . . . . . : '%s'", + weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL])); + weechat_printf (NULL, " password . . . . . . : '%s'", + weechat_config_string (remote->options[RELAY_REMOTE_OPTION_PASSWORD])); + weechat_printf (NULL, " totp_secret. . . . . : '%s'", + weechat_config_string (remote->options[RELAY_REMOTE_OPTION_TOTP_SECRET])); + } + else + { + weechat_printf ( + NULL, + " %s: %s", + remote->name, + weechat_config_string (remote->options[RELAY_REMOTE_OPTION_URL])); + } +} + +/* + * Callback for command "/remote". + */ + +int +relay_command_remote (const void *pointer, void *data, + struct t_gui_buffer *buffer, int argc, + char **argv, char **argv_eol) +{ + struct t_relay_remote *ptr_remote, *ptr_remote2; + int i, detailed_list, one_remote_found; + const char *ptr_password, *ptr_totp_secret; + char *remote_name; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) buffer; + + if ((argc == 1) + || (weechat_strcmp (argv[1], "list") == 0) + || (weechat_strcmp (argv[1], "listfull") == 0)) + { + /* list remotes */ + remote_name = NULL; + detailed_list = 0; + for (i = 1; i < argc; i++) + { + if (weechat_strcmp (argv[i], "list") == 0) + continue; + if (weechat_strcmp (argv[i], "listfull") == 0) + { + detailed_list = 1; + continue; + } + if (!remote_name) + remote_name = argv[i]; + } + if (remote_name) + { + one_remote_found = 0; + for (ptr_remote = relay_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + if (strstr (ptr_remote->name, remote_name)) + { + if (!one_remote_found) + { + weechat_printf (NULL, ""); + weechat_printf (NULL, + _("Relay remotes with \"%s\":"), + remote_name); + } + one_remote_found = 1; + relay_command_display_remote (ptr_remote, detailed_list); + } + } + if (!one_remote_found) + { + weechat_printf (NULL, + _("No relay remote found with \"%s\""), + remote_name); + } + } + else + { + if (relay_remotes) + { + weechat_printf (NULL, ""); + weechat_printf (NULL, _("All relay remotes:")); + for (ptr_remote = relay_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + relay_command_display_remote (ptr_remote, detailed_list); + } + } + else + weechat_printf (NULL, _("No relay remote")); + } + return WEECHAT_RC_OK; + } + + if (weechat_strcmp (argv[1], "add") == 0) + { + WEECHAT_COMMAND_MIN_ARGS(4, "add"); + + ptr_remote = relay_remote_search (argv[2]); + if (ptr_remote) + { + weechat_printf ( + NULL, + _("%s%s: remote \"%s\" already exists, can't add it!"), + weechat_prefix ("error"), RELAY_PLUGIN_NAME, ptr_remote->name); + return WEECHAT_RC_OK; + } + + if (!relay_remote_name_valid (argv[2])) + { + weechat_printf (NULL, + _("%s%s: invalid remote name: \"%s\""), + weechat_prefix ("error"), + RELAY_PLUGIN_NAME, + argv[2]); + return WEECHAT_RC_OK; + } + + + if (!relay_remote_url_valid (argv[3])) + { + weechat_printf (NULL, + _("%s%s: invalid remote URL: \"%s\""), + weechat_prefix ("error"), + RELAY_PLUGIN_NAME, + argv[3]); + return WEECHAT_RC_OK; + } + + ptr_password = NULL; + ptr_totp_secret = NULL; + + for (i = 4; i < argc; i++) + { + if (strncmp (argv[i], "-password=", 10) == 0) + { + ptr_password = argv[i] + 10; + } + else if (strncmp (argv[i], "-totp_secret=", 13) == 0) + { + ptr_totp_secret = argv[i] + 13; + } + else + { + weechat_printf (NULL, + _("%s%s: invalid remote option: \"%s\""), + weechat_prefix ("error"), + RELAY_PLUGIN_NAME, + argv[i]); + return WEECHAT_RC_OK; + } + } + + ptr_remote = relay_remote_new ( + argv[2], + argv[3], + (ptr_password) ? ptr_password : "", + (ptr_totp_secret) ? ptr_totp_secret : ""); + + if (ptr_remote) + { + weechat_printf (NULL, _("Remote \"%s\" created"), argv[2]); + } + else + { + weechat_printf ( + NULL, + _("%s%s: failed to create remote \"%s\""), + weechat_prefix ("error"), RELAY_PLUGIN_NAME, + argv[2]); + } + + return WEECHAT_RC_OK; + } + + if (weechat_strcmp (argv[1], "connect") == 0) + { + WEECHAT_COMMAND_MIN_ARGS(3, "connect"); + + ptr_remote = relay_remote_search (argv[2]); + if (!ptr_remote) + { + weechat_printf ( + NULL, + _("%s%s: remote \"%s\" not found for \"%s\" command"), + weechat_prefix ("error"), + RELAY_PLUGIN_NAME, + argv[2], + "remote connect"); + return WEECHAT_RC_OK; + } + + WEECHAT_COMMAND_ERROR; + } + + if (weechat_strcmp (argv[1], "rename") == 0) + { + WEECHAT_COMMAND_MIN_ARGS(4, "rename"); + + /* look for remote by name */ + ptr_remote = relay_remote_search (argv[2]); + if (!ptr_remote) + { + weechat_printf ( + NULL, + _("%s%s: remote \"%s\" not found for \"%s\" command"), + weechat_prefix ("error"), + RELAY_PLUGIN_NAME, + argv[2], + "remote rename"); + return WEECHAT_RC_OK; + } + + /* check if target name already exists */ + ptr_remote2 = relay_remote_search (argv[3]); + if (ptr_remote2) + { + weechat_printf ( + NULL, + _("%s%s: remote \"%s\" already exists for \"%s\" command"), + weechat_prefix ("error"), + RELAY_PLUGIN_NAME, + ptr_remote2->name, + "server rename"); + return WEECHAT_RC_OK; + } + + /* rename remote */ + if (relay_remote_rename (ptr_remote, argv[3])) + { + weechat_printf ( + NULL, + _("%s: remote \"%s\" has been renamed to \"%s\""), + RELAY_PLUGIN_NAME, + argv[2], + argv[3]); + return WEECHAT_RC_OK; + } + + WEECHAT_COMMAND_ERROR; + } + + if (weechat_strcmp (argv[1], "del") == 0) + { + WEECHAT_COMMAND_MIN_ARGS(3, "del"); + + /* look for remote by name */ + ptr_remote = relay_remote_search (argv[2]); + if (!ptr_remote) + { + weechat_printf ( + NULL, + _("%s%s: remote \"%s\" not found for \"%s\" command"), + weechat_prefix ("error"), + RELAY_PLUGIN_NAME, + argv[2], + "remote del"); + return WEECHAT_RC_OK; + } + if (!RELAY_STATUS_HAS_ENDED(ptr_remote->status)) + { + weechat_printf ( + NULL, + _("%s%s: you can not delete remote \"%s\" because you are " + "connected to. Try \"/remote disconnect %s\" before."), + weechat_prefix ("error"), + RELAY_PLUGIN_NAME, + argv[2], + argv[2]); + return WEECHAT_RC_OK; + } + remote_name = strdup (ptr_remote->name); + relay_remote_free (ptr_remote); + weechat_printf ( + NULL, + _("%s: remote \"%s\" has been deleted"), + RELAY_PLUGIN_NAME, + (remote_name) ? remote_name : "???"); + if (remote_name) + free (remote_name); + + return WEECHAT_RC_OK; + } + + WEECHAT_COMMAND_ERROR; +} + +/* * Hooks command. */ @@ -435,8 +740,13 @@ relay_command_init () "", N_("The \"irc\" protocol allows any IRC client (including WeeChat " "itself) to connect on the port."), - N_("The \"weechat\" protocol allows a remote interface to connect on " - "the port, see the list here: https://weechat.org/about/interfaces/"), + N_("The \"api\" protocol allows a remote interface (including " + "WeeChat itself) to connect on the port."), + N_("The \"weechat\" protocol allows a remote interface " + "(but not WeeChat itself) to connect on the port."), + "", + N_("The list of remote interfaces is here: " + "https://weechat.org/about/interfaces/"), "", N_("Without argument, this command opens buffer with list of relay " "clients."), @@ -462,4 +772,43 @@ relay_command_init () " || raw" " || tlscertkey", &relay_command_relay, NULL, NULL); + weechat_hook_command ( + "remote", + N_("control of remote relay servers"), + /* TRANSLATORS: only text between angle brackets (eg: "<name>") must be translated */ + N_("list|listfull [<name>]" + " || add <name> <url> [-<option>[=<value>]]" + " || connect <name>" + " || rename <name> <new_name>" + " || del <name>"), + WEECHAT_CMD_ARGS_DESC( + N_("raw[list]: list remote relay servers"), + N_("raw[listfull]: list remote relay servers (verbose)"), + N_("raw[add]: add a remote relay server"), + N_("name: name of remote relay server, for internal and display use; " + "this name is used to connect to the server and to set server " + "options: relay.remote.name.xxx"), + N_("url: URL of the remote, format is https://example.com:9000 " + "or http://example.com:9000 (plain-text connection, not recommended)"), + N_("option: set option for remote: password or totp_secret"), + N_("raw[connect]: connect to a remote relay server"), + N_("raw[rename]: rename a remote relay server"), + N_("raw[del]: delete a remote relay server"), + "", + N_("Without argument, this command opens buffer with list of relay " + "clients."), + "", + N_("Examples:"), + AI(" /remote add example https://localhost:9000 " + "-password=my_secret_password -totp_secret=secrettotp"), + AI(" /remote connect example"), + AI(" /remote del example")), + "list %(relay_remotes)" + " || listfull %(relay_remotes)" + " || add %(relay_remotes) https://localhost:9000 " + "-password=${xxx}|-totp_secret=${xxx}|%*" + " || connect %(relay_remotes)" + " || rename %(relay_remotes) %(relay_remotes)" + " || del %(relay_remotes)", + &relay_command_remote, NULL, NULL); } diff --git a/src/plugins/relay/relay-completion.c b/src/plugins/relay/relay-completion.c index 65bf39925..0c09693ce 100644 --- a/src/plugins/relay/relay-completion.c +++ b/src/plugins/relay/relay-completion.c @@ -25,6 +25,7 @@ #include "../weechat-plugin.h" #include "relay.h" +#include "relay-remote.h" #include "relay-server.h" @@ -156,6 +157,35 @@ relay_completion_free_port_cb (const void *pointer, void *data, } /* + * Adds relay remotes to completion list. + */ + +int +relay_completion_remotes_cb (const void *pointer, void *data, + const char *completion_item, + struct t_gui_buffer *buffer, + struct t_gui_completion *completion) +{ + struct t_relay_remote *ptr_remote; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) buffer; + (void) completion_item; + + for (ptr_remote = relay_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + weechat_completion_list_add (completion, + ptr_remote->name, + 0, WEECHAT_LIST_POS_SORT); + } + + return WEECHAT_RC_OK; +} + +/* * Hooks completions. */ @@ -172,4 +202,7 @@ relay_completion_init () weechat_hook_completion ("relay_free_port", N_("first free port for relay plugin"), &relay_completion_free_port_cb, NULL, NULL); + weechat_hook_completion ("relay_remotes", + N_("relay remotes"), + &relay_completion_remotes_cb, NULL, NULL); } diff --git a/src/plugins/relay/relay-config.c b/src/plugins/relay/relay-config.c index 150ed8d03..c37669af3 100644 --- a/src/plugins/relay/relay-config.c +++ b/src/plugins/relay/relay-config.c @@ -21,6 +21,7 @@ #include <unistd.h> #include <stdlib.h> +#include <stdio.h> #include <string.h> #include <limits.h> #include <regex.h> @@ -31,10 +32,11 @@ #include "../weechat-plugin.h" #include "relay.h" -#include "relay-config.h" #include "relay-client.h" +#include "relay-config.h" #include "relay-buffer.h" #include "relay-network.h" +#include "relay-remote.h" #include "relay-server.h" #include "irc/relay-irc.h" @@ -49,6 +51,7 @@ struct t_config_section *relay_config_section_network = NULL; struct t_config_section *relay_config_section_irc = NULL; struct t_config_section *relay_config_section_port = NULL; struct t_config_section *relay_config_section_path = NULL; +struct t_config_section *relay_config_section_remote = NULL; /* relay config, look section */ @@ -960,6 +963,217 @@ relay_config_create_option_port_path (const void *pointer, void *data, } /* + * Creates an option for a remote. + * + * Returns pointer to new option, NULL if error. + */ + +struct t_config_option * +relay_config_create_remote_option (const char *remote_name, int index_option, + const char *value) +{ + struct t_config_option *ptr_option; + int length; + char *option_name; + + ptr_option = NULL; + + length = strlen (remote_name) + 1 + + strlen (relay_remote_option_string[index_option]) + 1; + option_name = malloc (length); + if (!option_name) + return NULL; + + snprintf (option_name, length, "%s.%s", + remote_name, relay_remote_option_string[index_option]); + + switch (index_option) + { + case RELAY_REMOTE_OPTION_URL: + ptr_option = weechat_config_new_option ( + relay_config_file, relay_config_section_remote, + option_name, "string", + N_("remote URL"), + NULL, 0, 0, value, NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + break; + case RELAY_REMOTE_OPTION_PASSWORD: + ptr_option = weechat_config_new_option ( + relay_config_file, relay_config_section_remote, + option_name, "string", + N_("password"), + NULL, 0, 0, value, NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + break; + case RELAY_REMOTE_OPTION_TOTP_SECRET: + ptr_option = weechat_config_new_option ( + relay_config_file, relay_config_section_remote, + option_name, "string", + N_("TOTP secret"), + NULL, 0, 0, value, NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + break; + case RELAY_REMOTE_NUM_OPTIONS: + break; + } + + free (option_name); + + return ptr_option; +} + +/* + * Creates option for a temporary remote (when reading configuration file). + */ + +void +relay_config_create_option_temp (struct t_relay_remote *temp_remote, + int index_option, const char *value) +{ + struct t_config_option *new_option; + + new_option = relay_config_create_remote_option (temp_remote->name, + index_option, value); + if (new_option + && (index_option >= 0) && (index_option < RELAY_REMOTE_NUM_OPTIONS)) + { + temp_remote->options[index_option] = new_option; + } +} + +/* + * Uses temporary remotes (created by reading configuration file). + */ + +void +relay_config_use_temp_remotes () +{ + struct t_relay_remote *ptr_temp_remote, *next_temp_remote; + int i, num_options_ok; + + for (ptr_temp_remote = relay_remotes_temp; ptr_temp_remote; + ptr_temp_remote = ptr_temp_remote->next_remote) + { + num_options_ok = 0; + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + if (!ptr_temp_remote->options[i]) + { + ptr_temp_remote->options[i] = + relay_config_create_remote_option ( + ptr_temp_remote->name, + i, + relay_remote_option_default[i]); + } + if (ptr_temp_remote->options[i]) + num_options_ok++; + } + + if (num_options_ok == RELAY_REMOTE_NUM_OPTIONS) + { + relay_remote_new_with_options (ptr_temp_remote->name, + ptr_temp_remote->options); + } + else + { + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + if (ptr_temp_remote->options[i]) + { + weechat_config_option_free (ptr_temp_remote->options[i]); + ptr_temp_remote->options[i] = NULL; + } + } + } + } + + /* free all temporary remotes */ + while (relay_remotes_temp) + { + next_temp_remote = relay_remotes_temp->next_remote; + + if (relay_remotes_temp->name) + free (relay_remotes_temp->name); + free (relay_remotes_temp); + + relay_remotes_temp = next_temp_remote; + } + last_relay_remote_temp = NULL; +} + +/* + * Reads a remote option in relay configuration file. + */ + +int +relay_config_remote_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) +{ + char *pos_option, *remote_name; + struct t_relay_remote *ptr_temp_remote; + int index_option; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) config_file; + (void) section; + + if (!option_name) + return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE; + + pos_option = strchr (option_name, '.'); + if (!pos_option) + return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE; + + remote_name = weechat_strndup (option_name, pos_option - option_name); + if (!remote_name) + return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE; + + pos_option++; + + /* search temporary remote */ + for (ptr_temp_remote = relay_remotes_temp; ptr_temp_remote; + ptr_temp_remote = ptr_temp_remote->next_remote) + { + if (strcmp (ptr_temp_remote->name, remote_name) == 0) + break; + } + if (!ptr_temp_remote) + { + /* create new temporary remote */ + ptr_temp_remote = relay_remote_alloc (remote_name); + if (ptr_temp_remote) + relay_remote_add (ptr_temp_remote, &relay_remotes_temp, &last_relay_remote_temp); + } + + if (ptr_temp_remote) + { + index_option = relay_remote_search_option (pos_option); + if (index_option >= 0) + { + relay_config_create_option_temp (ptr_temp_remote, index_option, + value); + } + else + { + weechat_printf (NULL, + _("%sWarning: unknown option for section \"%s\": " + "%s (value: \"%s\")"), + weechat_prefix ("error"), + RELAY_CONFIG_SECTION_REMOTE, + option_name, value); + } + } + + free (remote_name); + + return WEECHAT_CONFIG_OPTION_SET_OK_SAME_VALUE; +} + +/* * Reloads relay configuration file. */ @@ -1196,10 +1410,10 @@ relay_config_init () NULL, NULL, NULL, &relay_config_refresh_cb, NULL, NULL, NULL, NULL, NULL); - relay_config_color_status[RELAY_STATUS_WAITING_AUTH] = weechat_config_new_option ( + relay_config_color_status[RELAY_STATUS_AUTHENTICATING] = weechat_config_new_option ( relay_config_file, relay_config_section_color, - "status_waiting_auth", "color", - N_("text color for \"waiting authentication\" status"), + "status_authenticating", "color", + N_("text color for \"authenticating\" status"), NULL, 0, 0, "yellow", NULL, 0, NULL, NULL, NULL, &relay_config_refresh_cb, NULL, NULL, @@ -1518,6 +1732,17 @@ relay_config_init () &relay_config_create_option_port_path, NULL, NULL, NULL, NULL, NULL); + /* remote */ + relay_config_section_remote = weechat_config_new_section ( + relay_config_file, + RELAY_CONFIG_SECTION_REMOTE, + 0, 0, + &relay_config_remote_read_cb, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL); + return 1; } @@ -1538,6 +1763,7 @@ relay_config_read () relay_config_change_network_allowed_ips (NULL, NULL, NULL); relay_config_change_network_password_hash_algo (NULL, NULL, NULL); relay_config_change_irc_backlog_tags (NULL, NULL, NULL); + relay_config_use_temp_remotes (); } return rc; } diff --git a/src/plugins/relay/relay-config.h b/src/plugins/relay/relay-config.h index e454ba97e..afc8afc96 100644 --- a/src/plugins/relay/relay-config.h +++ b/src/plugins/relay/relay-config.h @@ -24,6 +24,7 @@ #define RELAY_CONFIG_NAME "relay" #define RELAY_CONFIG_PRIO_NAME (TO_STR(RELAY_PLUGIN_PRIORITY) "|" RELAY_CONFIG_NAME) +#define RELAY_CONFIG_SECTION_REMOTE "remote" #define RELAY_CONFIG_VERSION 2 @@ -85,6 +86,9 @@ extern int relay_config_create_option_port_path (const void *pointer, void *data const char *value); extern int relay_config_check_path_length (const char *path); extern int relay_config_check_path_available (const char *path); +extern struct t_config_option *relay_config_create_remote_option (const char *remote_name, + int index_option, + const char *value); extern int relay_config_init (); extern int relay_config_read (); extern int relay_config_write (); diff --git a/src/plugins/relay/relay-info.c b/src/plugins/relay/relay-info.c index c7fb864fd..18322ffbf 100644 --- a/src/plugins/relay/relay-info.c +++ b/src/plugins/relay/relay-info.c @@ -69,7 +69,7 @@ relay_info_info_relay_client_count_cb (const void *pointer, void *data, protocol = relay_protocol_search (items[0]); if (protocol < 0) { - status = relay_client_status_search (items[0]); + status = relay_status_search (items[0]); if (status < 0) goto end; } @@ -86,7 +86,7 @@ relay_info_info_relay_client_count_cb (const void *pointer, void *data, } if (strcmp (items[1], "*") != 0) { - status = relay_client_status_search (items[1]); + status = relay_status_search (items[1]); if (status < 0) goto end; } diff --git a/src/plugins/relay/relay-remote.c b/src/plugins/relay/relay-remote.c new file mode 100644 index 000000000..b10222977 --- /dev/null +++ b/src/plugins/relay/relay-remote.c @@ -0,0 +1,677 @@ +/* + * relay-remote.c - remote relay server functions for relay plugin + * + * Copyright (C) 2024 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <gnutls/gnutls.h> + +#include "../weechat-plugin.h" +#include "relay.h" +#include "relay-config.h" +#include "relay-remote.h" + + +char *relay_remote_option_string[RELAY_REMOTE_NUM_OPTIONS] = +{ "url", "password", "totp_secret" }; +char *relay_remote_option_default[RELAY_REMOTE_NUM_OPTIONS] = +{ "", "", "" }; + +struct t_relay_remote *relay_remotes = NULL; +struct t_relay_remote *last_relay_remote = NULL; +int relay_remotes_count = 0; /* number of remotes */ + +struct t_relay_remote *relay_remotes_temp = NULL; /* first temp. remote */ +struct t_relay_remote *last_relay_remote_temp = NULL; /* last temp. remote */ + + +/* + * Searches for a remote option name. + * + * Returns index of option in enum t_relay_remote_option, -1 if not found. + */ + +int +relay_remote_search_option (const char *option_name) +{ + int i; + + if (!option_name) + return -1; + + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + if (strcmp (relay_remote_option_string[i], option_name) == 0) + return i; + } + + /* remote option not found */ + return -1; +} + +/* + * Checks if a remote pointer is valid. + * + * Returns: + * 1: remote exists + * 0: remote does not exist + */ + +int +relay_remote_valid (struct t_relay_remote *remote) +{ + struct t_relay_remote *ptr_remote; + + if (!remote) + return 0; + + for (ptr_remote = relay_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + if (ptr_remote == remote) + return 1; + } + + /* remote not found */ + return 0; +} + +/* + * Searches for a remote by name. + * + * Returns pointer to remote found, NULL if not found. + */ + +struct t_relay_remote * +relay_remote_search (const char *name) +{ + struct t_relay_remote *ptr_remote; + + if (!name || !name[0]) + return NULL; + + for (ptr_remote = relay_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + if (strcmp (ptr_remote->name, name) == 0) + return ptr_remote; + } + + /* remote not found */ + return NULL; +} + +/* + * Searches for a remote by number (first remote is 0). + * + * Returns pointer to remote found, NULL if not found. + */ + +struct t_relay_remote * +relay_remote_search_by_number (int number) +{ + struct t_relay_remote *ptr_remote; + int i; + + i = 0; + for (ptr_remote = relay_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + if (i == number) + return ptr_remote; + i++; + } + + /* remote not found */ + return NULL; +} + +/* + * Checks if a remote name is valid: it must contain only alphabetic chars + * or digits. + * + * Returns: + * 1: name is valid + * 0: name is invalid + */ + +int +relay_remote_name_valid (const char *name) +{ + const char *ptr_name; + + if (!name || !name[0]) + return 0; + + ptr_name = name; + while (ptr_name[0]) + { + if (!isalnum ((unsigned char)ptr_name[0])) + return 0; + ptr_name++; + } + + /* name is valid */ + return 1; +} + +/* + * Checks if a remote URL is valid; + * + * Returns: + * 1: URL is valid + * 0: URL is invalid + */ + +int +relay_remote_url_valid (const char *url) +{ + const char *pos; + + if (!url || !url[0]) + return 0; + + /* URL must start with "https://" or "http://" */ + if ((strncmp (url, "https://", 8) != 0) && (strncmp (url, "http://", 7) != 0)) + return 0; + + pos = strchr (url + 7, ':'); + + /* invalid port? */ + if (pos && !isdigit ((unsigned char)pos[1])) + return 0; + + /* URL is valid */ + return 1; +} + +/* + * Sends a signal with the status of remote ("relay_remote_xxx"). + */ + +void +relay_remote_send_signal (struct t_relay_remote *remote) +{ + char signal[128]; + + snprintf (signal, sizeof (signal), + "relay_remote_%s", + relay_status_name[remote->status]); + weechat_hook_signal_send (signal, WEECHAT_HOOK_SIGNAL_POINTER, remote); +} + +/* + * Allocates and initializes new remote structure. + * + * Returns pointer to new remote, NULL if error. + */ + +struct t_relay_remote * +relay_remote_alloc (const char *name) +{ + struct t_relay_remote *new_remote; + int i; + + if (!relay_remote_name_valid (name)) + return NULL; + + if (relay_remote_search (name)) + return NULL; + + new_remote = malloc (sizeof (*new_remote)); + if (!new_remote) + return NULL; + + new_remote->name = strdup (name); + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + new_remote->options[i] = NULL; + } + new_remote->address = NULL; + new_remote->port = 0; + new_remote->tls = 0; + new_remote->status = RELAY_STATUS_DISCONNECTED; + new_remote->sock = -1; + new_remote->gnutls_sess = NULL; + new_remote->prev_remote = NULL; + new_remote->next_remote = NULL; + + return new_remote; +} + +/* + * Searches for position of remote in list (to keep remotes sorted by name). + */ + +struct t_relay_remote * +relay_remote_find_pos (struct t_relay_remote *remote, + struct t_relay_remote *list_remotes) +{ + struct t_relay_remote *ptr_remote; + + for (ptr_remote = list_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + if (weechat_strcmp (remote->name, ptr_remote->name) < 0) + return ptr_remote; + } + + /* position not found */ + return NULL; +} + +/* + * Adds a remote in a linked list. + */ + +void +relay_remote_add (struct t_relay_remote *remote, + struct t_relay_remote **list_remotes, + struct t_relay_remote **last_list_remote) +{ + struct t_relay_remote *pos_remote; + + pos_remote = relay_remote_find_pos (remote, *list_remotes); + if (pos_remote) + { + /* add remote before "pos_remote" */ + remote->prev_remote = pos_remote->prev_remote; + remote->next_remote = pos_remote; + if (pos_remote->prev_remote) + (pos_remote->prev_remote)->next_remote = remote; + else + *list_remotes = remote; + pos_remote->prev_remote = remote; + } + else + { + /* add remote to end of list */ + remote->prev_remote = *last_list_remote; + remote->next_remote = NULL; + if (*last_list_remote) + (*last_list_remote)->next_remote = remote; + else + *list_remotes = remote; + *last_list_remote = remote; + } +} + +/* + * Creates a new remote with options. + * + * Returns pointer to new remote, NULL if error. + */ + +struct t_relay_remote * +relay_remote_new_with_options (const char *name, struct t_config_option **options) +{ + struct t_relay_remote *new_remote; + int i; + + new_remote = relay_remote_alloc (name); + if (!new_remote) + return NULL; + + if (!relay_remote_url_valid (weechat_config_string (options[RELAY_REMOTE_OPTION_URL]))) + { + free (new_remote); + return NULL; + } + + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + new_remote->options[i] = options[i]; + } + relay_remote_add (new_remote, &relay_remotes, &last_relay_remote); + relay_remotes_count++; + + relay_remote_send_signal (new_remote); + + return new_remote; +} + +/* + * Creates a new remote. + * + * Returns pointer to new remote, NULL if error. + */ + +struct t_relay_remote * +relay_remote_new (const char *name, const char *url, const char *password, + const char *totp_secret) +{ + struct t_config_option *option[RELAY_REMOTE_NUM_OPTIONS]; + const char *value[RELAY_REMOTE_NUM_OPTIONS]; + struct t_relay_remote *new_remote; + int i; + + if (!name || !name[0] || !url || !url[0]) + return NULL; + + new_remote = malloc (sizeof (*new_remote)); + if (!new_remote) + return NULL; + + value[RELAY_REMOTE_OPTION_URL] = url; + value[RELAY_REMOTE_OPTION_PASSWORD] = password; + value[RELAY_REMOTE_OPTION_TOTP_SECRET] = totp_secret; + + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + option[i] = relay_config_create_remote_option (name, i, value[i]); + } + + new_remote = relay_remote_new_with_options (name, option); + if (!new_remote) + { + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + weechat_config_option_free (option[i]); + } + } + + return new_remote; +} + +/* + * Creates a new remote using an infolist. + * + * This is called to restore remotes after /upgrade. + */ + +struct t_relay_remote * +relay_remote_new_with_infolist (struct t_infolist *infolist) +{ + struct t_relay_remote *new_remote; + + new_remote = malloc (sizeof (*new_remote)); + if (!new_remote) + return NULL; + + new_remote->address = strdup (weechat_infolist_string (infolist, "name")); + new_remote->address = strdup (weechat_infolist_string (infolist, "address")); + new_remote->port = weechat_infolist_integer (infolist, "port"); + new_remote->tls = weechat_infolist_integer (infolist, "tls"); + new_remote->status = weechat_infolist_integer (infolist, "status"); + new_remote->sock = weechat_infolist_integer (infolist, "sock"); + new_remote->gnutls_sess = NULL; + new_remote->prev_remote = NULL; + new_remote->next_remote = relay_remotes; + if (relay_remotes) + relay_remotes->prev_remote = new_remote; + else + last_relay_remote = new_remote; + relay_remotes = new_remote; + + relay_remotes_count++; + + return new_remote; +} + +/* + * Sets status for a remote. + */ + +void +relay_remote_set_status (struct t_relay_remote *remote, + enum t_relay_status status) +{ + /* + * IMPORTANT: if changes are made in this function or sub-functions called, + * please also update the function relay_remote_add_to_infolist: + * when the flag force_disconnected_state is set to 1 we simulate + * a disconnected state for remote in infolist (used on /upgrade -save) + */ + + remote->status = status; + + relay_remote_send_signal (remote); +} + +/* + * Renames a remote. + * + * Returns: + * 1: OK + * 0: error (remote not renamed) + */ + +int +relay_remote_rename (struct t_relay_remote *remote, const char *name) +{ + int length, i; + char *option_name; + + if (!remote || !name || !name[0] || !relay_remote_name_valid (name) + || relay_remote_search (name)) + { + return 0; + } + + length = strlen (name) + 64; + option_name = malloc (length); + if (!option_name) + return 0; + + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + if (remote->options[i]) + { + snprintf (option_name, length, + "%s.%s", + name, + relay_remote_option_string[i]); + weechat_config_option_rename (remote->options[i], option_name); + } + } + + if (remote->name) + free (remote->name); + remote->name = strdup (name); + + free (option_name); + + /* re-insert remote in list (for sorting remotes by name) */ + if (remote->prev_remote) + (remote->prev_remote)->next_remote = remote->next_remote; + else + relay_remotes = remote->next_remote; + if (remote->next_remote) + (remote->next_remote)->prev_remote = remote->prev_remote; + else + last_relay_remote = remote->prev_remote; + relay_remote_add (remote, &relay_remotes, &last_relay_remote); + + return 1; +} + +/* + * Deletes a remote. + */ + +void +relay_remote_free (struct t_relay_remote *remote) +{ + int i; + + if (!remote) + return; + + /* remove remote from list */ + if (remote->prev_remote) + (remote->prev_remote)->next_remote = remote->next_remote; + if (remote->next_remote) + (remote->next_remote)->prev_remote = remote->prev_remote; + if (relay_remotes == remote) + relay_remotes = remote->next_remote; + if (last_relay_remote == remote) + last_relay_remote = remote->prev_remote; + + /* free data */ + if (remote->name) + free (remote->name); + for (i = 0; i < RELAY_REMOTE_NUM_OPTIONS; i++) + { + if (remote->options[i]) + weechat_config_option_free (remote->options[i]); + } + if (remote->address) + free (remote->address); + + free (remote); + + relay_remotes_count--; +} + +/* + * Removes all remotes. + */ + +void +relay_remote_free_all () +{ + while (relay_remotes) + { + relay_remote_free (relay_remotes); + } +} + +/* + * Disconnects one remote. + */ + +void +relay_remote_disconnect (struct t_relay_remote *remote) +{ + if (remote->sock >= 0) + { + relay_remote_set_status (remote, RELAY_STATUS_DISCONNECTED); + } +} + +/* + * Disconnects all remotes. + */ + +void +relay_remote_disconnect_all () +{ + struct t_relay_remote *ptr_remote; + + for (ptr_remote = relay_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + relay_remote_disconnect (ptr_remote); + } +} + +/* + * Adds a remote in an infolist. + * + * If force_disconnected_state == 1, the infolist contains the remote + * in a disconnected state (but the remote is unchanged, still connected if it + * was). + * + * Returns: + * 1: OK + * 0: error + */ + +int +relay_remote_add_to_infolist (struct t_infolist *infolist, + struct t_relay_remote *remote, + int force_disconnected_state) +{ + struct t_infolist_item *ptr_item; + + if (!infolist || !remote) + return 0; + + ptr_item = weechat_infolist_new_item (infolist); + if (!ptr_item) + return 0; + + if (!weechat_infolist_new_var_string (ptr_item, "name", remote->name)) + return 0; + if (!weechat_infolist_new_var_string (ptr_item, "address", remote->address)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "port", remote->port)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "tls", remote->tls)) + return 0; + if (!RELAY_STATUS_HAS_ENDED(remote->status) && force_disconnected_state) + { + if (!weechat_infolist_new_var_integer (ptr_item, "status", RELAY_STATUS_DISCONNECTED)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "sock", -1)) + return 0; + } + else + { + if (!weechat_infolist_new_var_integer (ptr_item, "status", remote->status)) + return 0; + if (!weechat_infolist_new_var_integer (ptr_item, "sock", remote->sock)) + return 0; + } + + return 1; +} + +/* + * Prints remotes in WeeChat log file (usually for crash dump). + */ + +void +relay_remote_print_log () +{ + struct t_relay_remote *ptr_remote; + + for (ptr_remote = relay_remotes; ptr_remote; + ptr_remote = ptr_remote->next_remote) + { + weechat_log_printf (""); + weechat_log_printf ("[relay remote (addr:0x%lx)]", ptr_remote); + weechat_log_printf (" name. . . . . . . . . : '%s'", ptr_remote->name); + weechat_log_printf (" url . . . . . . . . . : '%s'", + weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_URL])); + weechat_log_printf (" password. . . . . . . : '%s'", + weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_PASSWORD])); + weechat_log_printf (" totp_secret . . . . . : '%s'", + weechat_config_string (ptr_remote->options[RELAY_REMOTE_OPTION_TOTP_SECRET])); + weechat_log_printf (" address . . . . . . . : '%s'", ptr_remote->address); + weechat_log_printf (" port. . . . . . . . . : %d", ptr_remote->port); + weechat_log_printf (" tls . . . . . . . . . : %d", ptr_remote->tls); + weechat_log_printf (" status. . . . . . . . : %d (%s)", + ptr_remote->status, + relay_status_string[ptr_remote->status]); + weechat_log_printf (" sock. . . . . . . . . : %d", ptr_remote->sock); + weechat_log_printf (" gnutls_sess . . . . . : 0x%lx", ptr_remote->gnutls_sess); + weechat_log_printf (" prev_remote . . . . . : 0x%lx", ptr_remote->prev_remote); + weechat_log_printf (" next_remote . . . . . : 0x%lx", ptr_remote->next_remote); + } +} diff --git a/src/plugins/relay/relay-remote.h b/src/plugins/relay/relay-remote.h new file mode 100644 index 000000000..aa7b69e6e --- /dev/null +++ b/src/plugins/relay/relay-remote.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2024 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef WEECHAT_PLUGIN_RELAY_REMOTE_H +#define WEECHAT_PLUGIN_RELAY_REMOTE_H + +#include <gnutls/gnutls.h> + +enum t_relay_remote_option +{ + RELAY_REMOTE_OPTION_URL = 0, /* remote URL */ + RELAY_REMOTE_OPTION_PASSWORD, /* password for remote relay */ + RELAY_REMOTE_OPTION_TOTP_SECRET, /* TOTP secret for remote relay */ + /* number of relay remote options */ + RELAY_REMOTE_NUM_OPTIONS, +}; + +/* relay remote */ + +struct t_relay_remote +{ + char *name; /* internal remote name */ + struct t_config_option *options[RELAY_REMOTE_NUM_OPTIONS]; + char *address; /* address */ + int port; /* port number */ + int tls; /* 1 if TLS is enabled */ + enum t_relay_status status; /* status (connecting, active,..) */ + int sock; /* connected socket */ + gnutls_session_t gnutls_sess; /* gnutls session (only if TLS used) */ + struct t_relay_remote *prev_remote;/* link to previous remote */ + struct t_relay_remote *next_remote;/* link to next remote */ +}; + +extern char *relay_remote_option_string[]; +extern char *relay_remote_option_default[]; +extern struct t_relay_remote *relay_remotes; +extern struct t_relay_remote *last_relay_remote; +extern int relay_remotes_count; +extern struct t_relay_remote *relay_remotes_temp; +extern struct t_relay_remote *last_relay_remote_temp; + +extern int relay_remote_search_option (const char *option_name); +extern int relay_remote_valid (struct t_relay_remote *remote); +extern struct t_relay_remote *relay_remote_search (const char *name); +extern struct t_relay_remote *relay_remote_search_by_number (int number); +extern int relay_remote_name_valid (const char *name); +extern int relay_remote_url_valid (const char *url); +extern struct t_relay_remote *relay_remote_alloc (const char *name); +extern void relay_remote_add (struct t_relay_remote *remote, + struct t_relay_remote **list_remotes, + struct t_relay_remote **last_list_remote); +extern struct t_relay_remote *relay_remote_new_with_options (const char *name, + struct t_config_option **options); +extern struct t_relay_remote *relay_remote_new (const char *name, + const char *url, + const char *password, + const char *totp_secret); +extern struct t_relay_remote *relay_remote_new_with_infolist (struct t_infolist *infolist); +extern void relay_remote_set_status (struct t_relay_remote *remote, + enum t_relay_status status); +extern int relay_remote_rename (struct t_relay_remote *remote, const char *name); +extern void relay_remote_free (struct t_relay_remote *remote); +extern void relay_remote_free_all (); +extern void relay_remote_disconnect (struct t_relay_remote *remote); +extern void relay_remote_disconnect_all (); +extern int relay_remote_add_to_infolist (struct t_infolist *infolist, + struct t_relay_remote *remote, + int force_disconnected_state); +extern void relay_remote_print_log (); + +#endif /* WEECHAT_PLUGIN_RELAY_REMOTE_H */ diff --git a/src/plugins/relay/relay.c b/src/plugins/relay/relay.c index debce9167..bf3188937 100644 --- a/src/plugins/relay/relay.c +++ b/src/plugins/relay/relay.c @@ -32,6 +32,7 @@ #include "relay-info.h" #include "relay-network.h" #include "relay-raw.h" +#include "relay-remote.h" #include "relay-server.h" #include "relay-upgrade.h" @@ -49,6 +50,15 @@ struct t_weechat_plugin *weechat_relay_plugin = NULL; char *relay_protocol_string[] = /* strings for protocols */ { "weechat", "irc", "api" }; +char *relay_status_string[] = /* status strings for display */ +{ N_("connecting"), N_("authenticating"), + N_("connected"), N_("authentication failed"), N_("disconnected") +}; +char *relay_status_name[] = /* name of status (for signal/info) */ +{ "connecting", "waiting_auth", + "connected", "auth_failed", "disconnected" +}; + struct t_hdata *relay_hdata_buffer = NULL; struct t_hdata *relay_hdata_lines = NULL; struct t_hdata *relay_hdata_line = NULL; @@ -90,6 +100,30 @@ relay_protocol_search (const char *name) } /* + * Searches for a status. + * + * Returns index of status in enum t_relay_status, -1 if status is not found. + */ + +int +relay_status_search (const char *name) +{ + int i; + + if (!name) + return -1; + + for (i = 0; i < RELAY_NUM_STATUS; i++) + { + if (strcmp (relay_status_name[i], name) == 0) + return i; + } + + /* status not found */ + return -1; +} + +/* * Callback for signal "upgrade". */ @@ -191,6 +225,7 @@ relay_debug_dump_cb (const void *pointer, void *data, relay_server_print_log (); relay_client_print_log (); + relay_remote_print_log (); weechat_log_printf (""); weechat_log_printf ("***** End of \"%s\" plugin dump *****", diff --git a/src/plugins/relay/relay.h b/src/plugins/relay/relay.h index b2797f2ab..f933021b7 100644 --- a/src/plugins/relay/relay.h +++ b/src/plugins/relay/relay.h @@ -49,13 +49,33 @@ enum t_relay_protocol RELAY_NUM_PROTOCOLS, }; +/* client/remote status */ + +enum t_relay_status +{ + RELAY_STATUS_CONNECTING = 0, /* network connection in progress */ + RELAY_STATUS_AUTHENTICATING, /* authentication in progress */ + RELAY_STATUS_CONNECTED, /* connected and authenticated */ + RELAY_STATUS_AUTH_FAILED, /* authentication failed */ + RELAY_STATUS_DISCONNECTED, /* disconnected */ + /* number of relay status */ + RELAY_NUM_STATUS, +}; + +#define RELAY_STATUS_HAS_ENDED(status) \ + ((status == RELAY_STATUS_AUTH_FAILED) || \ + (status == RELAY_STATUS_DISCONNECTED)) + #define RELAY_COLOR_CHAT weechat_color("chat") #define RELAY_COLOR_CHAT_HOST weechat_color("chat_host") #define RELAY_COLOR_CHAT_BUFFER weechat_color("chat_buffer") #define RELAY_COLOR_CHAT_CLIENT weechat_color(weechat_config_string(relay_config_color_client)) extern char *relay_protocol_string[]; +extern char *relay_status_string[]; +extern char *relay_status_name[]; extern int relay_protocol_search (const char *name); +extern int relay_status_search (const char *name); #endif /* WEECHAT_PLUGIN_RELAY_H */ diff --git a/src/plugins/relay/weechat/relay-weechat-protocol.c b/src/plugins/relay/weechat/relay-weechat-protocol.c index 9340d9453..56aa69cfe 100644 --- a/src/plugins/relay/weechat/relay-weechat-protocol.c +++ b/src/plugins/relay/weechat/relay-weechat-protocol.c @@ -225,7 +225,7 @@ RELAY_WEECHAT_PROTOCOL_CALLBACK(handshake) RELAY_WEECHAT_PROTOCOL_MIN_ARGS(0); - if (client->status != RELAY_STATUS_WAITING_AUTH) + if (client->status != RELAY_STATUS_AUTHENTICATING) return WEECHAT_RC_OK; /* only one handshake is allowed */ @@ -1724,7 +1724,7 @@ relay_weechat_protocol_recv (struct t_relay_client *client, const char *data) { NULL, NULL } }; - if (!data || !data[0] || RELAY_CLIENT_HAS_ENDED(client)) + if (!data || !data[0] || RELAY_STATUS_HAS_ENDED(client->status)) return; /* display debug message */ diff --git a/src/plugins/relay/weechat/relay-weechat.c b/src/plugins/relay/weechat/relay-weechat.c index b217c3d04..12fa78b1a 100644 --- a/src/plugins/relay/weechat/relay-weechat.c +++ b/src/plugins/relay/weechat/relay-weechat.c @@ -286,12 +286,12 @@ relay_weechat_alloc_with_infolist (struct t_relay_client *client, &relay_weechat_free_buffers_nicklist); RELAY_WEECHAT_DATA(client, hook_timer_nicklist) = NULL; - if (!RELAY_CLIENT_HAS_ENDED(client)) + if (!RELAY_STATUS_HAS_ENDED(client->status)) relay_weechat_hook_signals (client); } /* - * Returns the client initial status: it is always "waiting_auth" for weechat + * Returns the client initial status: it is always "authenticating" for weechat * protocol because we always expect the "init" command, even without any * password. */ @@ -302,7 +302,7 @@ relay_weechat_get_initial_status (struct t_relay_client *client) /* make C compiler happy */ (void) client; - return RELAY_STATUS_WAITING_AUTH; + return RELAY_STATUS_AUTHENTICATING; } /* diff --git a/tests/unit/plugins/relay/api/test-relay-api.cpp b/tests/unit/plugins/relay/api/test-relay-api.cpp index 08d1cb4bb..795b30daa 100644 --- a/tests/unit/plugins/relay/api/test-relay-api.cpp +++ b/tests/unit/plugins/relay/api/test-relay-api.cpp @@ -126,7 +126,7 @@ TEST(RelayApi, AllocWithInfolist) TEST(RelayApi, GetInitialStatus) { - LONGS_EQUAL(RELAY_STATUS_WAITING_AUTH, relay_api_get_initial_status (NULL)); + LONGS_EQUAL(RELAY_STATUS_AUTHENTICATING, relay_api_get_initial_status (NULL)); } /* |