// Server network command handlers for HaxServ // // Written by: Test_User // // 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 #include #include #include #include #include #include #include #include #include "network.h" #include "types.h" #include "table.h" #include "tls.h" #include "config.h" #include "utils.h" #include "commands.h" pthread_mutex_t send_lock = PTHREAD_MUTEX_INITIALIZER; int resolve(char *address, char *port, struct sockaddr *sockaddr) { int success; struct addrinfo hints = {0}, *info; hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG; success = getaddrinfo(address, port, &hints, &info); if (success == 0) { *sockaddr = *(info->ai_addr); freeaddrinfo(info); } return success; } struct table server_network_commands = {0}; struct table server_list = {0}; struct table user_list = {0}; struct table channel_list = {0}; int ping_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 2) { puts("Invalid PING recieved! (Missing parameters)"); return 1; } uint64_t len = 1 + argv[1].len + 6 + argv[1].len + 1 + sender.len + 1; char msg[len]; uint64_t offset = 0; msg[0] = ':'; offset++; memcpy(msg+offset, argv[1].data, argv[1].len); offset += argv[1].len; memcpy(msg+offset, " PONG ", 6); offset += 6; memcpy(msg+offset, argv[1].data, argv[1].len); offset += argv[1].len; msg[offset] = ' '; offset++; memcpy(msg+offset, sender.data, sender.len); offset += sender.len; msg[offset] = '\n'; SEND(((struct string){msg, len})); return 0; } int server_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 5) { puts("Invalid SERVER recieved! (Missing parameters)"); return 1; } if (argv[2].len > 20) { puts("Invalid SERVER recieved! (Distance too large)"); return 1; } char err; uint64_t distance = str_to_unsigned(argv[2], &err); if (err) { puts("Invalid SERVER recieved! (Invalid distance given)"); return 1; } if (sender.len != 0) { struct server_info *from = get_table_index(server_list, sender); if (!from) { puts("Invalid SERVER recieved! (Unknown source)"); return 1; } distance += from->distance + 1; } if (get_table_index(server_list, argv[3]) != 0) { WRITES(2, STRING("Invalid SERVER recieved! (Duplicate SID already connected)")); return 1; } struct string address; address.data = malloc(argv[0].len); if (address.data == 0) goto server_handler_oom; address.len = argv[0].len; memcpy(address.data, argv[0].data, argv[0].len); struct string name; name.data = malloc(argv[4].len); if (name.data == 0) goto server_handler_free_address; name.len = argv[4].len; memcpy(name.data, argv[4].data, argv[4].len); struct server_info *server = malloc(sizeof(*server)); if (server == 0) goto server_handler_free_name; struct string via; if (sender.len != 0) { // connected to the sender via.data = malloc(sender.len); if (!via.data) goto server_handler_free_server; via.len = sender.len; memcpy(via.data, sender.data, sender.len); } else { // connected directly to us via = (struct string){0}; } *server = (struct server_info){ .name = name, .address = address, .distance = distance, .via = via, }; if (set_table_index(&server_list, argv[3], server) != 0) goto server_handler_free_via; return 0; server_handler_free_via: if (sender.len != 0) free(via.data); server_handler_free_server: free(server); server_handler_free_name: free(name.data); server_handler_free_address: free(address.data); server_handler_oom: puts("OOM! (server_handler)"); return 1; } int uid_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 10) { puts("Invalid UID recieved! (Missing parameters)"); return 1; } if (argv[1].len > 20 || argv[7].len > 20) { puts("Invalid UID recieved! (Timestamp too long)"); return 1; } if (sender.len == 0) { puts("Invalid UID recieved! (No source)"); return 1; } if (get_table_index(user_list, argv[0])) { WRITES(2, STRING("Invalid UID revieved! (Attempted to create already-existing user)\n")); return 1; } // TODO: modes char err; uint64_t nick_ts = str_to_unsigned(argv[1], &err); if (err) { puts("Invalid UID recieved! (Invalid nick timestamp)"); return 1; } uint64_t user_ts = str_to_unsigned(argv[7], &err); if (err) { puts("Invalid UID recieved! (Invalid user timestamp)"); return 1; } struct string server; server.data = malloc(sender.len); if (!server.data) goto uid_handler_oom; server.len = sender.len; memcpy(server.data, sender.data, sender.len); struct string nick; nick.data = malloc(argv[2].len); if (!nick.data) goto uid_handler_free_server; nick.len = argv[2].len; memcpy(nick.data, argv[2].data, argv[2].len); struct string hostname; hostname.data = malloc(argv[3].len); if (!hostname.data) goto uid_handler_free_nick; hostname.len = argv[3].len; memcpy(hostname.data, argv[3].data, argv[3].len); struct string vhost; vhost.data = malloc(argv[4].len); if (!vhost.data) goto uid_handler_free_hostname; vhost.len = argv[4].len; memcpy(vhost.data, argv[4].data, argv[4].len); struct string ident; ident.data = malloc(argv[5].len); if (!ident.data) goto uid_handler_free_vhost; ident.len = argv[5].len; memcpy(ident.data, argv[5].data, argv[5].len); struct string ip; ip.data = malloc(argv[6].len); if (!ip.data) goto uid_handler_free_ident; ip.len = argv[6].len; memcpy(ip.data, argv[6].data, argv[6].len); struct string realname; realname.data = malloc(argv[argc - 1].len); if (!realname.data) goto uid_handler_free_ip; realname.len = argv[argc - 1].len; memcpy(realname.data, argv[argc - 1].data, argv[argc - 1].len); struct user_info *user = malloc(sizeof(*user)); if (!user) goto uid_handler_free_realname; *user = (struct user_info){ .nick_ts = nick_ts, .user_ts = user_ts, .server = server, .nick = nick, .hostname = hostname, .vhost = vhost, .ident = ident, .ip = ip, .realname = realname, .opertype = {.data = malloc(0), .len = 0}, .metadata = {.array = malloc(0), .len = 0}, }; if (set_table_index(&user_list, argv[0], user) != 0) goto uid_handler_free_user; return 0; uid_handler_free_user: free(user); uid_handler_free_realname: free(realname.data); uid_handler_free_ip: free(ip.data); uid_handler_free_ident: free(ident.data); uid_handler_free_vhost: free(vhost.data); uid_handler_free_hostname: free(hostname.data); uid_handler_free_nick: free(nick.data); uid_handler_free_server: free(server.data); uid_handler_oom: puts("OOM! (uid_handler)"); return 1; } int opertype_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 1) { WRITES(2, STRING("Invalid OPERTYPE recieved! (Missing parameters)\n")); return 1; } if (sender.len == 0) { WRITES(2, STRING("Invalid OPERTYPE recieved! (No source)\n")); return 1; } struct user_info *user = get_table_index(user_list, sender); if (!user) { WRITES(2, STRING("Server ")); WRITES(2, sender); WRITES(2, STRING(" attempted to set OPERTYPE on a nonexistent user!\n")); return 1; } free(user->opertype.data); struct string opertype = {.data = malloc(argv[0].len), .len = argv[0].len}; if (!opertype.data) { WRITES(2, STRING("OOM! (opertype_handler)\n")); return 1; } memcpy(opertype.data, argv[0].data, argv[0].len); if (user->opertype.len) free(user->opertype.data); user->opertype = opertype; return 0; } int quit_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 1) return remove_user(sender, (struct string){0}); else return remove_user(sender, argv[0]); } int kill_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 2) { WRITES(2, STRING("Invalid KILL recieved! (Missing parameters)\n")); return 1; } // TODO: Get accurate list of what got killed, what to rejoin, etc struct user_info *user = get_table_index(user_list, argv[0]); if (!user) return 0; // TODO: Currently not all local users are considered; fix that, then make this give an error if (STRING_EQ(user->server, STRING("1HC"))) { SEND(STRING(":")); SEND(argv[0]); SEND(STRING(" QUIT :")); SEND(argv[1]); SEND(STRING("\n")); SEND(STRING("GLOADMODULE m_servprotect\n")); // required for the +k we're about to use char user_time[21]; snprintf(user_time, 21, "%ld", user->user_ts); char nick_time[21]; snprintf(nick_time, 21, "%ld", user->nick_ts); SEND(STRING("UID ")); SEND(argv[0]); SEND(STRING(" ")); SEND(NULSTR(user_time)); SEND(STRING(" ")); SEND(user->nick); SEND(STRING(" ")); SEND(user->hostname); SEND(STRING(" ")); SEND(user->vhost); SEND(STRING(" ")); SEND(user->ident); SEND(STRING(" ")); SEND(user->ip); SEND(STRING(" ")); SEND(NULSTR(nick_time)); SEND(STRING(" +k :")); SEND(user->realname); SEND(STRING("\n")); if (STRING_EQ(argv[0], STRING("1HC000001"))) { SEND(STRING(":1HC METADATA 1HC000001 ssl_cert :vTrse ")); SEND(client_cert); SEND(STRING("\n:")); SEND(argv[0]); SEND(STRING(" OPERTYPE ")); SEND(opertype); SEND(STRING("\n")); } for (uint64_t i = 0; i < channel_list.len; i++) { struct channel_info *channel = channel_list.array[i].ptr; if (has_table_index(channel->user_list, argv[0])) { char timestamp[21]; SEND(STRING("FJOIN ")); SEND(channel_list.array[i].name); SEND(STRING(" ")); snprintf(timestamp, 21, "%ld", channel->ts); SEND(NULSTR(timestamp)); SEND(STRING(" + :,")); SEND(argv[0]); SEND(STRING("\n")); } } } else { return remove_user(argv[0], argv[1]); } return 0; } int nick_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 2) { WRITES(2, STRING("Invalid NICK recieved! (Missing parameters)\n")); return 1; } struct user_info *info = get_table_index(user_list, sender); if (!info) { WRITES(2, STRING("NICK: Unknown user!\n")); return 1; } for (uint64_t i = 0; i < channel_list.len; i++) { // TODO: More efficient way of doing this struct channel_info *channel = channel_list.array[i].ptr; if (has_table_index(channel->user_list, sender) && has_table_index(channel->user_list, STRING("1HC000001"))) { SENDCLIENT(STRING(":")); SENDCLIENT(info->nick); SENDCLIENT(STRING("!")); SENDCLIENT(info->ident); SENDCLIENT(STRING("@")); SENDCLIENT(info->vhost); SENDCLIENT(STRING(" NICK :")); SENDCLIENT(argv[0]); SENDCLIENT(STRING("\r\n")); break; } } void *tmp = malloc(argv[0].len); if (!tmp) { WRITES(2, STRING("OOM! (nick_handler)\n")); return 1; } memcpy(tmp, argv[0].data, argv[0].len); free(info->nick.data); info->nick.data = tmp; info->nick.len = argv[0].len; if (argv[1].len > 20) { WRITES(2, STRING("Invalid NICK recieved! (Timestamp too long)\n")); return 1; } char err; uint64_t ts = str_to_unsigned(argv[1], &err); if (err) { WRITES(2, STRING("Invalid NICK recieved! (Invalid timestamp)\n")); return 1; } info->nick_ts = ts; return 0; } int fjoin_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 4) { WRITES(2, STRING("Invalid FJOIN recieved! (Missing parameters")); return 1; } char err; uint64_t timestamp = str_to_unsigned(argv[1], &err); if (err) { WRITES(2, STRING("Invalid FJOIN recieved! (Invalid timestamp given)\n")); return 1; } // TODO: Parse modes, then make the rest of this work uint64_t userlist_offset = 3; { char dir = '?'; for (uint64_t offset = 0; offset < argv[2].len; offset++) { if (argv[2].data[offset] == '+') { dir = '+'; } else if (argv[2].data[offset] == '-') { dir = '-'; } else if (dir == '?') { WRITES(2, STRING("Invalid FJOIN recieved! (No direction set for modes)\n")); return 1; } else if (channel_mode_types[(unsigned char)argv[2].data[offset]] == MODE_TYPE_UNKNOWN) { WRITES(2, STRING("Invalid FJOIN recieved! (Unknown mode set on the channel)\n")); return 1; } else if (channel_mode_types[(unsigned char)argv[2].data[offset]] != MODE_TYPE_NOARGS && dir == '+') { userlist_offset++; } } } if (argc < userlist_offset + 1) { WRITES(2, STRING("Invalid FJOIN recieved! (Missing mode parameters or user list)\n")); return 1; } struct channel_info *channel = get_table_index(channel_list, argv[0]); if (!channel) { channel = malloc(sizeof(*channel)); if (!channel) { WRITES(2, STRING("OOM! (fjoin_handler)\n")); return 1; } *channel = (struct channel_info){ .ts = timestamp, .topic = {.data = malloc(0), .len = 0}, .topic_ts = 0, .modes = {.array = malloc(0), .len = 0}, .user_list = {.array = malloc(0), .len = 0}, .metadata = {.array = malloc(0), .len = 0}, }; set_table_index(&channel_list, argv[0], channel); } struct string userlist = argv[userlist_offset]; if (userlist.len < 10) // Not enough for any users return 0; uint8_t sendclient = has_table_index(channel->user_list, STRING("1HC000001")); uint64_t offset = 0; while (1) { while (offset < userlist.len && userlist.data[offset] != ',') offset++; if (offset > (userlist.len - 1) - 9) break; offset++; struct string user = {.data = &(userlist.data[offset]), .len = 9}; struct user_info *user_info = get_table_index(user_list, user); set_table_index(&(channel->user_list), user, user_info); if (user_info && sendclient) { SENDCLIENT(STRING(":")); SENDCLIENT(user_info->nick); SENDCLIENT(STRING("!")); SENDCLIENT(user_info->ident); SENDCLIENT(STRING("@")); SENDCLIENT(user_info->vhost); SENDCLIENT(STRING(" JOIN :")); SENDCLIENT(argv[0]); SENDCLIENT(STRING("\r\n")); } offset += 10; } return 0; } int squit_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 1) { WRITES(2, STRING("Invalid SQUIT recieved! (Missing parameters)\n")); return 1; } struct server_info *server = remove_table_index(&server_list, argv[0]); if (!server) { WRITES(2, STRING("Invalid SQUIT received! (Unknown server)\n")); WRITES(2, STRING("Known servers:\r\n")); for (uint64_t i = 0; i < server_list.len; i++) { WRITES(2, server_list.array[i].name); WRITES(2, STRING("\r\n")); } return 1; } for (uint64_t i = 0; i < user_list.len;) { struct user_info *info = user_list.array[i].ptr; if (STRING_EQ(info->server, argv[0])) { remove_user(user_list.array[i].name, STRING("*.net *.split")); } else { i++; // removal of the user from the table shifts the next user into place for us } } free(server->name.data); free(server->address.data); if (server->via.data != 0) free(server->via.data); free(server); return 0; } int privmsg_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 2) { WRITES(2, STRING("Invalid PRIVMSG recieved (Missing parameters)\n")); return 1; } if (sender.len == 0) { WRITES(2, STRING("Invalid PRIVMSG recieved (No source)\n")); return 1; } struct user_info *user = get_table_index(user_list, sender); struct server_info *server; if (user) { server = 0; if (argv[0].data[0] == '#') { struct channel_info *channel = get_table_index(channel_list, argv[0]); if (channel && has_table_index(channel->user_list, STRING("1HC000001"))) { SENDCLIENT(STRING(":")); SENDCLIENT(user->nick); SENDCLIENT(STRING("!")); SENDCLIENT(user->ident); SENDCLIENT(STRING("@")); SENDCLIENT(user->vhost); SENDCLIENT(STRING(" PRIVMSG ")); SENDCLIENT(argv[0]); SENDCLIENT(STRING(" :")); SENDCLIENT(argv[1]); SENDCLIENT(STRING("\r\n")); } } else if (STRING_EQ(argv[0], STRING("1HC000001"))) { SENDCLIENT(STRING(":")); SENDCLIENT(user->nick); SENDCLIENT(STRING("!")); SENDCLIENT(user->ident); SENDCLIENT(STRING("@")); SENDCLIENT(user->vhost); SENDCLIENT(STRING(" PRIVMSG ")); SENDCLIENT(client_nick); SENDCLIENT(STRING(" :")); SENDCLIENT(argv[1]); SENDCLIENT(STRING("\r\n")); } } else { server = get_table_index(server_list, sender); if (server) { if (argv[0].data[0] == '#') { struct channel_info *channel = get_table_index(channel_list, argv[0]); if (channel && has_table_index(channel->user_list, STRING("1HC000001"))) { SENDCLIENT(STRING(":")); SENDCLIENT(server->address); SENDCLIENT(STRING(" PRIVMSG ")); SENDCLIENT(argv[0]); SENDCLIENT(STRING(" :")); SENDCLIENT(argv[1]); SENDCLIENT(STRING("\r\n")); } } else if (STRING_EQ(argv[0], STRING("1HC000001"))) { SENDCLIENT(STRING(":")); SENDCLIENT(server->address); SENDCLIENT(STRING(" PRIVMSG ")); SENDCLIENT(client_nick); SENDCLIENT(STRING(" :")); SENDCLIENT(argv[1]); SENDCLIENT(STRING("\r\n")); } } } uint64_t offset; if (argv[0].data[0] == '#') { if (argv[1].len < command_prefix.len || memcmp(argv[1].data, command_prefix.data, command_prefix.len) != 0) return 0; offset = command_prefix.len; } else if (STRING_EQ(argv[0], STRING("1HC000000"))) { offset = 0; } else { return 0; } if (offset >= argv[1].len || argv[1].data[offset] == ' ') return 0; uint64_t command_argc = 0; uint64_t old_offset = offset; while (offset < argv[1].len) { while (offset < argv[1].len && argv[1].data[offset] != ' ') offset++; command_argc++; while (offset < argv[1].len && argv[1].data[offset] == ' ') offset++; } offset = old_offset; struct string command_argv[command_argc]; // argv[0] in this case is the command itself, unlike network command handlers... might change one of these two later to match uint64_t i = 0; while (offset < argv[1].len) { command_argv[i].data = argv[1].data+offset; uint64_t start = offset; while (offset < argv[1].len && argv[1].data[offset] != ' ') offset++; command_argv[i].len = offset - start; while (offset < argv[1].len && argv[1].data[offset] == ' ') offset++; i++; } argv[1].data += old_offset; argv[1].len -= old_offset; struct command_def *cmd = get_table_index(user_commands, command_argv[0]); if (cmd) { if (!cmd->local_only) { if (cmd->privs.len != 0 && sender.len != 3) { // servers always count as oper :P if (!user) { WRITES(2, STRING("User is unknown!\n")); SEND(STRING(":1HC000000 NOTICE ")); if (argv[0].data[0] == '#') SEND(argv[0]); else SEND(sender); SEND(STRING(" :You don't seem to exist... and neither do I!\n")); return 1; // have already desynced } if (user->opertype.len != cmd->privs.len || memcmp(user->opertype.data, cmd->privs.data, cmd->privs.len)) { // TODO: complain about missing privs SEND(STRING(":1HC000000 NOTICE ")); if (argv[0].data[0] == '#') SEND(argv[0]); else SEND(sender); SEND(STRING(" :You are not authorized to execute this command.\n")); return 0; } } struct string message[] = { user ? STRING("User ") : (server ? STRING("Server ") : STRING("An unknown user (something desycned... this shouldn't happen)")), user ? user->nick : (server ? server->address : STRING("")), STRING(" executes `"), argv[1], STRING("'\n"), }; privmsg(STRING("1HC000000"), log_channel, sizeof(message)/sizeof(*message), message); return cmd->func(sender, argv[1], argv[0].data[0] == '#' ? argv[0] : sender, command_argc, command_argv, 0); } else { SEND(STRING(":1HC000000 NOTICE ")); if (argv[0].data[0] == '#') SEND(argv[0]); else SEND(sender); SEND(STRING(" :You are not authorized to execute this command.\n")); WRITES(1, STRING("Not executing local-only command from a remote source!\n")); return 0; } } else { SEND(STRING(":1HC000000 NOTICE ")); if (argv[0].data[0] == '#') SEND(argv[0]); else SEND(sender); SEND(STRING(" :Unknown command: ")); SEND(command_prefix); SEND(command_argv[0]); SEND(STRING("\n")); return 0; } } int part_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 1) { WRITES(2, STRING("Invalid PART received! (Missing parameters)\n")); return 1; } struct channel_info *channel = get_table_index(channel_list, argv[0]); struct user_info *user = get_table_index(user_list, sender); if (user && channel && has_table_index(channel->user_list, STRING("1HC000001"))) { SENDCLIENT(STRING(":")); SENDCLIENT(user->nick); SENDCLIENT(STRING("!")); SENDCLIENT(user->ident); SENDCLIENT(STRING("@")); SENDCLIENT(user->vhost); SENDCLIENT(STRING(" PART ")); SENDCLIENT(argv[0]); if (argc >= 2) { SENDCLIENT(STRING(" :")); SENDCLIENT(argv[1]); } SENDCLIENT(STRING("\r\n")); } if (channel) remove_table_index(&(channel->user_list), sender); return 0; } int notice_handler(struct string sender, uint64_t argc, struct string *argv) { if (argc < 2) { WRITES(2, STRING("Invalid NOTICE received! (Missing parameters)\n")); return 1; } struct user_info *user = get_table_index(user_list, sender); struct server_info *server; if (user) server = 0; else server = get_table_index(server_list, sender); if (!user && !server) { WRITES(2, STRING("Invalid NOTICE received! (Unknown source)\n")); return 1; } if (argv[0].data[0] == '#') { struct channel_info *channel = get_table_index(channel_list, argv[0]); if (channel && has_table_index(channel->user_list, STRING("1HC000001"))) { SENDCLIENT(STRING(":")); if (user) { SENDCLIENT(user->nick); SENDCLIENT(STRING("!")); SENDCLIENT(user->ident); SENDCLIENT(STRING("@")); SENDCLIENT(user->vhost); } else { SENDCLIENT(server->address); } SENDCLIENT(STRING(" NOTICE ")); SENDCLIENT(argv[0]); SENDCLIENT(STRING(" :")); SENDCLIENT(argv[1]); SENDCLIENT(STRING("\r\n")); } } else if (STRING_EQ(argv[0], STRING("1HC000001"))) { SENDCLIENT(STRING(":")); if (user) { SENDCLIENT(user->nick); SENDCLIENT(STRING("!")); SENDCLIENT(user->ident); SENDCLIENT(STRING("@")); SENDCLIENT(user->vhost); } else { SENDCLIENT(server->address); } SENDCLIENT(STRING(" NOTICE ")); SENDCLIENT(argv[0]); SENDCLIENT(STRING(" :")); SENDCLIENT(argv[1]); SENDCLIENT(STRING("\r\n")); } return 0; } int initservernetwork(void) { server_network_commands.array = malloc(0); server_list.array = malloc(0); user_list.array = malloc(0); channel_list.array = malloc(0); set_table_index(&server_network_commands, STRING("PING"), &ping_handler); set_table_index(&server_network_commands, STRING("SERVER"), &server_handler); set_table_index(&server_network_commands, STRING("UID"), &uid_handler); set_table_index(&server_network_commands, STRING("OPERTYPE"), &opertype_handler); set_table_index(&server_network_commands, STRING("PRIVMSG"), &privmsg_handler); set_table_index(&server_network_commands, STRING("QUIT"), &quit_handler); set_table_index(&server_network_commands, STRING("KILL"), &kill_handler); set_table_index(&server_network_commands, STRING("NICK"), &nick_handler); set_table_index(&server_network_commands, STRING("FJOIN"), &fjoin_handler); set_table_index(&server_network_commands, STRING("SQUIT"), &squit_handler); set_table_index(&server_network_commands, STRING("PART"), &part_handler); set_table_index(&server_network_commands, STRING("NOTICE"), ¬ice_handler); init_user_commands(); int retval = connect_tls(); if (retval != 0) { printf("connect_tls(): %d\n", retval); return 1; } // probably inefficient to be calling SSL_write this frequently, but also less effort SEND(STRING("SERVER ")); SEND(server_name); SEND(STRING(" ")); SEND(send_password); SEND(STRING(" 0 1HC :HaxServ\n")); SEND(STRING("BURST ")); time_t current_time = time(NULL); char current_time_str[21]; // C HaxServ will be deprecated long before we reach 20-digit timestamps snprintf(current_time_str, 21, "%ld", current_time); SEND(NULSTR(current_time_str)); SEND(STRING("\n")); if (add_local_client(STRING("1HC000000"), nick, hostmask, nick, nick, current_time, 0) != 0) return 1; for (uint64_t i = 0; i < num_prejoin_channels; i++) { SEND(STRING("FJOIN ")); SEND(prejoin_channels[i]); SEND(STRING(" ")); SEND(NULSTR(current_time_str)); SEND(STRING(" + :,1HC000000\nMODE ")); SEND(prejoin_channels[i]); SEND(STRING(" +o 1HC000000\n")); } SEND(STRING("ENDBURST\n")); return 0; }