commands.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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.
  12. //
  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.
  20. //
  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.
  28. #include <stdint.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <stdio.h>
  32. #include "types.h"
  33. #include "table.h"
  34. #include "commands.h"
  35. #include "network.h"
  36. #include "tls.h"
  37. #include "config.h"
  38. #include "utils.h"
  39. #define MAX_SPAM_COUNT 65536
  40. struct table user_commands = {0};
  41. int help_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  42. for (uint64_t i = 0; i < user_commands.len; i++) {
  43. struct command_def *def = user_commands.array[i].ptr;
  44. struct string message[] = {
  45. command_prefix,
  46. user_commands.array[i].name,
  47. STRING("\x0F" " "),
  48. def->summary,
  49. };
  50. privmsg(STRING("1HC000000"), to, sizeof(message)/sizeof(*message), message);
  51. }
  52. return 0;
  53. }
  54. static struct command_def help_command_def = {
  55. .func = help_command,
  56. .privs = {0},
  57. .local_only = 0,
  58. .summary = STRING("Shows a list of commands, or, when I write it up, help about a specific command"),
  59. };
  60. int raw_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  61. if (argv[0].len < original_message.len) {
  62. original_message.data += argv[0].len + 1;
  63. original_message.len -= argv[0].len + 1;
  64. SEND(original_message);
  65. }
  66. SEND(STRING("\n"));
  67. return 0;
  68. }
  69. static struct command_def raw_command_def = {
  70. .func = raw_command,
  71. .privs = STRING("NetAdmin"), // TODO: Make this configurable elsewhere
  72. .local_only = 0,
  73. .summary = STRING("Sends a raw message to the server"),
  74. };
  75. static struct pref_type_suff {
  76. struct string pref;
  77. uint8_t type;
  78. struct string suff;
  79. } sus_strings[] = {
  80. {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :Andrew is very sus.\n")},
  81. {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :I was the impostor, but you only know because I killed you.\n")},
  82. {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :\\x1b(0\n")},
  83. {STRING(":1HC000000 KILL "), 1, STRING(" :Ejected (1 Impostor remains)\n")},
  84. {STRING(":1HC000000 KILL "), 1, STRING(" :Ejected, and the crewmates have won.\n")},
  85. }, cr_strings[] = {
  86. {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian toxicpod, kill the sharded crewmates.\n")},
  87. {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian omura, kill the sharded crewmates.\n")},
  88. {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You are now a cruxian oct, but you ran out of reactors.\n")},
  89. {STRING(":1HC000000 KILL "), 1, STRING(" :Eliminated (You became a cruxian eclipse, but were drawn to my bait reactor)\n")},
  90. {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :You attempted to change into a cruxian navanax, but were caught in the act.\n")},
  91. };
  92. int sus_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  93. uint64_t index = (uint64_t)random() % (sizeof(sus_strings)/sizeof(sus_strings[0]));
  94. SEND(sus_strings[index].pref);
  95. if (sus_strings[index].type == 0)
  96. SEND(to);
  97. else
  98. SEND(sender);
  99. SEND(sus_strings[index].suff);
  100. return 0;
  101. }
  102. static struct command_def sus_command_def = {
  103. .func = sus_command,
  104. .privs = {0},
  105. .local_only = 0,
  106. .summary = STRING("You seem a bit sus today"),
  107. };
  108. int cr_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  109. uint64_t index = (uint64_t)random() % (sizeof(cr_strings)/sizeof(cr_strings[0]));
  110. SEND(cr_strings[index].pref);
  111. if (cr_strings[index].type == 0)
  112. SEND(to);
  113. else
  114. SEND(sender);
  115. SEND(cr_strings[index].suff);
  116. return 0;
  117. }
  118. static struct command_def cr_command_def = {
  119. .func = cr_command,
  120. .privs = {0},
  121. .local_only = 0,
  122. .summary = STRING("Join the crux side"),
  123. };
  124. int spam_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  125. if (argc < 3) {
  126. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
  127. return 0;
  128. }
  129. char err;
  130. uint64_t count = str_to_unsigned(argv[1], &err);
  131. if (err || (count > MAX_SPAM_COUNT && !is_local)) {
  132. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Unknown number or number exceeds limit.")});
  133. return 0;
  134. }
  135. char wasspace = 1;
  136. uint64_t offset = 0;
  137. char found = 0;
  138. for (; offset < original_message.len; offset++) {
  139. if (original_message.data[offset] == ' ' && !wasspace)
  140. found++;
  141. wasspace = (original_message.data[offset] == ' ');
  142. if (found >= 2 && !wasspace)
  143. break;
  144. }
  145. if (found < 2) {
  146. WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
  147. return 0;
  148. }
  149. struct command_def *cmd = get_table_index(user_commands, argv[2]);
  150. if (cmd) {
  151. if (!cmd->local_only) {
  152. if (cmd->privs.len != 0 && sender.len != 3) {
  153. struct user_info *user = get_table_index(user_list, sender);
  154. if (!user)
  155. return 1; // really shouldn't happen
  156. if (!STRING_EQ(user->opertype, cmd->privs)) {
  157. SEND(STRING(":1HC000000 NOTICE "));
  158. SEND(to);
  159. SEND(STRING(" :You are not authorized to execute that command.\n"));
  160. return 0;
  161. }
  162. if (cmd->func == spam_command) {
  163. SEND(STRING(":1HC000000 NOTICE "));
  164. SEND(to);
  165. SEND(STRING(" :Spam recursion is not allowed. The limit is for your own sake, please do not violate it.\n"));
  166. return 0;
  167. }
  168. }
  169. } else {
  170. SEND(STRING(":1HC000000 NOTICE "));
  171. SEND(to);
  172. SEND(STRING(" :Spamming of local-only commands is disabled.\n"));
  173. return 0;
  174. }
  175. } else {
  176. SEND(STRING(":1HC000000 NOTICE "));
  177. SEND(to);
  178. SEND(STRING(" :Unknown command.\n"));
  179. return 0;
  180. }
  181. struct string fake_original_message = {.data = original_message.data + offset, .len = original_message.len - offset};
  182. WRITES(2, fake_original_message);
  183. WRITES(2, STRING("\n"));
  184. for (uint64_t i = 0; i < count; i++) {
  185. int ret = cmd->func(sender, fake_original_message, to, argc - 2, &(argv[2]), 0);
  186. if (ret)
  187. return ret;
  188. }
  189. return 0;
  190. }
  191. static struct command_def spam_command_def = {
  192. .func = spam_command,
  193. .privs = STRING("NetAdmin"),
  194. .local_only = 0,
  195. .summary = STRING("Repeats a command <n> times"),
  196. };
  197. int clear_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  198. if (argc < 2) {
  199. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
  200. return 0;
  201. }
  202. struct channel_info *channel = get_table_index(channel_list, argv[1]);
  203. if (!channel) {
  204. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("That channel doesn't seem to exist, so is thereby already cleared.")});
  205. return 0;
  206. }
  207. for (uint64_t i = 0; i < channel->user_list.len; i++) {
  208. // TODO: Proper kick function, will prob come as a part of my next HaxServ rewrite
  209. SEND(STRING(":1HC000000 KICK "));
  210. SEND(argv[1]);
  211. SEND(STRING(" "));
  212. SEND(channel->user_list.array[i].name);
  213. SEND(STRING(" :(B\n"));
  214. if (client_connected) {
  215. SENDCLIENT(STRING(":"));
  216. SENDCLIENT(nick);
  217. SENDCLIENT(STRING("!"));
  218. SENDCLIENT(nick);
  219. SENDCLIENT(STRING("@"));
  220. SENDCLIENT(hostmask);
  221. SENDCLIENT(STRING(" KICK "));
  222. SENDCLIENT(argv[1]);
  223. SENDCLIENT(STRING(" "));
  224. struct user_info *user = get_table_index(user_list, channel->user_list.array[i].name);
  225. if (user)
  226. SENDCLIENT(user->nick);
  227. else
  228. SENDCLIENT(channel->user_list.array[i].name);
  229. SENDCLIENT(STRING(" :(B\r\n"));
  230. }
  231. }
  232. clear_table(&(channel->user_list));
  233. return 0;
  234. }
  235. static struct command_def clear_command_def = {
  236. .func = clear_command,
  237. .privs = STRING("NetAdmin"),
  238. .local_only = 0,
  239. .summary = STRING("Clears a channel"),
  240. };
  241. struct sh_command_args {
  242. char *command;
  243. struct string to;
  244. };
  245. void * async_sh_command(void *tmp) {
  246. struct sh_command_args *sh_args = tmp;
  247. FILE *f = popen(sh_args->command, "r");
  248. free(sh_args->command);
  249. char *line = NULL;
  250. size_t buflen;
  251. while (1) {
  252. ssize_t len = getline(&line, &buflen, f);
  253. if (len <= 0)
  254. break;
  255. struct string linestr = {.data = line, .len = (size_t)(len)};
  256. while (linestr.len > 0 && (linestr.data[linestr.len-1] == '\n' || linestr.data[linestr.len-1] == '\r'))
  257. linestr.len--;
  258. if (linestr.len == 0)
  259. linestr = STRING(" ");
  260. pthread_mutex_lock(&send_lock);
  261. SEND(STRING(":1HC000000 PRIVMSG "));
  262. SEND(sh_args->to);
  263. SEND(STRING(" :"));
  264. SEND(linestr);
  265. SEND(STRING("\n"));
  266. SENDCLIENT(STRING(":"));
  267. SENDCLIENT(nick);
  268. SENDCLIENT(STRING("!e@e PRIVMSG "));
  269. SENDCLIENT(sh_args->to);
  270. SENDCLIENT(STRING(" :"));
  271. SENDCLIENT(linestr);
  272. SENDCLIENT(STRING("\r\n"));
  273. pthread_mutex_unlock(&send_lock);
  274. }
  275. free(line);
  276. pclose(f);
  277. free(sh_args->to.data);
  278. free(sh_args);
  279. return 0;
  280. }
  281. int sh_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  282. if (!is_local) {
  283. return 0;
  284. }
  285. if (argc < 2) {
  286. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
  287. return 0;
  288. }
  289. char wasspace = 1;
  290. uint64_t offset = 0;
  291. char found = 0;
  292. for (; offset < original_message.len; offset++) {
  293. if (original_message.data[offset] == ' ' && !wasspace)
  294. found++;
  295. wasspace = (original_message.data[offset] == ' ');
  296. if (found >= 1 && !wasspace)
  297. break;
  298. }
  299. if (found < 1) {
  300. WRITES(2, STRING("WARNING: Apparently there was no argument... shouldn't happen.\n"));
  301. return 0;
  302. }
  303. struct string command = {.data = original_message.data + offset, .len = original_message.len - offset};
  304. struct sh_command_args *sh_args;
  305. sh_args = malloc(sizeof(*sh_args));
  306. if (!sh_args) {
  307. WRITES(2, STRING("ERROR: OOM\n"));
  308. return 0;
  309. }
  310. sh_args->command = malloc(command.len+1);
  311. if (!sh_args->command) {
  312. free(sh_args);
  313. WRITES(2, STRING("ERROR: OOM\n"));
  314. return 0;
  315. }
  316. memcpy(sh_args->command, command.data, command.len);
  317. sh_args->command[command.len] = '\0';
  318. sh_args->to.len = to.len;
  319. sh_args->to.data = malloc(to.len);
  320. if (!sh_args->to.data) {
  321. free(sh_args->command);
  322. free(sh_args);
  323. WRITES(2, STRING("ERROR: OOM\n"));
  324. return 0;
  325. }
  326. memcpy(sh_args->to.data, to.data, to.len);
  327. pthread_t trash;
  328. pthread_create(&trash, NULL, async_sh_command, sh_args);
  329. return 0;
  330. }
  331. static struct command_def sh_command_def = {
  332. .func = sh_command,
  333. .privs = STRING("NetAdmin"),
  334. .local_only = 1,
  335. .summary = STRING("Executes a command locally"),
  336. };
  337. int kill_old_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  338. if (argc < 2) {
  339. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
  340. return 0;
  341. }
  342. uint64_t current_time = (uint64_t)time(0);
  343. char err;
  344. uint64_t age = str_to_unsigned(argv[1], &err);
  345. if (err) {
  346. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Invalid age!")});
  347. return 0;
  348. }
  349. if (age >= current_time)
  350. age = 0;
  351. else
  352. age = current_time - age;
  353. for (size_t i = 0; i < user_list.len; i++) {
  354. struct user_info *user = user_list.array[i].ptr;
  355. if ((user->user_ts <= age || STRING_EQ(user->nick, STRING("OperServ"))) && !STRING_EQ(user->server, STRING("1HC"))) {
  356. SEND(STRING(":1HC000000 KILL "));
  357. SEND(user_list.array[i].name);
  358. SEND(STRING(" :Your connection is too old.\n"));
  359. }
  360. }
  361. return 0;
  362. }
  363. static struct command_def kill_old_command_def = {
  364. .func = kill_old_command,
  365. .privs = STRING("NetAdmin"),
  366. .local_only = 0,
  367. .summary = STRING("Kills old connections (with a time you specify), and OperServ because OperServ is wrong"),
  368. };
  369. int echo_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  370. if (argc < 2) {
  371. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
  372. return 0;
  373. }
  374. char wasspace = 1;
  375. uint64_t offset = 0;
  376. char found = 0;
  377. for (; offset < original_message.len; offset++) {
  378. if (original_message.data[offset] == ' ' && !wasspace)
  379. found++;
  380. wasspace = (original_message.data[offset] == ' ');
  381. if (found >= 1 && !wasspace)
  382. break;
  383. }
  384. if (found < 1) {
  385. WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
  386. return 0;
  387. }
  388. struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}};
  389. privmsg(STRING("1HC000000"), to, 1, message);
  390. return 0;
  391. }
  392. static struct command_def echo_command_def = {
  393. .func = echo_command,
  394. .privs = {0},
  395. .local_only = 0,
  396. .summary = STRING("Repeats a message back"),
  397. };
  398. int tell_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) {
  399. if (argc < 3) {
  400. privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")});
  401. return 0;
  402. }
  403. char wasspace = 1;
  404. uint64_t offset = 0;
  405. char found = 0;
  406. for (; offset < original_message.len; offset++) {
  407. if (original_message.data[offset] == ' ' && !wasspace)
  408. found++;
  409. wasspace = (original_message.data[offset] == ' ');
  410. if (found >= 2 && !wasspace)
  411. break;
  412. }
  413. if (found < 2) {
  414. WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n"));
  415. return 0;
  416. }
  417. struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}};
  418. privmsg(STRING("1HC000000"), argv[1], 1, message);
  419. return 0;
  420. }
  421. static struct command_def tell_command_def = {
  422. .func = tell_command,
  423. .privs = STRING("NetAdmin"),
  424. .local_only = 0,
  425. .summary = STRING("Sends a message to a target"),
  426. };
  427. int init_user_commands(void) {
  428. srandom(time(NULL));
  429. user_commands.array = malloc(0);
  430. set_table_index(&user_commands, STRING(":"), &raw_command_def);
  431. set_table_index(&user_commands, STRING("sus"), &sus_command_def);
  432. set_table_index(&user_commands, STRING("cr"), &cr_command_def);
  433. set_table_index(&user_commands, STRING("help"), &help_command_def);
  434. set_table_index(&user_commands, STRING("spam"), &spam_command_def);
  435. set_table_index(&user_commands, STRING("clear"), &clear_command_def);
  436. set_table_index(&user_commands, STRING("sh"), &sh_command_def);
  437. set_table_index(&user_commands, STRING("kill_old"), &kill_old_command_def);
  438. set_table_index(&user_commands, STRING("echo"), &echo_command_def);
  439. set_table_index(&user_commands, STRING("tell"), &tell_command_def);
  440. return 0;
  441. }