summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/relay/CMakeLists.txt1
-rw-r--r--src/plugins/relay/api/remote/relay-remote-event.c479
-rw-r--r--src/plugins/relay/api/remote/relay-remote-event.h46
-rw-r--r--src/plugins/relay/api/remote/relay-remote-network.c46
-rw-r--r--src/plugins/relay/api/remote/relay-remote-network.h4
-rw-r--r--src/plugins/relay/relay-remote.c5
-rw-r--r--src/plugins/relay/relay-remote.h1
7 files changed, 581 insertions, 1 deletions
diff --git a/src/plugins/relay/CMakeLists.txt b/src/plugins/relay/CMakeLists.txt
index 280b155ec..cbafd394d 100644
--- a/src/plugins/relay/CMakeLists.txt
+++ b/src/plugins/relay/CMakeLists.txt
@@ -50,6 +50,7 @@ if(ENABLE_CJSON)
api/relay-api-msg.c api/relay-api-msg.h
api/relay-api-protocol.c api/relay-api-protocol.h
# API relay remote
+ api/remote/relay-remote-event.c api/remote/relay-remote-event.h
api/remote/relay-remote-network.c api/remote/relay-remote-network.h
)
endif()
diff --git a/src/plugins/relay/api/remote/relay-remote-event.c b/src/plugins/relay/api/remote/relay-remote-event.c
new file mode 100644
index 000000000..2ab445be0
--- /dev/null
+++ b/src/plugins/relay/api/remote/relay-remote-event.c
@@ -0,0 +1,479 @@
+/*
+ * relay-remote-event.c - process events received from relay remote
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <gcrypt.h>
+#include <gnutls/gnutls.h>
+#include <cjson/cJSON.h>
+
+#include "../../../weechat-plugin.h"
+#include "../../relay.h"
+#include "../../relay-auth.h"
+#include "../../relay-http.h"
+#include "../../relay-raw.h"
+#include "../../relay-remote.h"
+#include "../../relay-websocket.h"
+#include "../relay-api.h"
+#include "relay-remote-event.h"
+#include "relay-remote-network.h"
+
+#define JSON_GET_NUM(__json, __var, __default) \
+ json_obj = cJSON_GetObjectItem (__json, #__var); \
+ if (json_obj && cJSON_IsNumber (json_obj)) \
+ __var = cJSON_GetNumberValue (json_obj); \
+ else \
+ __var = __default;
+
+#define JSON_GET_STR(__json, __var) \
+ json_obj = cJSON_GetObjectItem (__json, #__var); \
+ if (json_obj && cJSON_IsString (json_obj)) \
+ __var = cJSON_GetStringValue (json_obj); \
+ else \
+ __var = NULL;
+
+
+/*
+ * Searches a buffer used for a remote.
+ *
+ * Returns pointer to buffer, NULL if not found.
+ */
+
+struct t_gui_buffer *
+relay_remote_event_search_buffer (struct t_relay_remote *remote, long long id)
+{
+ struct t_gui_buffer *ptr_buffer, *ptr_buffer_found;
+ const char *ptr_name, *ptr_id;
+ char str_id[64];
+
+ if (!remote || (id < 0))
+ return NULL;
+
+ ptr_buffer_found = NULL;
+
+ snprintf (str_id, sizeof (str_id), "%lld", id);
+
+ ptr_buffer = weechat_hdata_get_list (relay_hdata_buffer, "gui_buffers");
+ while (ptr_buffer)
+ {
+ ptr_name = weechat_buffer_get_string (ptr_buffer, "localvar_relay_remote");
+ ptr_id = weechat_buffer_get_string (ptr_buffer, "localvar_relay_remote_id");
+ if (ptr_name
+ && ptr_id
+ && (strcmp (ptr_name, remote->name) == 0)
+ && (strcmp (ptr_id, str_id) == 0))
+ {
+ ptr_buffer_found = ptr_buffer;
+ break;
+ }
+ ptr_buffer = weechat_hdata_move (relay_hdata_buffer, ptr_buffer, 1);
+ }
+
+ return ptr_buffer_found;
+}
+
+/*
+ * Gets the remote buffer id.
+ *
+ * Returns id found, -1 if error.
+ */
+
+long long
+relay_remote_event_get_buffer_id (struct t_gui_buffer *buffer)
+{
+ const char *ptr_id;
+ char *error;
+ long long buffer_id;
+
+ if (!buffer)
+ return -1;
+
+ ptr_id = weechat_buffer_get_string (buffer, "localvar_relay_remote_id");
+ if (!ptr_id)
+ return -1;
+
+ error = NULL;
+ buffer_id = strtoll (ptr_id, &error, 10);
+ if (!error || error[0])
+ return -1;
+
+ return buffer_id;
+}
+
+/*
+ * Callback for body type "line".
+ */
+
+RELAY_REMOTE_EVENT_CALLBACK(line)
+{
+ cJSON *json_obj;
+ const char *date, *prefix, *message;
+ struct timeval tv_date;
+
+ if (!event->buffer)
+ return WEECHAT_RC_ERROR;
+
+ JSON_GET_STR(event->json, date);
+ JSON_GET_STR(event->json, prefix);
+ JSON_GET_STR(event->json, message);
+
+ if (!weechat_util_parse_time (date, &tv_date))
+ {
+ tv_date.tv_sec = 0;
+ tv_date.tv_usec = 0;
+ }
+
+ weechat_printf_datetime_tags (
+ event->buffer,
+ tv_date.tv_sec,
+ tv_date.tv_usec,
+ NULL,
+ "%s%s%s",
+ (prefix && prefix[0]) ? prefix : "",
+ (prefix && prefix[0]) ? "\t" : "",
+ message);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Applies properties to a buffer.
+ */
+
+void
+relay_remote_event_apply_props (void *data,
+ struct t_hashtable *hashtable,
+ const void *key,
+ const void *value)
+{
+ /* make C compiler happy */
+ (void) hashtable;
+
+ weechat_buffer_set ((struct t_gui_buffer *)data,
+ (const char *)key,
+ (const char *)value);
+}
+
+/*
+ * Callback for remote buffer input.
+ */
+
+int
+relay_remote_event_buffer_input_cb (const void *pointer,
+ void *data,
+ struct t_gui_buffer *buffer,
+ const char *input_data)
+{
+ struct t_relay_remote *remote;
+ cJSON *json, *json_body;
+ long long buffer_id;
+
+ /* make C compiler happy */
+ (void) data;
+
+ remote = (struct t_relay_remote *)pointer;
+
+ json = NULL;
+
+ buffer_id = relay_remote_event_get_buffer_id (buffer);
+ if (buffer_id < 0)
+ goto error;
+
+ json = cJSON_CreateObject ();
+ if (!json)
+ goto error;
+
+ cJSON_AddItemToObject (json, "request",
+ cJSON_CreateString ("POST /api/input"));
+ json_body = cJSON_CreateObject ();
+ if (!json_body)
+ goto error;
+
+ cJSON_AddItemToObject (json_body, "buffer_id",
+ cJSON_CreateNumber (buffer_id));
+ cJSON_AddItemToObject (json_body, "command",
+ cJSON_CreateString (input_data));
+ cJSON_AddItemToObject (json, "body", json_body);
+
+ relay_remote_network_send_json (remote, json);
+
+ cJSON_Delete (json);
+
+ return WEECHAT_RC_OK;
+
+error:
+ if (json)
+ cJSON_Delete (json);
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Callback for body type "buffer".
+ */
+
+RELAY_REMOTE_EVENT_CALLBACK(buffer)
+{
+ struct t_gui_buffer *ptr_buffer;
+ struct t_hashtable *buffer_props;
+ struct t_relay_remote_event event_line;
+ cJSON *json_obj, *json_lines, *json_line;
+ const char *name, *short_name, *type, *title;
+ char *full_name, str_number[64];
+ long long id;
+ int number;
+
+ JSON_GET_NUM(event->json, id, -1);
+ JSON_GET_STR(event->json, name);
+ JSON_GET_STR(event->json, short_name);
+ JSON_GET_NUM(event->json, number, -1);
+ JSON_GET_STR(event->json, type);
+ JSON_GET_STR(event->json, title);
+
+ buffer_props = weechat_hashtable_new (32,
+ WEECHAT_HASHTABLE_STRING,
+ WEECHAT_HASHTABLE_STRING,
+ NULL,
+ NULL);
+ if (!buffer_props)
+ return WEECHAT_RC_ERROR;
+
+ /* buffer base properties */
+ weechat_hashtable_set (buffer_props, "type", type);
+ weechat_hashtable_set (buffer_props, "short_name", short_name);
+ weechat_hashtable_set (buffer_props, "title", title);
+
+ /* extra properties for relay */
+ weechat_hashtable_set (buffer_props,
+ "localvar_set_relay_remote", event->remote->name);
+ snprintf (str_number, sizeof (str_number), "%lld", id);
+ weechat_hashtable_set (buffer_props,
+ "localvar_set_relay_remote_id", str_number);
+ snprintf (str_number, sizeof (str_number), "%d", number);
+ weechat_hashtable_set (buffer_props,
+ "localvar_set_relay_remote_number", str_number);
+ weechat_hashtable_set (buffer_props, "input_get_any_user_data", "1");
+
+ /* if buffer exists, set properties, otherwise create buffer */
+ ptr_buffer = relay_remote_event_search_buffer (event->remote, id);
+ if (ptr_buffer)
+ {
+ weechat_hashtable_map (buffer_props,
+ &relay_remote_event_apply_props, ptr_buffer);
+ }
+ else
+ {
+ if (weechat_asprintf (&full_name, "remote.%s.%s", event->remote->name, name) >= 0)
+ {
+ ptr_buffer = weechat_buffer_new_props (
+ full_name, buffer_props,
+ &relay_remote_event_buffer_input_cb, event->remote, NULL,
+ NULL, NULL, NULL);
+ free (full_name);
+ }
+ }
+
+ if (ptr_buffer)
+ {
+ json_lines = cJSON_GetObjectItem (event->json, "lines");
+ if (json_lines && cJSON_IsArray (json_lines))
+ {
+ event_line.remote = event->remote;
+ event_line.buffer = ptr_buffer;
+ cJSON_ArrayForEach (json_line, json_lines)
+ {
+ event_line.json = json_line;
+ relay_remote_event_cb_line (&event_line);
+ }
+ }
+ }
+
+ weechat_hashtable_free (buffer_props);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Callback for body type "version".
+ */
+
+RELAY_REMOTE_EVENT_CALLBACK(version)
+{
+ cJSON *json_obj;
+ const char *weechat_version, *weechat_version_git, *relay_api_version;
+
+ JSON_GET_STR(event->json, weechat_version);
+ JSON_GET_STR(event->json, weechat_version_git);
+ JSON_GET_STR(event->json, relay_api_version);
+
+ weechat_printf (NULL,
+ _("remote[%s]: WeeChat: %s (%s), API: %s"),
+ event->remote->name,
+ weechat_version,
+ weechat_version_git,
+ relay_api_version);
+
+ return WEECHAT_RC_OK;
+}
+
+/*
+ * Synchronizes with remote.
+ */
+
+void
+relay_remote_event_sync_with_remote (struct t_relay_remote *remote)
+{
+ cJSON *json, *json_body;
+
+ json = cJSON_CreateObject ();
+ if (!json)
+ goto end;
+
+ cJSON_AddItemToObject (json, "request",
+ cJSON_CreateString ("POST /api/sync"));
+ json_body = cJSON_CreateObject ();
+ if (!json_body)
+ goto end;
+
+ cJSON_AddItemToObject (json_body, "colors",
+ cJSON_CreateString ("weechat"));
+ cJSON_AddItemToObject (json, "body", json_body);
+
+ relay_remote_network_send_json (remote, json);
+
+ remote->synced = 1;
+
+end:
+ cJSON_Delete (json);
+}
+
+/*
+ * Reads an event from a remote.
+ */
+
+void
+relay_remote_event_recv (struct t_relay_remote *remote, const char *data)
+{
+ cJSON *json, *json_body, *json_event, *json_obj;
+ const char *body_type;
+ long long buffer_id;
+ int i, rc, code;
+ struct t_relay_remote_event_cb event_cb[] = {
+ /* body_type, callback */
+ { "buffer", &relay_remote_event_cb_buffer },
+ { "line", &relay_remote_event_cb_line },
+ { "version", &relay_remote_event_cb_version },
+ { NULL, NULL },
+ };
+ t_relay_remote_event_func *callback;
+ struct t_relay_remote_event event;
+
+ if (!remote || !data)
+ return;
+
+ /* display debug message */
+ if (weechat_relay_plugin->debug >= 2)
+ {
+ weechat_printf (NULL,
+ "%s: recv from remote %s: \"%s\"",
+ RELAY_PLUGIN_NAME, remote->name, data);
+ }
+
+ json = cJSON_Parse (data);
+ if (!json)
+ goto error_data;
+
+ event.remote = remote;
+ event.buffer = NULL;
+ event.buffer = NULL;
+
+ JSON_GET_NUM(json, code, -1);
+ JSON_GET_STR(json, body_type);
+ json_event = cJSON_GetObjectItem (json, "event");
+ json_body = cJSON_GetObjectItem (json, "body");
+
+ if (!body_type)
+ {
+ if ((code == 200) || (code == 204))
+ return;
+ goto error_data;
+ }
+
+ if (json_event && cJSON_IsObject (json_event))
+ {
+ JSON_GET_NUM(json_event, buffer_id, -1);
+ event.buffer = relay_remote_event_search_buffer (remote, buffer_id);
+ }
+
+ callback = NULL;
+ for (i = 0; event_cb[i].body_type; i++)
+ {
+ if (strcmp (event_cb[i].body_type, body_type) == 0)
+ {
+ callback = event_cb[i].func;
+ break;
+ }
+ }
+ if (!callback)
+ return;
+
+ if (cJSON_IsArray (json_body))
+ {
+ cJSON_ArrayForEach (json_obj, json_body)
+ {
+ event.json = json_obj;
+ rc = (callback) (&event);
+ }
+ }
+ else
+ {
+ event.json = json_body;
+ rc = (callback) (&event);
+ }
+
+ if (rc == WEECHAT_RC_ERROR)
+ goto error_cb;
+
+ if (!remote->synced && (code == 200) && (strcmp (body_type, "buffer") == 0))
+ relay_remote_event_sync_with_remote (remote);
+
+ return;
+
+error_data:
+ weechat_printf (NULL,
+ "%sremote[%s]: invalid data received from remote: \"%s\"",
+ weechat_prefix ("error"),
+ remote->name,
+ data);
+ return;
+
+error_cb:
+ weechat_printf (NULL,
+ "%sremote[%s]: callback failed for body type \"%s\"",
+ weechat_prefix ("error"),
+ body_type,
+ data);
+ return;
+}
diff --git a/src/plugins/relay/api/remote/relay-remote-event.h b/src/plugins/relay/api/remote/relay-remote-event.h
new file mode 100644
index 000000000..8d015d734
--- /dev/null
+++ b/src/plugins/relay/api/remote/relay-remote-event.h
@@ -0,0 +1,46 @@
+/*
+ * 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_EVENT_H
+#define WEECHAT_PLUGIN_RELAY_REMOTE_EVENT_H
+
+#define RELAY_REMOTE_EVENT_CALLBACK(__body_type) \
+ int \
+ relay_remote_event_cb_##__body_type ( \
+ struct t_relay_remote_event *event)
+
+struct t_relay_remote_event
+{
+ struct t_relay_remote *remote; /* relay remote */
+ struct t_gui_buffer *buffer; /* buffer (can be NULL) */
+ cJSON *json; /* JSON object */
+};
+
+typedef int (t_relay_remote_event_func)(struct t_relay_remote_event *event);
+
+struct t_relay_remote_event_cb
+{
+ char *body_type; /* body type ("buffer", etc.) */
+ t_relay_remote_event_func *func; /* callback */
+};
+
+extern void relay_remote_event_recv (struct t_relay_remote *remote,
+ const char *data);
+
+#endif /* WEECHAT_PLUGIN_RELAY_REMOTE_EVENT_H */
diff --git a/src/plugins/relay/api/remote/relay-remote-network.c b/src/plugins/relay/api/remote/relay-remote-network.c
index f3e7165f3..1b4035bc3 100644
--- a/src/plugins/relay/api/remote/relay-remote-network.c
+++ b/src/plugins/relay/api/remote/relay-remote-network.c
@@ -37,6 +37,7 @@
#include "../../relay-remote.h"
#include "../../relay-websocket.h"
#include "../relay-api.h"
+#include "relay-remote-event.h"
/*
@@ -101,6 +102,7 @@ relay_remote_network_close_connection (struct t_relay_remote *remote)
}
relay_websocket_deflate_free (remote->ws_deflate);
remote->ws_deflate = NULL;
+ remote->synced = 0;
}
/*
@@ -235,6 +237,9 @@ int
relay_remote_network_send_data (struct t_relay_remote *remote,
const char *data, int data_size)
{
+ if (!remote)
+ return 0;
+
if (remote->tls)
{
return (remote->sock >= 0) ?
@@ -267,6 +272,9 @@ relay_remote_network_send (struct t_relay_remote *remote,
unsigned long long length_frame;
int opcode, flags, num_sent;
+ if (!remote)
+ return 0;
+
ptr_data = data;
websocket_frame = NULL;
@@ -318,6 +326,34 @@ relay_remote_network_send (struct t_relay_remote *remote,
}
/*
+ * Sends JSON data to the remote.
+ *
+ * Returns the number of bytes sent to the remote.
+ */
+
+int
+relay_remote_network_send_json (struct t_relay_remote *remote, cJSON *json)
+{
+ char *string;
+ int num_bytes;
+
+ if (!remote || !json)
+ return 0;
+
+ num_bytes = 0;
+
+ string = cJSON_PrintUnformatted (json);
+ if (string)
+ {
+ num_bytes = relay_remote_network_send (remote, RELAY_MSG_STANDARD,
+ string, strlen (string));
+ free (string);
+ }
+
+ return num_bytes;
+}
+
+/*
* Reads text buffer from a remote.
*/
@@ -340,10 +376,18 @@ relay_remote_network_recv_text (struct t_relay_remote *remote,
}
relay_remote_set_status (remote, RELAY_STATUS_CONNECTED);
snprintf (request, sizeof (request),
- "{\"request\": \"GET /api/buffers\"}");
+ "{\"request\": \"GET /api/version\"}");
+ relay_remote_network_send (remote, RELAY_MSG_STANDARD,
+ request, strlen (request));
+ snprintf (request, sizeof (request),
+ "{\"request\": \"GET /api/buffers?lines=-100&colors=weechat\"}");
relay_remote_network_send (remote, RELAY_MSG_STANDARD,
request, strlen (request));
}
+ else
+ {
+ relay_remote_event_recv (remote, buffer);
+ }
}
/*
diff --git a/src/plugins/relay/api/remote/relay-remote-network.h b/src/plugins/relay/api/remote/relay-remote-network.h
index d0defa87e..bfd53ed2e 100644
--- a/src/plugins/relay/api/remote/relay-remote-network.h
+++ b/src/plugins/relay/api/remote/relay-remote-network.h
@@ -20,9 +20,13 @@
#ifndef WEECHAT_PLUGIN_RELAY_REMOTE_NETWORK_H
#define WEECHAT_PLUGIN_RELAY_REMOTE_NETWORK_H
+#include <cjson/cJSON.h>
+
extern int relay_remote_network_send (struct t_relay_remote *remote,
enum t_relay_msg_type msg_type,
const char *data, int data_size);
+extern int relay_remote_network_send_json (struct t_relay_remote *remote,
+ cJSON *json);
extern int relay_remote_network_connect (struct t_relay_remote *remote);
extern void relay_remote_network_disconnect (struct t_relay_remote *remote);
diff --git a/src/plugins/relay/relay-remote.c b/src/plugins/relay/relay-remote.c
index 43c85d508..12880d3da 100644
--- a/src/plugins/relay/relay-remote.c
+++ b/src/plugins/relay/relay-remote.c
@@ -326,6 +326,7 @@ relay_remote_alloc (const char *name)
new_remote->hook_fd = NULL;
new_remote->gnutls_sess = NULL;
new_remote->ws_deflate = relay_websocket_deflate_alloc ();
+ new_remote->synced = 0;
new_remote->prev_remote = NULL;
new_remote->next_remote = NULL;
@@ -545,6 +546,7 @@ relay_remote_new_with_infolist (struct t_infolist *infolist)
}
}
}
+ new_remote->synced = weechat_infolist_integer (infolist, "synced");
new_remote->prev_remote = NULL;
new_remote->next_remote = relay_remotes;
if (relay_remotes)
@@ -846,6 +848,8 @@ relay_remote_add_to_infolist (struct t_infolist *infolist,
free (dict);
}
}
+ if (!weechat_infolist_new_var_integer (ptr_item, "synced", remote->synced))
+ return 0;
return 1;
}
@@ -889,6 +893,7 @@ relay_remote_print_log ()
weechat_log_printf (" hook_fd . . . . . . . . : 0x%lx", ptr_remote->hook_fd);
weechat_log_printf (" gnutls_sess . . . . . . : 0x%lx", ptr_remote->gnutls_sess);
relay_websocket_deflate_print_log (ptr_remote->ws_deflate, "");
+ weechat_log_printf (" synced. . . . . . . . . : %d", ptr_remote->synced);
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
index 231e1e608..f7524d3fd 100644
--- a/src/plugins/relay/relay-remote.h
+++ b/src/plugins/relay/relay-remote.h
@@ -55,6 +55,7 @@ struct t_relay_remote
struct t_hook *hook_fd; /* hook for socket */
gnutls_session_t gnutls_sess; /* gnutls session (only if TLS used) */
struct t_relay_websocket_deflate *ws_deflate; /* websocket deflate data */
+ int synced; /* 1 if synced with remote */
struct t_relay_remote *prev_remote;/* link to previous remote */
struct t_relay_remote *next_remote;/* link to next remote */
};