mirror of https://github.com/pissnet/pissircd.git
334 lines
8.3 KiB
C
334 lines
8.3 KiB
C
/* spamfilter.* RPC calls
|
|
* (C) Copyright 2022-.. Bram Matthys (Syzop) and the UnrealIRCd team
|
|
* License: GPLv2 or later
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"rpc/spamfilter",
|
|
"1.0.3",
|
|
"spamfilter.* RPC calls",
|
|
"UnrealIRCd Team",
|
|
"unrealircd-6",
|
|
};
|
|
|
|
/* Forward declarations */
|
|
RPC_CALL_FUNC(rpc_spamfilter_list);
|
|
RPC_CALL_FUNC(rpc_spamfilter_get);
|
|
RPC_CALL_FUNC(rpc_spamfilter_del);
|
|
RPC_CALL_FUNC(rpc_spamfilter_add);
|
|
|
|
MOD_INIT()
|
|
{
|
|
RPCHandlerInfo r;
|
|
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
|
|
memset(&r, 0, sizeof(r));
|
|
r.method = "spamfilter.list";
|
|
r.loglevel = ULOG_DEBUG;
|
|
r.call = rpc_spamfilter_list;
|
|
if (!RPCHandlerAdd(modinfo->handle, &r))
|
|
{
|
|
config_error("[rpc/spamfilter] Could not register RPC handler");
|
|
return MOD_FAILED;
|
|
}
|
|
r.method = "spamfilter.get";
|
|
r.loglevel = ULOG_DEBUG;
|
|
r.call = rpc_spamfilter_get;
|
|
if (!RPCHandlerAdd(modinfo->handle, &r))
|
|
{
|
|
config_error("[rpc/spamfilter] Could not register RPC handler");
|
|
return MOD_FAILED;
|
|
}
|
|
r.method = "spamfilter.del";
|
|
r.call = rpc_spamfilter_del;
|
|
if (!RPCHandlerAdd(modinfo->handle, &r))
|
|
{
|
|
config_error("[rpc/spamfilter] Could not register RPC handler");
|
|
return MOD_FAILED;
|
|
}
|
|
r.method = "spamfilter.add";
|
|
r.call = rpc_spamfilter_add;
|
|
if (!RPCHandlerAdd(modinfo->handle, &r))
|
|
{
|
|
config_error("[rpc/spamfilter] Could not register RPC handler");
|
|
return MOD_FAILED;
|
|
}
|
|
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
RPC_CALL_FUNC(rpc_spamfilter_list)
|
|
{
|
|
json_t *result, *list, *item;
|
|
int index;
|
|
TKL *tkl;
|
|
|
|
result = json_object();
|
|
list = json_array();
|
|
json_object_set_new(result, "list", list);
|
|
|
|
for (index = 0; index < TKLISTLEN; index++)
|
|
{
|
|
for (tkl = tklines[index]; tkl; tkl = tkl->next)
|
|
{
|
|
if (TKLIsSpamfilter(tkl))
|
|
{
|
|
item = json_object();
|
|
json_expand_tkl(item, NULL, tkl, 1);
|
|
json_array_append_new(list, item);
|
|
}
|
|
}
|
|
}
|
|
|
|
rpc_response(client, request, result);
|
|
json_decref(result);
|
|
}
|
|
|
|
/* Shared code for selecting a spamfilter, for .add/.del/get */
|
|
int spamfilter_select_criteria(Client *client, json_t *request, json_t *params, const char **name, int *match_type,
|
|
int *targets, char *targetbuf, size_t targetbuflen, BanActionValue *action, char *actionbuf)
|
|
{
|
|
const char *str;
|
|
int actval;
|
|
|
|
*name = json_object_get_string(params, "name");
|
|
if (!*name)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Missing parameter: 'name'");
|
|
return 0;
|
|
}
|
|
|
|
str = json_object_get_string(params, "match_type");
|
|
if (!str)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Missing parameter: 'match_type'");
|
|
return 0;
|
|
}
|
|
*match_type = unreal_match_method_strtoval(str);
|
|
if (!*match_type)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Invalid value for parameter 'match_type'");
|
|
return 0;
|
|
}
|
|
|
|
str = json_object_get_string(params, "spamfilter_targets");
|
|
if (!str)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Missing parameter: 'spamfilter_targets'");
|
|
return 0;
|
|
}
|
|
*targets = spamfilter_gettargets(str, NULL);
|
|
if (!*targets)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Invalid value(s) for parameter 'spamfilter_targets'");
|
|
return 0;
|
|
}
|
|
strlcpy(targetbuf, spamfilter_target_inttostring(*targets), targetbuflen);
|
|
|
|
str = json_object_get_string(params, "ban_action");
|
|
if (!str)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Missing parameter: 'ban_action'");
|
|
return 0;
|
|
}
|
|
*action = banact_stringtoval(str);
|
|
if (!*action)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Invalid value for parameter 'ban_action'");
|
|
return 0;
|
|
}
|
|
/* I hope to remove this restriction at a later point: */
|
|
if (banact_config_only(*action))
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Invalid value for parameter 'ban_action': action is config-based only");
|
|
return 0;
|
|
}
|
|
actionbuf[0] = banact_valtochar(*action);
|
|
actionbuf[1] = '\0';
|
|
return 1;
|
|
}
|
|
|
|
RPC_CALL_FUNC(rpc_spamfilter_get)
|
|
{
|
|
json_t *result;
|
|
int type = TKL_SPAMF|TKL_GLOBAL;
|
|
const char *name;
|
|
TKL *tkl;
|
|
BanActionValue action;
|
|
int match_type = 0;
|
|
int targets = 0;
|
|
char targetbuf[64];
|
|
char actionbuf[2];
|
|
|
|
if (!spamfilter_select_criteria(client, request, params, &name, &match_type, &targets, targetbuf, sizeof(targetbuf), &action, actionbuf))
|
|
return; /* Error already communicated to client */
|
|
|
|
tkl = find_tkl_spamfilter(type, name, action, targets);
|
|
if (!tkl)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Spamfilter not found");
|
|
return;
|
|
}
|
|
|
|
result = json_object();
|
|
json_expand_tkl(result, "tkl", tkl, 1);
|
|
rpc_response(client, request, result);
|
|
json_decref(result);
|
|
}
|
|
|
|
RPC_CALL_FUNC(rpc_spamfilter_add)
|
|
{
|
|
json_t *result;
|
|
int type = TKL_SPAMF|TKL_GLOBAL;
|
|
const char *str;
|
|
const char *name, *reason;
|
|
const char *set_by;
|
|
time_t ban_duration = 0;
|
|
TKL *tkl;
|
|
Match *m;
|
|
BanActionValue action;
|
|
int match_type = 0;
|
|
int targets = 0;
|
|
char targetbuf[64];
|
|
char actionbuf[2];
|
|
char reasonbuf[512];
|
|
char *err = NULL;
|
|
|
|
if (!spamfilter_select_criteria(client, request, params, &name, &match_type, &targets, targetbuf, sizeof(targetbuf), &action, actionbuf))
|
|
return; /* Error already communicated to client */
|
|
|
|
/* Reason */
|
|
reason = json_object_get_string(params, "reason");
|
|
if (!reason)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Missing parameter: 'reason'");
|
|
return;
|
|
}
|
|
|
|
/* Ban duration */
|
|
if ((str = json_object_get_string(params, "ban_duration")))
|
|
{
|
|
ban_duration = config_checkval(str, CFG_TIME);
|
|
if (ban_duration < 0)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Invalid value for parameter 'ban_duration'");
|
|
return;
|
|
}
|
|
}
|
|
|
|
OPTIONAL_PARAM_STRING("set_by", set_by);
|
|
if (!set_by)
|
|
set_by = client->name;
|
|
|
|
if (find_tkl_spamfilter(type, name, action, targets))
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_ALREADY_EXISTS, "A spamfilter with that regex+action+target already exists");
|
|
return;
|
|
}
|
|
|
|
/* Convert reason to use internal storage and wire format */
|
|
reason = unreal_encodespace(reason);
|
|
strlcpy(reasonbuf, reason, sizeof(reasonbuf));
|
|
reason = reasonbuf;
|
|
|
|
/* now check the regex / match field (only when adding) */
|
|
m = unreal_create_match(match_type, name, &err);
|
|
if (!m)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Invalid regex or match string specified");
|
|
return;
|
|
}
|
|
|
|
tkl = tkl_add_spamfilter(type, NULL, targets, banact_value_to_struct(action), m, NULL, NULL, set_by, 0, TStime(),
|
|
ban_duration, reason, 0);
|
|
|
|
if (!tkl)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_INTERNAL_ERROR, "Unable to add item");
|
|
return;
|
|
}
|
|
|
|
tkl_added(client, tkl);
|
|
|
|
result = json_object();
|
|
json_expand_tkl(result, "tkl", tkl, 1);
|
|
rpc_response(client, request, result);
|
|
json_decref(result);
|
|
}
|
|
|
|
RPC_CALL_FUNC(rpc_spamfilter_del)
|
|
{
|
|
json_t *result;
|
|
int type = TKL_SPAMF|TKL_GLOBAL;
|
|
const char *name;
|
|
const char *set_by;
|
|
TKL *tkl;
|
|
BanActionValue action;
|
|
int match_type = 0;
|
|
int targets = 0;
|
|
char targetbuf[64];
|
|
char actionbuf[2];
|
|
const char *tkllayer[13];
|
|
|
|
if (!spamfilter_select_criteria(client, request, params, &name, &match_type, &targets, targetbuf, sizeof(targetbuf), &action, actionbuf))
|
|
return; /* Error already communicated to client */
|
|
|
|
OPTIONAL_PARAM_STRING("set_by", set_by);
|
|
if (!set_by)
|
|
set_by = client->name;
|
|
|
|
tkl = find_tkl_spamfilter(type, name, action, targets);
|
|
if (!tkl)
|
|
{
|
|
rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Spamfilter not found");
|
|
return;
|
|
}
|
|
|
|
result = json_object();
|
|
json_expand_tkl(result, "tkl", tkl, 1);
|
|
|
|
/* Wait.. this is a bit dull? */
|
|
tkllayer[1] = "-";
|
|
tkllayer[2] = "F";
|
|
tkllayer[3] = targetbuf;
|
|
tkllayer[4] = actionbuf;
|
|
tkllayer[5] = set_by;
|
|
tkllayer[6] = "-";
|
|
tkllayer[7] = "0";
|
|
tkllayer[8] = "0";
|
|
tkllayer[9] = "-";
|
|
tkllayer[10] = unreal_match_method_valtostr(match_type);
|
|
tkllayer[11] = name;
|
|
tkllayer[12] = NULL;
|
|
|
|
cmd_tkl(&me, NULL, 12, tkllayer);
|
|
|
|
tkl = find_tkl_spamfilter(type, name, action, targets);
|
|
if (!tkl)
|
|
{
|
|
rpc_response(client, request, result);
|
|
} else {
|
|
/* Spamfilter still exists so failure to remove.
|
|
* Actually this may not be an internal error, it could be an
|
|
* incorrect request, such as asking to remove a config-based spamfilter.
|
|
*/
|
|
rpc_error(client, request, JSON_RPC_ERROR_INTERNAL_ERROR, "Unable to remove item");
|
|
return;
|
|
}
|
|
json_decref(result);
|
|
}
|