changeset 1011:a35537c50f09

irccd: hide conn implementation
author David Demelier <markand@malikania.fr>
date Wed, 17 Feb 2021 20:05:00 +0100
parents d1ec9b99b580
children 027ef1952696
files irccd/conf.y irccd/js-plugin.c irccd/jsapi-server.c irccd/peer.c lib/CMakeLists.txt lib/irccd.pc lib/irccd/irccd.c lib/irccd/server.c lib/irccd/server.h plugins/links/links.c tests/test-dl-plugin.c tests/test-plugin-ask.c tests/test-plugin-auth.c tests/test-plugin-hangman.c tests/test-plugin-history.c tests/test-plugin-joke.c tests/test-plugin-plugin.c tests/test-plugin-tictactoe.c
diffstat 18 files changed, 175 insertions(+), 156 deletions(-) [+]
line wrap: on
line diff
--- a/irccd/conf.y	Wed Feb 17 20:01:00 2021 +0100
+++ b/irccd/conf.y	Wed Feb 17 20:05:00 2021 +0100
@@ -21,6 +21,7 @@
 #include <err.h>
 #include <stdio.h>
 #include <string.h>
+#include <stdlib.h>
 
 #include <irccd/compat.h>
 #include <irccd/irccd.h>
--- a/irccd/js-plugin.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/irccd/js-plugin.c	Wed Feb 17 20:05:00 2021 +0100
@@ -16,8 +16,6 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <compat.h>
-
 #include <sys/stat.h>
 #include <assert.h>
 #include <errno.h>
@@ -28,6 +26,8 @@
 #include <unistd.h>
 
 #include <irccd/channel.h>
+#include <irccd/compat.h>
+#include <irccd/config.h>
 #include <irccd/event.h>
 #include <irccd/log.h>
 #include <irccd/plugin.h>
--- a/irccd/jsapi-server.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/irccd/jsapi-server.c	Wed Feb 17 20:05:00 2021 +0100
@@ -21,6 +21,7 @@
 #include <duktape.h>
 
 #include <irccd/channel.h>
+#include <irccd/conn.h>
 #include <irccd/irccd.h>
 #include <irccd/server.h>
 #include <irccd/util.h>
@@ -152,9 +153,9 @@
 	duk_push_object(ctx);
 	duk_push_string(ctx, s->name);
 	duk_put_prop_string(ctx, -2, "name");
-	duk_push_string(ctx, s->conn.hostname);
+	duk_push_string(ctx, s->conn->hostname);
 	duk_put_prop_string(ctx, -2, "hostname");
-	duk_push_uint(ctx, s->conn.port);
+	duk_push_uint(ctx, s->conn->port);
 	duk_put_prop_string(ctx, -2, "port");
 	duk_push_boolean(ctx, s->flags & IRC_SERVER_FLAGS_SSL);
 	duk_put_prop_string(ctx, -2, "ssl");
--- a/irccd/peer.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/irccd/peer.c	Wed Feb 17 20:05:00 2021 +0100
@@ -29,6 +29,7 @@
 #include <assert.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/irccd.h>
 #include <irccd/log.h>
 #include <irccd/server.h>
@@ -750,7 +751,7 @@
 		return errno;
 
 	fprintf(fp, "OK %s\n", s->name);
-	fprintf(fp, "%s %u%s\n", s->conn.hostname, s->conn.port,
+	fprintf(fp, "%s %u%s\n", s->conn->hostname, s->conn->port,
 	    s->flags & IRC_SERVER_FLAGS_SSL ? " ssl" : "");
 	fprintf(fp, "%s %s %s\n", s->ident.nickname, s->ident.username, s->ident.realname);
 
--- a/lib/CMakeLists.txt	Wed Feb 17 20:01:00 2021 +0100
+++ b/lib/CMakeLists.txt	Wed Feb 17 20:05:00 2021 +0100
@@ -23,7 +23,6 @@
 set(
 	HEADERS
 	irccd/channel.h
-	irccd/conn.h
 	irccd/event.h
 	irccd/hook.h
 	irccd/irccd.h
@@ -74,7 +73,6 @@
 	$<BUILD_INTERFACE:${libirccd_BINARY_DIR}/irccd>
 	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
 	$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/irccd/extern>
-	${OPENSSL_INCLUDE_DIR}
 	${COMPAT_INCS}
 )
 
--- a/lib/irccd.pc	Wed Feb 17 20:01:00 2021 +0100
+++ b/lib/irccd.pc	Wed Feb 17 20:05:00 2021 +0100
@@ -1,5 +1,5 @@
 Name: irccd
 Description: Native C interface for irccd plugins
 Version: @IRCCD_VERSION@
-Cflags: -I@CMAKE_INSTALL_FULL_INCLUDEDIR@ -I@CMAKE_INSTALL_FULL_INCLUDEDIR@/irccd/extern -I@OPENSSL_INCLUDE_DIR@
+Cflags: -I@CMAKE_INSTALL_FULL_INCLUDEDIR@ -I@CMAKE_INSTALL_FULL_INCLUDEDIR@/irccd/extern
 Libs: @EXTRA_LIBS@
--- a/lib/irccd/irccd.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/lib/irccd/irccd.c	Wed Feb 17 20:05:00 2021 +0100
@@ -24,6 +24,7 @@
 #include <poll.h>
 #include <signal.h>
 #include <stdlib.h>
+#include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
--- a/lib/irccd/server.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/lib/irccd/server.c	Wed Feb 17 20:05:00 2021 +0100
@@ -34,6 +34,7 @@
 #endif
 
 #include "channel.h"
+#include "conn.h"
 #include "event.h"
 #include "log.h"
 #include "server.h"
@@ -61,7 +62,7 @@
 static inline void
 clear_server(struct irc_server *s)
 {
-	irc_conn_finish(&s->conn);
+	irc_conn_finish(s->conn);
 
 	free(s->bufwhois.nickname);
 	free(s->bufwhois.username);
@@ -697,8 +698,11 @@
 
 	/* Connection. */
 	s = irc_util_calloc(1, sizeof (*s));
-	s->conn.port = port;
-	strlcpy(s->conn.hostname, hostname, sizeof (s->conn.hostname));
+
+	/* Hide implementation to get rid of OpenSSL headers in public API. */
+	s->conn = irc_util_calloc(1, sizeof (*s->conn));
+	s->conn->port = port;
+	strlcpy(s->conn->hostname, hostname, sizeof (s->conn->hostname));
 
 	/* Identity. */
 	strlcpy(s->ident.nickname, nickname, sizeof (s->ident.nickname));
@@ -721,11 +725,11 @@
 	assert(s);
 
 	if (s->flags & IRC_SERVER_FLAGS_SSL)
-		s->conn.flags |= IRC_CONN_SSL;
+		s->conn->flags |= IRC_CONN_SSL;
 
-	s->conn.sv = s;
+	s->conn->sv = s;
 
-	if (irc_conn_connect(&s->conn) < 0)
+	if (irc_conn_connect(s->conn) < 0)
 		fail(s);
 	else
 		s->state = IRC_SERVER_STATE_CONNECTING;
@@ -755,7 +759,7 @@
 	assert(s);
 	assert(pfd);
 
-	irc_conn_prepare(&s->conn, pfd);
+	irc_conn_prepare(s->conn, pfd);
 }
 
 void
@@ -773,7 +777,7 @@
 		if (difftime(time(NULL), s->last_tp) >= TIMEOUT) {
 			irc_log_warn("server %s: no message in more than %u seconds", s->name, TIMEOUT);
 			fail(s);
-		} else if (irc_conn_flush(&s->conn, pfd) < 0) {
+		} else if (irc_conn_flush(s->conn, pfd) < 0) {
 			irc_log_warn("server %s: %s", s->name, strerror(errno));
 			return fail(s);
 		}
@@ -811,7 +815,7 @@
 		return 1;
 	}
 
-	if (irc_conn_poll(&s->conn, &msg))
+	if (irc_conn_poll(s->conn, &msg))
 		return handle(s, ev, &msg), 1;
 
 	return 0;
@@ -850,7 +854,7 @@
 	vsnprintf(buf, sizeof (buf), fmt, ap);
 	va_end(ap);
 
-	return irc_conn_send(&s->conn, buf);
+	return irc_conn_send(s->conn, buf);
 }
 
 int
@@ -1061,5 +1065,6 @@
 	if (--s->refc == 0) {
 		clear_channels(s, 1);
 		free(s);
+		free(s->conn);
 	}
 }
--- a/lib/irccd/server.h	Wed Feb 17 20:01:00 2021 +0100
+++ b/lib/irccd/server.h	Wed Feb 17 20:05:00 2021 +0100
@@ -24,12 +24,12 @@
 #include <time.h>
 
 #include "channel.h"
-#include "conn.h"
 #include "event.h"
 #include "limits.h"
 
 struct pollfd;
 
+struct irc_conn;
 struct irc_channel;
 
 enum irc_server_state {
@@ -88,7 +88,7 @@
 	enum irc_server_flags flags;
 	struct irc_channel_list channels;
 	struct irc_event_whois bufwhois;
-	struct irc_conn conn;
+	struct irc_conn *conn;
 	size_t refc;
 	time_t lost_tp, last_tp;
 	LIST_ENTRY(irc_server) link;
--- a/plugins/links/links.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/plugins/links/links.c	Wed Feb 17 20:05:00 2021 +0100
@@ -20,6 +20,7 @@
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
+#include <pthread.h>
 #include <regex.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -28,6 +29,7 @@
 #include <curl/curl.h>
 
 #include <irccd/compat.h>
+#include <irccd/config.h>
 #include <irccd/irccd.h>
 #include <irccd/limits.h>
 #include <irccd/server.h>
--- a/tests/test-dl-plugin.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/tests/test-dl-plugin.c	Wed Feb 17 20:05:00 2021 +0100
@@ -22,6 +22,7 @@
 #include <greatest.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/dl-plugin.h>
 #include <irccd/event.h>
 #include <irccd/plugin.h>
@@ -131,8 +132,10 @@
 GREATEST_TEST
 calls_simple(void)
 {
+	struct irc_conn conn = {0};
 	struct irc_server server = {
-		.state = IRC_SERVER_STATE_CONNECTED
+		.state = IRC_SERVER_STATE_CONNECTED,
+		.conn = &conn
 	};
 	struct irc_event ev = {
 		.server = &server
@@ -142,7 +145,7 @@
 	irc_plugin_unload(plugin);
 	irc_plugin_reload(plugin);
 	irc_plugin_handle(plugin, &ev);
-	GREATEST_ASSERT_STR_EQ("EVENT\r\n", server.conn.out);
+	GREATEST_ASSERT_STR_EQ("EVENT\r\n", server.conn->out);
 
 	GREATEST_PASS();
 }
--- a/tests/test-plugin-ask.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/tests/test-plugin-ask.c	Wed Feb 17 20:05:00 2021 +0100
@@ -22,6 +22,7 @@
 #include <greatest.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/js-plugin.h>
 #include <irccd/plugin.h>
 #include <irccd/server.h>
@@ -77,12 +78,12 @@
 			}
 		});
 
-		if (strcmp(server->conn.out, "PRIVMSG #test :jean, NO\r\n") == 0)
+		if (strcmp(server->conn->out, "PRIVMSG #test :jean, NO\r\n") == 0)
 			yes = 1;
-		else if (strcmp(server->conn.out, "PRIVMSG #test :jean, YES\r\n") == 0)
+		else if (strcmp(server->conn->out, "PRIVMSG #test :jean, YES\r\n") == 0)
 			no = 1;
 
-		memset(server->conn.out, 0, sizeof (server->conn.out));
+		memset(server->conn->out, 0, sizeof (server->conn->out));
 	}
 
 	GREATEST_ASSERT(no);
--- a/tests/test-plugin-auth.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/tests/test-plugin-auth.c	Wed Feb 17 20:05:00 2021 +0100
@@ -22,6 +22,7 @@
 #include <greatest.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/js-plugin.h>
 #include <irccd/plugin.h>
 #include <irccd/server.h>
@@ -83,7 +84,7 @@
 		.server = servers[0]
 	});
 
-	GREATEST_ASSERT_STR_EQ("PRIVMSG NickServ :identify plopation\r\n", servers[0]->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG NickServ :identify plopation\r\n", servers[0]->conn->out);
 	GREATEST_PASS();
 }
 
@@ -95,7 +96,7 @@
 		.server = servers[1]
 	});
 
-	GREATEST_ASSERT_STR_EQ("PRIVMSG NickServ :identify jean something\r\n", servers[1]->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG NickServ :identify jean something\r\n", servers[1]->conn->out);
 	GREATEST_PASS();
 }
 
@@ -107,7 +108,7 @@
 		.server = servers[2]
 	});
 
-	GREATEST_ASSERT_STR_EQ("PRIVMSG Q@CServe.quakenet.org :AUTH mario hello\r\n", servers[2]->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG Q@CServe.quakenet.org :AUTH mario hello\r\n", servers[2]->conn->out);
 	GREATEST_PASS();
 }
 
--- a/tests/test-plugin-hangman.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/tests/test-plugin-hangman.c	Wed Feb 17 20:05:00 2021 +0100
@@ -22,35 +22,36 @@
 #include <greatest.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/js-plugin.h>
 #include <irccd/log.h>
 #include <irccd/plugin.h>
 #include <irccd/server.h>
 
 #define CALL(t, m) do {                                                 \
-	memset(server->conn.out, 0, sizeof (server->conn.out));         \
-	irc_plugin_handle(plugin, &(const struct irc_event) {           \
-		.type = t,                                              \
-		.server = server,                                       \
-			.message = {                                    \
-			.origin = "jean!jean@localhost",                \
-			.channel = "#hangman",                          \
-			.message = m                                    \
-		}                                                       \
-	});                                                             \
+        memset(server->conn->out, 0, sizeof (server->conn->out));       \
+        irc_plugin_handle(plugin, &(const struct irc_event) {           \
+                .type = t,                                              \
+                .server = server,                                       \
+                        .message = {                                    \
+                        .origin = "jean!jean@localhost",                \
+                        .channel = "#hangman",                          \
+                        .message = m                                    \
+                }                                                       \
+        });                                                             \
 } while (0)
 
 #define CALL_EX(t, o, c, m) do {                                        \
-	memset(server->conn.out, 0, sizeof (server->conn.out));         \
-	irc_plugin_handle(plugin, &(const struct irc_event) {           \
-		.type = t,                                              \
-		.server = server,                                       \
-			.message = {                                    \
-			.origin = o,                                    \
-			.channel = c,                                   \
-			.message = m                                    \
-		}                                                       \
-	});                                                             \
+        memset(server->conn->out, 0, sizeof (server->conn->out));       \
+        irc_plugin_handle(plugin, &(const struct irc_event) {           \
+                .type = t,                                              \
+                .server = server,                                       \
+                        .message = {                                    \
+                        .origin = o,                                    \
+                        .channel = c,                                   \
+                        .message = m                                    \
+                }                                                       \
+        });                                                             \
 } while (0)
 
 static struct irc_server *server;
@@ -99,13 +100,13 @@
 basics_asked(void)
 {
 	CALL(IRC_EVENT_COMMAND, "");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :start=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :start=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ _\r\n", server->conn->out);
 
 	CALL(IRC_EVENT_MESSAGE, "s");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _\r\n", server->conn->out);
 
 	CALL(IRC_EVENT_MESSAGE, "s");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :asked=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :asked=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -123,7 +124,7 @@
 	CALL(IRC_EVENT_MESSAGE, "h");
 	CALL(IRC_EVENT_MESSAGE, "i");
 	CALL(IRC_EVENT_MESSAGE, "j");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :dead=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :dead=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -132,7 +133,7 @@
 {
 	CALL(IRC_EVENT_COMMAND, "");
 	CALL(IRC_EVENT_MESSAGE, "s");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -140,7 +141,7 @@
 basics_start(void)
 {
 	CALL(IRC_EVENT_COMMAND, "");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :start=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :start=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ _\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -151,7 +152,7 @@
 	CALL(IRC_EVENT_MESSAGE, "s");
 	CALL(IRC_EVENT_MESSAGE, "k");
 	CALL(IRC_EVENT_MESSAGE, "y");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :win=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :win=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -160,7 +161,7 @@
 {
 	CALL(IRC_EVENT_COMMAND, "");
 	CALL(IRC_EVENT_COMMAND, "sky");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :win=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :win=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:sky\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -169,7 +170,7 @@
 {
 	CALL(IRC_EVENT_COMMAND, "");
 	CALL(IRC_EVENT_MESSAGE, "x");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :wrong-letter=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:x\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :wrong-letter=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:x\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -178,7 +179,7 @@
 {
 	CALL(IRC_EVENT_COMMAND, "");
 	CALL(IRC_EVENT_COMMAND, "cheese");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :wrong-word=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:cheese\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :wrong-word=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:cheese\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -192,11 +193,11 @@
 
 	/* Forbidden to play twice. */
 	CALL(IRC_EVENT_MESSAGE, "k");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :wrong-player=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:k\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :wrong-player=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:k\r\n", server->conn->out);
 
 	/* Use a different nickname now. */
 	CALL_EX(IRC_EVENT_MESSAGE, "francis!francis@localhost", "#hangman", "k");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:francis!francis@localhost:francis:s k _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:francis!francis@localhost:francis:s k _\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -207,10 +208,10 @@
 	CALL_EX(IRC_EVENT_COMMAND, "jean!jean@localhost", "#hangman", "");
 
 	CALL_EX(IRC_EVENT_MESSAGE, "jean!jean@localhost", "#HANGMAN", "s");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s _ _\r\n", server->conn->out);
 
 	CALL_EX(IRC_EVENT_MESSAGE, "jean!jean@localhost", "#HaNGMaN", "k");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s k _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :found=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:s k _\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -225,16 +226,16 @@
 	irc_plugin_set_option(plugin, "collaborative", "true");
 
 	CALL_EX(IRC_EVENT_COMMAND, "jean!jean@localhost", "t", "");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG jean!jean@localhost :start=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:_ _ _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG jean!jean@localhost :start=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:_ _ _\r\n", server->conn->out);
 
 	CALL_EX(IRC_EVENT_MESSAGE, "jean!jean@localhost", "t", "s");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG jean!jean@localhost :found=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:s _ _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG jean!jean@localhost :found=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:s _ _\r\n", server->conn->out);
 
 	CALL_EX(IRC_EVENT_MESSAGE, "jean!jean@localhost", "t", "k");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG jean!jean@localhost :found=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:s k _\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG jean!jean@localhost :found=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:s k _\r\n", server->conn->out);
 
 	CALL_EX(IRC_EVENT_COMMAND, "jean!jean@localhost", "t", "sky");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG jean!jean@localhost :win=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:sky\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG jean!jean@localhost :win=hangman:!hangman:test:jean!jean@localhost:jean!jean@localhost:jean:sky\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -245,7 +246,7 @@
 	CALL(IRC_EVENT_COMMAND, "");
 	CALL(IRC_EVENT_MESSAGE, "y");
 	CALL(IRC_EVENT_COMMAND, "");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :running=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ y\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #hangman :running=hangman:!hangman:test:#hangman:jean!jean@localhost:jean:_ _ y\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
--- a/tests/test-plugin-history.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/tests/test-plugin-history.c	Wed Feb 17 20:05:00 2021 +0100
@@ -22,35 +22,36 @@
 #include <greatest.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/js-plugin.h>
 #include <irccd/log.h>
 #include <irccd/plugin.h>
 #include <irccd/server.h>
 
 #define CALL(t, m) do {                                                 \
-	memset(server->conn.out, 0, sizeof (server->conn.out));         \
-	irc_plugin_handle(plugin, &(const struct irc_event) {           \
-		.type = t,                                              \
-		.server = server,                                       \
-			.message = {                                    \
-			.origin = "jean!jean@localhost",                \
-			.channel = "#history",                          \
-			.message = m                                    \
-		}                                                       \
-	});                                                             \
+        memset(server->conn->out, 0, sizeof (server->conn->out));       \
+        irc_plugin_handle(plugin, &(const struct irc_event) {           \
+                .type = t,                                              \
+                .server = server,                                       \
+                        .message = {                                    \
+                        .origin = "jean!jean@localhost",                \
+                        .channel = "#history",                          \
+                        .message = m                                    \
+                }                                                       \
+        });                                                             \
 } while (0)
 
 #define CALL_EX(t, o, c, m) do {                                        \
-	memset(server->conn.out, 0, sizeof (server->conn.out));         \
-	irc_plugin_handle(plugin, &(const struct irc_event) {           \
-		.type = t,                                              \
-		.server = server,                                       \
-			.message = {                                    \
-			.origin = o,                                    \
-			.channel = c,                                   \
-			.message = m                                    \
-		}                                                       \
-	});                                                             \
+        memset(server->conn->out, 0, sizeof (server->conn->out));         \
+        irc_plugin_handle(plugin, &(const struct irc_event) {           \
+                .type = t,                                              \
+                .server = server,                                       \
+                        .message = {                                    \
+                        .origin = o,                                    \
+                        .channel = c,                                   \
+                        .message = m                                    \
+                }                                                       \
+        });                                                             \
 } while (0)
 
 static struct irc_server *server;
@@ -96,7 +97,7 @@
 {
 	irc_plugin_set_option(plugin, "file", SOURCE "/data/error.json");
 	CALL(IRC_EVENT_COMMAND, "seen francis");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #history :error=history:!history:test:#history:jean!jean@localhost:jean\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #history :error=history:!history:test:#history:jean!jean@localhost:jean\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -108,7 +109,7 @@
 	CALL_EX(IRC_EVENT_MESSAGE, "jean!jean@localhost", "#history", "hello");
 	CALL_EX(IRC_EVENT_COMMAND, "francis!francis@localhost", "#history", "seen jean");
 
-	GREATEST_ASSERT_EQ(2, sscanf(server->conn.out, "PRIVMSG #history :seen=history:!history:test:#history:francis!francis@localhost:francis:jean:%d:%d\r\n", &d1, &d2));
+	GREATEST_ASSERT_EQ(2, sscanf(server->conn->out, "PRIVMSG #history :seen=history:!history:test:#history:francis!francis@localhost:francis:jean:%d:%d\r\n", &d1, &d2));
 
 	GREATEST_PASS();
 }
@@ -121,7 +122,7 @@
 	CALL_EX(IRC_EVENT_MESSAGE, "jean!jean@localhost", "#history", "hello");
 	CALL_EX(IRC_EVENT_COMMAND, "francis!francis@localhost", "#history", "said jean");
 
-	GREATEST_ASSERT_EQ(2, sscanf(server->conn.out, "PRIVMSG #history :said=history:!history:test:#history:francis!francis@localhost:francis:jean:hello:%d:%d", &d1, &d2));
+	GREATEST_ASSERT_EQ(2, sscanf(server->conn->out, "PRIVMSG #history :said=history:!history:test:#history:francis!francis@localhost:francis:jean:hello:%d:%d", &d1, &d2));
 
 	GREATEST_PASS();
 }
@@ -132,7 +133,7 @@
 	CALL_EX(IRC_EVENT_MESSAGE, "jean!jean@localhost", "#history", "hello");
 	CALL_EX(IRC_EVENT_COMMAND, "francis!francis@localhost", "#history", "said nobody");
 
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #history :unknown=history:!history:test:#history:francis!francis@localhost:francis:nobody\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #history :unknown=history:!history:test:#history:francis!francis@localhost:francis:nobody\r\n", server->conn->out);
 	GREATEST_PASS();
 }
 
@@ -144,10 +145,10 @@
 	CALL_EX(IRC_EVENT_MESSAGE, "JeaN!JeaN@localhost", "#history", "hello");
 
 	CALL_EX(IRC_EVENT_COMMAND, "destructor!dst@localhost", "#HISTORY", "said JEAN");
-	GREATEST_ASSERT_EQ(2, sscanf(server->conn.out, "PRIVMSG #history :said=history:!history:test:#history:destructor!dst@localhost:destructor:jean:hello:%d:%d\r\n", &d1, &d2));
+	GREATEST_ASSERT_EQ(2, sscanf(server->conn->out, "PRIVMSG #history :said=history:!history:test:#history:destructor!dst@localhost:destructor:jean:hello:%d:%d\r\n", &d1, &d2));
 
 	CALL_EX(IRC_EVENT_COMMAND, "destructor!dst@localhost", "#HiSToRy", "said JeaN");
-	GREATEST_ASSERT_EQ(2, sscanf(server->conn.out, "PRIVMSG #history :said=history:!history:test:#history:destructor!dst@localhost:destructor:jean:hello:%d:%d\r\n", &d1, &d2));
+	GREATEST_ASSERT_EQ(2, sscanf(server->conn->out, "PRIVMSG #history :said=history:!history:test:#history:destructor!dst@localhost:destructor:jean:hello:%d:%d\r\n", &d1, &d2));
 
 	GREATEST_PASS();
 }
--- a/tests/test-plugin-joke.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/tests/test-plugin-joke.c	Wed Feb 17 20:05:00 2021 +0100
@@ -22,21 +22,22 @@
 #include <greatest.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/js-plugin.h>
 #include <irccd/plugin.h>
 #include <irccd/server.h>
 
 #define CALL() do {                                                     \
-	memset(server->conn.out, 0, sizeof (server->conn.out));         \
-	irc_plugin_handle(plugin, &(const struct irc_event) {           \
-		.type = IRC_EVENT_COMMAND,                              \
-		.server = server,                                       \
-			.message = {                                    \
-			.origin = "jean!jean@localhost",                \
-			.channel = "#joke",                             \
-			.message = ""                                   \
-		}                                                       \
-	});                                                             \
+        memset(server->conn->out, 0, sizeof (server->conn->out));       \
+        irc_plugin_handle(plugin, &(const struct irc_event) {           \
+                .type = IRC_EVENT_COMMAND,                              \
+                .server = server,                                       \
+                        .message = {                                    \
+                        .origin = "jean!jean@localhost",                \
+                        .channel = "#joke",                             \
+                        .message = ""                                   \
+                }                                                       \
+        });                                                             \
 } while (0)
 
 static struct irc_server *server;
@@ -90,9 +91,9 @@
 	for (int i = 0; i < 2; ++i) {
 		CALL();
 
-		if (strcmp(server->conn.out, "PRIVMSG #joke :aaa\r\n") == 0)
+		if (strcmp(server->conn->out, "PRIVMSG #joke :aaa\r\n") == 0)
 			aaa = 1;
-		else if (strcmp(server->conn.out, "PRIVMSG #joke :bbbb\r\nPRIVMSG #joke :bbbb\r\n") == 0)
+		else if (strcmp(server->conn->out, "PRIVMSG #joke :bbbb\r\nPRIVMSG #joke :bbbb\r\n") == 0)
 			bbbb = 1;
 	}
 
@@ -114,7 +115,7 @@
 
 	for (int i = 0; i < 64; ++i) {
 		CALL();
-		GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :a\r\n", server->conn.out);
+		GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :a\r\n", server->conn->out);
 	}
 
 	GREATEST_PASS();
@@ -129,7 +130,7 @@
 
 	for (int i = 0; i < 64; ++i) {
 		CALL();
-		GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :a\r\n", server->conn.out);
+		GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :a\r\n", server->conn->out);
 	}
 
 	GREATEST_PASS();
@@ -141,7 +142,7 @@
 	irc_plugin_set_option(plugin, "file", "doesnotexist.json");
 
 	CALL();
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :error=joke:!joke:test:#joke:jean!jean@localhost:jean\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :error=joke:!joke:test:#joke:jean!jean@localhost:jean\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -152,7 +153,7 @@
 	irc_plugin_set_option(plugin, "file", SOURCE "/data/joke/error-not-array.json");
 
 	CALL();
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :error=joke:!joke:test:#joke:jean!jean@localhost:jean\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :error=joke:!joke:test:#joke:jean!jean@localhost:jean\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -163,7 +164,7 @@
 	irc_plugin_set_option(plugin, "file", SOURCE "/data/joke/error-empty.json");
 
 	CALL();
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :error=joke:!joke:test:#joke:jean!jean@localhost:jean\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #joke :error=joke:!joke:test:#joke:jean!jean@localhost:jean\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
--- a/tests/test-plugin-plugin.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/tests/test-plugin-plugin.c	Wed Feb 17 20:05:00 2021 +0100
@@ -23,6 +23,7 @@
 #include <greatest.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/irccd.h>
 #include <irccd/js-plugin.h>
 #include <irccd/log.h>
@@ -31,16 +32,16 @@
 #include <irccd/util.h>
 
 #define CALL(t, m) do {                                                 \
-	memset(server->conn.out, 0, sizeof (server->conn.out));         \
-	irc_plugin_handle(plugin, &(const struct irc_event) {           \
-		.type = t,                                              \
-		.server = server,                                       \
-			.message = {                                    \
-			.origin = "jean!jean@localhost",                \
-			.channel = "#plugin",                           \
-			.message = m                                    \
-		}                                                       \
-	});                                                             \
+        memset(server->conn->out, 0, sizeof (server->conn->out));       \
+        irc_plugin_handle(plugin, &(const struct irc_event) {           \
+                .type = t,                                              \
+                .server = server,                                       \
+                        .message = {                                    \
+                        .origin = "jean!jean@localhost",                \
+                        .channel = "#plugin",                           \
+                        .message = m                                    \
+                }                                                       \
+        });                                                             \
 } while (0)
 
 static struct irc_server *server;
@@ -105,13 +106,13 @@
 basics_usage(void)
 {
 	CALL(IRC_EVENT_COMMAND, "");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :usage=plugin:!plugin:test:#plugin:jean!jean@localhost:jean\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :usage=plugin:!plugin:test:#plugin:jean!jean@localhost:jean\r\n", server->conn->out);
 
 	CALL(IRC_EVENT_COMMAND, "fail");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :usage=plugin:!plugin:test:#plugin:jean!jean@localhost:jean\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :usage=plugin:!plugin:test:#plugin:jean!jean@localhost:jean\r\n", server->conn->out);
 
 	CALL(IRC_EVENT_COMMAND, "info");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :usage=plugin:!plugin:test:#plugin:jean!jean@localhost:jean\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :usage=plugin:!plugin:test:#plugin:jean!jean@localhost:jean\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -120,7 +121,7 @@
 basics_info(void)
 {
 	CALL(IRC_EVENT_COMMAND, "info fake");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :info=plugin:!plugin:test:#plugin:jean!jean@localhost:jean:David:BEER:fake:Fake White Beer 2000:0.0.0.0.0.0.1\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :info=plugin:!plugin:test:#plugin:jean!jean@localhost:jean:David:BEER:fake:Fake White Beer 2000:0.0.0.0.0.0.1\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -129,7 +130,7 @@
 basics_not_found(void)
 {
 	CALL(IRC_EVENT_COMMAND, "info doesnotexist");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :not-found=plugin:!plugin:test:#plugin:jean!jean@localhost:jean:doesnotexist\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :not-found=plugin:!plugin:test:#plugin:jean!jean@localhost:jean:doesnotexist\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -141,7 +142,7 @@
 		irc_bot_plugin_add(fake_new(i));
 
 	CALL(IRC_EVENT_COMMAND, "list");
-	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :too-long=plugin:!plugin:test:#plugin:jean!jean@localhost:jean\r\n", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("PRIVMSG #plugin :too-long=plugin:!plugin:test:#plugin:jean!jean@localhost:jean\r\n", server->conn->out);
 
 	GREATEST_PASS();
 }
--- a/tests/test-plugin-tictactoe.c	Wed Feb 17 20:01:00 2021 +0100
+++ b/tests/test-plugin-tictactoe.c	Wed Feb 17 20:05:00 2021 +0100
@@ -22,6 +22,7 @@
 #include <greatest.h>
 
 #include <irccd/compat.h>
+#include <irccd/conn.h>
 #include <irccd/irccd.h>
 #include <irccd/js-plugin.h>
 #include <irccd/log.h>
@@ -30,29 +31,29 @@
 #include <irccd/util.h>
 
 #define CALL(t, m) do {                                                 \
-	memset(server->conn.out, 0, sizeof (server->conn.out));         \
-	irc_plugin_handle(plugin, &(const struct irc_event) {           \
-		.type = t,                                              \
-		.server = server,                                       \
-			.message = {                                    \
-			.origin = "jean!jean@localhost",                \
-			.channel = "#hangman",                          \
-			.message = m                                    \
-		}                                                       \
-	});                                                             \
+        memset(server->conn->out, 0, sizeof (server->conn->out));       \
+        irc_plugin_handle(plugin, &(const struct irc_event) {           \
+                .type = t,                                              \
+                .server = server,                                       \
+                        .message = {                                    \
+                        .origin = "jean!jean@localhost",                \
+                        .channel = "#hangman",                          \
+                        .message = m                                    \
+                }                                                       \
+        });                                                             \
 } while (0)
 
 #define CALL_EX(t, o, c, m) do {                                        \
-	memset(server->conn.out, 0, sizeof (server->conn.out));         \
-	irc_plugin_handle(plugin, &(const struct irc_event) {           \
-		.type = t,                                              \
-		.server = server,                                       \
-			.message = {                                    \
-			.origin = o,                                    \
-			.channel = c,                                   \
-			.message = m                                    \
-		}                                                       \
-	});                                                             \
+        memset(server->conn->out, 0, sizeof (server->conn->out));       \
+        irc_plugin_handle(plugin, &(const struct irc_event) {           \
+                .type = t,                                              \
+                .server = server,                                       \
+                        .message = {                                    \
+                        .origin = o,                                    \
+                        .channel = c,                                   \
+                        .message = m                                    \
+                }                                                       \
+        });                                                             \
 } while (0)
 
 static struct irc_server *server;
@@ -104,7 +105,7 @@
 	char player = 0, *buf;
 
 	/* We need to skip 4 lines.*/
-	buf = irc_util_strdup(server->conn.out);
+	buf = irc_util_strdup(server->conn->out);
 	irc_util_split(buf, lines, 5, '\n');
 
 	if (!lines[4] || sscanf(lines[4], "PRIVMSG #tictactoe :turn=#tictactoe:!tictactoe:%c:tictactoe:test\r\n", &player) != 1)
@@ -137,7 +138,7 @@
 	play("b2");
 	play("a3");
 
-	GREATEST_ASSERT_EQ(5U, irc_util_split(server->conn.out, lines, 5, '\n'));
+	GREATEST_ASSERT_EQ(5U, irc_util_split(server->conn->out, lines, 5, '\n'));
 	GREATEST_ASSERT_EQ(0, sscanf(lines[0], "PRIVMSG #tictactoe :  a b c\r"));
 	GREATEST_ASSERT_EQ(2, sscanf(lines[1], "PRIVMSG #tictactoe :1 %c %c .\r", &k1, &k2));
 	GREATEST_ASSERT_EQ(2, sscanf(lines[2], "PRIVMSG #tictactoe :2 %c %c .\r", &k1, &k2));
@@ -171,7 +172,7 @@
 	play("a 1");
 	play("b 1");
 
-	GREATEST_ASSERT_EQ(5U, irc_util_split(server->conn.out, lines, 5, '\n'));
+	GREATEST_ASSERT_EQ(5U, irc_util_split(server->conn->out, lines, 5, '\n'));
 	GREATEST_ASSERT_EQ(0, sscanf(lines[0], "PRIVMSG #tictactoe :  a b c\r"));
 	GREATEST_ASSERT_EQ(3, sscanf(lines[1], "PRIVMSG #tictactoe :1 %c %c %c\r", &k1, &k2, &k3));
 	GREATEST_ASSERT_EQ(3, sscanf(lines[2], "PRIVMSG #tictactoe :2 %c %c %c\r", &k1, &k2, &k3));
@@ -191,7 +192,7 @@
 	play("a 1");
 	play("a 1");
 
-	GREATEST_ASSERT_EQ(2, sscanf(server->conn.out, "PRIVMSG #tictactoe :used=#tictactoe:!tictactoe:%c:%c:tictactoe:test\r\n", &k1, &k2));
+	GREATEST_ASSERT_EQ(2, sscanf(server->conn->out, "PRIVMSG #tictactoe :used=#tictactoe:!tictactoe:%c:%c:tictactoe:test\r\n", &k1, &k2));
 
 	GREATEST_PASS();
 }
@@ -203,15 +204,15 @@
 
 	/* Player select itself. */
 	CALL_EX(IRC_EVENT_COMMAND, "a", "#tictactoe", "a");
-	GREATEST_ASSERT_EQ(2, sscanf(server->conn.out, "PRIVMSG #tictactoe :invalid=#tictactoe:!tictactoe:%c:%c:tictactoe:test\r\n", &k1, &k2));
+	GREATEST_ASSERT_EQ(2, sscanf(server->conn->out, "PRIVMSG #tictactoe :invalid=#tictactoe:!tictactoe:%c:%c:tictactoe:test\r\n", &k1, &k2));
 
 	/* Player select the bot. */
 	CALL_EX(IRC_EVENT_COMMAND, "a", "#tictactoe", "t");
-	GREATEST_ASSERT_EQ(2, sscanf(server->conn.out, "PRIVMSG #tictactoe :invalid=#tictactoe:!tictactoe:%c:%c:tictactoe:test\r\n", &k1, &k2));
+	GREATEST_ASSERT_EQ(2, sscanf(server->conn->out, "PRIVMSG #tictactoe :invalid=#tictactoe:!tictactoe:%c:%c:tictactoe:test\r\n", &k1, &k2));
 
 	/* Someone not on the channel. */
 	CALL_EX(IRC_EVENT_COMMAND, "a", "#tictactoe", "jean");
-	GREATEST_ASSERT_EQ(2, sscanf(server->conn.out, "PRIVMSG #tictactoe :invalid=#tictactoe:!tictactoe:%c:%c:tictactoe:test\r\n", &k1, &k2));
+	GREATEST_ASSERT_EQ(2, sscanf(server->conn->out, "PRIVMSG #tictactoe :invalid=#tictactoe:!tictactoe:%c:%c:tictactoe:test\r\n", &k1, &k2));
 
 	GREATEST_PASS();
 }
@@ -260,7 +261,7 @@
 	});
 
 	play("a 1");
-	GREATEST_ASSERT_STR_EQ("", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -282,7 +283,7 @@
 	});
 
 	play("a 1");
-	GREATEST_ASSERT_STR_EQ("", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("", server->conn->out);
 
 	GREATEST_PASS();
 }
@@ -303,7 +304,7 @@
 	});
 
 	play("a 1");
-	GREATEST_ASSERT_STR_EQ("", server->conn.out);
+	GREATEST_ASSERT_STR_EQ("", server->conn->out);
 
 	GREATEST_PASS();
 }