1
0
Fork 0
coupserv/commands.c

524 lines
15 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Used-executed commands part of HaxServ
//
// Written by: Test_User <hax@andrewyu.org>
//
// This is free and unencumbered software released into the public
// domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "types.h"
#include "table.h"
#include "commands.h"
#include "network.h"
#include "tls.h"
#include "config.h"
#include "utils.h"
#define MAX_SPAM_COUNT 65536
struct table user_commands = {0};
int help_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
for (uint64_t i = 0; i < user_commands.len; i++) {
struct command_def *def = user_commands.array[i].ptr;
struct string message[] = {
command_prefix,
user_commands.array[i].name,
STRING("\x0F" " "),
def->summary,
};
privmsg(STRING("1HC000000"), to, sizeof(message)/sizeof(*message), message);
}
return 0;
}
static struct command_def help_command_def = {
.func = help_command,
.privs = {0},
.local_only = 0,
.summary = STRING("Shows a list of commands, or, when I write it up, help about a specific command"),
};
int raw_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
if (argv[0].len < original_message.len) {
original_message.data += argv[0].len + 1;
original_message.len -= argv[0].len + 1;
SEND(original_message);
}
SEND(STRING("\n"));
return 0;
}
static struct command_def raw_command_def = {
.func = raw_command,
.privs = STRING("NetAdmin"), // TODO: Make this configurable elsewhere
.local_only = 0,
.summary = STRING("Sends a raw message to the server"),
};
static struct pref_type_suff {
struct string pref;
uint8_t type;
struct string suff;
} sus_strings[] = {
{STRING(":1HC000000 PRIVMSG "), 0, STRING(" :Andrew is very sus.\n")},
{STRING(":1HC000000 PRIVMSG "), 0, STRING(" :I was the impostor, but you only know because I killed you.\n")},
{STRING(":1HC000000 PRIVMSG "), 0, STRING(" :\\x1b(0\n")},
{STRING(":1HC000000 KILL "), 1, STRING(" :Ejected (1 Impostor remains)\n")},
{STRING(":1HC000000 KILL "), 1, STRING(" :Ejected, and the crewmates have won.\n")},
}, cr_strings[] = {
{STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian toxicpod, kill the sharded crewmates.\n")},
{STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian omura, kill the sharded crewmates.\n")},
{STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian oct, but you ran out of reactors.\n")},
{STRING(":1HC000000 KILL "), 1, STRING(" :Eliminated (You became a cruxian eclipse, but were drawn to my bait reactor)\n")},
{STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You attempted to change into a cruxian navanax, but were caught in the act.\n")},
};
int sus_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
uint64_t index = (uint64_t)random() % (sizeof(sus_strings)/sizeof(sus_strings[0]));
SEND(sus_strings[index].pref);
if (sus_strings[index].type == 0)
SEND(to);
else
SEND(sender);
SEND(sus_strings[index].suff);
return 0;
}
static struct command_def sus_command_def = {
.func = sus_command,
.privs = {0},
.local_only = 0,
.summary = STRING("You seem a bit sus today"),
};
int cr_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
uint64_t index = (uint64_t)random() % (sizeof(cr_strings)/sizeof(cr_strings[0]));
SEND(cr_strings[index].pref);
if (cr_strings[index].type == 0)
SEND(to);
else
SEND(sender);
SEND(cr_strings[index].suff);
return 0;
}
static struct command_def cr_command_def = {
.func = cr_command,
.privs = {0},
.local_only = 0,
.summary = STRING("Join the crux side"),
};
int spam_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
if (argc < 3) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
return 0;
}
char err;
uint64_t count = str_to_unsigned(argv[1], &err);
if (err || (count > MAX_SPAM_COUNT && !is_local)) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Unknown number or number exceeds limit.")});
return 0;
}
char wasspace = 1;
uint64_t offset = 0;
char found = 0;
for (; offset < original_message.len; offset++) {
if (original_message.data[offset] == ' ' && !wasspace)
found++;
wasspace = (original_message.data[offset] == ' ');
if (found >= 2 && !wasspace)
break;
}
if (found < 2) {
WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
return 0;
}
struct command_def *cmd = get_table_index(user_commands, argv[2]);
if (cmd) {
if (!cmd->local_only) {
if (cmd->privs.len != 0 && sender.len != 3) {
struct user_info *user = get_table_index(user_list, sender);
if (!user)
return 1; // really shouldn't happen
if (!STRING_EQ(user->opertype, cmd->privs)) {
SEND(STRING(":1HC000000 NOTICE "));
SEND(to);
SEND(STRING(" :You are not authorized to execute that command.\n"));
return 0;
}
if (cmd->func == spam_command) {
SEND(STRING(":1HC000000 NOTICE "));
SEND(to);
SEND(STRING(" :Spam recursion is not allowed. The limit is for your own sake, please do not violate it.\n"));
return 0;
}
}
} else {
SEND(STRING(":1HC000000 NOTICE "));
SEND(to);
SEND(STRING(" :Spamming of local-only commands is disabled.\n"));
return 0;
}
} else {
SEND(STRING(":1HC000000 NOTICE "));
SEND(to);
SEND(STRING(" :Unknown command.\n"));
return 0;
}
struct string fake_original_message = {.data = original_message.data + offset, .len = original_message.len - offset};
WRITES(2, fake_original_message);
WRITES(2, STRING("\n"));
for (uint64_t i = 0; i < count; i++) {
int ret = cmd->func(sender, fake_original_message, to, argc - 2, &(argv[2]), 0);
if (ret)
return ret;
}
return 0;
}
static struct command_def spam_command_def = {
.func = spam_command,
.privs = STRING("NetAdmin"),
.local_only = 0,
.summary = STRING("Repeats a command <n> times"),
};
int clear_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
if (argc < 2) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
return 0;
}
struct channel_info *channel = get_table_index(channel_list, argv[1]);
if (!channel) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("That channel doesn't seem to exist, so is thereby already cleared.")});
return 0;
}
for (uint64_t i = 0; i < channel->user_list.len; i++) {
// TODO: Proper kick function, will prob come as a part of my next HaxServ rewrite
SEND(STRING(":1HC000000 KICK "));
SEND(argv[1]);
SEND(STRING(" "));
SEND(channel->user_list.array[i].name);
SEND(STRING(" :(B\n"));
if (client_connected) {
SENDCLIENT(STRING(":"));
SENDCLIENT(nick);
SENDCLIENT(STRING("!"));
SENDCLIENT(nick);
SENDCLIENT(STRING("@"));
SENDCLIENT(hostmask);
SENDCLIENT(STRING(" KICK "));
SENDCLIENT(argv[1]);
SENDCLIENT(STRING(" "));
struct user_info *user = get_table_index(user_list, channel->user_list.array[i].name);
if (user)
SENDCLIENT(user->nick);
else
SENDCLIENT(channel->user_list.array[i].name);
SENDCLIENT(STRING(" :(B\r\n"));
}
}
clear_table(&(channel->user_list));
return 0;
}
static struct command_def clear_command_def = {
.func = clear_command,
.privs = STRING("NetAdmin"),
.local_only = 0,
.summary = STRING("Clears a channel"),
};
struct sh_command_args {
char *command;
struct string to;
};
void * async_sh_command(void *tmp) {
struct sh_command_args *sh_args = tmp;
FILE *f = popen(sh_args->command, "r");
free(sh_args->command);
char *line = NULL;
size_t buflen;
while (1) {
ssize_t len = getline(&line, &buflen, f);
if (len <= 0)
break;
struct string linestr = {.data = line, .len = (size_t)(len)};
while (linestr.len > 0 && (linestr.data[linestr.len-1] == '\n' || linestr.data[linestr.len-1] == '\r'))
linestr.len--;
if (linestr.len == 0)
linestr = STRING(" ");
pthread_mutex_lock(&send_lock);
SEND(STRING(":1HC000000 PRIVMSG "));
SEND(sh_args->to);
SEND(STRING(" :"));
SEND(linestr);
SEND(STRING("\n"));
SENDCLIENT(STRING(":"));
SENDCLIENT(nick);
SENDCLIENT(STRING("!e@e PRIVMSG "));
SENDCLIENT(sh_args->to);
SENDCLIENT(STRING(" :"));
SENDCLIENT(linestr);
SENDCLIENT(STRING("\r\n"));
pthread_mutex_unlock(&send_lock);
}
free(line);
pclose(f);
free(sh_args->to.data);
free(sh_args);
return 0;
}
int sh_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
if (!is_local) {
return 0;
}
if (argc < 2) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
return 0;
}
char wasspace = 1;
uint64_t offset = 0;
char found = 0;
for (; offset < original_message.len; offset++) {
if (original_message.data[offset] == ' ' && !wasspace)
found++;
wasspace = (original_message.data[offset] == ' ');
if (found >= 1 && !wasspace)
break;
}
if (found < 1) {
WRITES(2, STRING("WARNING: Apparently there was no argument... shouldn't happen.\n"));
return 0;
}
struct string command = {.data = original_message.data + offset, .len = original_message.len - offset};
struct sh_command_args *sh_args;
sh_args = malloc(sizeof(*sh_args));
if (!sh_args) {
WRITES(2, STRING("ERROR: OOM\n"));
return 0;
}
sh_args->command = malloc(command.len+1);
if (!sh_args->command) {
free(sh_args);
WRITES(2, STRING("ERROR: OOM\n"));
return 0;
}
memcpy(sh_args->command, command.data, command.len);
sh_args->command[command.len] = '\0';
sh_args->to.len = to.len;
sh_args->to.data = malloc(to.len);
if (!sh_args->to.data) {
free(sh_args->command);
free(sh_args);
WRITES(2, STRING("ERROR: OOM\n"));
return 0;
}
memcpy(sh_args->to.data, to.data, to.len);
pthread_t trash;
pthread_create(&trash, NULL, async_sh_command, sh_args);
return 0;
}
static struct command_def sh_command_def = {
.func = sh_command,
.privs = STRING("NetAdmin"),
.local_only = 1,
.summary = STRING("Executes a command locally"),
};
int kill_old_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
if (argc < 2) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
return 0;
}
uint64_t current_time = (uint64_t)time(0);
char err;
uint64_t age = str_to_unsigned(argv[1], &err);
if (err) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Invalid age!")});
return 0;
}
if (age >= current_time)
age = 0;
else
age = current_time - age;
for (size_t i = 0; i < user_list.len; i++) {
struct user_info *user = user_list.array[i].ptr;
if ((user->user_ts <= age || STRING_EQ(user->nick, STRING("OperServ"))) && !STRING_EQ(user->server, STRING("1HC"))) {
SEND(STRING(":1HC000000 KILL "));
SEND(user_list.array[i].name);
SEND(STRING(" :Your connection is too old.\n"));
}
}
return 0;
}
static struct command_def kill_old_command_def = {
.func = kill_old_command,
.privs = STRING("NetAdmin"),
.local_only = 0,
.summary = STRING("Kills old connections (with a time you specify), and OperServ because OperServ is wrong"),
};
int echo_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
if (argc < 2) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
return 0;
}
char wasspace = 1;
uint64_t offset = 0;
char found = 0;
for (; offset < original_message.len; offset++) {
if (original_message.data[offset] == ' ' && !wasspace)
found++;
wasspace = (original_message.data[offset] == ' ');
if (found >= 1 && !wasspace)
break;
}
if (found < 1) {
WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
return 0;
}
struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}};
privmsg(STRING("1HC000000"), to, 1, message);
return 0;
}
static struct command_def echo_command_def = {
.func = echo_command,
.privs = {0},
.local_only = 0,
.summary = STRING("Repeats a message back"),
};
int tell_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
if (argc < 3) {
privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
return 0;
}
char wasspace = 1;
uint64_t offset = 0;
char found = 0;
for (; offset < original_message.len; offset++) {
if (original_message.data[offset] == ' ' && !wasspace)
found++;
wasspace = (original_message.data[offset] == ' ');
if (found >= 2 && !wasspace)
break;
}
if (found < 2) {
WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
return 0;
}
struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}};
privmsg(STRING("1HC000000"), argv[1], 1, message);
return 0;
}
static struct command_def tell_command_def = {
.func = tell_command,
.privs = STRING("NetAdmin"),
.local_only = 0,
.summary = STRING("Sends a message to a target"),
};
int init_user_commands(void) {
srandom(time(NULL));
user_commands.array = malloc(0);
set_table_index(&user_commands, STRING(":"), &raw_command_def);
set_table_index(&user_commands, STRING("sus"), &sus_command_def);
set_table_index(&user_commands, STRING("cr"), &cr_command_def);
set_table_index(&user_commands, STRING("help"), &help_command_def);
set_table_index(&user_commands, STRING("spam"), &spam_command_def);
set_table_index(&user_commands, STRING("clear"), &clear_command_def);
set_table_index(&user_commands, STRING("sh"), &sh_command_def);
set_table_index(&user_commands, STRING("kill_old"), &kill_old_command_def);
set_table_index(&user_commands, STRING("echo"), &echo_command_def);
set_table_index(&user_commands, STRING("tell"), &tell_command_def);
return 0;
}