forked from noisytoot/rubyserv-iirc
981 lines
26 KiB
C
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"), ¬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("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;
|
|
}
|