free bugs [fixed]
[coupserv.git] / commands.c
blobb3fb4227418c5940075cde25fca47ed31ee59643
1 // Used-executed commands part of HaxServ
2 //
3 // Written by: Test_User <hax@andrewyu.org>
4 //
5 // This is free and unencumbered software released into the public
6 // domain.
7 //
8 // Anyone is free to copy, modify, publish, use, compile, sell, or
9 // distribute this software, either in source code form or as a compiled
10 // binary, for any purpose, commercial or non-commercial, and by any
11 // means.
13 // In jurisdictions that recognize copyright laws, the author or authors
14 // of this software dedicate any and all copyright interest in the
15 // software to the public domain. We make this dedication for the benefit
16 // of the public at large and to the detriment of our heirs and
17 // successors. We intend this dedication to be an overt act of
18 // relinquishment in perpetuity of all present and future rights to this
19 // software under copyright law.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 // OTHER DEALINGS IN THE SOFTWARE.
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
34 #include "types.h"
35 #include "table.h"
36 #include "commands.h"
37 #include "network.h"
38 #include "tls.h"
39 #include "config.h"
40 #include "utils.h"
42 #define MAX_SPAM_COUNT 65536
44 struct table user_commands = {0};
46 int help_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
47 for (uint64_t i = 0; i < user_commands.len; i++) {
48 struct command_def *def = user_commands.array[i].ptr;
50 struct string message[] = {
51 command_prefix,
52 user_commands.array[i].name,
53 STRING("\x0F" " "),
54 def->summary,
57 privmsg(STRING("1HC000000"), to, sizeof(message)/sizeof(*message), message);
60 return 0;
62 static struct command_def help_command_def = {
63 .func = help_command,
64 .privs = {0},
65 .local_only = 0,
66 .summary = STRING("Shows a list of commands, or, when I write it up, help about a specific command"),
69 int raw_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
70 if (argv[0].len < original_message.len) {
71 original_message.data += argv[0].len + 1;
72 original_message.len -= argv[0].len + 1;
73 SEND(original_message);
75 SEND(STRING("\n"));
77 return 0;
79 static struct command_def raw_command_def = {
80 .func = raw_command,
81 .privs = STRING("NetAdmin"), // TODO: Make this configurable elsewhere
82 .local_only = 0,
83 .summary = STRING("Sends a raw message to the server"),
86 static struct pref_type_suff {
87 struct string pref;
88 uint8_t type;
89 struct string suff;
90 } sus_strings[] = {
91 {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :Andrew is very sus.\n")},
92 {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :I was the impostor, but you only know because I killed you.\n")},
93 {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :\\x1b(0\n")},
94 {STRING(":1HC000000 KILL "), 1, STRING(" :Ejected (1 Impostor remains)\n")},
95 {STRING(":1HC000000 KILL "), 1, STRING(" :Ejected, and the crewmates have won.\n")},
96 }, cr_strings[] = {
97 {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian toxicpod, kill the sharded crewmates.\n")},
98 {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian omura, kill the sharded crewmates.\n")},
99 {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian oct, but you ran out of reactors.\n")},
100 {STRING(":1HC000000 KILL "), 1, STRING(" :Eliminated (You became a cruxian eclipse, but were drawn to my bait reactor)\n")},
101 {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You attempted to change into a cruxian navanax, but were caught in the act.\n")},
104 int sus_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
105 uint64_t index = (uint64_t)random() % (sizeof(sus_strings)/sizeof(sus_strings[0]));
107 SEND(sus_strings[index].pref);
108 if (sus_strings[index].type == 0)
109 SEND(to);
110 else
111 SEND(sender);
112 SEND(sus_strings[index].suff);
114 return 0;
116 static struct command_def sus_command_def = {
117 .func = sus_command,
118 .privs = {0},
119 .local_only = 0,
120 .summary = STRING("You seem a bit sus today"),
123 int cr_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
124 uint64_t index = (uint64_t)random() % (sizeof(cr_strings)/sizeof(cr_strings[0]));
126 SEND(cr_strings[index].pref);
127 if (cr_strings[index].type == 0)
128 SEND(to);
129 else
130 SEND(sender);
131 SEND(cr_strings[index].suff);
133 return 0;
135 static struct command_def cr_command_def = {
136 .func = cr_command,
137 .privs = {0},
138 .local_only = 0,
139 .summary = STRING("Join the crux side"),
142 int spam_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
143 if (argc < 3) {
144 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
145 return 0;
148 char err;
149 uint64_t count = str_to_unsigned(argv[1], &err);
150 if (err || (count > MAX_SPAM_COUNT && !is_local)) {
151 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Unknown number or number exceeds limit.")});
152 return 0;
155 char wasspace = 1;
156 uint64_t offset = 0;
157 char found = 0;
158 for (; offset < original_message.len; offset++) {
159 if (original_message.data[offset] == ' ' && !wasspace)
160 found++;
162 wasspace = (original_message.data[offset] == ' ');
164 if (found >= 2 && !wasspace)
165 break;
168 if (found < 2) {
169 WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
170 return 0;
173 struct command_def *cmd = get_table_index(user_commands, argv[2]);
174 if (cmd) {
175 if (!cmd->local_only) {
176 if (cmd->privs.len != 0 && sender.len != 3) {
177 struct user_info *user = get_table_index(user_list, sender);
178 if (!user)
179 return 1; // really shouldn't happen
180 if (!STRING_EQ(user->opertype, cmd->privs)) {
181 SEND(STRING(":1HC000000 NOTICE "));
182 SEND(to);
183 SEND(STRING(" :You are not authorized to execute that command.\n"));
185 return 0;
188 if (cmd->func == spam_command) {
189 SEND(STRING(":1HC000000 NOTICE "));
190 SEND(to);
191 SEND(STRING(" :Spam recursion is not allowed. The limit is for your own sake, please do not violate it.\n"));
193 return 0;
196 } else {
197 SEND(STRING(":1HC000000 NOTICE "));
198 SEND(to);
199 SEND(STRING(" :Spamming of local-only commands is disabled.\n"));
201 return 0;
203 } else {
204 SEND(STRING(":1HC000000 NOTICE "));
205 SEND(to);
206 SEND(STRING(" :Unknown command.\n"));
208 return 0;
211 struct string fake_original_message = {.data = original_message.data + offset, .len = original_message.len - offset};
212 WRITES(2, fake_original_message);
213 WRITES(2, STRING("\n"));
215 for (uint64_t i = 0; i < count; i++) {
216 int ret = cmd->func(sender, fake_original_message, to, argc - 2, &(argv[2]), 0);
217 if (ret)
218 return ret;
221 return 0;
223 static struct command_def spam_command_def = {
224 .func = spam_command,
225 .privs = STRING("NetAdmin"),
226 .local_only = 0,
227 .summary = STRING("Repeats a command <n> times"),
230 int clear_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
231 if (argc < 2) {
232 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
233 return 0;
236 struct channel_info *channel = get_table_index(channel_list, argv[1]);
237 if (!channel) {
238 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("That channel doesn't seem to exist, so is thereby already cleared.")});
239 return 0;
242 for (uint64_t i = 0; i < channel->user_list.len; i++) {
243 // TODO: Proper kick function, will prob come as a part of my next HaxServ rewrite
245 SEND(STRING(":1HC000000 KICK "));
246 SEND(argv[1]);
247 SEND(STRING(" "));
248 SEND(channel->user_list.array[i].name);
249 SEND(STRING(" :\e(B\n"));
251 if (client_connected) {
252 SENDCLIENT(STRING(":"));
253 SENDCLIENT(nick);
254 SENDCLIENT(STRING("!"));
255 SENDCLIENT(nick);
256 SENDCLIENT(STRING("@"));
257 SENDCLIENT(hostmask);
258 SENDCLIENT(STRING(" KICK "));
259 SENDCLIENT(argv[1]);
260 SENDCLIENT(STRING(" "));
262 struct user_info *user = get_table_index(user_list, channel->user_list.array[i].name);
263 if (user)
264 SENDCLIENT(user->nick);
265 else
266 SENDCLIENT(channel->user_list.array[i].name);
268 SENDCLIENT(STRING(" :\e(B\r\n"));
271 clear_table(&(channel->user_list));
273 return 0;
275 static struct command_def clear_command_def = {
276 .func = clear_command,
277 .privs = STRING("NetAdmin"),
278 .local_only = 0,
279 .summary = STRING("Clears a channel"),
282 struct sh_command_args {
283 char *command;
284 struct string to;
286 void * async_sh_command(void *tmp) {
287 struct sh_command_args *sh_args = tmp;
289 FILE *f = popen(sh_args->command, "r");
290 free(sh_args->command);
292 char *line = NULL;
293 size_t buflen;
294 while (1) {
295 ssize_t len = getline(&line, &buflen, f);
296 if (len <= 0)
297 break;
299 struct string linestr = {.data = line, .len = (size_t)(len)};
300 while (linestr.len > 0 && (linestr.data[linestr.len-1] == '\n' || linestr.data[linestr.len-1] == '\r'))
301 linestr.len--;
302 if (linestr.len == 0)
303 linestr = STRING(" ");
304 pthread_mutex_lock(&send_lock);
305 SEND(STRING(":1HC000000 PRIVMSG "));
306 SEND(sh_args->to);
307 SEND(STRING(" :"));
308 SEND(linestr);
309 SEND(STRING("\n"));
311 SENDCLIENT(STRING(":"));
312 SENDCLIENT(nick);
313 SENDCLIENT(STRING("!e@e PRIVMSG "));
314 SENDCLIENT(sh_args->to);
315 SENDCLIENT(STRING(" :"));
316 SENDCLIENT(linestr);
317 SENDCLIENT(STRING("\r\n"));
318 pthread_mutex_unlock(&send_lock);
321 free(line);
323 pclose(f);
325 free(sh_args->to.data);
326 free(sh_args);
328 return 0;
331 int sh_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
332 if (!is_local) {
333 return 0;
336 if (argc < 2) {
337 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
338 return 0;
341 char wasspace = 1;
342 uint64_t offset = 0;
343 char found = 0;
344 for (; offset < original_message.len; offset++) {
345 if (original_message.data[offset] == ' ' && !wasspace)
346 found++;
348 wasspace = (original_message.data[offset] == ' ');
350 if (found >= 1 && !wasspace)
351 break;
354 if (found < 1) {
355 WRITES(2, STRING("WARNING: Apparently there was no argument... shouldn't happen.\n"));
356 return 0;
359 struct string command = {.data = original_message.data + offset, .len = original_message.len - offset};
361 struct sh_command_args *sh_args;
362 sh_args = malloc(sizeof(*sh_args));
363 if (!sh_args) {
364 WRITES(2, STRING("ERROR: OOM\n"));
365 return 0;
368 sh_args->command = malloc(command.len+1);
369 if (!sh_args->command) {
370 free(sh_args);
371 WRITES(2, STRING("ERROR: OOM\n"));
372 return 0;
374 memcpy(sh_args->command, command.data, command.len);
375 sh_args->command[command.len] = '\0';
377 sh_args->to.len = to.len;
378 sh_args->to.data = malloc(to.len);
379 if (!sh_args->to.data) {
380 free(sh_args->command);
381 free(sh_args);
382 WRITES(2, STRING("ERROR: OOM\n"));
383 return 0;
385 memcpy(sh_args->to.data, to.data, to.len);
387 pthread_t trash;
388 pthread_create(&trash, NULL, async_sh_command, sh_args);
390 return 0;
392 static struct command_def sh_command_def = {
393 .func = sh_command,
394 .privs = STRING("NetAdmin"),
395 .local_only = 1,
396 .summary = STRING("Executes a command locally"),
399 int kill_old_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
400 if (argc < 2) {
401 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
402 return 0;
405 uint64_t current_time = (uint64_t)time(0);
406 char err;
407 uint64_t age = str_to_unsigned(argv[1], &err);
408 if (err) {
409 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Invalid age!")});
410 return 0;
412 if (age >= current_time)
413 age = 0;
414 else
415 age = current_time - age;
417 for (size_t i = 0; i < user_list.len; i++) {
418 struct user_info *user = user_list.array[i].ptr;
419 if ((user->user_ts <= age || STRING_EQ(user->nick, STRING("OperServ"))) && !STRING_EQ(user->server, STRING("1HC"))) {
420 SEND(STRING(":1HC000000 KILL "));
421 SEND(user_list.array[i].name);
422 SEND(STRING(" :Your connection is too old.\n"));
426 return 0;
428 static struct command_def kill_old_command_def = {
429 .func = kill_old_command,
430 .privs = STRING("NetAdmin"),
431 .local_only = 0,
432 .summary = STRING("Kills old connections (with a time you specify), and OperServ because OperServ is wrong"),
435 int echo_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
436 if (argc < 2) {
437 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
438 return 0;
441 char wasspace = 1;
442 uint64_t offset = 0;
443 char found = 0;
444 for (; offset < original_message.len; offset++) {
445 if (original_message.data[offset] == ' ' && !wasspace)
446 found++;
448 wasspace = (original_message.data[offset] == ' ');
450 if (found >= 1 && !wasspace)
451 break;
454 if (found < 1) {
455 WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
456 return 0;
459 struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}};
460 privmsg(STRING("1HC000000"), to, 1, message);
462 return 0;
464 static struct command_def echo_command_def = {
465 .func = echo_command,
466 .privs = {0},
467 .local_only = 0,
468 .summary = STRING("Repeats a message back"),
471 int tell_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
472 if (argc < 3) {
473 privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
474 return 0;
477 char wasspace = 1;
478 uint64_t offset = 0;
479 char found = 0;
480 for (; offset < original_message.len; offset++) {
481 if (original_message.data[offset] == ' ' && !wasspace)
482 found++;
484 wasspace = (original_message.data[offset] == ' ');
486 if (found >= 2 && !wasspace)
487 break;
490 if (found < 2) {
491 WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
492 return 0;
495 struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}};
496 privmsg(STRING("1HC000000"), argv[1], 1, message);
498 return 0;
500 static struct command_def tell_command_def = {
501 .func = tell_command,
502 .privs = STRING("NetAdmin"),
503 .local_only = 0,
504 .summary = STRING("Sends a message to a target"),
507 int init_user_commands(void) {
508 srandom(time(NULL));
510 user_commands.array = malloc(0);
512 set_table_index(&user_commands, STRING(":"), &raw_command_def);
513 set_table_index(&user_commands, STRING("sus"), &sus_command_def);
514 set_table_index(&user_commands, STRING("cr"), &cr_command_def);
515 set_table_index(&user_commands, STRING("help"), &help_command_def);
516 set_table_index(&user_commands, STRING("spam"), &spam_command_def);
517 set_table_index(&user_commands, STRING("clear"), &clear_command_def);
518 set_table_index(&user_commands, STRING("sh"), &sh_command_def);
519 set_table_index(&user_commands, STRING("kill_old"), &kill_old_command_def);
520 set_table_index(&user_commands, STRING("echo"), &echo_command_def);
521 set_table_index(&user_commands, STRING("tell"), &tell_command_def);
523 return 0;