1 // Used-executed commands part of HaxServ
3 // Written by: Test_User <hax@andrewyu.org>
5 // This is free and unencumbered software released into the public
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
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.
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
[] = {
52 user_commands
.array
[i
].name
,
57 privmsg(STRING("1HC000000"), to
, sizeof(message
)/sizeof(*message
), message
);
62 static struct command_def help_command_def
= {
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
);
79 static struct command_def raw_command_def
= {
81 .privs
= STRING("NetAdmin"), // TODO: Make this configurable elsewhere
83 .summary
= STRING("Sends a raw message to the server"),
86 static struct pref_type_suff
{
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")},
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)
112 SEND(sus_strings
[index
].suff
);
116 static struct command_def sus_command_def
= {
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)
131 SEND(cr_strings
[index
].suff
);
135 static struct command_def cr_command_def
= {
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
) {
144 privmsg(STRING("1HC000000"), to
, 1, (struct string
[]){STRING("Missing args!")});
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.")});
158 for (; offset
< original_message
.len
; offset
++) {
159 if (original_message
.data
[offset
] == ' ' && !wasspace
)
162 wasspace
= (original_message
.data
[offset
] == ' ');
164 if (found
>= 2 && !wasspace
)
169 WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
173 struct command_def
*cmd
= get_table_index(user_commands
, argv
[2]);
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
);
179 return 1; // really shouldn't happen
180 if (!STRING_EQ(user
->opertype
, cmd
->privs
)) {
181 SEND(STRING(":1HC000000 NOTICE "));
183 SEND(STRING(" :You are not authorized to execute that command.\n"));
188 if (cmd
->func
== spam_command
) {
189 SEND(STRING(":1HC000000 NOTICE "));
191 SEND(STRING(" :Spam recursion is not allowed. The limit is for your own sake, please do not violate it.\n"));
197 SEND(STRING(":1HC000000 NOTICE "));
199 SEND(STRING(" :Spamming of local-only commands is disabled.\n"));
204 SEND(STRING(":1HC000000 NOTICE "));
206 SEND(STRING(" :Unknown command.\n"));
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);
223 static struct command_def spam_command_def
= {
224 .func
= spam_command
,
225 .privs
= STRING("NetAdmin"),
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
) {
232 privmsg(STRING("1HC000000"), to
, 1, (struct string
[]){STRING("Missing args!")});
236 struct channel_info
*channel
= get_table_index(channel_list
, argv
[1]);
238 privmsg(STRING("1HC000000"), to
, 1, (struct string
[]){STRING("That channel doesn't seem to exist, so is thereby already cleared.")});
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 "));
248 SEND(channel
->user_list
.array
[i
].name
);
249 SEND(STRING(" :\e(B\n"));
251 if (client_connected
) {
252 SENDCLIENT(STRING(":"));
254 SENDCLIENT(STRING("!"));
256 SENDCLIENT(STRING("@"));
257 SENDCLIENT(hostmask
);
258 SENDCLIENT(STRING(" KICK "));
260 SENDCLIENT(STRING(" "));
262 struct user_info
*user
= get_table_index(user_list
, channel
->user_list
.array
[i
].name
);
264 SENDCLIENT(user
->nick
);
266 SENDCLIENT(channel
->user_list
.array
[i
].name
);
268 SENDCLIENT(STRING(" :\e(B\r\n"));
271 clear_table(&(channel
->user_list
));
275 static struct command_def clear_command_def
= {
276 .func
= clear_command
,
277 .privs
= STRING("NetAdmin"),
279 .summary
= STRING("Clears a channel"),
282 struct sh_command_args
{
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
);
295 ssize_t len
= getline(&line
, &buflen
, f
);
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'))
302 if (linestr
.len
== 0)
303 linestr
= STRING(" ");
304 pthread_mutex_lock(&send_lock
);
305 SEND(STRING(":1HC000000 PRIVMSG "));
311 SENDCLIENT(STRING(":"));
313 SENDCLIENT(STRING("!e@e PRIVMSG "));
314 SENDCLIENT(sh_args
->to
);
315 SENDCLIENT(STRING(" :"));
317 SENDCLIENT(STRING("\r\n"));
318 pthread_mutex_unlock(&send_lock
);
325 free(sh_args
->to
.data
);
331 int sh_command(struct string sender
, struct string original_message
, struct string to
, uint64_t argc
, struct string
*argv
, char is_local
) {
337 privmsg(STRING("1HC000000"), to
, 1, (struct string
[]){STRING("Missing args!")});
344 for (; offset
< original_message
.len
; offset
++) {
345 if (original_message
.data
[offset
] == ' ' && !wasspace
)
348 wasspace
= (original_message
.data
[offset
] == ' ');
350 if (found
>= 1 && !wasspace
)
355 WRITES(2, STRING("WARNING: Apparently there was no argument... shouldn't happen.\n"));
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
));
364 WRITES(2, STRING("ERROR: OOM\n"));
368 sh_args
->command
= malloc(command
.len
+1);
369 if (!sh_args
->command
) {
371 WRITES(2, STRING("ERROR: OOM\n"));
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
);
382 WRITES(2, STRING("ERROR: OOM\n"));
385 memcpy(sh_args
->to
.data
, to
.data
, to
.len
);
388 pthread_create(&trash
, NULL
, async_sh_command
, sh_args
);
392 static struct command_def sh_command_def
= {
394 .privs
= STRING("NetAdmin"),
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
) {
401 privmsg(STRING("1HC000000"), to
, 1, (struct string
[]){STRING("Missing args!")});
405 uint64_t current_time
= (uint64_t)time(0);
407 uint64_t age
= str_to_unsigned(argv
[1], &err
);
409 privmsg(STRING("1HC000000"), to
, 1, (struct string
[]){STRING("Invalid age!")});
412 if (age
>= current_time
)
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"));
428 static struct command_def kill_old_command_def
= {
429 .func
= kill_old_command
,
430 .privs
= STRING("NetAdmin"),
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
) {
437 privmsg(STRING("1HC000000"), to
, 1, (struct string
[]){STRING("Missing args!")});
444 for (; offset
< original_message
.len
; offset
++) {
445 if (original_message
.data
[offset
] == ' ' && !wasspace
)
448 wasspace
= (original_message
.data
[offset
] == ' ');
450 if (found
>= 1 && !wasspace
)
455 WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
459 struct string message
[] = {{.data
= original_message
.data
+ offset
, .len
= original_message
.len
- offset
}};
460 privmsg(STRING("1HC000000"), to
, 1, message
);
464 static struct command_def echo_command_def
= {
465 .func
= echo_command
,
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
) {
473 privmsg(STRING("1HC000000"), to
, 1, (struct string
[]){STRING("Missing args!")});
480 for (; offset
< original_message
.len
; offset
++) {
481 if (original_message
.data
[offset
] == ' ' && !wasspace
)
484 wasspace
= (original_message
.data
[offset
] == ' ');
486 if (found
>= 2 && !wasspace
)
491 WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
495 struct string message
[] = {{.data
= original_message
.data
+ offset
, .len
= original_message
.len
- offset
}};
496 privmsg(STRING("1HC000000"), argv
[1], 1, message
);
500 static struct command_def tell_command_def
= {
501 .func
= tell_command
,
502 .privs
= STRING("NetAdmin"),
504 .summary
= STRING("Sends a message to a target"),
507 int init_user_commands(void) {
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
);