summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTest_User <hax@andrewyu.org>2023-05-03 22:57:53 -0400
committerTest_User <hax@andrewyu.org>2023-05-03 22:57:53 -0400
commite4b5445b3ca844e568a84abbf931a026a6ca6226 (patch)
tree9d9d615406c5a91036ebcd5b23bd8af30d3e8f3d
parentec8b1682e86535333c34966f6aafee349e609641 (diff)
downloadcoupserv-e4b5445b3ca844e568a84abbf931a026a6ca6226.tar.gz
coupserv-e4b5445b3ca844e568a84abbf931a026a6ca6226.zip
C HaxServ
-rw-r--r--.gitignore7
-rwxr-xr-xCoupServ.lua200
-rw-r--r--Makefile33
-rw-r--r--commands.c65
-rw-r--r--commands.h14
-rw-r--r--commands.lua363
-rw-r--r--config.h17
-rw-r--r--main.c189
-rw-r--r--network.c463
-rw-r--r--network.h39
-rw-r--r--network.lua310
-rw-r--r--stdin.lua132
-rw-r--r--stdout2049
-rw-r--r--table.c163
-rw-r--r--table.h17
-rw-r--r--tls.c66
-rw-r--r--tls.h7
-rw-r--r--types.h12
-rw-r--r--utils.c38
-rw-r--r--utils.h4
20 files changed, 3179 insertions, 1009 deletions
diff --git a/.gitignore b/.gitignore
index 71ea114..2e479f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-CoupServConfig.json
-
-# deprecated version
-CoupServ.py
+config.c
+*.o
+haxserv
diff --git a/CoupServ.lua b/CoupServ.lua
deleted file mode 100755
index 20ee42e..0000000
--- a/CoupServ.lua
+++ /dev/null
@@ -1,200 +0,0 @@
-#!/usr/bin/env lua
---[[
-
-Main program for HaxServ, a pseudoserver for inspircd3.
-
-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.
-]]
-
-path = arg[0]:sub(1, -arg[0]:reverse():find('/'))
-
-socket = require("socket")
-json = require("json")
-ssl = require("ssl")
-
-servlist = {}
-userlist = {}
-chanlist = {}
-
-function has_permission(user, privs)
- if not user or type(user) ~= "table" then
- return false
- end
-
- privs = privs or {}
- for _, v in pairs(privs) do
- if v == "Admin" then
- if user.opertype ~= "Admin" then
- return false
- end
- elseif v == "Owner" then -- not dealing with this yet, so just always return false for now
- return false
- else -- unknown priv, naturally no one has it
- return false
- end
- end
- return true
-end
-
-config_file = io.open(path.."CoupServConfig.json", "r+")
-config = json.decode(config_file:read("*a"))
-
-for file, _ in pairs(config.files) do
- dofile(path..file)
-end
-
-stdin = socket.tcp()
-stdin:close()
-stdin:setfd(0) --clearly a hack but it works
-
-while true do
- servlist = {}
- userlist = {}
- chanlist = {}
-
- ::continue::
-
- local s = socket.tcp4()
- s:connect("irc.andrewyu.org", 7005)
- local con = ssl.wrap(s, {mode = "client", protocol = "tlsv1_3"})
-
- if not con:dohandshake() then
- socket.select(nil, nil, 5)
- con:close()
- goto continue
- end
-
- local time = tostring(os.time())
-
- con:send("SERVER hax.irc.andrewyu.org "..config.send_password.." 0 1HC :HaxServ\n")
- con:send("BURST "..time.."\n")
- con:send("UID 1HC000000 "..time.." "..config.nick.." "..config.hostmask.." "..config.hostmask.." HaxServ 192.168.1.1 "..time.." +k :HaxServ\n")
- con:send(":1HC000000 OPERTYPE Admin\n")
-
- userlist["1HC000000"] = {
- server = "1HC",
- nick_ts = time,
- nick = config.nick,
- hostname = config.hostmask,
- vhost = config.hostmask,
- ident = "HaxServ",
- ip = "192.168.1.1",
- user_ts = time,
- modes = {
- ["k"] = true,
- },
- realname = "HaxServ",
-
- metadata = {},
- }
-
- for channel, _ in pairs(config.channels) do
- con:send("FJOIN "..channel.." "..time.." + :,1HC000000\n")
- con:send("MODE "..channel.." +o 1HC000000\n")
- end
- con:send("ENDBURST\n")
-
- local proceed = true
- while proceed do
- ready, _, err = socket.select({con, stdin}, {}, 120)
- if err then
- con:send("") -- hack to make it properly timeout due to annoying bugs
- end
-
- for _, sock in ipairs(ready) do
- if sock == con then
- local msg, err = con:receive()
-
- local original = msg
-
- if err then
- proceed = false
- break
- end
-
- local source = nil
- if msg:sub(1, 1) == ":" then
- source = msg:sub(2):match("^[^ ]*")
- msg = msg:sub(string.len(source) + 3) -- 1 for the leading ':', 1 for the trailing ' ', and 1 for the offset
- end
-
- local lastarg = msg:match(" :.*")
- if lastarg ~= nil then
- msg = msg:sub(1, -string.len(lastarg) - 1) -- only offset
- lastarg = lastarg:sub(3)
- end
-
- local args = {}
- for arg in msg:gmatch("[^ ]*") do
- table.insert(args, arg)
- end
-
- local command = args[1]
- table.remove(args, 1)
-
- if lastarg ~= nil then
- table.insert(args, lastarg)
- end
-
- if message_handler[command] then
- local success, stop = pcall(message_handler[command], con, source, args, original)
- if success and stop then
- proceed = false
- break
- elseif not success then
- print(stop)
- end
- else
- print("Unhandled command:", ("%q"):format(original))
- end
- elseif sock == stdin then
- msg = io.stdin:read()
- local args = {}
- for arg in msg:gmatch("[^ ]*") do
- table.insert(args, arg)
- end
- local command = args[1]:upper()
- table.remove(args, 1)
-
- if stdin_commands[command] then
- local success, err = pcall(stdin_commands[command], con, msg, args)
- if not success then
- print(err)
- elseif err then
- proceed = false
- break
- end
- else
- print("Unknown command.")
- end
- end
- end
- if err then break end
- end
-
- con:close()
-end
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..34dcb75
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,33 @@
+#INCLUDEFLAGS =
+
+CFLAGS += $(INCLUDEFLAGS) -D_REENTRANT -ggdb3
+
+LDFLAGS = -lssl -lcrypto
+
+DEPS = $(shell $(CC) $(INCLUDEFLAGS) -MM -MT $(1).o $(1).c | sed -z 's/\\\n //g')
+
+.PHONY: all clean cleanall release
+all: haxserv
+
+haxserv: main.o network.o commands.o table.o config.o tls.o utils.o
+ $(CC) $^ -o $@ $(LDFLAGS)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+$(call DEPS,main)
+
+$(call DEPS,network)
+
+$(call DEPS,commands)
+
+$(call DEPS,table)
+
+$(call DEPS,config)
+
+$(call DEPS,tls)
+
+$(call DEPS,utils)
+
+clean:
+ $(RM) haxserv *.o
diff --git a/commands.c b/commands.c
new file mode 100644
index 0000000..ad0607d
--- /dev/null
+++ b/commands.c
@@ -0,0 +1,65 @@
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "types.h"
+#include "table.h"
+#include "commands.h"
+#include "network.h"
+#include "tls.h"
+
+struct table user_commands = {0};
+
+int raw_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv) {
+ if (argv[0].len < original_message.len) {
+ original_message.data += argv[0].len + 1;
+ original_message.len -= argv[0].len + 1;
+ SEND(original_message);
+ }
+ SEND(STRING("\n"));
+
+ return 0;
+}
+static struct command_def raw_command_def = {
+ .func = raw_command,
+ .privs = STRING("Admin"),
+ .local_only = 0,
+};
+
+static struct pref_type_suff {
+ struct string pref;
+ uint8_t type;
+ struct string suff;
+} sus_strings[] = {
+ {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :Andrew is very sus.\n")},
+ {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :I was the impostor, but you only know because I killed you.\n")},
+ {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :\x1b(0\n")},
+ {STRING(":1HC000000 KILL "), 1, STRING(" :Ejected (1 Impostor remains)\n")},
+ {STRING(":1HC000000 KILL "), 1, STRING(" :Ejected, and the crewmates have won.\n")},
+};
+
+int sus_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv) {
+ uint64_t index = random() % (sizeof(sus_strings)/sizeof(sus_strings[0]));
+
+ SEND(sus_strings[index].pref);
+ if (sus_strings[index].type == 0)
+ SEND(to);
+ else
+ SEND(sender);
+ SEND(sus_strings[index].suff);
+
+ return 0;
+}
+static struct command_def sus_command_def = {
+ .func = sus_command,
+ .privs = {0},
+ .local_only = 0,
+};
+
+int init_user_commands(void) {
+ user_commands.array = malloc(0);
+
+ set_table_index(&user_commands, STRING(":"), &raw_command_def);
+ set_table_index(&user_commands, STRING("sus"), &sus_command_def);
+
+ return 0;
+}
diff --git a/commands.h b/commands.h
new file mode 100644
index 0000000..610ed89
--- /dev/null
+++ b/commands.h
@@ -0,0 +1,14 @@
+#include <stdint.h>
+
+#include "types.h"
+#include "table.h"
+
+struct command_def {
+ int (*func)(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv);
+ struct string privs;
+ uint8_t local_only;
+};
+
+extern struct table user_commands;
+
+extern int init_user_commands(void);
diff --git a/commands.lua b/commands.lua
deleted file mode 100644
index b758812..0000000
--- a/commands.lua
+++ /dev/null
@@ -1,363 +0,0 @@
---[[
-
-Commands file 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.
-]]
-
-commands = {
- ["SANICK"] = {
- func = function(con, user, cmd, args, resp)
- if #args < 2 then
- con:send(":1HC000000 NOTICE "..resp.." :Not enough args.\n")
- else
- con:send("SANICK "..args[1].." :"..table.concat(args, " ", 2).."\n")
- end
- end,
- privs = {"Admin"},
- args = "<target> <new nick>",
- },
-
- ["RELOAD"] = {
- func = function(con, user, cmd, args, resp)
- if #args == 0 then
- config_file:seek("set", 0)
- local success, value_or_err = pcall(json.decode, config_file:read("*a"))
- if success then
- config = value_or_err
- con:send(":1HC000000 NOTICE "..resp.." :Successfully reloaded config.json\n")
- else
- con:send(":1HC000000 NOTICE "..resp.." :Unable to reload config.json, check /dev/stdout for details.\n")
- print("config.json")
- print(value_or_err)
- end
-
- for file, _ in pairs(config.files) do
- local success, err = pcall(dofile, path..file)
- if success then
- con:send(":1HC000000 NOTICE "..resp.." :Successfully reloaded "..file.."\n")
- else
- con:send(":1HC000000 NOTICE "..resp.." :Unable to reload "..file..", check /dev/stdout for details.\n")
- print(file)
- print(err)
- end
- end
- else
- local file = args[1]..".lua"
- if config.files[file] then
- local success, err = pcall(dofile, path..file)
- if success then
- con:send(":1HC000000 NOTICE "..resp.." :Successfully reloaded "..file.."\n")
- else
- con:send(":1HC000000 NOTICE "..resp.." :Unable to reload "..file..", check /dev/stdout for details.\n")
- print(file)
- print(err)
- end
- elseif args[1] == "config" then
- config_file:seek("set", 0)
- local success, value_or_err = pcall(json.decode, config_file:read("*a"))
- if success then
- config = value_or_err
- con:send(":1HC000000 NOTICE "..resp.." :Successfully reloaded config.json\n")
- else
- con:send(":1HC000000 NOTICE "..resp.." :Unable to reload config.json, check /dev/stdout for details.\n")
- print("CoupServConfig.json")
- print(value_or_err)
- end
- else
- con:send(":1HC000000 NOTICE "..resp.." :Invalid section.\n")
- end
- end
- end,
- privs = {"Admin"},
- args = "[<section>]",
- },
-
- ["RECONNECT"] = {
- func = function(con, user, cmd, args, resp)
- return true
- end,
- privs = {"Admin"},
- },
-
- [":"] = {
- func = function(con, user, cmd, args, resp)
- con:send(table.concat(args, " ").."\n")
- end,
- privs = {"Admin"},
- args = "<raw IRC message>",
- },
-
- ["HELP"] = {
- func = function(con, user, cmd, args, resp)
- for command, tbl in pairs(commands) do
- if has_permission(userlist[user], tbl.privs) then
- if tbl.args then
- con:send(":1HC000000 NOTICE "..resp.." :"..command.." "..tbl.args.."\n")
- else
- con:send(":1HC000000 NOTICE "..resp.." :"..command.."\n")
- end
- end
- end
- end,
- },
-
- ["SHUTDOWN"] = {
- func = function(con, user, cmd, args, resp)
- local crash_me = nil
- crash_me()
- end,
- privs = {"Owner"},
- },
-
- ["SPAM"] = {
- func = function(con, user, cmd, args, resp)
- if #args < 3 then
- con:send(":1HC000000 NOTICE "..resp.." :Not enough args.\n")
- elseif tonumber(args[2]) == nil then
- con:send(":1HC000000 NOTICE "..resp.." :"..args[2]..": Not a valid positive integer.\n")
- elseif tonumber(args[2]) < 1 then
- con:send(":1HC000000 NOTICE "..resp.." :"..args[2]..": Not a valid positive integer.\n")
- elseif tonumber(args[2]) > 65535 then
- con:send(":1HC000000 NOTICE "..resp.." :"..args[2]..": Too large of a number, max is 65535.\n")
- else
- local msg = ":1HC000000 PRIVMSG "..args[1].." :"..table.concat(args, " ", 3).."\n"
- for i=1, tonumber(args[2]), 1 do
- con:send(msg)
- end
- end
- end,
- privs = {"Admin"},
- args = "<target> <count> <message>",
- },
-
- ["OP"] = {
- func = function(con, user, cmd, args, resp)
- if resp:sub(1, 1) ~= "#" then
- con:send(":1HC000000 NOTICE "..resp.." :This command must be executed within a channel.\n")
- return
- end
-
- if #args == 0 then
- con:send(":1HC000000 MODE "..resp.." +o "..user.."\n")
- else
- con:send(":1HC000000 MODE "..resp.." +o "..args[1].."\n")
- end
- end,
- privs = {"Admin"},
- args = "[<target>]",
- },
-
- ["GETUID"] = {
- func = function(con, user, cmd, args, resp)
- if #args == 0 then
- con:send(":1HC000000 NOTICE "..resp.." :"..user.."\n")
- else
- local nick = table.concat(args, " ")
- for uid, tbl in pairs(userlist) do
- if tbl.nick == nick then
- con:send(":1HC000000 NOTICE "..resp.." :"..uid.."\n")
- return
- end
- end
- con:send(":1HC000000 NOTICE "..resp.." :Nick not found.\n")
- end
- end,
- args = "[<nick>]",
- },
-
- ["PRINT"] = {
- func = function(con, user, cmd, args, resp)
- if #args == 0 then
- con:send(":1HC000000 NOTICE "..resp.." :Not enough args.\n")
- else
- local list
- if args[1] == "userlist" then
- list = userlist
- elseif args[1] == "chanlist" then
- list = chanlist
- elseif args[1] == "servlist" then
- list = servlist
- else
- con:send(":1HC000000 NOTICE "..resp.." :Unknown list.\n")
- return
- end
-
- for k, v in pairs(list) do
- local msg = {}
- for key, val in pairs(v) do
- table.insert(msg, "["..(type(key) == "string" and ("%q"):format(key) or tostring(key)).."] = "..(type(val) == "string" and ("%q"):format(val) or tostring(val)))
- end
- print("["..(type(k) == "string" and ("%q"):format(k) or tostring(k)).."] = {"..table.concat(msg, ", ").."}")
- end
- end
- end,
- privs = {"Admin"},
- args = "<list>",
- },
-
- ["GETUSERINFO"] = {
- func = function(con, user, cmd, args, resp)
- if #args == 0 then
- args[1] = source
- end
-
- if userlist[args[1]] then
- local msg = {}
- for key, val in pairs(userlist[args[1]]) do
- table.insert(msg, "["..(type(key) == "string" and ("%q"):format(key) or tostring(key)).."] = "..(type(val) == "string" and ("%q"):format(val) or tostring(val)))
- end
- con:send(":1HC000000 PRIVMSG "..resp.." :{"..table.concat(msg, ", ").."}\n")
- else
- con:send(":1HC000000 PRIVMSG "..resp.." :Nonexistent UID\n")
- end
- end,
- privs = {"Admin"},
- args = "[<UID>]",
- },
-
- ["GETNICK"] = {
- func = function(con, user, cmd, args, resp)
- if userlist[args[1]] then
- con:send(":1HC000000 NOTICE "..resp.." :"..userlist[args[1]].nick.."\n")
- else
- con:send(":1HC000000 NOTICE "..resp.." :Nonexistent UID\n")
- end
- end,
- args = "[<UID>]",
- },
-
- ["JUPE"] = {
- func = function(con, user, cmd, args, resp)
- if #args == 0 then
- con:send(":1HC000000 NOTICE "..resp.." :Not enough args.\n")
- else
- for id, tbl in pairs(servlist) do
- if tbl.address == args[1] then
- con:send("RSQUIT "..args[1].." :"..table.concat(args, " ", 2).."\n")
- con:send(":1HC SERVER "..args[1].." * 0 "..id.." :Juped.\n")
- return
- end
- end
- con:send(":1HC000000 NOTICE "..resp.." :Server not found.\n")
- end
- end,
- privs = {"Admin"},
- args = "<server>",
- },
-
- ["ALLOW"] = {
- func = function(con, user, cmd, args, resp)
- if #args == 0 then
- con:send(":1HC000000 NOTICE "..resp.." :Not enough args.\n")
- else
- for id, tbl in pairs(userlist) do
- if tbl.nick == args[1] then
- userlist[id].opertype = "Admin"
- con:send(":1HC000000 NOTICE "..resp.." :"..args[1].." is now considered an oper.\n")
- return
- end
- end
- con:send(":1HC000000 NOTICE "..resp.." :Nick not found.\n")
- end
- end,
- privs = {"Admin"},
- args = "<user>",
- },
-
- ["DENY"] = {
- func = function(con, user, cmd, args, resp)
- if #args == 0 then
- con:send(":1HC000000 NOTICE "..resp.." :Not enough args.\n")
- else
- for id, tbl in pairs(userlist) do
- if tbl.nick == args[1] then
- userlist[id].opertype = nil
- con:send(":1HC000000 MODE "..id.." -o\n")
- con:send(":1HC000000 NOTICE "..resp.." :"..args[1].." is no longer an oper.\n")
- return
- end
- end
- con:send(":1HC000000 NOTICE "..resp.." :Nick not found.\n")
- end
- end,
- privs = {"Admin"},
- args = "<user>",
- },
-
- ["SH"] = {
- func = function(con, user, cmd, args, resp)
- local command = table.concat(args, " ")
- if bash_command ~= nil and command == bash_command then
- local fd = io.popen(command)
- local message = fd:read("*a")
- for line in message:gmatch("[^\n]*") do
- if line == "" then
- con:send(":1HC000000 PRIVMSG "..resp.." : \n")
- else
- con:send(":1HC000000 PRIVMSG "..resp.." :"..line:gsub("\t", (" "):rep(8)).."\n")
- end
- end
- fd:close()
- bash_command = nil
- else
- if user:sub(1, 1) == "1" and resp:sub(1, 1) == "#" then
- con:send(":1HC000000 KICK "..resp.." "..user.." :Thought they could execute arbitrary code on hax's computer.)\n")
- else
- con:send(":1HC000000 KILL "..user.." :Killed (Thought they could execute arbitrary code on hax's computer.)\n")
- end
- end
- end,
- args = "<command>",
- },
-
- ["SUS"] = {
- func = function(con, user, cmd, args, resp)
- local lines = {
- ":1HC000000 PRIVMSG "..resp.." :Andrew is very sus.\n",
- ":1HC000000 PRIVMSG "..resp.." :I was the impostor, but you only know because I killed you.\n",
- ":1HC000000 PRIVMSG "..resp.." :\x1b(0\n",
- ":1HC000000 KILL "..user.." :Ejected (1 Impostor remains)\n",
- ":1HC000000 KILL "..user.." :Ejected, and the crewmates have won.\n",
- }
- con:send(lines[math.random(#lines)])
- end,
- },
-
- ["CR"] = {
- func = function(con, user, cmd, args, resp)
- local lines = {
- ":1HC000000 PRIVMSG "..resp.." :You are now a cruxian toxicpod, kill the sharded crewmates.\n",
- ":1HC000000 PRIVMSG "..resp.." :You are now a cruxian omura, kill the sharded crewmates.\n",
- ":1HC000000 PRIVMSG "..resp.." :You are now a cruxian oct, but you ran out of reactors.\n",
- ":1HC000000 KILL "..user.." :Eliminated (You became a cruxian eclipse, but were drawn to my bait reactor)\n",
- ":1HC000000 PRIVMSG "..resp.." :You attempted to change into a cruxian navanax, but were caught in the act.\n",
- }
- con:send(lines[math.random(#lines)])
- end,
- },
-}
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..dc42263
--- /dev/null
+++ b/config.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <stdint.h>
+
+#include "types.h"
+
+extern struct string address;
+extern struct string port;
+extern struct string nick;
+extern struct string log_channel;
+extern struct string recv_password;
+extern struct string send_password;
+extern struct string hostmask;
+extern struct string prejoin_channels[];
+extern uint64_t num_prejoin_channels;
+
+extern struct string command_prefix;
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..6bebabb
--- /dev/null
+++ b/main.c
@@ -0,0 +1,189 @@
+#include <openssl/ssl.h>
+#include <string.h>
+
+#include "network.h"
+#include "config.h"
+#include "types.h"
+#include "tls.h"
+#include "types.h"
+
+int main(void) {
+ initservernetwork();
+
+ struct string full_msg = {malloc(0), 0};
+ while (1) {
+ uint8_t data[512];
+ uint64_t new_len = SSL_read(ssl, data, 512);
+
+ if (new_len == 0) {
+ puts("Disconnected.");
+ return 0;
+ }
+
+ uint8_t found = 0;
+ uint64_t msg_len;
+ for (uint64_t i = 0; i < new_len; i++) {
+ if (data[i] == '\n') {
+ found = 1;
+ msg_len = i + full_msg.len;
+ break;
+ }
+ }
+
+ void *tmp = realloc(full_msg.data, full_msg.len+new_len);
+ if (tmp == 0 && full_msg.len+new_len != 0) {
+ puts("OOM... currently just exiting bc there's no automatic reconnect in here yet, and the only sane solution to this is resyncing.");
+ return 1;
+ }
+ full_msg.data = tmp;
+
+ memcpy(full_msg.data+full_msg.len, data, new_len);
+
+ full_msg.len += new_len;
+
+ if (!found)
+ continue;
+
+ while (1) {
+ WRITES(1, STRING("Recvd: "));
+ write(1, full_msg.data, msg_len+1); // +1: \n
+
+ uint64_t offset = 0;
+ while (offset < msg_len && full_msg.data[offset] == ' ')
+ offset++;
+
+ if (msg_len == offset) {
+ puts("Protocol violation: Empty message.");
+ return 2;
+ }
+
+ struct string source;
+ if (full_msg.data[0] == ':') {
+ source.data = full_msg.data + 1;
+ found = 0;
+ for (uint64_t i = offset + 1; i < msg_len; i++) {
+ if (full_msg.data[i] == ' ') {
+ found = 1;
+ offset = i + 1;
+ source.len = i - 1;
+ break;
+ }
+ }
+ if (!found || source.len + 1 == msg_len) {
+ puts("Protocol violation: Sender but no command.");
+ return 2;
+ }
+ } else {
+ source = (struct string){0};
+ offset = 0;
+ }
+
+ while (offset < msg_len && full_msg.data[offset] == ' ')
+ offset++;
+
+ if (offset == msg_len) {
+ puts("Protocol violation: No command.");
+ return 2;
+ }
+
+ struct string command;
+ command.data = full_msg.data+offset;
+ found = 0;
+ for (uint64_t i = offset; i < msg_len; i++) {
+ if (full_msg.data[i] == ' ') {
+ found = 1;
+ command.len = i - offset;
+ offset = i;
+ break;
+ }
+ }
+ if (!found) {
+ command.len = msg_len - offset;
+ offset = msg_len;
+ }
+
+ while (offset < msg_len && full_msg.data[offset] == ' ')
+ offset++;
+
+ uint64_t argc = 0;
+ uint64_t old_offset = offset;
+ if (offset < msg_len) {
+ while (offset < msg_len) {
+ if (full_msg.data[offset] == ':') {
+ argc++;
+ break;
+ }
+
+ while (offset < msg_len && full_msg.data[offset] != ' ')
+ offset++;
+
+ argc++;
+
+ while (offset < msg_len && full_msg.data[offset] == ' ')
+ offset++;
+ }
+ }
+ offset = old_offset;
+
+ struct string argv[argc];
+ if (offset < msg_len) {
+ uint64_t i = 0;
+ while (offset < msg_len) {
+ if (full_msg.data[offset] == ':') {
+ argv[i].data = full_msg.data+offset+1;
+ argv[i].len = msg_len - offset - 1;
+ break;
+ }
+
+ argv[i].data = full_msg.data+offset;
+ uint64_t start = offset;
+
+ while (offset < msg_len && full_msg.data[offset] != ' ')
+ offset++;
+
+ argv[i].len = offset - start;
+
+ while (offset < msg_len && full_msg.data[offset] == ' ')
+ offset++;
+
+ i++;
+ }
+ }
+
+ int (*func)(struct string source, uint64_t argc, struct string *argv) = get_table_index(network_commands, command);
+
+ if (func == 0) {
+ WRITES(1, STRING("WARNING: Command is unknown, ignoring...\n"));
+ } else {
+ write(1, "\n", 1);
+ func(source, argc, argv);
+ }
+ write(1, "\n", 1);
+
+ memmove(full_msg.data, full_msg.data+msg_len+1, full_msg.len - msg_len - 1);
+ full_msg.len -= msg_len+1;
+
+ found = 0;
+ for (uint64_t i = 0; i < full_msg.len; i++) {
+ if (full_msg.data[i] == '\n') {
+ found = 1;
+ msg_len = i;
+ break;
+ }
+ }
+
+ if (found == 0) {
+ void *tmp = realloc(full_msg.data, full_msg.len);
+ if (tmp == 0 && full_msg.len != 0) {
+ puts("AAAAAAAAA (OOM shrinking allocated data?)");
+ return 1;
+ }
+ full_msg.data = tmp;
+
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/network.c b/network.c
new file mode 100644
index 0000000..3a92bd5
--- /dev/null
+++ b/network.c
@@ -0,0 +1,463 @@
+#include <openssl/ssl.h>
+#include <netdb.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.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"
+
+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 network_commands = {0};
+struct table server_list = {0};
+struct table user_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;
+ uint8_t 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';
+
+ SSL_write(ssl, 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;
+ }
+
+ uint8_t 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 remote_server *from = get_table_index(server_list, sender);
+ if (!from) {
+ puts("Invalid SERVER recieved! (Unknown source)");
+ return 1;
+ }
+
+ distance += from->distance + 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 remote_server *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 remote_server){
+ .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;
+ }
+
+ // TODO: modes
+
+ uint8_t 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;
+
+ 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,
+ .metadata = (struct table){.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);
+
+ if (user->opertype.len)
+ free(user->opertype.data);
+
+ user->opertype = opertype;
+
+ 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;
+ }
+
+ 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) {
+ WRITES(1, STRING("Message is not a command.\n"));
+ return 0; // not for us
+ }
+
+ offset = command_prefix.len;
+ } else {
+ offset = 0;
+ }
+
+ if (offset >= argv[1].len || argv[1].data[offset] == ' ') {
+ // TODO: complain about empty command
+ WRITES(1, STRING("Command is empty?\n"));
+ 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
+ struct user_info *user = get_table_index(user_list, sender);
+ if (!user) {
+ // TODO: complain about unknown user
+ WRITES(1, STRING("User is unknown!\n"));
+ return 0;
+ }
+
+ if (user->opertype.len != cmd->privs.len || memcmp(user->opertype.data, cmd->privs.data, cmd->privs.len)) {
+ // TODO: complain about missing privs
+ WRITES(1, STRING("User lacks privs for this command!\n"));
+ return 0;
+ }
+ }
+
+ WRITES(1, STRING("Executing command "));
+ WRITES(1, command_argv[0]);
+ write(1, "\n", 1);
+ return cmd->func(sender, argv[1], argv[0], command_argc, command_argv);
+ } else {
+ // TODO: complain about remote access
+ WRITES(1, STRING("Not executing local-only command from a remote source!\n"));
+ return 0;
+ }
+ } else {
+ // TODO: complain about unknown command
+ WRITES(1, STRING("Command is unknown!\n"));
+ WRITES(1, command_argv[0]);
+ write(1, "\n", 1);
+ return 0;
+ }
+}
+
+int initservernetwork(void) {
+ network_commands.array = malloc(0);
+ server_list.array = malloc(0);
+ user_list.array = malloc(0);
+
+ set_table_index(&network_commands, STRING("PING"), &ping_handler);
+ set_table_index(&network_commands, STRING("SERVER"), &server_handler);
+ set_table_index(&network_commands, STRING("UID"), &uid_handler);
+ set_table_index(&network_commands, STRING("OPERTYPE"), &opertype_handler);
+ set_table_index(&network_commands, STRING("PRIVMSG"), &privmsg_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 hax.irc.andrewyu.org "));
+ SEND(send_password);
+ SEND(STRING(" 0 1HC :HaxServ\n"));
+ SEND(STRING("BURST "));
+ uint8_t current_time[21]; // C HaxServ will be deprecated long before we reach 20-digit timestamps
+ snprintf(current_time, 21, "%d", time(NULL));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING("\nUID 1HC000000 "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" "));
+ SEND(nick);
+ SEND(STRING(" "));
+ SEND(hostmask);
+ SEND(STRING(" "));
+ SEND(hostmask);
+ SEND(STRING(" HaxServ 192.168.1.1 "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" +k :HaxServ\n:1HC000000 OPERTYPE Admin\n"));
+
+ for (uint64_t i = 0; i < num_prejoin_channels; i++) {
+ SEND(STRING("FJOIN "));
+ SEND(prejoin_channels[i]);
+ SEND(STRING(" "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" + :,1HC000000\nMODE "));
+ SEND(prejoin_channels[i]);
+ SEND(STRING(" +o 1HC000000\n"));
+ }
+
+ SEND(STRING("ENDBURST\n"));
+
+ return 0;
+}
diff --git a/network.h b/network.h
new file mode 100644
index 0000000..7cc4a28
--- /dev/null
+++ b/network.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <netinet/in.h>
+
+#include "types.h"
+#include "table.h"
+
+// ID is the index you got this from
+struct remote_server {
+ uint64_t distance; // gl if you exceed this
+
+ struct string address;
+ struct string name;
+ struct string via; // netsplit purposes
+ // TODO: metadata
+};
+
+struct user_info {
+ uint64_t nick_ts;
+ uint64_t user_ts;
+
+ struct string server;
+ struct string nick;
+ struct string hostname;
+ struct string vhost;
+ struct string ident;
+ struct string ip;
+ struct string realname;
+ struct string opertype;
+
+ struct table metadata;
+};
+
+extern struct table network_commands;
+extern struct table server_list;
+extern struct table user_list;
+
+int resolve(char* address, char* port, struct sockaddr *server);
+int initservernetwork(void);
diff --git a/network.lua b/network.lua
deleted file mode 100644
index 84cb712..0000000
--- a/network.lua
+++ /dev/null
@@ -1,310 +0,0 @@
---[[
-
-Network protocol file 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.
-]]
-
-local function mode_snomask(current, mode, dir, arg)
- if dir == "+" then
- current[mode] = current[mode] or {}
- parse_modes(arg, {}, {}, current[mode])
- if not next(current[mode]) then
- current[mode] = nil
- end
- return true
- else
- current[mode] = nil
- end
-end
-
-local function mode_replace(current, mode, dir, arg)
- if dir == "+" then
- current[mode] = arg
- return true
- else
- current[mode] = nil
- end
-end
-
-local function mode_multi(current, mode, dir, arg)
- if dir == "+" then
- current[mode] = current[mode] or {}
- current[mode][arg] = true
- else
- if current[mode] == nil then
- print("Invalid mode change attempt!\n")
- current[mode] = {}
- end
- current[mode][arg] = nil
- if next(current[mode]) == nil then
- current[mode] = nil
- end
- end
- return true
-end
-
-local usermodes = {
- ["s"] = mode_snomask,
-}
-
-local chanmodes = {
- ["l"] = mode_replace,
- ["L"] = mode_replace,
- ["v"] = mode_multi,
- ["o"] = mode_multi,
- ["h"] = mode_multi,
- ["a"] = mode_multi,
- ["q"] = mode_multi,
- ["Y"] = mode_multi,
- ["d"] = mode_replace,
- ["f"] = mode_replace,
- ["g"] = mode_multi,
- ["b"] = mode_multi,
- ["e"] = mode_multi,
- ["I"] = mode_multi,
- ["k"] = mode_replace,
- ["w"] = mode_multi,
- ["E"] = mode_replace,
- ["F"] = mode_replace,
- ["H"] = mode_replace,
- ["J"] = mode_replace,
- ["X"] = mode_multi,
-}
-
-local function parse_modes(modes, args, has_args, current)
- local dir = "-"
- for i = 1, #modes do
- local mode = modes:sub(i, i)
-
- if mode == "+" or mode == "-" then
- dir = mode
- elseif has_args[mode] and has_args[mode][dir] then
- if not args[1] then return false end
-
- if dir == "+" then
- current[mode] = (type(current[mode]) == "table" and current[mode] or {})
- current[mode][args[1]] = true
- else
- if current[mode] then
- current[mode][args[1]] = nil
- end
- end
- table.remove(args, 1)
- else
- current[mode] = (dir == "+" and true or nil)
- end
- end
- return true
-end
-
-local function mode_to_string(modes)
- local res = "+"
- local args = {}
- for mode, arg in pairs(modes) do
- if arg == true then
- res = res..mode
- elseif type(arg) == "string" then
- res = res..mode
- table.insert(args, arg)
- elseif type(arg) == "table" then
- for _, a in pairs(arg) do
- res = res..mode
- table.insert(args, a)
- end
- end
- end
-
- res = res.." "..table.concat(args, " ")
-
- return res
-end
-
-message_handler = {
- ["PING"] = function(con, source, args, original)
- con:send(":"..args[2].." PONG "..args[2].." "..source.."\n")
- end,
-
- ["SERVER"] = function(con, source, args, original)
- if source then
- servlist[args[4]] = {address = args[1], distance = args[3] + 1 + servlist[source].distance, name = args[5], metadata = {}}
- else
- servlist[args[4]] = {address = args[1], distance = args[3], name = args[5], metadata = {}}
- end
- end,
-
- ["METADATA"] = function(con, source, args, original)
- if args[1] == "*" then
- if servlist[source] then
- servlist[source].metadata[args[2]] = args[3]
- else
- print("Got metadata command from an unknown server!\n")
- end
- elseif args[1]:sub(1, 1) == "#" then
- print(string.format("%q", original))
- print("Channels not yet handled!\n")
- else
- if userlist[args[1]] then
- userlist[args[1]].metadata[args[2]] = args[3]
- else
- print(("%q"):format(original))
- print("Got metadata for an unknown user!\n")
- end
- end
- end,
-
- ["UID"] = function(con, source, args, original)
- userlist[args[1]] = {
- server = source,
- nick_ts = args[2],
- nick = args[3],
- hostname = args[4],
- vhost = args[5],
- ident = args[6],
- ip = args[7],
- user_ts = args[8],
- modes = {},
- realname = args[-1], -- last one is safer as any extra are arguments to umodes (or protocol violations, but at that point nothing is a safe option)
-
- metadata = {}, -- controlled by METADATA network commands
- }
-
- if not parse_modes(args[9], {table.unpack(args, 10, #args-1)}, {["s"] = {["+"] = true, ["-"] = false}}, userlist[args[1]].modes) then return true end
- end,
-
- ["OPERTYPE"] = function(con, source, args, original)
- if userlist[source] then
- userlist[source].opertype = args[1]
- else
- print("Server "..source.." attempted to set OPERTYPE on a nonexistent user!\n")
- end
- end,
-
- ["NICK"] = function(con, source, args, original)
- if userlist[source] then
- userlist[source].nick = args[1]
- userlist[source].nick_ts = args[2]
- end
- end,
-
- ["PRIVMSG"] = function(con, source, args, original)
- if args[1] == cur_channel then
- print(("%q"):format("<"..userlist[source].nick.."> "..args[2]))
- end
-
- local cmd_args = {}
- for part in args[2]:gmatch("[^ ]*") do
- table.insert(cmd_args, part)
- end
- cmd = cmd_args[1]:upper()
- table.remove(cmd_args, 1)
-
- local resp
- if args[1]:sub(1, 1) == "#" then
- resp = args[1]
-
- if cmd:sub(1, 3) ~= "\x0304" then return end
-
- cmd = cmd:sub(4) -- remove leading '-'
- else
- resp = source
- end
-
- if commands[cmd] then
- if has_permission(userlist[source], commands[cmd].privs) then
- print(("%q"):format(userlist[source].nick.." executed command: "..cmd))
- return commands[cmd].func(con, source, cmd, cmd_args, resp)
- else
- con:send(":1HC000000 NOTICE "..resp.." :You are not authorized to execute that command.\n")
- end
- else
- con:send(":1HC000000 NOTICE "..resp.." :Unknown command: "..cmd.."\n")
- end
- end,
-
- ["MODE"] = function(con, source, args, original)
- if args[1]:sub(1, 1) == "#" then
- print("Channels not handled yet!\n")
- else
- if not userlist[args[1]] then
- print("Attempted to set mode on an unknown user!\n")
- elseif not parse_modes(args[2], {table.unpack(args, 3)}, usermodes, userlist[args[1]].modes) then
- return true
- elseif not userlist[args[1]].modes.o then
- userlist[args[1]].opertype = nil
- end
- end
- end,
-
- ["QUIT"] = function(con, source, args, original)
- userlist[source] = nil
- for name, chan in pairs(chanlist) do
- chan["users"][source] = nil
- if next(chan["users"]) == nil and not chan["modes"]["P"] then
- chanlist[name] = nil
- end
- end
- end,
-
- ["KILL"] = function(con, source, args, original)
- if args[1]:sub(1,3) ~= "1HC" then
- print("Kill remote", original)
- userlist[source] = nil
- for name, chan in pairs(chanlist) do
- chan["users"][source] = nil
- if next(chan["users"]) == nil and not chan["modes"]["P"] then
- chanlist[name] = nil
- end
- end
- else
- print("Kill local", original)
- local user = userlist[args[1]]
- if type(user) == "table" then
- con:send("UID "..args[1].." "..user.nick_ts.." "..user.nick.." "..user.hostname.." "..user.vhost.." "..user.ident.." "..user.ip.." "..user.user_ts.." "..mode_to_string(user.modes).." :"..user.realname.."\n")
-
- -- temporary before I handle channels
- for channel, _ in pairs(config.channels) do
- con:send(":"..args[1].." JOIN "..channel.."\n")
- con:send("MODE "..channel.." +o "..args[1].."\n")
- end
- end
- end
- end,
-
- ["KICK"] = function(con, soure, args, original)
- if args[2]:sub(1,3) == "1HC" then
- con:send(":"..args[2].." JOIN "..args[1].."\n")
- con:send("MODE "..args[1].." +o "..args[2].."\n")
- else
- print("Channels not yet handled: "..("%q"):format(original))
- end
- end,
-
---[[ ["FJOIN"] = function(con, source, args, original)
-
- end]]
-}
diff --git a/stdin.lua b/stdin.lua
deleted file mode 100644
index 4b69590..0000000
--- a/stdin.lua
+++ /dev/null
@@ -1,132 +0,0 @@
---[[
-
-Network protocol file 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.
-]]
-
-stdin_commands = {
- [":"] = function(con, msg, args)
- con:send(msg:sub(3).."\n")
- end,
-
- ["RELOAD"] = function(con, msg, args)
- if #args == 0 then
- config_file:seek("set", 0)
- local success, value_or_err = pcall(json.decode, config_file:read("*a"))
- if success then
- config = value_or_err
- print("Successfully reloaded config.json")
- else
- print("Unable to reload config.json")
- print(value_or_err)
- end
-
- for file, _ in pairs(config.files) do
- local success, err = pcall(dofile, path..file)
- if success then
- print("Successfully reloaded "..file)
- else
- print("Unable to reload "..file)
- print(err)
- end
- end
- else
- local file = args[1]..".lua"
- if config.files[file] then
- local success, err = pcall(dofile, path..file)
- if success then
- print("Successfully reloaded "..file)
- else
- print("Unable to reload "..file)
- print(err)
- end
- elseif args[1] == "config" then
- config_file:seek("set", 0)
- local success, value_or_err = pcall(json.decode, config_file:read("*a"))
- if success then
- config = value_or_err
- print("Successfully reloaded config.json")
- else
- print("Unable to reload config.json")
- print(value_or_err)
- end
- else
- print("Invalid section.")
- end
- end
- end,
-
- ["ALLOW"] = function(con, msg, args)
- if #args == 0 then
- print("Not enough args.")
- else
- for id, tbl in pairs(userlist) do
- if tbl.nick == args[1] then
- userlist[id].opertype = "Admin"
- print(args[1].." is now considered an oper.")
- return
- end
- end
- print("Nick not found.")
- end
- end,
-
- ["DENY"] = function(con, msg, args)
- if #args == 0 then
- print("Not enough args.")
- else
- for id, tbl in pairs(userlist) do
- if tbl.nick == args[1] then
- userlist[id].opertype = nil
- con:send(":1HC000000 MODE "..id.." -o\n")
- print(args[1].." is no longer an oper.")
- return
- end
- end
- print("Nick not found.")
- end
- end,
-
- ["ADDSH"] = function(con, msg, args)
- if msg:find(" ") == nil then
- print("Not enough args.")
- else
- local i = msg:find(" ")
- bash_command = msg:sub(i + 1) -- yes, global
- print(bash_command)
- end
- end,
-
- ["SET_CHANNEL"] = function(con, msg, args)
- cur_channel = args[1]
- end,
-
- ["M"] = function(con, msg, args)
- con:send(":1HC000000 PRIVMSG "..cur_channel.." :"..table.concat(args, " ").."\n")
- end,
-}
diff --git a/stdout b/stdout
new file mode 100644
index 0000000..189c948
--- /dev/null
+++ b/stdout
@@ -0,0 +1,2049 @@
+Recvd: CAPAB START 1205
+
+CAPAB
+START
+1205
+WARNING: Command is unknown, ignoring...
+
+Recvd: CAPAB MODULES :m_anticaps.so m_banredirect.so m_callerid.so m_cban.so=glob m_cloaking.so=guest/j0oku4e9.IP m_dccallow.so m_filter.so=regex/glob m_globalload.so m_ircv3_ctctags.so m_repeat.so=20:0:50:20 m_rline.so=regex/pcre m_shun.so m_svshold.so m_timedbans.so m_topiclock.so
+
+CAPAB
+MODULES
+m_anticaps.so m_banredirect.so m_callerid.so m_cban.so=glob m_cloaking.so=guest/j0oku4e9.IP m_dccallow.so m_filter.so=regex/glob m_globalload.so m_ircv3_ctctags.so m_repeat.so=20:0:50:20 m_rline.so=regex/pcre m_shun.so m_svshold.so m_timedbans.so m_topiclock.so
+WARNING: Command is unknown, ignoring...
+
+Recvd: CAPAB MODSUPPORT :m_alltime.so m_channelban.so m_check.so m_chghost.so m_chgident.so m_chgname.so m_classban.so m_clearchan.so m_customtitle.so m_gecosban.so m_knock.so m_muteban.so m_nicklock.so m_nopartmsg.so m_remove.so m_sajoin.so m_sakick.so m_sanick.so m_sapart.so m_saquit.so m_serverban.so m_services_account.so m_showwhois.so m_silence.so m_swhois.so m_uninvite.so m_watch.so
+
+CAPAB
+MODSUPPORT
+m_alltime.so m_channelban.so m_check.so m_chghost.so m_chgident.so m_chgname.so m_classban.so m_clearchan.so m_customtitle.so m_gecosban.so m_knock.so m_muteban.so m_nicklock.so m_nopartmsg.so m_remove.so m_sajoin.so m_sakick.so m_sanick.so m_sapart.so m_saquit.so m_serverban.so m_services_account.so m_showwhois.so m_silence.so m_swhois.so m_uninvite.so m_watch.so
+WARNING: Command is unknown, ignoring...
+
+Recvd: CAPAB CHANMODES :admin=&a allowinvite=A anticaps=B auditorium=u autoop=w ban=b banexception=e blockcolor=c c_registered=r censor=G delayjoin=D delaymsg=d exemptchanops=X filter=g flood=f founder=~q halfop=%h history=H invex=I inviteonly=i joinflood=j key=k kicknorejoin=J limit=l moderated=m namebase=Z nickflood=F noctcp=C noextmsg=n nokick=Q noknock=K nonick=N nonotice=T official-join=!Y op=@o operonly=O permanent=P private=p redirect=L reginvite=R regmoderated=M repeat=E secret=s sslonly=z stripcolor=S topiclock=t voice=+v
+
+CAPAB
+CHANMODES
+admin=&a allowinvite=A anticaps=B auditorium=u autoop=w ban=b banexception=e blockcolor=c c_registered=r censor=G delayjoin=D delaymsg=d exemptchanops=X filter=g flood=f founder=~q halfop=%h history=H invex=I inviteonly=i joinflood=j key=k kicknorejoin=J limit=l moderated=m namebase=Z nickflood=F noctcp=C noextmsg=n nokick=Q noknock=K nonick=N nonotice=T official-join=!Y op=@o operonly=O permanent=P private=p redirect=L reginvite=R regmoderated=M repeat=E secret=s sslonly=z stripcolor=S topiclock=t voice=+v
+WARNING: Command is unknown, ignoring...
+
+Recvd: CAPAB USERMODES :antiredirect=L bot=B callerid=g cloak=x deaf=d deaf_commonchan=c helpop=h hidechans=I hideoper=H invisible=i nohistory=N oper=o override=O privdeaf=D regdeaf=R servprotect=k showwhois=W snomask=s sslqueries=z u_censor=G u_noctcp=T u_registered=r u_stripcolor=S wallops=w
+
+CAPAB
+USERMODES
+antiredirect=L bot=B callerid=g cloak=x deaf=d deaf_commonchan=c helpop=h hidechans=I hideoper=H invisible=i nohistory=N oper=o override=O privdeaf=D regdeaf=R servprotect=k showwhois=W snomask=s sslqueries=z u_censor=G u_noctcp=T u_registered=r u_stripcolor=S wallops=w
+WARNING: Command is unknown, ignoring...
+
+Recvd: CAPAB CAPABILITIES :NICKMAX=30 CHANMAX=60 MAXMODES=20 IDENTMAX=10 MAXQUIT=300 MAXTOPIC=330 MAXKICK=300 MAXREAL=130 MAXAWAY=200 MAXHOST=64 MAXLINE=512 CHALLENGE=WDHJ|stab^bUSeFIVtnt EXTBANS=SzRUsOpTNQCmarnjwcA CASEMAPPING=ascii GLOBOPS=1
+
+CAPAB
+CAPABILITIES
+NICKMAX=30 CHANMAX=60 MAXMODES=20 IDENTMAX=10 MAXQUIT=300 MAXTOPIC=330 MAXKICK=300 MAXREAL=130 MAXAWAY=200 MAXHOST=64 MAXLINE=512 CHALLENGE=WDHJ|stab^bUSeFIVtnt EXTBANS=SzRUsOpTNQCmarnjwcA CASEMAPPING=ascii GLOBOPS=1
+WARNING: Command is unknown, ignoring...
+
+Recvd: CAPAB END
+
+CAPAB
+END
+WARNING: Command is unknown, ignoring...
+
+Recvd: SERVER irc.andrewyu.org YJDtoVxBONGEoV04QIJfqaP8oUK9DzbDFGaJx84p 0 900 :LibreIRC Root Server
+
+SERVER
+irc.andrewyu.org
+YJDtoVxBONGEoV04QIJfqaP8oUK9DzbDFGaJx84p
+0
+900
+LibreIRC Root Server
+
+#args: 2
+#args: 2
+#args: 2
+#args: 2
+#args: 2
+#args: 2
+#args: 1
+#args: 5
+Distance: 0
+Name: LibreIRC Root Server
+Address: irc.andrewyu.org
+
+Recvd: :900 BURST 1683099334
+900
+BURST
+1683099334
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 VERSION :InspIRCd-3. irc.andrewyu.org :LibreIRCd
+900
+VERSION
+InspIRCd-3. irc.andrewyu.org :LibreIRCd
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 SERVER services.irc.andrewyu.org * 0 000 :Atheme IRC Services
+900
+SERVER
+services.irc.andrewyu.org
+*
+0
+000
+Atheme IRC Services
+
+#args: 1
+#args: 1
+#args: 5
+Distance: 1
+Name: Atheme IRC Services
+Address: services.irc.andrewyu.org
+
+Recvd: :000 VERSION :atheme 7.2.11. services.irc.andrewyu.org v7.2.11-0-g82866a59bef5f1be76f3 :ceFljRrT [InspIRCd] [enc:posix]
+000
+VERSION
+atheme 7.2.11. services.irc.andrewyu.org v7.2.11-0-g82866a59bef5f1be76f3 :ceFljRrT [InspIRCd] [enc:posix]
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AACQ8I 1683084489 hax[xor] 2603-6081-4701-ccd2-06ea-56ff-fe86-cba8.res6.spectrum.com LibreIRC/staff/hax ~Test_User 2603:6081:4701:ccd2:6ea:56ff:fe86:cba8 1683084489 +iow :Test_User
+900
+UID
+900AACQ8I
+1683084489
+hax[xor]
+2603-6081-4701-ccd2-06ea-56ff-fe86-cba8.res6.spectrum.com
+LibreIRC/staff/hax
+~Test_User
+2603:6081:4701:ccd2:6ea:56ff:fe86:cba8
+1683084489
++iow
+Test_User
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900AACQ8I OPERTYPE Admin
+900AACQ8I
+OPERTYPE
+Admin
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACQ8I accountname :*!*@*
+900
+METADATA
+900AACQ8I
+accountname
+*!*@*
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACQ8I ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AACQ8I
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AACQ5M 1683082198 Discard 144.172.70.127 guest/q46.ksk.172.144.IP misc 144.172.70.127 1683082198 +iwx :Discard
+900
+UID
+900AACQ5M
+1683082198
+Discard
+144.172.70.127
+guest/q46.ksk.172.144.IP
+misc
+144.172.70.127
+1683082198
++iwx
+Discard
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACQ5M ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AACQ5M
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AACM9C 1682966754 Peaksol9 222.246.236.124 user/Peaksol ~Peaksol 222.246.236.124 1682966399 +iw :Rayan Wu
+900
+UID
+900AACM9C
+1682966754
+Peaksol9
+222.246.236.124
+user/Peaksol
+~Peaksol
+222.246.236.124
+1682966399
++iw
+Rayan Wu
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACM9C accountname :Peaksol
+900
+METADATA
+900AACM9C
+accountname
+Peaksol
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACM9C ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AACM9C
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AACI4F 1682846953 Noisytoot cpc92718-cmbg20-2-0-cust83.5-4.cable.virginm.net LibreIRC/staff/Noisytoot noisytoot 82.20.110.84 1682846952 +ITWiosw +AFKLORXafgklortvx :Ron
+900
+UID
+900AACI4F
+1682846953
+Noisytoot
+cpc92718-cmbg20-2-0-cust83.5-4.cable.virginm.net
+LibreIRC/staff/Noisytoot
+noisytoot
+82.20.110.84
+1682846952
++ITWiosw
++AFKLORXafgklortvx
+Ron
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900AACI4F OPERTYPE Admin
+900AACI4F
+OPERTYPE
+Admin
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACI4F accountname :Noisytoot
+900
+METADATA
+900AACI4F
+accountname
+Noisytoot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACI4F ssl_cert :vTrse 07793a9956a2d432cfcfee0b43b597298825f175dbb6d515b191349b5c20dd8a CN=Noisytoot CN=Noisytoot
+900
+METADATA
+900AACI4F
+ssl_cert
+vTrse 07793a9956a2d432cfcfee0b43b597298825f175dbb6d515b191349b5c20dd8a CN=Noisytoot CN=Noisytoot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AAB9K2 1682572938 vernmatrixbridgebot mail.vern.cc guest/r03jg7.vern.cc ~vernmatri 5.161.108.85 1682572938 +iwx :vernmatrixbridgebot
+900
+UID
+900AAB9K2
+1682572938
+vernmatrixbridgebot
+mail.vern.cc
+guest/r03jg7.vern.cc
+~vernmatri
+5.161.108.85
+1682572938
++iwx
+vernmatrixbridgebot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAB9K2 accountname :vernmatrixbridgebot
+900
+METADATA
+900AAB9K2
+accountname
+vernmatrixbridgebot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAB9K2 ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AAB9K2
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AABYDC 1682240373 Doggie7112886 58.35.80.74 guest/3fo.dbe.35.58.IP ~doge 58.35.80.74 1682240368 +iwx :doge
+900
+UID
+900AABYDC
+1682240373
+Doggie7112886
+58.35.80.74
+guest/3fo.dbe.35.58.IP
+~doge
+58.35.80.74
+1682240368
++iwx
+doge
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AABYBO 1682239043 tapuz4 58.35.80.74 guest/3fo.dbe.35.58.IP ~tapuz 58.35.80.74 1682239042 +iwx :tapuz
+900
+UID
+900AABYBO
+1682239043
+tapuz4
+58.35.80.74
+guest/3fo.dbe.35.58.IP
+~tapuz
+58.35.80.74
+1682239042
++iwx
+tapuz
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAC 1680483093 ChanServ LibreIRC/service/ChanServ LibreIRC/service/ChanServ ChanServ 0.0.0.0 1680483093 +Iiko :Channel Services
+000
+UID
+000AAAAAC
+1680483093
+ChanServ
+LibreIRC/service/ChanServ
+LibreIRC/service/ChanServ
+ChanServ
+0.0.0.0
+1680483093
++Iiko
+Channel Services
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAJ 1680483093 OperServ LibreIRC/service/OperServ LibreIRC/service/OperServ OperServ 0.0.0.0 1680483093 +Iiko :Operator Services
+000
+UID
+000AAAAAJ
+1680483093
+OperServ
+LibreIRC/service/OperServ
+LibreIRC/service/OperServ
+OperServ
+0.0.0.0
+1680483093
++Iiko
+Operator Services
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAH 1680483093 MemoServ LibreIRC/service/MemoServ LibreIRC/service/MemoServ MemoServ 0.0.0.0 1680483093 +Iiko :Memo Services
+000
+UID
+000AAAAAH
+1680483093
+MemoServ
+LibreIRC/service/MemoServ
+LibreIRC/service/MemoServ
+MemoServ
+0.0.0.0
+1680483093
++Iiko
+Memo Services
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AAACJK 1680554346 jjakob 2a01:260:8028:10f0::62 guest/a385ds.158v.n6po.0260.2a01.IP ~quassel 2a01:260:8028:10f0::62 1680554346 +iwx :jjakob
+900
+UID
+900AAACJK
+1680554346
+jjakob
+2a01:260:8028:10f0::62
+guest/a385ds.158v.n6po.0260.2a01.IP
+~quassel
+2a01:260:8028:10f0::62
+1680554346
++iwx
+jjakob
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAACJK ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AAACJK
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAI 1680483093 NickServ LibreIRC/service/NickServ LibreIRC/service/NickServ NickServ 0.0.0.0 1680483093 +Iiko :Nickname Services
+000
+UID
+000AAAAAI
+1680483093
+NickServ
+LibreIRC/service/NickServ
+LibreIRC/service/NickServ
+NickServ
+0.0.0.0
+1680483093
++Iiko
+Nickname Services
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AACQ8M 1683084555 Test_Relay 066-026-006-176.inf.spectrum.com LibreIRC/staff/hax ~Test_Rela 66.26.6.176 1683084555 +Biow :Test_User's Relay
+900
+UID
+900AACQ8M
+1683084555
+Test_Relay
+066-026-006-176.inf.spectrum.com
+LibreIRC/staff/hax
+~Test_Rela
+66.26.6.176
+1683084555
++Biow
+Test_User's Relay
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900AACQ8M OPERTYPE Admin
+900AACQ8M
+OPERTYPE
+Admin
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACQ8M accountname :*!*@*
+900
+METADATA
+900AACQ8M
+accountname
+*!*@*
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACQ8M ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AACQ8M
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AAAAZV 1680574928 Andrew andrewyu.org LibreIRC/staff/Andrew andrew 2a02:c207:2078:7509::1 1680503392 +iow :andrew
+900
+UID
+900AAAAZV
+1680574928
+Andrew
+andrewyu.org
+LibreIRC/staff/Andrew
+andrew
+2a02:c207:2078:7509::1
+1680503392
++iow
+andrew
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900AAAAZV OPERTYPE Admin
+900AAAAZV
+OPERTYPE
+Admin
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAAAZV accountname :Andrew
+900
+METADATA
+900AAAAZV
+accountname
+Andrew
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAAAZV ssl_cert :vTrse c9c5e208bb4a63f7e7669650803502a82dd38314bba21264d5fbde70aaccac64 CN=Andrew CN=Andrew
+900
+METADATA
+900AAAAZV
+ssl_cert
+vTrse c9c5e208bb4a63f7e7669650803502a82dd38314bba21264d5fbde70aaccac64 CN=Andrew CN=Andrew
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAE 1680483093 GroupServ LibreIRC/service/GroupServ LibreIRC/service/GroupServ GroupServ 0.0.0.0 1680483093 +Iiko :Group Management Services
+000
+UID
+000AAAAAE
+1680483093
+GroupServ
+LibreIRC/service/GroupServ
+LibreIRC/service/GroupServ
+GroupServ
+0.0.0.0
+1680483093
++Iiko
+Group Management Services
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AAABIS 1680518489 f_fasesbot 2001:1600:10:100::4b9 guest/n0228v.knv7.htp2.1600.2001.IP ~f_fasesbo 2001:1600:10:100::4b9 1680518489 +iwx :f_fasesbot
+900
+UID
+900AAABIS
+1680518489
+f_fasesbot
+2001:1600:10:100::4b9
+guest/n0228v.knv7.htp2.1600.2001.IP
+~f_fasesbo
+2001:1600:10:100::4b9
+1680518489
++iwx
+f_fasesbot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAABIS ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AAABIS
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAB 1680483093 ALIS LibreIRC/service/ALIS LibreIRC/service/ALIS alis 0.0.0.0 1680483093 +Iiko :Channel Directory
+000
+UID
+000AAAAAB
+1680483093
+ALIS
+LibreIRC/service/ALIS
+LibreIRC/service/ALIS
+alis
+0.0.0.0
+1680483093
++Iiko
+Channel Directory
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AACQDP 1683059391 lurk 144.172.70.127 LibreIRC/services/lurk lurk 144.172.70.127 1683059390 +iw :lurk
+900
+UID
+900AACQDP
+1683059391
+lurk
+144.172.70.127
+LibreIRC/services/lurk
+lurk
+144.172.70.127
+1683059390
++iw
+lurk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACQDP accountname :lurk
+900
+METADATA
+900AACQDP
+accountname
+lurk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACQDP ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AACQDP
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AAAAAE 1680483104 LibreBot andrewyu.org LibreIRC/services/LibreBot bot 2a02:c207:2078:7509::1 1680483104 +Biw :LibreBot
+900
+UID
+900AAAAAE
+1680483104
+LibreBot
+andrewyu.org
+LibreIRC/services/LibreBot
+bot
+2a02:c207:2078:7509::1
+1680483104
++Biw
+LibreBot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAAAAE accountname :LibreBot
+900
+METADATA
+900AAAAAE
+accountname
+LibreBot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAAAAE ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AAAAAE
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAG 1680483093 InfoServ LibreIRC/service/InfoServ LibreIRC/service/InfoServ InfoServ 0.0.0.0 1680483093 +Iiko :Information Service
+000
+UID
+000AAAAAG
+1680483093
+InfoServ
+LibreIRC/service/InfoServ
+LibreIRC/service/InfoServ
+InfoServ
+0.0.0.0
+1680483093
++Iiko
+Information Service
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAF 1680483093 HostServ LibreIRC/service/GroupServ LibreIRC/service/GroupServ HostServ 0.0.0.0 1680483093 +Iiko :Host Management Services
+000
+UID
+000AAAAAF
+1680483093
+HostServ
+LibreIRC/service/GroupServ
+LibreIRC/service/GroupServ
+HostServ
+0.0.0.0
+1680483093
++Iiko
+Host Management Services
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AAAAAG 1680504503 aohifwueyaiu andrewyu.org LibreIRC/staff/Andrew Andrew 2a02:c207:2078:7509::1 1680483108 +iow :Andrew Yu
+900
+UID
+900AAAAAG
+1680504503
+aohifwueyaiu
+andrewyu.org
+LibreIRC/staff/Andrew
+Andrew
+2a02:c207:2078:7509::1
+1680483108
++iow
+Andrew Yu
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900AAAAAG OPERTYPE Admin
+900AAAAAG
+OPERTYPE
+Admin
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900AAAAAG AWAY 1680483109 :Auto away at Mon Apr 3 00:51:49 2023 UTC
+900AAAAAG
+AWAY
+1680483109
+Auto away at Mon Apr 3 00:51:49 2023 UTC
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAAAAG accountname :Andrew
+900
+METADATA
+900AAAAAG
+accountname
+Andrew
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAAAAG ssl_cert :vTrse c9c5e208bb4a63f7e7669650803502a82dd38314bba21264d5fbde70aaccac64 CN=Andrew CN=Andrew
+900
+METADATA
+900AAAAAG
+ssl_cert
+vTrse c9c5e208bb4a63f7e7669650803502a82dd38314bba21264d5fbde70aaccac64 CN=Andrew CN=Andrew
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AACQ7C 1683083564 LibUser 185.220.101.77 guest/b5m.hcm.220.185.IP ~libuser 185.220.101.77 1683083557 +iwx :LibreIRC User
+900
+UID
+900AACQ7C
+1683083564
+LibUser
+185.220.101.77
+guest/b5m.hcm.220.185.IP
+~libuser
+185.220.101.77
+1683083557
++iwx
+LibreIRC User
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900AACQ7C AWAY 1683083566 :Sleeping
+900AACQ7C
+AWAY
+1683083566
+Sleeping
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AACQ7C ssl_cert :vtrsE No certificate was found.
+900
+METADATA
+900AACQ7C
+ssl_cert
+vtrsE No certificate was found.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAK 1680483093 SaslServ LibreIRC/service/SaslServ LibreIRC/service/SaslServ SaslServ 0.0.0.0 1680483093 +Iiko :SASL Authentication Agent
+000
+UID
+000AAAAAK
+1680483093
+SaslServ
+LibreIRC/service/SaslServ
+LibreIRC/service/SaslServ
+SaslServ
+0.0.0.0
+1680483093
++Iiko
+SASL Authentication Agent
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 UID 000AAAAAD 1680483093 Global LibreIRC/service/Global LibreIRC/service/Global Global 0.0.0.0 1680483093 +Iiko :Network Announcements
+000
+UID
+000AAAAAD
+1680483093
+Global
+LibreIRC/service/Global
+LibreIRC/service/Global
+Global
+0.0.0.0
+1680483093
++Iiko
+Network Announcements
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 UID 900AAAAAM 1680483131 luk3yx andrewyu.org LibreIRC/staff/luk3yx luk3yx 2a02:c207:2078:7509::1 1680483131 +iow :Luke
+900
+UID
+900AAAAAM
+1680483131
+luk3yx
+andrewyu.org
+LibreIRC/staff/luk3yx
+luk3yx
+2a02:c207:2078:7509::1
+1680483131
++iow
+Luke
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900AAAAAM OPERTYPE Admin
+900AAAAAM
+OPERTYPE
+Admin
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAAAAM accountname :luk3yx
+900
+METADATA
+900AAAAAM
+accountname
+luk3yx
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA 900AAAAAM ssl_cert :vTrse 7f724413ceadd1503643106d4bba5b95f7d5529464dab51528ec63eeff085d0e CN=Luke CN=Luke
+900
+METADATA
+900AAAAAM
+ssl_cert
+vTrse 7f724413ceadd1503643106d4bba5b95f7d5529464dab51528ec63eeff085d0e CN=Luke CN=Luke
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #main 1682846956 +Cnt :o,900AACI4F
+900
+FJOIN
+#main
+1682846956
++Cnt
+o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #main maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#main
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN ##chaos 1682846956 +Cnt :o,900AACI4F
+900
+FJOIN
+##chaos
+1682846956
++Cnt
+o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA ##chaos maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+##chaos
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN ###9pfs-bot-debug 1682846956 +Cnt :o,900AACI4F
+900
+FJOIN
+###9pfs-bot-debug
+1682846956
++Cnt
+o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA ###9pfs-bot-debug maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+###9pfs-bot-debug
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #chaos 1682846956 +Cnt :o,000AAAAAC o,900AACI4F
+900
+FJOIN
+#chaos
+1682846956
++Cnt
+o,000AAAAAC o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #chaos 1657179236 hello-smile6 :quiet time
+900
+FTOPIC
+#chaos
+1657179236
+hello-smile6
+quiet time
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #chaos maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#chaos
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #chaos mlock :lL
+900
+METADATA
+#chaos
+mlock
+lL
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #services 1680712623 +Cnt :,900AACI4F
+900
+FJOIN
+#services
+1680712623
++Cnt
+,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #services maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#services
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #fases 1680503394 +Cnt :o,000AAAAAC v,900AAABIS
+900
+FJOIN
+#fases
+1680503394
++Cnt
+o,000AAAAAC v,900AAABIS
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #fases 1679163043 f_ :fases, simple core utilties for a fully functional UNIX-like system -- available in the FSF's software directory <https://directory.fsf.org/wiki/Fases> -- latest POSIX specification <https://pubs.opengroup.org/onlinepubs/9699919799/> -- bridged to Libera.Chat (irc.libera.chat) and XMPP (conference.vitali64.duckdns.org)
+900
+FTOPIC
+#fases
+1679163043
+f_
+fases, simple core utilties for a fully functional UNIX-like system -- available in the FSF's software directory <https://directory.fsf.org/wiki/Fases> -- latest POSIX specification <https://pubs.opengroup.org/onlinepubs/9699919799/> -- bridged to Libera.Chat (irc.libera.chat) and XMPP (conference.vitali64.duckdns.org)
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #fases maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#fases
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #fases 1680503394 +b vernmatrixbridgebot!*@*
+900
+FMODE
+#fases
+1680503394
++b
+vernmatrixbridgebot!*@*
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #fases mlock :ntlk
+900
+METADATA
+#fases
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #evosaur 1661187093 +CPnt :,900AACQ8I ,900AACI4F ,900AAAAAM ,900AAAAZV
+900
+FJOIN
+#evosaur
+1661187093
++CPnt
+,900AACQ8I ,900AACI4F ,900AAAAAM ,900AAAAZV
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #evosaur 1665170638 ay!guy@LibreIRC/staff/Andrew :Evosaur - Sane computing | https://www.evosaur.org/ | https://mail.andrewyu.org/mailman/listinfo/evosaur-general
+900
+FTOPIC
+#evosaur
+1665170638
+ay!guy@LibreIRC/staff/Andrew
+Evosaur - Sane computing | https://www.evosaur.org/ | https://mail.andrewyu.org/mailman/listinfo/evosaur-general
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #evosaur maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#evosaur
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #evosaur mlock :ntCP
+900
+METADATA
+#evosaur
+mlock
+ntCP
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #fsi 1649354236 +CPnt :,900AACQ8I ,900AACM9C o,000AAAAAC o,900AAAAAE ,900AACI4F o,900AACQDP ,900AAAAAM v,900AACQ5M
+900
+FJOIN
+#fsi
+1649354236
++CPnt
+,900AACQ8I ,900AACM9C o,000AAAAAC o,900AAAAAE ,900AACI4F o,900AACQDP ,900AAAAAM v,900AACQ5M
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #fsi 1652857784 Andrew!andrew@LibreIRC/staff/Andrew :Free/Libre Software Introductions
+900
+FTOPIC
+#fsi
+1652857784
+Andrew!andrew@LibreIRC/staff/Andrew
+Free/Libre Software Introductions
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #fsi maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#fsi
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #fsi 1649354236 +bbg _9pfs-bot*!*@* *!*pylink*@guest/*.googleusercontent.com *@everyone*
+900
+FMODE
+#fsi
+1649354236
++bbg
+_9pfs-bot*!*@*
+*!*pylink*@guest/*.googleusercontent.com
+*@everyone*
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #fsi mlock :ntC
+900
+METADATA
+#fsi
+mlock
+ntC
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #LibreIRC-logs 1649353732 +Pinst :ao,900AACQ8I o,000AAAAAG o,000AAAAAF o,000AAAAAE o,000AAAAAD o,000AAAAAC o,000AAAAAB o,000AAAAAH o,000AAAAAI o,000AAAAAJ o,000AAAAAK qo,900AAAAAG ao,900AACI4F ao,900AAAAAM qo,900AAAAZV
+900
+FJOIN
+#LibreIRC-logs
+1649353732
++Pinst
+ao,900AACQ8I o,000AAAAAG o,000AAAAAF o,000AAAAAE o,000AAAAAD o,000AAAAAC o,000AAAAAB o,000AAAAAH o,000AAAAAI o,000AAAAAJ o,000AAAAAK qo,900AAAAAG ao,900AACI4F ao,900AAAAAM qo,900AAAAZV
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #LibreIRC-logs 1653474628 Andrew!andrew@LibreIRC/staff/Andrew :logs
+900
+FTOPIC
+#LibreIRC-logs
+1653474628
+Andrew!andrew@LibreIRC/staff/Andrew
+logs
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #LibreIRC-logs maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#LibreIRC-logs
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #LibreIRC-logs 1649353732 +II R:Noisytoot O:*
+900
+FMODE
+#LibreIRC-logs
+1649353732
++II
+R:Noisytoot
+O:*
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #LibreIRC-logs mlock :ntlk
+900
+METADATA
+#LibreIRC-logs
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #test 1682846956 +Cnt :o,900AACI4F
+900
+FJOIN
+#test
+1682846956
++Cnt
+o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #test maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#test
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #game 1680483109 +Cnt :,900AACQ8I ,900AACI4F ,900AAAAAM
+900
+FJOIN
+#game
+1680483109
++Cnt
+,900AACQ8I ,900AACI4F ,900AAAAAM
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #game maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#game
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #discard 1680826776 +Cnt :,900AACQDP ,900AACQ5M
+900
+FJOIN
+#discard
+1680826776
++Cnt
+,900AACQDP ,900AACQ5M
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #discard maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#discard
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #hackers 1649931400 +CPnt :,900AAAAAE ,900AACI4F
+900
+FJOIN
+#hackers
+1649931400
++CPnt
+,900AAAAAE ,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #hackers 1665328575 Andrew!guy@LibreIRC/staff/Andrew :sus
+900
+FTOPIC
+#hackers
+1665328575
+Andrew!guy@LibreIRC/staff/Andrew
+sus
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #hackers maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#hackers
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #humans 1651372739 +CPnt :,900AACQ8I ,900AACI4F ,900AAAAAM ,900AAB9K2
+900
+FJOIN
+#humans
+1651372739
++CPnt
+,900AACQ8I ,900AACI4F ,900AAAAAM ,900AAB9K2
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #humans 1652503691 Andrew :Humans only | AIs and bots not allowed! | Discussions unrelated to any specific project, or ones about RFDs
+900
+FTOPIC
+#humans
+1652503691
+Andrew
+Humans only | AIs and bots not allowed! | Discussions unrelated to any specific project, or ones about RFDs
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #humans maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#humans
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #humans 1651372739 +be *!*@LibreIRC/services/* R:lurk
+900
+FMODE
+#humans
+1651372739
++be
+*!*@LibreIRC/services/*
+R:lurk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #hawkland 1650912031 +CPnt :,900AACQ8I o,000AAAAAC ,900AACI4F
+900
+FJOIN
+#hawkland
+1650912031
++CPnt
+,900AACQ8I o,000AAAAAC ,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #hawkland 1661834471 Noisytoot!noisytoot@LibreIRC/staff/Noisytoot :Hawk's Minetest Server | hawk.minecity.online:30005
+900
+FTOPIC
+#hawkland
+1661834471
+Noisytoot!noisytoot@LibreIRC/staff/Noisytoot
+Hawk's Minetest Server | hawk.minecity.online:30005
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #hawkland maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#hawkland
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #hawkland 1650912031 +w Y:R:Test_User
+900
+FMODE
+#hawkland
+1650912031
++w
+Y:R:Test_User
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #hawkland mlock :ntlk
+900
+METADATA
+#hawkland
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #LibreIRC 1649353732 +CEPTnrt 3:60 :o,900AACQ8I ,900AACM9C o,000AAAAAC o,900AAAAAE qo,900AAAAAG o,900AACI4F o,900AACQDP ,900AACQ7C o,900AAAAAM qo,900AAAAZV ,900AABYDC ,900AABYBO
+900
+FJOIN
+#LibreIRC
+1649353732
++CEPTnrt
+3:60
+o,900AACQ8I ,900AACM9C o,000AAAAAC o,900AAAAAE qo,900AAAAAG o,900AACI4F o,900AACQDP ,900AACQ7C o,900AAAAAM qo,900AAAAZV ,900AABYDC ,900AABYBO
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #LibreIRC 1666877583 Andrew :AndrewIRC network channel | This network is mainly English and has a bit of Chinese
+900
+FTOPIC
+#LibreIRC
+1666877583
+Andrew
+AndrewIRC network channel | This network is mainly English and has a bit of Chinese
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #LibreIRC maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#LibreIRC
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #LibreIRC 1649353732 +bwwweeeXXI _9pfs-bot-nodejs!~_9pfs@guest/*.googleusercontent.com o:R:Noisytoot o:*!*@LibreIRC/* Y:R:downgrade R:hax R:Noisytoot \Test_User/Libera!*@* flood:v repeat:v R:Noisytoot
+900
+FMODE
+#LibreIRC
+1649353732
++bwwweeeXXI
+_9pfs-bot-nodejs!~_9pfs@guest/*.googleusercontent.com
+o:R:Noisytoot
+o:*!*@LibreIRC/*
+Y:R:downgrade
+R:hax
+R:Noisytoot
+\Test_User/Libera!*@*
+flood:v
+repeat:v
+R:Noisytoot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #LibreIRC mlock :nCP
+900
+METADATA
+#LibreIRC
+mlock
+nCP
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #LibreIRC topiclock :1
+900
+METADATA
+#LibreIRC
+topiclock
+1
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #aviation 1657726600 +CPnt :
+900
+FJOIN
+#aviation
+1657726600
++CPnt
+
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #aviation 1657726626 IDCUser!andrew@LibreIRC/staff/Andrew :What would you do in case of: Airspeed unreliable?
+900
+FTOPIC
+#aviation
+1657726626
+IDCUser!andrew@LibreIRC/staff/Andrew
+What would you do in case of: Airspeed unreliable?
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #aviation maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#aviation
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #vf-technic 1649480682 +CPnt :,900AACQDP Y,900AACQ8M
+900
+FJOIN
+#vf-technic
+1649480682
++CPnt
+,900AACQDP Y,900AACQ8M
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #vf-technic 1655006390 Andrew!andrew@LibreIRC/staff/Andrew :Haxxorland
+900
+FTOPIC
+#vf-technic
+1655006390
+Andrew!andrew@LibreIRC/staff/Andrew
+Haxxorland
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #vf-technic maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#vf-technic
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #vf-technic 1649480682 +w Y:*!*@66.26.6.176
+900
+FMODE
+#vf-technic
+1649480682
++w
+Y:*!*@66.26.6.176
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #LibreIRC-opers 1649353732 +OPint :o,000AAAAAC o,900AACI4F o,900AAAAAM
+900
+FJOIN
+#LibreIRC-opers
+1649353732
++OPint
+o,000AAAAAC o,900AACI4F o,900AAAAAM
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #LibreIRC-opers 1651610632 vitali64!~vitali64@LibreIRC/staff/vitali64 :LibreIRC Operator Discussions
+900
+FTOPIC
+#LibreIRC-opers
+1651610632
+vitali64!~vitali64@LibreIRC/staff/vitali64
+LibreIRC Operator Discussions
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #LibreIRC-opers maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#LibreIRC-opers
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #LibreIRC-opers 1649353732 +II O:* R:Noisytoot
+900
+FMODE
+#LibreIRC-opers
+1649353732
++II
+O:*
+R:Noisytoot
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #LibreIRC-opers mlock :ntlk
+900
+METADATA
+#LibreIRC-opers
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #librespeech 1653494594 +Pt :o,900AACQ8I o,900AACM9C o,000AAAAAC o,900AAAAAE o,900AACI4F o,900AACQDP o,900AACQ7C o,900AAAAAM o,900AAAAZV o,900AABYBO
+900
+FJOIN
+#librespeech
+1653494594
++Pt
+o,900AACQ8I o,900AACM9C o,000AAAAAC o,900AAAAAE o,900AACI4F o,900AACQDP o,900AACQ7C o,900AAAAAM o,900AAAAZV o,900AABYBO
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #librespeech 1682854685 Andrew!andrew@LibreIRC/staff/Andrew :YAY
+900
+FTOPIC
+#librespeech
+1682854685
+Andrew!andrew@LibreIRC/staff/Andrew
+YAY
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #librespeech maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#librespeech
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #librespeech 1653494594 +eeeeeeeegI luk3yx[m]!*@guest/hkda43.cc Rush!*@* MatrixBot!~matrixbot@guest/hkda43.cc *!*@*.xyz *!*@user/_9pfs R:luk3yx *!*@* O:* e *!*@*.noisytoot.org
+900
+FMODE
+#librespeech
+1653494594
++eeeeeeeegI
+luk3yx[m]!*@guest/hkda43.cc
+Rush!*@*
+MatrixBot!~matrixbot@guest/hkda43.cc
+*!*@*.xyz
+*!*@user/_9pfs
+R:luk3yx
+*!*@*
+O:*
+e
+*!*@*.noisytoot.org
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #pissnet 1682846956 +Cnt :o,900AACI4F
+900
+FJOIN
+#pissnet
+1682846956
++Cnt
+o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #pissnet 1649040018 Noisytoot :Welcome to the pissnet autonomous zone
+900
+FTOPIC
+#pissnet
+1649040018
+Noisytoot
+Welcome to the pissnet autonomous zone
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #pissnet maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#pissnet
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #pissnet mlock :ntlk
+900
+METADATA
+#pissnet
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #lurklite 1682846956 +Cnt :o,900AACI4F
+900
+FJOIN
+#lurklite
+1682846956
++Cnt
+o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #lurklite maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#lurklite
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #IDC 1649730748 +CPnt :,900AACQ8I o,000AAAAAC o,900AAAAAE ,900AAAAAG ,900AACI4F o,900AACQDP ,900AAAAAM
+900
+FJOIN
+#IDC
+1649730748
++CPnt
+,900AACQ8I o,000AAAAAC o,900AAAAAE ,900AAAAAG ,900AACI4F o,900AACQDP ,900AAAAAM
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #IDC 1667013277 Andrew :Internet Delay Chat protocol | May see active development in a month or two | https://git.andrewyu.org/andrew/ememo.git/about/
+900
+FTOPIC
+#IDC
+1667013277
+Andrew
+Internet Delay Chat protocol | May see active development in a month or two | https://git.andrewyu.org/andrew/ememo.git/about/
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #IDC maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#IDC
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #IDC 1649730748 +bgg afads!*@* (IRC)?idcbot?has?joined?#IDC ^hi$
+900
+FMODE
+#IDC
+1649730748
++bgg
+afads!*@*
+(IRC)?idcbot?has?joined?#IDC
+^hi$
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #IDC mlock :ntCP
+900
+METADATA
+#IDC
+mlock
+ntCP
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #botwar 1682846956 +Cnt :o,000AAAAAC o,900AACI4F
+900
+FJOIN
+#botwar
+1682846956
++Cnt
+o,000AAAAAC o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #botwar maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#botwar
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #botwar mlock :ntlk
+900
+METADATA
+#botwar
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #spam 1680483105 +Cnt :o,900AAAAAE ,900AACI4F
+900
+FJOIN
+#spam
+1680483105
++Cnt
+o,900AAAAAE ,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #spam maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#spam
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #ykpao 1652768120 +CPint :
+900
+FJOIN
+#ykpao
+1652768120
++CPint
+
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #ykpao 1670135244 Andrew :Economics
+900
+FTOPIC
+#ykpao
+1670135244
+Andrew
+Economics
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #ykpao maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#ykpao
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #ykpao 1652768120 +II *!*@ykpao/* *!*@LibreIRC/staff/Andrew
+900
+FMODE
+#ykpao
+1652768120
++II
+*!*@ykpao/*
+*!*@LibreIRC/staff/Andrew
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #ykpao mlock :nCP
+900
+METADATA
+#ykpao
+mlock
+nCP
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #libresociety 1680483105 +ACQnt :o,000AAAAAC o,900AAAAAE ,900AAAAAG ,900AACI4F
+900
+FJOIN
+#libresociety
+1680483105
++ACQnt
+o,000AAAAAC o,900AAAAAE ,900AAAAAG ,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #libresociety 1647580917 Andrew :https://libre.andrewyu.org | Creating a free (mathematical?) society
+900
+FTOPIC
+#libresociety
+1647580917
+Andrew
+https://libre.andrewyu.org | Creating a free (mathematical?) society
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #libresociety maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#libresociety
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #libresociety mlock :ntAQ
+900
+METADATA
+#libresociety
+mlock
+ntAQ
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #libresociety topiclock :1
+900
+METADATA
+#libresociety
+topiclock
+1
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #school 1680483105 +Cnt :o,900AAAAAE
+900
+FJOIN
+#school
+1680483105
++Cnt
+o,900AAAAAE
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #school maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#school
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #AmongTest 1680483105 +Cnt :o,900AAAAAE o,900AAAAAG ,900AACI4F
+900
+FJOIN
+#AmongTest
+1680483105
++Cnt
+o,900AAAAAE o,900AAAAAG ,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #AmongTest 1652589039 Andrew :Among Test (Among Us 'Clone') Development Channel | Minetest version is at mt.andrewyu.org 30000 | PyGame 2D version is inside a Additive Reconstructer
+900
+FTOPIC
+#AmongTest
+1652589039
+Andrew
+Among Test (Among Us 'Clone') Development Channel | Minetest version is at mt.andrewyu.org 30000 | PyGame 2D version is inside a Additive Reconstructer
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #AmongTest maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#AmongTest
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #AmongTest mlock :ntlk
+900
+METADATA
+#AmongTest
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #:\ 1664085102 +CPinpst :
+900
+FJOIN
+#:\
+1664085102
++CPinpst
+
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #:\ 1664732287 ay!guy@LibreIRC/staff/Andrew :WebView2, basically Microsoft Edge with a chromium engine wrapped around native APIs
+900
+FTOPIC
+#:\
+1664732287
+ay!guy@LibreIRC/staff/Andrew
+WebView2, basically Microsoft Edge with a chromium engine wrapped around native APIs
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #:\ maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#:\
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #LibreIDC 1680483109 +Cnt :o,900AAAAAG ,900AACI4F
+900
+FJOIN
+#LibreIDC
+1680483109
++Cnt
+o,900AAAAAG ,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #LibreIDC maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#LibreIDC
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #os 1680483109 +Cnt :,900AACQ8I o,000AAAAAC ,900AACI4F o,900AACQDP ,900AAAAAM
+900
+FJOIN
+#os
+1680483109
++Cnt
+,900AACQ8I o,000AAAAAC ,900AACI4F o,900AACQDP ,900AAAAAM
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #os 1654274851 Andrew :Operating System Development | https://github.com/cfenollosa/os-tutorial | Better tutorial at https://segmentfault.com/a/1190000040124650 but you can't understand it | http://www.jamesmolloy.co.uk/tutorial_html/ | http://www.mmix.cs.hm.edu/doc/fasc1.pdf | http://harmful.cat-v.org/software/operating-systems/os-suck
+900
+FTOPIC
+#os
+1654274851
+Andrew
+Operating System Development | https://github.com/cfenollosa/os-tutorial | Better tutorial at https://segmentfault.com/a/1190000040124650 but you can't understand it | http://www.jamesmolloy.co.uk/tutorial_html/ | http://www.mmix.cs.hm.edu/doc/fasc1.pdf | http://harmful.cat-v.org/software/operating-systems/os-suck
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #os maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#os
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #os mlock :ntlk
+900
+METADATA
+#os
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN # 1682846956 +Cnt :o,000AAAAAC o,900AACI4F
+900
+FJOIN
+#
+1682846956
++Cnt
+o,000AAAAAC o,900AACI4F
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA # maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA # mlock :ntlk
+900
+METADATA
+#
+mlock
+ntlk
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FJOIN #evosaur-internal 1663661018 +CPint :qo,900AACQ8I ,900AAAAZV
+900
+FJOIN
+#evosaur-internal
+1663661018
++CPint
+qo,900AACQ8I ,900AAAAZV
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FTOPIC #evosaur-internal 1661616956 Andrew :Internal Channel for Evosaur Developers
+900
+FTOPIC
+#evosaur-internal
+1661616956
+Andrew
+Internal Channel for Evosaur Developers
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #evosaur-internal maxlist :b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+900
+METADATA
+#evosaur-internal
+maxlist
+b 1000 w 1000 e 1000 g 1000 X 1000 I 1000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 FMODE #evosaur-internal 1663661018 +II R:Andrew R:*!*@*
+900
+FMODE
+#evosaur-internal
+1663661018
++II
+R:Andrew
+R:*!*@*
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 METADATA #evosaur-internal mlock :intCP
+900
+METADATA
+#evosaur-internal
+mlock
+intCP
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE E *@*.noisytoot.org Noisytoot 1649728931 0 :
+900
+ADDLINE
+E
+*@*.noisytoot.org
+Noisytoot
+1649728931
+0
+
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE E *@/tmp/inspircd.sock Noisytoot 1655054525 0 :localhost
+900
+ADDLINE
+E
+*@/tmp/inspircd.sock
+Noisytoot
+1655054525
+0
+localhost
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE E *@0::1 Noisytoot 1653750410 0 :localhost
+900
+ADDLINE
+E
+*@0::1
+Noisytoot
+1653750410
+0
+localhost
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE E *@2a02:c207:2078:7509::1 Noisytoot 1653748587 0 :andrewyu.org
+900
+ADDLINE
+E
+*@2a02:c207:2078:7509::1
+Noisytoot
+1653748587
+0
+andrewyu.org
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE E *@66.26.6.176 eggman 1658954211 0 :yes
+900
+ADDLINE
+E
+*@66.26.6.176
+eggman
+1658954211
+0
+yes
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE E *@82.17.135.195 Noisytoot 1658954293 0 :
+900
+ADDLINE
+E
+*@82.17.135.195
+Noisytoot
+1658954293
+0
+
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE Q #* Noisytoot 1668541568 0 :Channel names are not valid nicks.
+900
+ADDLINE
+Q
+#*
+Noisytoot
+1668541568
+0
+Channel names are not valid nicks.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE Q *S3rv TronaldDump 1657293178 0 :Impostor
+900
+ADDLINE
+Q
+*S3rv
+TronaldDump
+1657293178
+0
+Impostor
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE Q *Serv Andrew_ 1649921657 0 :Nickname reserved for services
+900
+ADDLINE
+Q
+*Serv
+Andrew_
+1649921657
+0
+Nickname reserved for services
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE Q ? Andrew_ 1649850818 0 :Single letter nicknames are not allowed
+900
+ADDLINE
+Q
+?
+Andrew_
+1649850818
+0
+Single letter nicknames are not allowed
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE SHUN ReplIRC!*@* Noisytoot 1666714980 0 :Spam
+900
+ADDLINE
+SHUN
+ReplIRC!*@*
+Noisytoot
+1666714980
+0
+Spam
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE Z 106.75.173.138 Noisytoot 1666367066 0 :Server link spam. Probably a port scanner.
+900
+ADDLINE
+Z
+106.75.173.138
+Noisytoot
+1666367066
+0
+Server link spam. Probably a port scanner.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE Z 128.14.233.62 Noisytoot 1665533200 0 :Server link spam. Probably a port scanner.
+900
+ADDLINE
+Z
+128.14.233.62
+Noisytoot
+1665533200
+0
+Server link spam. Probably a port scanner.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE Z 152.89.196.211 Noisytoot 1665788124 0 :Server link spam. Probably a port scanner.
+900
+ADDLINE
+Z
+152.89.196.211
+Noisytoot
+1665788124
+0
+Server link spam. Probably a port scanner.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ADDLINE Z 80.85.85.235 Noisytoot 1665788097 0 :Port scanner.
+900
+ADDLINE
+Z
+80.85.85.235
+Noisytoot
+1665788097
+0
+Port scanner.
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 ENDBURST
+900
+ENDBURST
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000AAAAAJ PRIVMSG #LibreIRC-logs :OPER: HaxServ (hax.irc.andrewyu.org)
+000AAAAAJ
+PRIVMSG
+#LibreIRC-logs
+OPER: HaxServ (hax.irc.andrewyu.org)
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 PRIVMSG #LibreIRC-logs :GLOBOPS: From services.irc.andrewyu.org: OPER: HaxServ (hax.irc.andrewyu.org)
+900
+PRIVMSG
+#LibreIRC-logs
+GLOBOPS: From services.irc.andrewyu.org: OPER: HaxServ (hax.irc.andrewyu.org)
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 SNONOTICE g :OPER: HaxServ (hax.irc.andrewyu.org)
+000
+SNONOTICE
+g
+OPER: HaxServ (hax.irc.andrewyu.org)
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000AAAAAC FMODE #librespeech 1653494594 +o :1HC000000
+000AAAAAC
+FMODE
+#librespeech
+1653494594
++o
+1HC000000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000AAAAAC FMODE #IDC 1649730748 +o :1HC000000
+000AAAAAC
+FMODE
+#IDC
+1649730748
++o
+1HC000000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000AAAAAC FMODE #LibreIRC 1649353732 +o :1HC000000
+000AAAAAC
+FMODE
+#LibreIRC
+1649353732
++o
+1HC000000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 FJOIN #ykpao 1652768120 +CPint :o,000AAAAAC
+000
+FJOIN
+#ykpao
+1652768120
++CPint
+o,000AAAAAC
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000AAAAAC FMODE #ykpao 1652768120 +o :1HC000000
+000AAAAAC
+FMODE
+#ykpao
+1652768120
++o
+1HC000000
+WARNING: Command is unknown, ignoring...
+
+Recvd: :900 PRIVMSG #LibreIRC-logs :LINK: Received end of netburst from hax.irc.andrewyu.org (burst time: 198 msecs)
+900
+PRIVMSG
+#LibreIRC-logs
+LINK: Received end of netburst from hax.irc.andrewyu.org (burst time: 198 msecs)
+WARNING: Command is unknown, ignoring...
+
+Recvd: :000 PING 000 :1HC
+000
+PING
+000
+1HC
+
+
+Recvd: :000 METADATA * saslmechlist :PLAIN,AUTHCOOKIE,EXTERNAL,ECDSA-NIST256P-CHALLENGE
+000
+METADATA
+*
+saslmechlist
+PLAIN,AUTHCOOKIE,EXTERNAL,ECDSA-NIST256P-CHALLENGE
+WARNING: Command is unknown, ignoring...
+
diff --git a/table.c b/table.c
new file mode 100644
index 0000000..f663139
--- /dev/null
+++ b/table.c
@@ -0,0 +1,163 @@
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include "types.h"
+#include "table.h"
+
+// currently going with a binary lookup...
+
+static inline int compare(struct string a, struct string b) {
+ size_t len;
+ if (a.len > b.len)
+ len = b.len;
+ else
+ len = a.len;
+
+ int val = memcmp(a.data, b.data, len);
+
+ if (val == 0 && a.len != b.len)
+ return 1;
+ else
+ return val;
+}
+
+static inline uint64_t search(struct table tbl, struct string name, uint8_t *exists) {
+ if (tbl.len == 0)
+ return 0;
+
+ size_t low = 0, high = tbl.len - 1;
+
+ size_t mid = high/2;
+
+ while (low != high) {
+ int val = compare(tbl.array[mid].name, name);
+
+ if (val == 0) {
+ *exists = 1;
+ return mid;
+ } else if (val > 0) {
+ low = mid + 1;
+ if (mid > low)
+ break;
+ if (low > high)
+ low = high;
+ } else {
+ high = mid - 1;
+ if (mid < high)
+ break;
+ if (high < low)
+ high = low;
+ }
+
+ mid = low + ((high-low)/2);
+ }
+
+ int val = compare(tbl.array[mid].name, name);
+ if (val > 0) {
+ *exists = 0;
+ return mid+1;
+ } else if (val == 0) {
+ *exists = 1;
+ return mid;
+ } else {
+ *exists = 0;
+ return mid;
+ }
+}
+
+int set_table_index(struct table *tbl, struct string name, void *ptr) {
+ uint8_t exists, err;
+ uint64_t index = search(*tbl, name, &exists);
+
+ if (index == tbl->len) {
+ void *tmp = realloc(tbl->array, sizeof(*(tbl->array)) * (tbl->len+1));
+ if (tmp == 0)
+ return 1;
+
+ tbl->array = tmp;
+
+ tbl->len++;
+ } else if (!exists) {
+ void *tmp = realloc(tbl->array, sizeof(*(tbl->array)) * (tbl->len+1));
+ if (tmp == 0)
+ return 1;
+
+ tbl->array = tmp;
+
+ memmove(&(tbl->array[index+1]), &(tbl->array[index]), (tbl->len - index) * sizeof(*(tbl->array)));
+ tbl->len++;
+ } else {
+ tbl->array[index].ptr = ptr;
+
+ return 0; // don't overwrite old allocated name
+ }
+
+ char *data = malloc(name.len);
+ if (data == 0)
+ return 1;
+
+ memcpy(data, name.data, name.len);
+
+ tbl->array[index] = (struct table_index){{data, name.len}, ptr};
+
+ return 0;
+}
+
+void * get_table_index(struct table tbl, struct string name) {
+ if (tbl.len == 0)
+ return 0;
+
+ size_t low = 0, high = tbl.len - 1;
+
+ size_t mid = high/2;
+
+ while (low != high) {
+ int val = compare(tbl.array[mid].name, name);
+ if (val == 0) {
+ return tbl.array[mid].ptr;
+ } else if (val > 0) {
+ low = mid + 1;
+ if (mid > low)
+ return 0;
+ if (low > high)
+ low = high;
+ } else {
+ high = mid - 1;
+ if (mid < high)
+ return 0;
+ if (high < low)
+ high = low;
+ }
+
+ mid = low + ((high-low)/2);
+ }
+
+ if (compare(tbl.array[mid].name, name) == 0)
+ return tbl.array[mid].ptr;
+ else
+ return 0;
+}
+
+void * remove_table_index(struct table *tbl, struct string name) {
+ uint8_t exists, err;
+ uint64_t index = search(*tbl, name, &exists);
+
+ if (!exists)
+ return 0;
+
+ void *ptr = tbl->array[index].ptr;
+ free(tbl->array[index].name.data);
+
+ memmove(&(tbl->array[index]), &(tbl->array[index+1]), (tbl->len - index) * sizeof(*(tbl->array)));
+ tbl->len--;
+
+ void *tmp = realloc(tbl->array, sizeof(*(tbl->array)) * tbl->len);
+ if (tmp)
+ tbl->array = tmp;
+ // else: realloc failed on shrinking... so now we have a table that's allocated a bit too big, not much of an issue
+
+ return ptr;
+}
diff --git a/table.h b/table.h
new file mode 100644
index 0000000..af37c7e
--- /dev/null
+++ b/table.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "types.h"
+
+struct table_index {
+ struct string name;
+ void *ptr;
+};
+
+struct table {
+ struct table_index *array;
+ size_t len;
+};
+
+extern int set_table_index(struct table *tbl, struct string name, void *ptr);
+extern void * get_table_index(struct table tbl, struct string name);
+extern void * remove_table_index(struct table *tbl, struct string name); // returns same as get_table_index
diff --git a/tls.c b/tls.c
new file mode 100644
index 0000000..aeb83fb
--- /dev/null
+++ b/tls.c
@@ -0,0 +1,66 @@
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509v3.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include "network.h"
+#include "config.h"
+#include "types.h"
+
+SSL *ssl;
+SSL_CTX *ctx;
+int fd;
+
+int connect_tls(void) {
+ // TODO: free used things on failure
+
+ SSL_library_init();
+ SSL_load_error_strings();
+
+ const SSL_METHOD *method = TLS_client_method();
+ if (method == NULL)
+ return 1;
+
+ ctx = SSL_CTX_new(method);
+ if (ctx == NULL)
+ return 2;
+
+ SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
+
+ int success = SSL_CTX_load_verify_locations(ctx, X509_get_default_cert_file(), NULL);
+ success |= SSL_CTX_load_verify_locations(ctx, NULL, X509_get_default_cert_dir());
+ if (!success)
+ return 3;
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ return 4;
+
+ ssl = SSL_new(ctx);
+ if (ssl == NULL)
+ return 5;
+
+ X509_VERIFY_PARAM *param = SSL_get0_param(ssl);
+ X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_WILDCARDS);
+ if (!X509_VERIFY_PARAM_set1_host(param, address.data, address.len))
+ return 6;
+
+ SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
+
+ struct sockaddr sockaddr;
+ resolve(address.data, port.data, &sockaddr);
+ int ret = connect(fd, &sockaddr, sizeof(sockaddr));
+ if (ret != 0)
+ return 7;
+
+ if (SSL_set_fd(ssl, fd) != 1)
+ return 8;
+
+ ret = SSL_connect(ssl);
+ if (ret != 1)
+ return 9;
+
+ return 0;
+}
diff --git a/tls.h b/tls.h
new file mode 100644
index 0000000..d59ad00
--- /dev/null
+++ b/tls.h
@@ -0,0 +1,7 @@
+#include <openssl/ssl.h>
+
+#define SEND(x) SSL_write(ssl, x.data, x.len)
+
+extern SSL *ssl;
+
+extern int connect_tls(void);
diff --git a/types.h b/types.h
new file mode 100644
index 0000000..0a7e592
--- /dev/null
+++ b/types.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stddef.h>
+#include <unistd.h>
+
+struct string {
+ char *data;
+ size_t len;
+};
+
+#define STRING(x) (struct string){x, sizeof(x)-1}
+#define WRITES(x, y) write(x, y.data, y.len);
diff --git a/utils.c b/utils.c
new file mode 100644
index 0000000..fa97087
--- /dev/null
+++ b/utils.c
@@ -0,0 +1,38 @@
+#include <stdint.h>
+
+#include "types.h"
+
+uint64_t str_to_unsigned(struct string str, uint8_t *err) {
+ if (str.len == 0) {
+ *err = 1;
+ return 0;
+ }
+
+ uint64_t val = 0;
+ while (str.len > 0) {
+ switch(str.data[0]) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ val *= 10;
+ val += str.data[0] - 0x30;
+ break;
+ default:
+ *err = 1;
+ return 0;
+ }
+
+ str.data++;
+ str.len--;
+ }
+
+ *err = 0;
+ return val;
+}
diff --git a/utils.h b/utils.h
new file mode 100644
index 0000000..8451ada
--- /dev/null
+++ b/utils.h
@@ -0,0 +1,4 @@
+#include <stdint.h>
+#include "types.h"
+
+uint64_t str_to_unsigned(struct string str, uint8_t *err);