1
0
mirror of https://github.com/weechat/weechat.git synced 2026-06-25 04:16:38 +02:00
Files
weechat/src/plugins/relay/api/relay-api-msg.c
T

672 lines
21 KiB
C

/*
* relay-api-msg.c - build JSON messages for "api" protocol
*
* Copyright (C) 2023-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 <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <cjson/cJSON.h>
#include "../../weechat-plugin.h"
#include "../relay.h"
#include "../relay-client.h"
#include "../relay-http.h"
#include "../relay-websocket.h"
#include "relay-api.h"
#include "relay-api-msg.h"
#include "relay-api-protocol.h"
#define MSG_ADD_STR_BUF(__json_name, __string) \
cJSON_AddItemToObject( \
json, __json_name, \
cJSON_CreateString (__string));
#define MSG_ADD_STR_PTR(__json_name, __string) \
cJSON_AddItemToObject( \
json, __json_name, \
cJSON_CreateString ((__string) ? __string : ""));
#define MSG_ADD_HDATA_VAR(__json_type, __json_name, \
__var_type, __var_name) \
cJSON_AddItemToObject( \
json, __json_name, \
cJSON_Create##__json_type ( \
weechat_hdata_##__var_type (hdata, pointer, __var_name)));
#define MSG_ADD_HDATA_TIME(__json_name, __var_name) \
date = weechat_hdata_time (hdata, pointer, __var_name); \
strftime (str_time, sizeof (str_time), "%FT%TZ", gmtime (&date)); \
MSG_ADD_STR_BUF(__json_name, str_time);
#define MSG_ADD_HDATA_TIME_USEC(__json_name, \
__var_name, __var_name_usec) \
time_value = weechat_hdata_time (hdata, pointer, __var_name); \
gmtime_r (&time_value, &gm_time); \
tv.tv_sec = mktime (&gm_time); \
tv.tv_usec = weechat_hdata_integer (hdata, pointer, \
__var_name_usec); \
weechat_util_strftimeval (str_time, sizeof (str_time), \
"%FT%T.%fZ", &tv); \
MSG_ADD_STR_BUF(__json_name, str_time);
#define MSG_ADD_HDATA_STR(__json_name, __var_name) \
ptr_string = weechat_hdata_string (hdata, pointer, __var_name); \
MSG_ADD_STR_PTR(__json_name, ptr_string);
#define MSG_ADD_HDATA_STR_COLORS(__json_name, __var_name) \
ptr_string = weechat_hdata_string (hdata, pointer, __var_name); \
switch (colors) \
{ \
case RELAY_API_COLORS_ANSI: \
string = weechat_hook_modifier_exec ( \
"color_encode_ansi", NULL, \
(ptr_string) ? ptr_string : ""); \
if (string) \
{ \
MSG_ADD_STR_PTR(__json_name, string); \
free (string); \
} \
break; \
case RELAY_API_COLORS_WEECHAT: \
MSG_ADD_STR_PTR(__json_name, ptr_string); \
break; \
case RELAY_API_COLORS_STRIP: \
string = weechat_string_remove_color ( \
(ptr_string) ? ptr_string : "", NULL); \
if (string) \
{ \
MSG_ADD_STR_PTR(__json_name, string); \
free (string); \
} \
case RELAY_API_NUM_COLORS: \
break; \
}
/*
* Sends JSON response to client (internal use).
*
* Returns number of bytes sent to client, -1 if error.
*/
int
relay_api_msg_send_json_internal (struct t_relay_client *client,
int return_code,
const char *message,
const char *event_name,
struct t_gui_buffer *event_buffer,
const char *headers,
const char *body_type,
cJSON *json_body)
{
cJSON *json, *json_event;
int num_bytes;
const char *ptr_id;
char *string, *error;
long long id;
if (!client || !message)
return -1;
num_bytes = -1;
if (client->websocket == RELAY_CLIENT_WEBSOCKET_READY)
{
/*
* with established websocket, we return JSON string instead of
* an HTTP response
*/
json = cJSON_CreateObject ();
if (json)
{
cJSON_AddItemToObject (json, "code", cJSON_CreateNumber (return_code));
cJSON_AddItemToObject (json, "message", cJSON_CreateString (message));
if (event_name)
{
json_event = cJSON_CreateObject ();
if (json_event)
{
cJSON_AddItemToObject (
json_event, "name",
cJSON_CreateString ((event_name) ? event_name : ""));
id = -1;
if (event_buffer)
{
ptr_id = weechat_buffer_get_string (event_buffer, "id");
error = NULL;
id = strtoll (ptr_id, &error, 10);
if (!error || error[0])
id = -1;
}
cJSON_AddItemToObject (
json_event, "buffer_id",
cJSON_CreateNumber (id));
cJSON_AddItemToObject (json, "event", json_event);
}
}
if (json_body)
{
cJSON_AddItemToObject (json, "body_type",
cJSON_CreateString ((body_type) ? body_type : ""));
cJSON_AddItemToObject (json, "body", json_body);
}
string = cJSON_PrintUnformatted (json);
num_bytes = relay_client_send (
client,
RELAY_MSG_STANDARD,
string,
(string) ? strlen (string) : 0,
NULL); /* raw_message */
if (string)
free (string);
cJSON_DetachItemFromObject (json, "body");
cJSON_Delete (json);
}
}
else
{
string = (json_body) ? cJSON_PrintUnformatted (json_body) : NULL;
num_bytes = relay_http_send_json (client, return_code, message, headers,
string);
if (string)
free (string);
}
return num_bytes;
}
/*
* Sends JSON response to client (internal use).
*
* Returns number of bytes sent to client, -1 if error.
*/
int
relay_api_msg_send_json (struct t_relay_client *client,
int return_code,
const char *message,
const char *body_type,
cJSON *json_body)
{
return relay_api_msg_send_json_internal (client,
return_code,
message,
NULL, /* event_name */
NULL, /* event_buffer */
NULL, /* headers */
body_type,
json_body);
}
/*
* Sends JSON error to client.
*
* Returns number of bytes sent to client, -1 if error.
*/
int
relay_api_msg_send_error_json (struct t_relay_client *client,
int return_code,
const char *message,
const char *headers,
const char *format, ...)
{
cJSON *json;
int num_bytes, length;
char *error_msg, *str_json;
if (!client || !message || !format)
return -1;
weechat_va_format (format);
if (!vbuffer)
return -1;
num_bytes = -1;
if (client->websocket == RELAY_CLIENT_WEBSOCKET_READY)
{
/*
* with established websocket, we return JSON string instead of
* an HTTP response
*/
json = cJSON_CreateObject ();
if (json)
{
cJSON_AddItemToObject (json, "error", cJSON_CreateString (vbuffer));
num_bytes = relay_api_msg_send_json_internal (
client,
return_code,
message,
NULL, /* event_name */
NULL, /* event_buffer */
headers,
NULL, /* body_type */
json);
cJSON_Delete (json);
}
}
else
{
error_msg = weechat_string_replace (vbuffer, "\"", "\\\"");
if (error_msg)
{
length = strlen (error_msg) + 64;
str_json = malloc (length);
if (str_json)
{
snprintf (str_json, length, "{\"error\": \"%s\"}", error_msg);
num_bytes = relay_http_send_json (client, return_code, message,
headers, str_json);
free (str_json);
}
free (error_msg);
}
}
free (vbuffer);
return num_bytes;
}
/*
* Sends event to the client.
*
* Returns number of bytes sent to client, -1 if error.
*/
int
relay_api_msg_send_event (struct t_relay_client *client,
const char *name,
struct t_gui_buffer *buffer,
const char *body_type,
cJSON *json_body)
{
return relay_api_msg_send_json_internal (client,
RELAY_API_HTTP_0_EVENT,
name,
buffer,
NULL, /* headers */
body_type,
json_body);
}
/*
* Adds a buffer local variable into a JSON object.
*/
void
relay_api_msg_buffer_add_local_vars_cb (void *data,
struct t_hashtable *hashtable,
const void *key,
const void *value)
{
cJSON *json;
/* make C compiler happy */
(void) hashtable;
json = (cJSON *)data;
cJSON_AddItemToObject (
json,
(const char *)key,
cJSON_CreateString ((value) ? (const char *)value : ""));
}
/*
* Creates a JSON object with a buffer.
*/
cJSON *
relay_api_msg_buffer_to_json (struct t_gui_buffer *buffer,
long lines,
int nicks,
enum t_relay_api_colors colors)
{
struct t_hdata *hdata;
struct t_gui_buffer *pointer;
cJSON *json, *json_local_vars, *json_lines, *json_nicks;
const char *ptr_string;
char *string;
hdata = relay_hdata_buffer;
pointer = buffer;
json = cJSON_CreateObject ();
if (!json)
return NULL;
if (!buffer)
return json;
MSG_ADD_HDATA_VAR(Number, "id", longlong, "id");
MSG_ADD_HDATA_STR("name", "full_name");
MSG_ADD_HDATA_STR("short_name", "short_name");
MSG_ADD_HDATA_VAR(Number, "number", integer, "number");
ptr_string = weechat_buffer_get_string (buffer, "type");
MSG_ADD_STR_PTR("type", ptr_string);
MSG_ADD_HDATA_STR_COLORS("title", "title");
/* local_variables */
json_local_vars = cJSON_CreateObject ();
if (json_local_vars)
{
weechat_hashtable_map (
weechat_hdata_pointer (hdata, buffer, "local_variables"),
&relay_api_msg_buffer_add_local_vars_cb,
json_local_vars);
cJSON_AddItemToObject (json, "local_variables", json_local_vars);
}
/* lines */
if (lines != 0)
{
json_lines = relay_api_msg_lines_to_json (buffer, lines, colors);
if (json_lines)
cJSON_AddItemToObject (json, "lines", json_lines);
}
/* nicks */
if (nicks)
{
json_nicks = relay_api_msg_nick_group_to_json (
weechat_hdata_pointer (hdata, buffer, "nicklist_root"));
if (json_nicks)
cJSON_AddItemToObject (json, "nicks", json_nicks);
}
return json;
}
/*
* Creates a JSON object with a buffer line data.
*/
cJSON *
relay_api_msg_line_data_to_json (struct t_gui_line_data *line_data,
enum t_relay_api_colors colors)
{
struct t_hdata *hdata;
struct t_gui_line_data *pointer;
cJSON *json, *json_tags;
const char *ptr_string;
char *string, str_time[256], str_var[64];
int i, tags_count;
time_t time_value;
struct timeval tv;
struct tm gm_time;
hdata = relay_hdata_line_data;
pointer = line_data;
json = cJSON_CreateObject ();
if (!json)
return NULL;
if (!line_data)
return json;
MSG_ADD_HDATA_VAR(Number, "id", integer, "id");
MSG_ADD_HDATA_VAR(Number, "y", integer, "y");
MSG_ADD_HDATA_TIME_USEC("date", "date", "date_usec");
MSG_ADD_HDATA_TIME_USEC("date_printed", "date_printed", "date_usec_printed");
MSG_ADD_HDATA_VAR(Bool, "highlight", char, "highlight");
MSG_ADD_HDATA_STR_COLORS("prefix", "prefix");
MSG_ADD_HDATA_STR_COLORS("message", "message");
/* tags */
json_tags = cJSON_CreateArray ();
if (json_tags)
{
tags_count = weechat_hdata_integer (hdata, line_data, "tags_count");
for (i = 0; i < tags_count; i++)
{
snprintf (str_var, sizeof (str_var), "%d|tags_array", i);
cJSON_AddItemToArray (
json_tags,
cJSON_CreateString (weechat_hdata_string (hdata, line_data, str_var)));
}
}
cJSON_AddItemToObject(json, "tags", json_tags);
return json;
}
/*
* Creates a JSON object with an array of buffer lines.
*/
cJSON *
relay_api_msg_lines_to_json (struct t_gui_buffer *buffer,
long lines,
enum t_relay_api_colors colors)
{
cJSON *json;
struct t_gui_lines *ptr_lines;
struct t_gui_line *ptr_line;
struct t_gui_line_data *ptr_line_data;
long i, count;
json = cJSON_CreateArray ();
if (!json)
return NULL;
if (lines == 0)
return json;
ptr_lines = weechat_hdata_pointer (relay_hdata_buffer, buffer, "own_lines");
if (!ptr_lines)
return json;
if (lines < 0)
{
/* search start line from the last line */
ptr_line = weechat_hdata_pointer (relay_hdata_lines, ptr_lines, "last_line");
if (ptr_line)
{
for (i = 1; i < lines * -1; i++)
{
ptr_line = weechat_hdata_move (relay_hdata_line, ptr_line, -1);
if (!ptr_line)
break;
}
if (!ptr_line)
ptr_line = weechat_hdata_pointer (relay_hdata_lines, ptr_lines, "first_line");
}
}
else
{
ptr_line = weechat_hdata_pointer (relay_hdata_lines, ptr_lines, "first_line");
}
if (!ptr_line)
return json;
count = 0;
while (ptr_line)
{
ptr_line_data = weechat_hdata_pointer (relay_hdata_line, ptr_line, "data");
if (ptr_line_data)
{
cJSON_AddItemToArray (
json,
relay_api_msg_line_data_to_json (ptr_line_data, colors));
}
count++;
if ((lines > 0) && (count >= lines))
break;
ptr_line = weechat_hdata_move (relay_hdata_line, ptr_line, 1);
}
return json;
}
/*
* Creates a nick JSON object.
*/
cJSON *
relay_api_msg_nick_to_json (struct t_gui_nick *nick)
{
struct t_hdata *hdata;
struct t_gui_nick *pointer;
cJSON *json;
const char *ptr_string;
hdata = relay_hdata_nick;
pointer = nick;
json = cJSON_CreateObject ();
if (!json)
return NULL;
if (!nick)
return json;
MSG_ADD_HDATA_STR("prefix", "prefix");
MSG_ADD_HDATA_STR("prefix_color", "prefix_color");
MSG_ADD_HDATA_STR("name", "name");
MSG_ADD_HDATA_STR("color", "color");
MSG_ADD_HDATA_VAR(Bool, "visible", integer, "visible");
return json;
}
/*
* Creates a nick group JSON object.
*/
cJSON *
relay_api_msg_nick_group_to_json (struct t_gui_nick_group *nick_group)
{
struct t_hdata *hdata;
struct t_gui_nick_group *pointer;
cJSON *json, *json_groups, *json_nicks;
const char *ptr_string;
struct t_gui_nick_group *ptr_group;
struct t_gui_nick *ptr_nick;
hdata = relay_hdata_nick_group;
pointer = nick_group;
json = cJSON_CreateObject ();
if (!json)
return NULL;
if (!nick_group)
return json;
MSG_ADD_HDATA_STR("name", "name");
MSG_ADD_HDATA_STR("color", "color");
MSG_ADD_HDATA_VAR(Bool, "visible", integer, "visible");
json_groups = cJSON_CreateArray ();
if (json_groups)
{
ptr_group = weechat_hdata_pointer (relay_hdata_nick_group, nick_group, "children");
if (ptr_group)
{
while (ptr_group)
{
cJSON_AddItemToArray (
json_groups,
relay_api_msg_nick_group_to_json (ptr_group));
ptr_group = weechat_hdata_move (relay_hdata_nick_group, ptr_group, 1);
}
}
cJSON_AddItemToObject (json, "groups", json_groups);
}
json_nicks = cJSON_CreateArray ();
if (json_nicks)
{
ptr_nick = weechat_hdata_pointer (relay_hdata_nick_group, nick_group, "nicks");
if (ptr_nick)
{
while (ptr_nick)
{
cJSON_AddItemToArray (
json_nicks,
relay_api_msg_nick_to_json (ptr_nick));
ptr_nick = weechat_hdata_move (relay_hdata_nick, ptr_nick, 1);
}
}
cJSON_AddItemToObject (json, "nicks", json_nicks);
}
return json;
}
/*
* Creates a JSON object with a hotlist entry.
*/
cJSON *
relay_api_msg_hotlist_to_json (struct t_gui_hotlist *hotlist)
{
struct t_hdata *hdata;
struct t_gui_hotlist *pointer;
struct t_gui_buffer *buffer;
cJSON *json, *json_count;
time_t time_value;
struct timeval tv;
struct tm gm_time;
char str_time[256], str_key[32];
int i, array_size;
long long buffer_id;
hdata = relay_hdata_hotlist;
pointer = hotlist;
json = cJSON_CreateObject ();
if (!json)
return NULL;
if (!hotlist)
return json;
MSG_ADD_HDATA_VAR(Number, "priority", integer, "priority");
MSG_ADD_HDATA_TIME_USEC("date", "time", "time_usec");
buffer = weechat_hdata_pointer (hdata, hotlist, "buffer");
buffer_id = (buffer) ?
weechat_hdata_longlong (relay_hdata_buffer, buffer, "id") : -1;
cJSON_AddItemToObject (json, "buffer_id", cJSON_CreateNumber (buffer_id));
json_count = cJSON_CreateArray ();
if (json_count)
{
array_size = weechat_hdata_get_var_array_size (hdata, hotlist, "count");
for (i = 0; i < array_size; i++)
{
snprintf (str_key, sizeof (str_key), "%d|count", i);
cJSON_AddItemToArray (
json_count,
cJSON_CreateNumber (weechat_hdata_integer (hdata, hotlist, str_key)));
}
}
cJSON_AddItemToObject (json, "count", json_count);
return json;
}