summary refs log tree commit diff
diff options
context:
space:
mode:
authorNakidai <nakidai@disroot.org>2026-02-04 12:21:47 +0300
committerNakidai <nakidai@disroot.org>2026-02-04 12:21:47 +0300
commitce0f41da41a2d97d4a8e7159747464ecef72de4c (patch)
tree7e09329f15932710eb9fc14d18053c812c55515f
parent58a7e4a4d7449d58732e024ea07ea3623c10d8de (diff)
downloadlibreircd-ce0f41da41a2d97d4a8e7159747464ecef72de4c.tar.gz
libreircd-ce0f41da41a2d97d4a8e7159747464ecef72de4c.zip
Add WHOIS and config parsing
Now users can query each other

Plus, server info and creation date can be changed without recompiling
using configuration file in IRC message format which is loaded on
startup

TODO: since readcfg uses IRC logic, handle() now should be able to deal
with commands ending with simply \n
-rw-r--r--Makefile1
-rw-r--r--config.c73
-rw-r--r--config.h15
-rw-r--r--config.irc2
-rw-r--r--handle.c64
-rw-r--r--ircd.h6
-rw-r--r--main.c15
-rw-r--r--reply.c40
8 files changed, 205 insertions, 11 deletions
diff --git a/Makefile b/Makefile
index e70390f..b9d6948 100644
--- a/Makefile
+++ b/Makefile
@@ -3,6 +3,7 @@ NAME ?= ircd
 CFLAGS += -Wall -Wextra
 
 OBJS += channel.o
+OBJS += config.o
 OBJS += handle.o
 OBJS += loop.o
 OBJS += main.o
diff --git a/config.c b/config.c
new file mode 100644
index 0000000..9d945eb
--- /dev/null
+++ b/config.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2026 Nakidai Perumenei <nakidai at disroot dot org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "ircd.h"
+
+#include <string.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+char creation[CREATION_MAX] = CREATION_DATE;
+char info[INFO_MAX] = INFO;
+
+int
+readcfg(const char *path)
+{
+	struct Peer peer;
+	ssize_t recvd;
+	int fd, ret;
+
+	fd = open(path, O_RDONLY);
+	ensure(fd != -1, warn("open(%s)", path), 0);
+
+	peer = (struct Peer){
+		.fd = STDERR_FILENO,
+		.type = CONFIG,
+	};
+	strlcpy(peer.nick, path, sizeof(peer.nick));
+
+	for (ret = 0;;)
+	{
+		recvd = read(
+			fd,
+			peer.buf + peer.recvd,
+			sizeof(peer.buf) - peer.recvd
+		);
+		peer.recvd += recvd;
+		if (recvd == -1)
+		{
+			warn("read(%s)", path);
+			++ret;
+			goto end;
+		} else if (!recvd)
+		{
+			goto end;
+		} else if (handle(&peer))
+		{
+			++ret;
+			goto end;
+		}
+	}
+end:
+	close(fd);
+	return ret;
+}
diff --git a/config.h b/config.h
index 394ac70..dce385b 100644
--- a/config.h
+++ b/config.h
@@ -15,19 +15,22 @@
  */
 
 #define CREATION_DATE "Thu Jan 1 1970 at 00:00:00 UTC"
+#define INFO "The best IRC server ever \\o/"
 #define IRCD_VERSION "0.1.0"
 
-#define PARAM_MAX 15
+#define INFO_MAX 64
+#define CREATION_MAX 64
 
+#define PARAM_MAX 15
 #define MESSAGE_MAX 512
-#define PEERS_MAX 512
-#define CHANNELS_MAX 512
+#define PEERS_MAX 100
+#define CHANNELS_MAX 100
 
-#define CHANNEL_USERS_MAX 512
-#define CHANNEL_MODES_MAX 512
+#define CHANNEL_USERS_MAX 100
+#define CHANNEL_MODES_MAX 50
 #define CHANNEL_NAME_MAX 64
 
-#define PEER_CHANNELS_MAX 32
+#define PEER_CHANNELS_MAX 10
 #define PEER_NICK_MAX 16
 #define PEER_USER_MAX 16
 #define PEER_REAL_MAX 32
diff --git a/config.irc b/config.irc
new file mode 100644
index 0000000..9902de4
--- /dev/null
+++ b/config.irc
@@ -0,0 +1,2 @@
+setcreation :Thu Jan 1 1970 at 00:00:00 UTC

+setinfo :The best IRC server ever \o/

diff --git a/handle.c b/handle.c
index 8e91404..9e8cb5f 100644
--- a/handle.c
+++ b/handle.c
@@ -282,6 +282,28 @@ quit(struct Message *msg, struct Peer *peer)
 }
 
 static int
+setcreation(struct Message *msg, struct Peer *peer)
+{
+	ensure(peer->type == CONFIG, reply(peer, 481, "You're not a config file"), 0);
+	ensure(msg->params[0] && *msg->params[0], reply(peer, 461), 0);
+
+	strlcpy(creation, msg->params[0], sizeof(creation));
+
+	return 0;
+}
+
+static int
+setinfo(struct Message *msg, struct Peer *peer)
+{
+	ensure(peer->type == CONFIG, reply(peer, 481, "You're not a config file"), 0);
+	ensure(msg->params[0] && *msg->params[0], reply(peer, 461), 0);
+
+	strlcpy(info, msg->params[0], sizeof(info));
+
+	return 0;
+}
+
+static int
 user(struct Message *msg, struct Peer *peer)
 {
 	size_t i;
@@ -304,6 +326,45 @@ user(struct Message *msg, struct Peer *peer)
 }
 
 static int
+whois(struct Message *msg, struct Peer *peer)
+{
+	size_t i;
+
+	ensure(peer->type, reply(peer, 451), 0);
+	ensure(msg->params[0] && *msg->params[0], reply(peer, 431), 0);
+
+	for (i = 0; i < peers_c; ++i)
+		if (!strcmp(msg->params[0], peers[i].nick))
+			break;
+	ensure(i != peers_c, reply(peer, 401, msg->params[0]), 0);
+
+	reply(
+		peer,
+		311,
+		peers[i].nick,
+		peers[i].user,
+		peers[i].host,
+		peers[i].real
+	);
+	reply(
+		peer,
+		312,
+		peers[i].nick,
+		hostname,
+		info
+	);
+	if (peers[i].modes & OPER)
+		reply(
+			peer,
+			313,
+			peers[i].nick
+		);
+	reply(peer, 318, peer[i].nick);
+
+	return 0;
+}
+
+static int
 default_handler(struct Message *msg, struct Peer *peer)
 {
 	ensure(peer->type, (void)0, 0);
@@ -326,7 +387,10 @@ static struct Handler {
 	{ "pong", pong },
 	{ "privmsg", privmsg },
 	{ "quit", quit },
+	{ "setcreation", setcreation },
+	{ "setinfo", setinfo },
 	{ "user", user },
+	{ "whois", whois },
 };
 
 Handler *
diff --git a/ircd.h b/ircd.h
index 0cde9e0..1f5bba8 100644
--- a/ircd.h
+++ b/ircd.h
@@ -38,7 +38,7 @@ struct Message
 struct Peer
 {
 	int fd;
-	enum ClientType { UNREGD, CLIENT, SERVER, SERVICE } type;
+	enum ClientType { UNREGD, CLIENT, CONFIG, SERVER, SERVICE } type;
 	char nick[PEER_NICK_MAX], user[PEER_USER_MAX], real[PEER_REAL_MAX], host[PEER_HOST_MAX];
 	struct Channel *channels[PEER_CHANNELS_MAX];
 	enum PeerStatus {
@@ -80,6 +80,10 @@ extern size_t channels_c, peers_c;
 extern const char *hostname;
 extern const char *host;
 extern unsigned long port;
+extern char creation[CREATION_MAX];
+extern char info[INFO_MAX];
+
+int readcfg(const char *path);
 
 const char *getnick(const struct Peer *peer);
 enum PeerMode user_mode(char m);
diff --git a/main.c b/main.c
index 324309f..3c364a1 100644
--- a/main.c
+++ b/main.c
@@ -37,7 +37,7 @@ main(int argc, char **argv)
 
 	for (i = 1; i < 4; ++i)
 		if (!argv[i] || !*argv[i])
-			errx(1, "usage: %s hostname bindaddr port", argv[0]);
+			errx(1, "usage: %s hostname bindaddr port [config]", argv[0]);
 	hostname = argv[1];
 	host = argv[2];
 	port = strtoul(argv[3], &p, 10);
@@ -45,9 +45,20 @@ main(int argc, char **argv)
 		errx(1, "invalid port");
 
 #ifdef __OpenBSD__
-	if (pledge("stdio inet", ""))
+	if (argv[4] && unveil(argv[4], "r"))
+		err(1, "unveil()");
+	if (pledge("stdio inet rpath unveil", ""))
 		err(1, "pledge()");
 #endif /* __OpenBSD__ */
 
+	if (argv[4])
+		readcfg(argv[4]);
+
+#ifdef __OpenBSD__
+	if (argv[4] && (unveil(argv[4], "") || unveil(NULL, NULL)))
+		err(1, "unveil()");
+	if (pledge("stdio inet", ""))
+		err(1, "pledge()");
+#endif /* __OpenBSD__ */
 	ircd();
 }
diff --git a/reply.c b/reply.c
index 2cd8c04..a295da8 100644
--- a/reply.c
+++ b/reply.c
@@ -81,9 +81,10 @@ vreply(const struct Peer *peer, int number, va_list ap)
 		hostname
 	), _);
 	REPLY(3, WRITE(
-		":%s 003 %s :This server was created " CREATION_DATE,
+		":%s 003 %s :This server was created %s",
 		hostname,
-		getnick(peer)
+		getnick(peer),
+		creation
 	), _);
 	REPLY(4, WRITE(
 		":%s 004 %s :%s libreircd-" IRCD_VERSION " aiwroOs :",
@@ -103,6 +104,35 @@ vreply(const struct Peer *peer, int number, va_list ap)
 		peer->modes & LOCALOPER ? "O" : "",
 		peer->modes & SNOTICE ? "s" : ""
 	), _);
+	REPLY(311, WRITE(
+		":%s 311 %s %s %s %s * :%s",
+		hostname,
+		getnick(peer),
+		nick,
+		user,
+		host,
+		real
+	), nick, user, host, real, _);
+	REPLY(312, WRITE(
+		":%s 312 %s %s %s :%s",
+		hostname,
+		getnick(peer),
+		nick,
+		server,
+		info
+	), nick, server, info, _);
+	REPLY(313, WRITE(
+		":%s 313 %s %s :is an IRC operator",
+		hostname,
+		getnick(peer),
+		nick
+	), nick, _);
+	REPLY(318, WRITE(
+		":%s 318 %s %s :End of WHOIS list",
+		hostname,
+		getnick(peer),
+		nick
+	), nick, _);
 	REPLY(401, WRITE(
 		":%s 401 %s %s :No such nick/channel",
 		hostname,
@@ -187,6 +217,12 @@ vreply(const struct Peer *peer, int number, va_list ap)
 		getnick(peer),
 		channel
 	), channel, _);
+	REPLY(481, WRITE(
+		":%s 481 %s :Permission Denied- %s",
+		hostname,
+		getnick(peer),
+		reason
+	), reason, _);
 	REPLY(484, WRITE(
 		":%s 484 %s :Your connection is restricted!",
 		hostname,