1
0
Fork 0
coupserv/server_network.c
2024-06-16 21:30:55 -04:00

981 lines
26 KiB
C

// Server network command handlers for 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 <gnutls/gnutls.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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)\n"));
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;
}
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);
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);
}
if (timestamp < channel->ts)
channel->ts = timestamp;
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;
struct channel_info *channel_info = get_table_index(channel_list, argv[0]);
if (!channel_info)
return 0;
if (!has_table_index(channel_info->user_list, STRING("1HC000000")))
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("'"),
};
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: "));
if (argv[0].data[0] == '#')
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"), &notice_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("CAPAB START 1202\nCAPAB END\n"));
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;
struct user_info *user_info = get_table_index(user_list, STRING("1HC000000"));
for (uint64_t i = 0; i < num_prejoin_channels; i++) {
struct channel_info *channel;
channel = malloc(sizeof(*channel));
if (!channel)
return 1;
*channel = (struct channel_info){
.ts = (uint64_t)current_time,
.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, prejoin_channels[i], channel);
set_table_index(&(channel->user_list), STRING("1HC000000"), user_info);
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;
}