1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-03 00:03:12 +02:00

relay/api: add handshake resource, without authentication required (issue #2066)

This commit is contained in:
Sébastien Helleu
2024-01-31 18:12:06 +01:00
parent 3f902dba28
commit 6acf1af828
2 changed files with 216 additions and 132 deletions
+214 -131
View File
@@ -28,6 +28,7 @@
#include "../../weechat-plugin.h"
#include "../relay.h"
#include "../relay-auth.h"
#include "../relay-buffer.h"
#include "../relay-client.h"
#include "../relay-config.h"
@@ -263,6 +264,83 @@ relay_api_protocol_signal_upgrade_cb (const void *pointer, void *data,
return WEECHAT_RC_OK;
}
/*
* Callback for resource "handshake".
*
* Routes:
* POST /api/handshake
*/
RELAY_API_PROTOCOL_CALLBACK(handshake)
{
cJSON *json_body, *json_algos, *json_algo, *json;
const char *ptr_algo;
char *totp_secret;
int hash_algo_found, index_hash_algo;
json_body = cJSON_Parse(client->http_req->body);
if (!json_body)
return WEECHAT_RC_ERROR;
hash_algo_found = -1;
json_algos = cJSON_GetObjectItem (json_body, "password_hash_algo");
if (json_algos)
{
cJSON_ArrayForEach(json_algo, json_algos)
{
ptr_algo = cJSON_GetStringValue (json_algo);
if (ptr_algo)
{
index_hash_algo = relay_auth_password_hash_algo_search (ptr_algo);
if ((index_hash_algo >= 0) && (index_hash_algo > hash_algo_found))
{
if (weechat_string_match_list (
relay_auth_password_hash_algo_name[index_hash_algo],
(const char **)relay_config_network_password_hash_algo_list,
1))
{
hash_algo_found = index_hash_algo;
}
}
}
}
}
json = cJSON_CreateObject ();
if (!json)
{
cJSON_Delete (json_body);
return WEECHAT_RC_ERROR;
}
totp_secret = weechat_string_eval_expression (
weechat_config_string (relay_config_network_totp_secret),
NULL, NULL, NULL);
cJSON_AddItemToObject (
json,
"password_hash_algo",
(hash_algo_found >= 0) ?
cJSON_CreateString (relay_auth_password_hash_algo_name[hash_algo_found]) :
cJSON_CreateNull ());
cJSON_AddItemToObject (
json,
"password_hash_iterations",
cJSON_CreateNumber (
weechat_config_integer (relay_config_network_password_hash_iterations)));
cJSON_AddItemToObject (json, "totp",
cJSON_CreateBool ((totp_secret && totp_secret[0]) ? 1 : 0));
relay_api_msg_send_json (client, RELAY_HTTP_200_OK, json);
free (totp_secret);
cJSON_Delete (json);
cJSON_Delete (json_body);
return WEECHAT_RC_OK;
}
/*
* Callback for resource "version".
*
@@ -599,137 +677,6 @@ RELAY_API_PROTOCOL_CALLBACK(sync)
return WEECHAT_RC_OK;
}
/*
* Reads a HTTP request from a client.
*/
void
relay_api_protocol_recv_http (struct t_relay_client *client)
{
int i, return_code, num_args;
struct t_relay_api_protocol_cb protocol_cb[] =
{ { "GET", "version", 0, 0, &relay_api_protocol_cb_version },
{ "GET", "buffers", 0, 3, &relay_api_protocol_cb_buffers },
{ "POST", "input", 0, 0, &relay_api_protocol_cb_input },
{ "POST", "ping", 0, 0, &relay_api_protocol_cb_ping },
{ "POST", "sync", 0, 0, &relay_api_protocol_cb_sync },
{ NULL, NULL, 0, 0, NULL },
};
if (!client->http_req || RELAY_CLIENT_HAS_ENDED(client))
return;
if ((client->status != RELAY_STATUS_CONNECTED)
&& !relay_http_check_auth (client))
{
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
}
/* display debug message */
if (weechat_relay_plugin->debug >= 2)
{
weechat_printf (NULL,
"%s: recv from client %s%s%s: \"%s %s\", body: \"%s\"",
RELAY_PLUGIN_NAME,
RELAY_COLOR_CHAT_CLIENT,
client->desc,
RELAY_COLOR_CHAT,
client->http_req->method,
client->http_req->path,
client->http_req->body);
}
if ((client->http_req->num_path_items < 2) || !client->http_req->path_items
|| !client->http_req->path_items[0] || !client->http_req->path_items[1])
{
goto error404;
}
if (strcmp (client->http_req->path_items[0], "api") != 0)
goto error404;
num_args = client->http_req->num_path_items - 2;
for (i = 0; protocol_cb[i].resource; i++)
{
if ((strcmp (protocol_cb[i].method, client->http_req->method) == 0)
&& (strcmp (protocol_cb[i].resource, client->http_req->path_items[1]) == 0))
{
if (num_args < protocol_cb[i].min_args)
{
if (weechat_relay_plugin->debug >= 1)
{
weechat_printf (
NULL,
_("%s%s: too few arguments received from client "
"%s%s%s for resource \"%s\" "
"(received: %d arguments, expected: at least %d)"),
weechat_prefix ("error"),
RELAY_PLUGIN_NAME,
RELAY_COLOR_CHAT_CLIENT,
client->desc,
RELAY_COLOR_CHAT,
client->http_req->path_items[1],
num_args,
protocol_cb[i].min_args);
}
goto error404;
}
if (num_args > protocol_cb[i].max_args)
{
if (weechat_relay_plugin->debug >= 1)
{
weechat_printf (
NULL,
_("%s%s: too many arguments received from client "
"%s%s%s for resource \"%s\" "
"(received: %d arguments, expected: at most %d)"),
weechat_prefix ("error"),
RELAY_PLUGIN_NAME,
RELAY_COLOR_CHAT_CLIENT,
client->desc,
RELAY_COLOR_CHAT,
client->http_req->path_items[1],
num_args,
protocol_cb[i].max_args);
}
goto error404;
}
return_code = (int) (protocol_cb[i].cmd_function) (client);
if (return_code == WEECHAT_RC_OK)
return;
else
goto error400;
}
}
goto error404;
error400:
relay_api_msg_send_json (client, RELAY_HTTP_400_BAD_REQUEST, NULL);
goto error;
error404:
relay_api_msg_send_json (client, RELAY_HTTP_404_NOT_FOUND, NULL);
goto error;
error:
if (weechat_relay_plugin->debug >= 1)
{
weechat_printf (NULL,
_("%s%s: failed to execute route \"%s %s\" "
"for client %s%s%s"),
weechat_prefix ("error"),
RELAY_PLUGIN_NAME,
client->http_req->method,
client->http_req->path,
RELAY_COLOR_CHAT_CLIENT,
client->desc,
RELAY_COLOR_CHAT);
}
}
/*
* Reads JSON string from a client: when connected via websocket (persistent
* connection), the client is sending JSON data as a request, which is
@@ -806,3 +753,139 @@ end:
if (json_obj)
cJSON_Delete (json_obj);
}
/*
* Reads a HTTP request from a client.
*/
void
relay_api_protocol_recv_http (struct t_relay_client *client)
{
int i, return_code, num_args;
struct t_relay_api_protocol_cb protocol_cb[] = {
/* method, resource, auth, min args, max args, callback */
{ "POST", "handshake", 0, 0, 0, &relay_api_protocol_cb_handshake },
{ "GET", "version", 1, 0, 0, &relay_api_protocol_cb_version },
{ "GET", "buffers", 1, 0, 3, &relay_api_protocol_cb_buffers },
{ "POST", "input", 1, 0, 0, &relay_api_protocol_cb_input },
{ "POST", "ping", 1, 0, 0, &relay_api_protocol_cb_ping },
{ "POST", "sync", 1, 0, 0, &relay_api_protocol_cb_sync },
{ NULL, NULL, 0, 0, 0, NULL },
};
if (!client->http_req || RELAY_CLIENT_HAS_ENDED(client))
return;
/* display debug message */
if (weechat_relay_plugin->debug >= 2)
{
weechat_printf (NULL,
"%s: recv from client %s%s%s: \"%s %s\", body: \"%s\"",
RELAY_PLUGIN_NAME,
RELAY_COLOR_CHAT_CLIENT,
client->desc,
RELAY_COLOR_CHAT,
client->http_req->method,
client->http_req->path,
client->http_req->body);
}
if ((client->http_req->num_path_items < 2) || !client->http_req->path_items
|| !client->http_req->path_items[0] || !client->http_req->path_items[1])
{
goto error404;
}
if (strcmp (client->http_req->path_items[0], "api") != 0)
goto error404;
num_args = client->http_req->num_path_items - 2;
for (i = 0; protocol_cb[i].resource; i++)
{
if ((strcmp (protocol_cb[i].method, client->http_req->method) != 0)
|| (strcmp (protocol_cb[i].resource, client->http_req->path_items[1]) != 0))
continue;
if (protocol_cb[i].auth_required
&& (client->status != RELAY_STATUS_CONNECTED)
&& !relay_http_check_auth (client))
{
relay_client_set_status (client, RELAY_STATUS_AUTH_FAILED);
return;
}
if (num_args < protocol_cb[i].min_args)
{
if (weechat_relay_plugin->debug >= 1)
{
weechat_printf (
NULL,
_("%s%s: too few arguments received from client "
"%s%s%s for resource \"%s\" "
"(received: %d arguments, expected: at least %d)"),
weechat_prefix ("error"),
RELAY_PLUGIN_NAME,
RELAY_COLOR_CHAT_CLIENT,
client->desc,
RELAY_COLOR_CHAT,
client->http_req->path_items[1],
num_args,
protocol_cb[i].min_args);
}
goto error404;
}
if (num_args > protocol_cb[i].max_args)
{
if (weechat_relay_plugin->debug >= 1)
{
weechat_printf (
NULL,
_("%s%s: too many arguments received from client "
"%s%s%s for resource \"%s\" "
"(received: %d arguments, expected: at most %d)"),
weechat_prefix ("error"),
RELAY_PLUGIN_NAME,
RELAY_COLOR_CHAT_CLIENT,
client->desc,
RELAY_COLOR_CHAT,
client->http_req->path_items[1],
num_args,
protocol_cb[i].max_args);
}
goto error404;
}
return_code = (int) (protocol_cb[i].cmd_function) (client);
if (return_code == WEECHAT_RC_OK)
return;
else
goto error400;
}
goto error404;
error400:
relay_api_msg_send_json (client, RELAY_HTTP_400_BAD_REQUEST, NULL);
goto error;
error404:
relay_api_msg_send_json (client, RELAY_HTTP_404_NOT_FOUND, NULL);
goto error;
error:
if (weechat_relay_plugin->debug >= 1)
{
weechat_printf (NULL,
_("%s%s: failed to execute route \"%s %s\" "
"for client %s%s%s"),
weechat_prefix ("error"),
RELAY_PLUGIN_NAME,
client->http_req->method,
client->http_req->path,
RELAY_COLOR_CHAT_CLIENT,
client->desc,
RELAY_COLOR_CHAT);
}
}
+2 -1
View File
@@ -30,6 +30,7 @@ struct t_relay_api_protocol_cb
{
char *method; /* method (eg: "GET", "POST", etc.) */
char *resource; /* resource (eg: "buffers") */
int auth_required; /* authentication required? */
int min_args; /* min number of items in path */
int max_args; /* max number of items in path */
t_relay_api_cmd_func *cmd_function; /* callback */
@@ -49,8 +50,8 @@ extern int relay_api_protocol_signal_upgrade_cb (const void *pointer,
const char *signal,
const char *type_data,
void *signal_data);
extern void relay_api_protocol_recv_http (struct t_relay_client *client);
extern void relay_api_protocol_recv_json (struct t_relay_client *client,
const char *json);
extern void relay_api_protocol_recv_http (struct t_relay_client *client);
#endif /* WEECHAT_PLUGIN_RELAY_API_PROTOCOL_H */