mirror of https://github.com/pissnet/pissircd.git
616 lines
20 KiB
C
616 lines
20 KiB
C
/************************************************************************
|
|
* UnralIRCd JSON functions, src/json.c
|
|
* (C) 2021-.. Bram Matthys (Syzop) and the UnrealIRCd Team
|
|
* License: GPLv2 or later
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
/** @file
|
|
* @brief JSON functions - used for logging and RPC.
|
|
*/
|
|
|
|
/** Are we currently in the logging code? */
|
|
int log_json_filter = 0;
|
|
|
|
/** Calculate expansion of a JSON string thanks to double escaping.
|
|
* orig => JSON => IRC
|
|
* " => \" => \\"
|
|
* \ => \\ => \\\\
|
|
*/
|
|
int json_dump_string_length(const char *s)
|
|
{
|
|
int len = 0;
|
|
for (; *s; s++)
|
|
{
|
|
if (*s == '\\')
|
|
len += 4;
|
|
else if (*s == '"')
|
|
len += 3;
|
|
else
|
|
len++;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/** Convert a regular string value to a JSON string.
|
|
* In UnrealIRCd, this must be used instead of json_string()
|
|
* as we may use non-UTF8 sequences. Also, this takes care
|
|
* of using json_null() if the string was NULL, which is
|
|
* usually what we want as well.
|
|
* @param s Input string
|
|
* @returns a json string value or json null value.
|
|
*/
|
|
json_t *json_string_unreal(const char *s)
|
|
{
|
|
char buf1[512], buf2[512];
|
|
char *verified_s;
|
|
const char *stripped;
|
|
|
|
if (s == NULL)
|
|
return json_null();
|
|
|
|
if (log_json_filter)
|
|
{
|
|
stripped = StripControlCodesEx(s, buf1, sizeof(buf1), UNRL_STRIP_LOW_ASCII|UNRL_STRIP_KEEP_LF);
|
|
verified_s = unrl_utf8_make_valid(buf1, buf2, sizeof(buf2), 0);
|
|
} else {
|
|
verified_s = unrl_utf8_make_valid(s, buf2, sizeof(buf2), 0);
|
|
}
|
|
|
|
return json_string(verified_s);
|
|
}
|
|
|
|
const char *json_object_get_string(json_t *j, const char *name)
|
|
{
|
|
json_t *v = json_object_get(j, name);
|
|
return v ? json_string_value(v) : NULL;
|
|
}
|
|
|
|
/** Get integer value of a JSON object.
|
|
* @param j The JSON object that should contain a 'name' item
|
|
* @param name The item to search for
|
|
* @param default_value The value to return when the JSON object 'name' is not found
|
|
* or not an integer.
|
|
* @returns The integer value, or default_value if the object does not exist or is not an integer.
|
|
*/
|
|
int json_object_get_integer(json_t *j, const char *name, int default_value)
|
|
{
|
|
json_t *v = json_object_get(j, name);
|
|
if (!v || !json_is_integer(v))
|
|
return default_value;
|
|
return json_integer_value(v);
|
|
}
|
|
|
|
int json_object_get_boolean(json_t *j, const char *name, int default_value)
|
|
{
|
|
json_t *v = json_object_get(j, name);
|
|
if (!v)
|
|
return default_value;
|
|
if (json_is_true(v))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
#define json_string __BAD___DO__NOT__USE__JSON__STRING__PLZ
|
|
|
|
const char *json_get_value(json_t *t)
|
|
{
|
|
static char buf[32];
|
|
|
|
if (json_is_string(t))
|
|
return json_string_value(t);
|
|
|
|
if (json_is_integer(t))
|
|
{
|
|
snprintf(buf, sizeof(buf), "%lld", (long long)json_integer_value(t));
|
|
return buf;
|
|
}
|
|
|
|
if (json_is_boolean(t))
|
|
{
|
|
if (json_is_true(t))
|
|
return "true";
|
|
return "false";
|
|
}
|
|
|
|
if (json_is_array(t))
|
|
return "<array>";
|
|
|
|
return NULL;
|
|
}
|
|
|
|
json_t *json_timestamp(time_t v)
|
|
{
|
|
const char *ts = timestamp_iso8601(v);
|
|
if (ts)
|
|
return json_string_unreal(ts);
|
|
return json_null();
|
|
}
|
|
|
|
const char *timestamp_iso8601_now(void)
|
|
{
|
|
struct timeval t;
|
|
struct tm *tm;
|
|
time_t sec;
|
|
static char buf[64];
|
|
|
|
gettimeofday(&t, NULL);
|
|
sec = t.tv_sec;
|
|
tm = gmtime(&sec);
|
|
|
|
snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
|
|
tm->tm_year + 1900,
|
|
tm->tm_mon + 1,
|
|
tm->tm_mday,
|
|
tm->tm_hour,
|
|
tm->tm_min,
|
|
tm->tm_sec,
|
|
(int)(t.tv_usec / 1000));
|
|
|
|
return buf;
|
|
}
|
|
|
|
const char *timestamp_iso8601(time_t v)
|
|
{
|
|
struct tm *tm;
|
|
static char buf[64];
|
|
|
|
if (v == 0)
|
|
return NULL;
|
|
|
|
tm = gmtime(&v);
|
|
|
|
if (tm == NULL)
|
|
return NULL;
|
|
|
|
snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
|
|
tm->tm_year + 1900,
|
|
tm->tm_mon + 1,
|
|
tm->tm_mday,
|
|
tm->tm_hour,
|
|
tm->tm_min,
|
|
tm->tm_sec,
|
|
0);
|
|
|
|
return buf;
|
|
}
|
|
|
|
void json_expand_client_security_groups(json_t *parent, Client *client)
|
|
{
|
|
SecurityGroup *s;
|
|
json_t *child = json_array();
|
|
json_object_set_new(parent, "security-groups", child);
|
|
|
|
/* We put known-users or unknown-users at the beginning.
|
|
* The latter is special and doesn't actually exist
|
|
* in the linked list, hence the special code here,
|
|
* and again later in the for loop to skip it.
|
|
*/
|
|
if (user_allowed_by_security_group_name(client, "known-users"))
|
|
json_array_append_new(child, json_string_unreal("known-users"));
|
|
else
|
|
json_array_append_new(child, json_string_unreal("unknown-users"));
|
|
|
|
for (s = securitygroups; s; s = s->next)
|
|
if (strcmp(s->name, "known-users") && user_allowed_by_security_group(client, s))
|
|
json_array_append_new(child, json_string_unreal(s->name));
|
|
}
|
|
|
|
/* detail=0: only name, id
|
|
* detail=1: only name, id, hostname, ip, details, geoip
|
|
* detail=2: everything, except 'channels'
|
|
* detail=3: everything, with 'channels' being a max 384 character string (meant for JSON logging only)
|
|
* detail=4: everything, with 'channels' object (full).
|
|
*/
|
|
void json_expand_client(json_t *j, const char *key, Client *client, int detail)
|
|
{
|
|
char buf[BUFSIZE+1];
|
|
json_t *child;
|
|
json_t *user = NULL;
|
|
time_t ts;
|
|
|
|
if (key)
|
|
{
|
|
child = json_object();
|
|
json_object_set_new(j, key, child);
|
|
} else {
|
|
child = j;
|
|
}
|
|
|
|
/* First the information that is available for ALL client types: */
|
|
json_object_set_new(child, "name", json_string_unreal(client->name));
|
|
json_object_set_new(child, "id", json_string_unreal(client->id));
|
|
|
|
if (detail == 0)
|
|
return;
|
|
|
|
/* hostname is available for all, it just depends a bit on whether it is DNS or IP */
|
|
if (client->user && *client->user->realhost)
|
|
json_object_set_new(child, "hostname", json_string_unreal(client->user->realhost));
|
|
else if (client->local && *client->local->sockhost)
|
|
json_object_set_new(child, "hostname", json_string_unreal(client->local->sockhost));
|
|
else
|
|
json_object_set_new(child, "hostname", json_string_unreal(GetIP(client)));
|
|
|
|
/* same for ip, is there for all (well, some services pseudo-users may not have one) */
|
|
json_object_set_new(child, "ip", json_string_unreal(client->ip));
|
|
/* client.details is always available: it is nick!user@host, nick@host, server@host
|
|
* server@ip, or just server.
|
|
*/
|
|
if (client->user)
|
|
{
|
|
if (IsUser(client) || !MyConnect(client))
|
|
{
|
|
/* Post-handshake, after register_user(), it is easy: */
|
|
snprintf(buf, sizeof(buf), "%s!%s@%s", client->name, client->user->username, client->user->realhost);
|
|
} else
|
|
{
|
|
/* In the handshake, more possibilities (ident could still be ongoing)
|
|
* and more speculative (a later class block may want to ignore ident,
|
|
* but we don't know that, so we assume that is not the case).
|
|
*/
|
|
const char *ident;
|
|
char temp[USERLEN+1];
|
|
if (IDENT_CHECK)
|
|
{
|
|
if (IsIdentSuccess(client))
|
|
{
|
|
/* ident succeeded means: use the identd and no ~ prefix */
|
|
ident = client->ident;
|
|
} else {
|
|
/* ident check failed means ~ prefix */
|
|
snprintf(temp, sizeof(temp), "~%s", client->user->username);
|
|
ident = temp;
|
|
}
|
|
} else {
|
|
/* no ident check means no ~ prefix */
|
|
ident = client->user->username;
|
|
}
|
|
snprintf(buf, sizeof(buf), "%s!%s@%s", client->name, ident, client->user->realhost);
|
|
}
|
|
json_object_set_new(child, "details", json_string_unreal(buf));
|
|
} else if (client->ip) {
|
|
if (*client->name)
|
|
snprintf(buf, sizeof(buf), "%s@%s", client->name, client->ip);
|
|
else
|
|
snprintf(buf, sizeof(buf), "[%s]", client->ip);
|
|
json_object_set_new(child, "details", json_string_unreal(buf));
|
|
} else {
|
|
json_object_set_new(child, "details", json_string_unreal(client->name));
|
|
}
|
|
|
|
if (detail < 2)
|
|
{
|
|
RunHook(HOOKTYPE_JSON_EXPAND_CLIENT, client, detail, child);
|
|
return;
|
|
}
|
|
|
|
if (client->local && client->local->listener)
|
|
json_object_set_new(child, "server_port", json_integer(client->local->listener->port));
|
|
if (client->local && client->local->port)
|
|
json_object_set_new(child, "client_port", json_integer(client->local->port));
|
|
if ((ts = get_creationtime(client)))
|
|
json_object_set_new(child, "connected_since", json_timestamp(ts));
|
|
if (client->local && client->local->idle_since)
|
|
json_object_set_new(child, "idle_since", json_timestamp(client->local->idle_since));
|
|
|
|
if (client->user)
|
|
{
|
|
char buf[512];
|
|
const char *str;
|
|
/* client.user */
|
|
user = json_object();
|
|
json_object_set_new(child, "user", user);
|
|
|
|
json_object_set_new(user, "username", json_string_unreal(client->user->username));
|
|
if (!BadPtr(client->info))
|
|
json_object_set_new(user, "realname", json_string_unreal(client->info));
|
|
if (has_user_mode(client, 'x') && client->user->virthost && strcmp(client->user->virthost, client->user->realhost))
|
|
json_object_set_new(user, "vhost", json_string_unreal(client->user->virthost));
|
|
if (*client->user->cloakedhost)
|
|
json_object_set_new(user, "cloakedhost", json_string_unreal(client->user->cloakedhost));
|
|
if (client->uplink)
|
|
json_object_set_new(user, "servername", json_string_unreal(client->uplink->name));
|
|
if (IsLoggedIn(client))
|
|
json_object_set_new(user, "account", json_string_unreal(client->user->account));
|
|
json_object_set_new(user, "reputation", json_integer(GetReputation(client)));
|
|
json_expand_client_security_groups(user, client);
|
|
|
|
/* user modes and snomasks */
|
|
get_usermode_string_r(client, buf, sizeof(buf));
|
|
json_object_set_new(user, "modes", json_string_unreal(buf+1));
|
|
if (client->user->snomask)
|
|
json_object_set_new(user, "snomasks", json_string_unreal(client->user->snomask));
|
|
|
|
/* if oper then we can possibly expand a bit more */
|
|
str = get_operlogin(client);
|
|
if (str)
|
|
json_object_set_new(user, "operlogin", json_string_unreal(str));
|
|
str = get_operclass(client);
|
|
if (str)
|
|
json_object_set_new(user, "operclass", json_string_unreal(str));
|
|
/* For detail>2 we will include the channels.
|
|
* Even if the user is on 0 channels we include "channels":[]
|
|
* so it is clear that the user is on 0 channels and it is
|
|
* not because of low detail level that channels are skipped.
|
|
*/
|
|
if (detail > 2)
|
|
{
|
|
Membership *m;
|
|
int cnt = 0;
|
|
int len = 0;
|
|
json_t *channels = json_array();
|
|
json_object_set_new(user, "channels", channels);
|
|
|
|
if (detail == 3)
|
|
{
|
|
/* Short format, mainly for JSON logging */
|
|
for (m = client->user->channel; m; m = m->next)
|
|
{
|
|
len += json_dump_string_length(m->channel->name);
|
|
if (len > 384)
|
|
{
|
|
/* Truncated */
|
|
json_array_append_new(channels, json_string_unreal("..."));
|
|
break;
|
|
}
|
|
json_array_append_new(channels, json_string_unreal(m->channel->name));
|
|
}
|
|
} else {
|
|
/* Long format for JSON-RPC */
|
|
for (m = client->user->channel; m; m = m->next)
|
|
{
|
|
json_t *e = json_object();
|
|
json_object_set_new(e, "name", json_string_unreal(m->channel->name));
|
|
if (*m->member_modes)
|
|
json_object_set_new(e, "level", json_string_unreal(m->member_modes));
|
|
json_array_append_new(channels, e);
|
|
}
|
|
}
|
|
}
|
|
RunHook(HOOKTYPE_JSON_EXPAND_CLIENT_USER, client, detail, child, user);
|
|
} else
|
|
if (IsMe(client))
|
|
{
|
|
json_t *server = json_object();
|
|
json_t *features;
|
|
|
|
/* client.server */
|
|
json_object_set_new(child, "server", server);
|
|
|
|
if (!BadPtr(client->info))
|
|
json_object_set_new(server, "info", json_string_unreal(client->info));
|
|
json_object_set_new(server, "num_users", json_integer(client->server->users));
|
|
json_object_set_new(server, "boot_time", json_timestamp(client->server->boottime));
|
|
|
|
/* client.server.features */
|
|
features = json_object();
|
|
json_object_set_new(server, "features", features);
|
|
if (!BadPtr(client->server->features.software))
|
|
{
|
|
char buf[256];
|
|
snprintf(buf, sizeof(buf), "UnrealIRCd-%s", buildid);
|
|
json_object_set_new(features, "software", json_string_unreal(buf));
|
|
}
|
|
json_object_set_new(features, "protocol", json_integer(UnrealProtocol));
|
|
if (!BadPtr(client->server->features.usermodes))
|
|
json_object_set_new(features, "usermodes", json_string_unreal(umodestring));
|
|
|
|
/* client.server.features.chanmodes (array) */
|
|
{
|
|
int i;
|
|
char buf[512];
|
|
json_t *chanmodes = json_array();
|
|
json_object_set_new(features, "chanmodes", chanmodes);
|
|
/* first one is special - wait.. is this still the case? lol. */
|
|
snprintf(buf, sizeof(buf), "%s%s", CHPAR1, EXPAR1);
|
|
json_array_append_new(chanmodes, json_string_unreal(buf));
|
|
for (i=1; i < 4; i++)
|
|
json_array_append_new(chanmodes, json_string_unreal(extchmstr[i]));
|
|
}
|
|
if (!BadPtr(client->server->features.nickchars))
|
|
json_object_set_new(features, "nick_character_sets", json_string_unreal(charsys_get_current_languages()));
|
|
RunHook(HOOKTYPE_JSON_EXPAND_CLIENT_SERVER, client, detail, child, server);
|
|
} else
|
|
if (IsServer(client) && client->server)
|
|
{
|
|
/* client.server */
|
|
|
|
/* Whenever a server is expanded, which is rare,
|
|
* we should probably expand as much as info as possible:
|
|
*/
|
|
json_t *server = json_object();
|
|
json_t *features;
|
|
|
|
/* client.server */
|
|
json_object_set_new(child, "server", server);
|
|
if (!BadPtr(client->info))
|
|
json_object_set_new(server, "info", json_string_unreal(client->info));
|
|
if (client->uplink)
|
|
json_object_set_new(server, "uplink", json_string_unreal(client->uplink->name));
|
|
json_object_set_new(server, "num_users", json_integer(client->server->users));
|
|
json_object_set_new(server, "boot_time", json_timestamp(client->server->boottime));
|
|
json_object_set_new(server, "synced", json_boolean(client->server->flags.synced));
|
|
json_object_set_new(server, "ulined", json_boolean(IsULine(client)));
|
|
|
|
/* client.server.features */
|
|
features = json_object();
|
|
json_object_set_new(server, "features", features);
|
|
if (!BadPtr(client->server->features.software))
|
|
json_object_set_new(features, "software", json_string_unreal(client->server->features.software));
|
|
json_object_set_new(features, "protocol", json_integer(client->server->features.protocol));
|
|
if (!BadPtr(client->server->features.usermodes))
|
|
json_object_set_new(features, "usermodes", json_string_unreal(client->server->features.usermodes));
|
|
if (!BadPtr(client->server->features.chanmodes[0]))
|
|
{
|
|
/* client.server.features.chanmodes (array) */
|
|
int i;
|
|
json_t *chanmodes = json_array();
|
|
json_object_set_new(features, "chanmodes", chanmodes);
|
|
for (i=0; i < 4; i++)
|
|
json_array_append_new(chanmodes, json_string_unreal(client->server->features.chanmodes[i]));
|
|
}
|
|
if (!BadPtr(client->server->features.nickchars))
|
|
json_object_set_new(features, "nick_character_sets", json_string_unreal(client->server->features.nickchars));
|
|
RunHook(HOOKTYPE_JSON_EXPAND_CLIENT_SERVER, client, detail, child, server);
|
|
}
|
|
RunHook(HOOKTYPE_JSON_EXPAND_CLIENT, client, detail, child);
|
|
}
|
|
|
|
void json_expand_channel_ban(json_t *child, const char *banlist_name, Ban *banlist)
|
|
{
|
|
Ban *ban;
|
|
json_t *list, *e;
|
|
|
|
list = json_array();
|
|
json_object_set_new(child, banlist_name, list);
|
|
for (ban = banlist; ban; ban = ban->next)
|
|
{
|
|
e = json_object();
|
|
json_array_append_new(list, e);
|
|
json_object_set_new(e, "name", json_string_unreal(ban->banstr));
|
|
json_object_set_new(e, "set_by", json_string_unreal(ban->who));
|
|
json_object_set_new(e, "set_at", json_timestamp(ban->when));
|
|
}
|
|
}
|
|
|
|
|
|
/* detail=1 adds bans, ban_exemptions and invite_exceptions
|
|
* detail=2 adds members
|
|
* detail=3+ makes the members more detailed
|
|
*/
|
|
void json_expand_channel(json_t *j, const char *key, Channel *channel, int detail)
|
|
{
|
|
char mode1[512], mode2[512], modes[512];
|
|
json_t *child;
|
|
|
|
if (key)
|
|
{
|
|
child = json_object();
|
|
json_object_set_new(j, key, child);
|
|
} else {
|
|
child = j;
|
|
}
|
|
|
|
json_object_set_new(child, "name", json_string_unreal(channel->name));
|
|
if (detail == 0)
|
|
return;
|
|
|
|
json_object_set_new(child, "creation_time", json_timestamp(channel->creationtime));
|
|
json_object_set_new(child, "num_users", json_integer(channel->users));
|
|
if (channel->topic)
|
|
{
|
|
json_object_set_new(child, "topic", json_string_unreal(channel->topic));
|
|
json_object_set_new(child, "topic_set_by", json_string_unreal(channel->topic_nick));
|
|
json_object_set_new(child, "topic_set_at", json_timestamp(channel->topic_time));
|
|
}
|
|
|
|
/* Add "mode" too */
|
|
channel_modes(NULL, mode1, mode2, sizeof(mode1), sizeof(mode2), channel, 0);
|
|
if (*mode2)
|
|
{
|
|
snprintf(modes, sizeof(modes), "%s %s", mode1+1, mode2);
|
|
json_object_set_new(child, "modes", json_string_unreal(modes));
|
|
} else {
|
|
json_object_set_new(child, "modes", json_string_unreal(mode1+1));
|
|
}
|
|
|
|
if (detail > 1)
|
|
{
|
|
json_expand_channel_ban(child, "bans", channel->banlist);
|
|
json_expand_channel_ban(child, "ban_exemptions", channel->exlist);
|
|
json_expand_channel_ban(child, "invite_exceptions", channel->invexlist);
|
|
}
|
|
|
|
if (detail >= 3)
|
|
{
|
|
Member *u;
|
|
json_t *list = json_array();
|
|
json_object_set_new(child, "members", list);
|
|
|
|
for (u = channel->members; u; u = u->next)
|
|
{
|
|
json_t *e = json_object();
|
|
if (*u->member_modes)
|
|
json_object_set_new(e, "level", json_string_unreal(u->member_modes));
|
|
json_expand_client(e, NULL, u->client, detail-3);
|
|
json_array_append_new(list, e);
|
|
}
|
|
}
|
|
|
|
// Possibly later: If detail is set to 1 then expand more...
|
|
RunHook(HOOKTYPE_JSON_EXPAND_CHANNEL, channel, detail, child);
|
|
}
|
|
|
|
void json_expand_tkl(json_t *root, const char *key, TKL *tkl, int detail)
|
|
{
|
|
char buf[BUFSIZE];
|
|
json_t *j;
|
|
|
|
if (key)
|
|
{
|
|
j = json_object();
|
|
json_object_set_new(root, key, j);
|
|
} else {
|
|
j = root;
|
|
}
|
|
|
|
json_object_set_new(j, "type", json_string_unreal(tkl_type_config_string(tkl))); // Eg 'kline'
|
|
json_object_set_new(j, "type_string", json_string_unreal(tkl_type_string(tkl))); // Eg 'Soft K-Line'
|
|
json_object_set_new(j, "set_by", json_string_unreal(tkl->set_by));
|
|
json_object_set_new(j, "set_at", json_timestamp(tkl->set_at));
|
|
json_object_set_new(j, "expire_at", json_timestamp(tkl->expire_at));
|
|
*buf = '\0';
|
|
short_date(tkl->set_at, buf);
|
|
strlcat(buf, " GMT", sizeof(buf));
|
|
json_object_set_new(j, "set_at_string", json_string_unreal(buf));
|
|
if (tkl->expire_at <= 0)
|
|
{
|
|
json_object_set_new(j, "expire_at_string", json_string_unreal("Never"));
|
|
json_object_set_new(j, "duration_string", json_string_unreal("permanent"));
|
|
} else {
|
|
*buf = '\0';
|
|
short_date(tkl->expire_at, buf);
|
|
strlcat(buf, " GMT", sizeof(buf));
|
|
json_object_set_new(j, "expire_at_string", json_string_unreal(buf));
|
|
json_object_set_new(j, "duration_string", json_string_unreal(pretty_time_val_r(buf, sizeof(buf), tkl->expire_at - tkl->set_at)));
|
|
}
|
|
json_object_set_new(j, "set_at_delta", json_integer(TStime() - tkl->set_at));
|
|
if (tkl->flags & TKL_FLAG_CONFIG)
|
|
json_object_set_new(j, "set_in_config", json_boolean(1));
|
|
if (TKLIsServerBan(tkl))
|
|
{
|
|
json_object_set_new(j, "name", json_string_unreal(tkl_uhost(tkl, buf, sizeof(buf), 0)));
|
|
json_object_set_new(j, "reason", json_string_unreal(tkl->ptr.serverban->reason));
|
|
} else
|
|
if (TKLIsNameBan(tkl))
|
|
{
|
|
json_object_set_new(j, "name", json_string_unreal(tkl->ptr.nameban->name));
|
|
json_object_set_new(j, "reason", json_string_unreal(tkl->ptr.nameban->reason));
|
|
} else
|
|
if (TKLIsBanException(tkl))
|
|
{
|
|
json_object_set_new(j, "name", json_string_unreal(tkl_uhost(tkl, buf, sizeof(buf), 0)));
|
|
json_object_set_new(j, "reason", json_string_unreal(tkl->ptr.banexception->reason));
|
|
json_object_set_new(j, "exception_types", json_string_unreal(tkl->ptr.banexception->bantypes));
|
|
} else
|
|
if (TKLIsSpamfilter(tkl))
|
|
{
|
|
if (tkl->ptr.spamfilter->match->str)
|
|
{
|
|
json_object_set_new(j, "name", json_string_unreal(tkl->ptr.spamfilter->match->str));
|
|
json_object_set_new(j, "match_type", json_string_unreal(unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type)));
|
|
}
|
|
if (tkl->ptr.spamfilter->prettyrule)
|
|
json_object_set_new(j, "rule", json_string_unreal(tkl->ptr.spamfilter->prettyrule));
|
|
json_object_set_new(j, "ban_action", json_string_unreal(ban_actions_to_string(tkl->ptr.spamfilter->action)));
|
|
json_object_set_new(j, "ban_duration", json_integer(tkl->ptr.spamfilter->tkl_duration));
|
|
json_object_set_new(j, "ban_duration_string", json_string_unreal(pretty_time_val_r(buf, sizeof(buf), tkl->ptr.spamfilter->tkl_duration)));
|
|
json_object_set_new(j, "spamfilter_targets", json_string_unreal(spamfilter_target_inttostring(tkl->ptr.spamfilter->target)));
|
|
json_object_set_new(j, "reason", json_string_unreal(unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)));
|
|
json_object_set_new(j, "hits", json_integer(tkl->ptr.spamfilter->hits));
|
|
json_object_set_new(j, "hits_except", json_integer(tkl->ptr.spamfilter->hits_except));
|
|
}
|
|
}
|