Mercurial > irccd
changeset 945:8ddeceeee0f2
irccd: add channel.h functions
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 15 Jan 2021 14:18:54 +0100 |
parents | d63a360811dd |
children | 2ec05b9db2ee |
files | .hgignore Makefile irccd/main.c lib/irccd/channel.c lib/irccd/channel.h lib/irccd/jsapi-server.c lib/irccd/server.c lib/irccd/server.h lib/irccd/set.h tests/test-channel.c |
diffstat | 10 files changed, 404 insertions(+), 77 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Fri Jan 15 10:38:45 2021 +0100 +++ b/.hgignore Fri Jan 15 14:18:54 2021 +0100 @@ -30,6 +30,7 @@ \.dylib$ # tests. +^tests/test-channel$ ^tests/test-dl-plugin$ ^tests/test-log$ ^tests/test-rule$
--- a/Makefile Fri Jan 15 10:38:45 2021 +0100 +++ b/Makefile Fri Jan 15 14:18:54 2021 +0100 @@ -42,7 +42,8 @@ endif LIBIRCCD= lib/libirccd.a -LIBIRCCD_SRCS= lib/irccd/dl-plugin.c +LIBIRCCD_SRCS= lib/irccd/channel.c +LIBIRCCD_SRCS+= lib/irccd/dl-plugin.c LIBIRCCD_SRCS+= lib/irccd/irccd.c LIBIRCCD_SRCS+= lib/irccd/log.c LIBIRCCD_SRCS+= lib/irccd/peer.c @@ -70,7 +71,8 @@ LIBIRCCD_OBJS= ${LIBIRCCD_SRCS:.c=.o} LIBIRCCD_DEPS= ${LIBIRCCD_SRCS:.c=.d} -TESTS= tests/test-dl-plugin.c +TESTS= tests/test-channel.c +TESTS+= tests/test-dl-plugin.c TESTS+= tests/test-log.c TESTS+= tests/test-rule.c TESTS+= tests/test-subst.c
--- a/irccd/main.c Fri Jan 15 10:38:45 2021 +0100 +++ b/irccd/main.c Fri Jan 15 14:18:54 2021 +0100 @@ -19,57 +19,26 @@ #include <stdio.h> #include <err.h> -#include <irccd/event.h> #include <irccd/irccd.h> -#include <irccd/js-plugin.h> -#include <irccd/plugin.h> #include <irccd/log.h> #include <irccd/server.h> -#include <irccd/transport.h> - -#include <irccd/rule.h> - -static struct irc_plugin js = { - .name = "example" -}; - -#include <string.h> - -static void -dump(void) -{ - for (size_t i = 0; i < irc.rulesz; ++i) { - printf("== rule %zd ==\n", i); - printf("servers => %s\n", irc.rules[i].servers); - } -} int main(int argc, char **argv) { - struct irc_rule r = {0}; - - irc_rule_add(r.servers, "malikania"); - //irc_rule_add(r.servers, "freenode"); - - printf("%d\n", irc_rule_match(&r, "malikania", "", "", "", "")); + struct irc_server s = { + .name = "malikania", + .username = "circ", + .nickname = "circ", + .hostname = "malikania.fr", + .port = 6667 + }; -#if 0 - irc_rule_add(r.servers, "malikania"); - irc_bot_insert_rule(&r, 0); - strcpy(r.servers, "freenode:"); - irc_bot_insert_rule(&r, 15); - strcpy(r.servers, "oftc:"); - irc_bot_insert_rule(&r, 0); - strcpy(r.servers, "jean:"); - irc_bot_insert_rule(&r, 1); + irc_server_join(&s, "#test", NULL); - puts("BEFORE"); - dump(); - irc_bot_remove_rule(3); - puts("AFTER"); - dump(); -#endif - + irc_init(); + irc_log_set_verbose(true); + irc_add_server(&s); + irc_run(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/channel.c Fri Jan 15 14:18:54 2021 +0100 @@ -0,0 +1,110 @@ +/* + * channel.h -- an IRC server channel + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or 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 <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "channel.h" +#include "util.h" +#include "set.h" + +static inline int +cmp(const struct irc_channel_user *u1, const struct irc_channel_user *u2) +{ + return strcmp(u1->nickname, u2->nickname); +} + +static inline struct irc_channel_user * +find(const struct irc_channel *ch, const char *nick) +{ + struct irc_channel_user key = {0}; + + strlcpy(key.nickname, nick, sizeof (key.nickname)); + + return IRC_SET_FIND(ch->users, ch->usersz, &key, cmp); +} + +void +irc_channel_add(struct irc_channel *ch, const char *nick, char mode) +{ + assert(ch); + assert(nick); + + struct irc_channel_user u = {0}; + + strlcpy(u.nickname, nick, sizeof (u.nickname)); + u.mode = mode; + + IRC_SET_ALLOC_PUSH(&ch->users, &ch->usersz, &u, cmp); +} + +void +irc_channel_set_user_mode(struct irc_channel *ch, const char *nick, char mode) +{ + assert(ch); + assert(nick); + + struct irc_channel_user *user; + + if ((user = find(ch, nick))) + user->mode = mode; +} + +void +irc_channel_set_user_nick(struct irc_channel *ch, const char *nick, const char *newnick) +{ + assert(ch); + assert(nick); + assert(newnick); + + struct irc_channel_user *user; + + if ((user = find(ch, nick))) + strlcpy(user->nickname, newnick, sizeof (user->nickname)); +} + +void +irc_channel_clear(struct irc_channel *ch) +{ + assert(ch); + + free(ch->users); + ch->users = 0; +} + +void +irc_channel_remove(struct irc_channel *ch, const char *nick) +{ + assert(ch); + assert(nick); + + struct irc_channel_user *user; + + if ((user = find(ch, nick))) + IRC_SET_ALLOC_REMOVE(&ch->users, &ch->usersz, user); +} + +void +irc_channel_finish(struct irc_channel *ch) +{ + assert(ch); + + irc_channel_clear(ch); + memset(ch, 0, sizeof (*ch)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/channel.h Fri Jan 15 14:18:54 2021 +0100 @@ -0,0 +1,61 @@ +/* + * channel.h -- an IRC server channel + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or 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. + */ + +#ifndef IRCCD_CHANNEL_H +#define IRCCD_CHANNEL_H + +#include <stdbool.h> +#include <stddef.h> + +#include "limits.h" + +#define IRC_CHANNEL_NAME_MAX 128 +#define IRC_CHANNEL_PASSWORD_MAX 128 + +struct irc_channel_user { + char nickname[IRC_NICKNAME_MAX]; + char mode; +}; + +struct irc_channel { + char name[IRC_CHANNEL_MAX]; + char password[IRC_PASSWORD_MAX]; + struct irc_channel_user *users; + size_t usersz; + bool joined; +}; + +void +irc_channel_add(struct irc_channel *, const char *, char); + +void +irc_channel_set_user_mode(struct irc_channel *, const char *, char); + +void +irc_channel_set_user_nick(struct irc_channel *, const char *, const char *); + +void +irc_channel_clear(struct irc_channel *); + +void +irc_channel_remove(struct irc_channel *, const char *); + +void +irc_channel_finish(struct irc_channel *); + +#endif /* !IRCCD_CHANNEL_H */
--- a/lib/irccd/jsapi-server.c Fri Jan 15 10:38:45 2021 +0100 +++ b/lib/irccd/jsapi-server.c Fri Jan 15 14:18:54 2021 +0100 @@ -20,6 +20,7 @@ #include <duktape.h> +#include "channel.h" #include "irccd.h" #include "server.h"
--- a/lib/irccd/server.c Fri Jan 15 10:38:45 2021 +0100 +++ b/lib/irccd/server.c Fri Jan 15 14:18:54 2021 +0100 @@ -37,6 +37,7 @@ #include "event.h" #include "log.h" #include "server.h" +#include "channel.h" #include "util.h" struct origin { @@ -76,8 +77,8 @@ compare_chan(const void *d1, const void *d2) { return strcmp( - ((const struct irc_server_channel *)d1)->name, - ((const struct irc_server_channel *)d2)->name + ((const struct irc_channel *)d1)->name, + ((const struct irc_channel *)d2)->name ); } @@ -103,10 +104,10 @@ qsort(s->channels, s->channelsz, sizeof (*s->channels), compare_chan); } -static struct irc_server_channel * +static struct irc_channel * add_channel(struct irc_server *s, const char *name, const char *password, bool joined) { - struct irc_server_channel ch = { + struct irc_channel ch = { .joined = joined }; @@ -124,7 +125,7 @@ } static void -remove_channel(struct irc_server *s, struct irc_server_channel *ch) +remove_channel(struct irc_server *s, struct irc_channel *ch) { /* Null channel name will be moved at the end. */ memset(ch, 0, sizeof (*ch)); @@ -190,7 +191,7 @@ convert_join(struct irc_server *s, struct irc_event *ev) { const struct origin *origin = parse_origin(ev->args[0]); - struct irc_server_channel *ch; + struct irc_channel *ch; ev->type = IRC_EVENT_JOIN; ev->server = s; @@ -221,7 +222,7 @@ * rejoin it automatically if the option is set. */ if (strcmp(ev->args[3], s->nickname) == 0) { - struct irc_server_channel *ch = irc_server_find(s, ev->args[2]); + struct irc_channel *ch = irc_server_find(s, ev->args[2]); if (ch) { ch->joined = false; @@ -241,22 +242,13 @@ for (size_t i = 0; i < ev->argsz; ++i) { printf("MODE: %zu=%s\n", i, ev->args[i]); } - -#if 0 - if (strcmp(m->args[0], s->nickname) == 0) { - /* Own user modes. */ - strlcpy(s->usermodes, m->args[1], sizeof (s->usermodes); - } else { - /* TODO: channel modes. */ - } -#endif } static void convert_part(struct irc_server *s, struct irc_event *ev) { const struct origin *origin = parse_origin(ev->args[0]); - struct irc_server_channel *ch = irc_server_find(s, ev->args[2]); + struct irc_channel *ch = irc_server_find(s, ev->args[2]); ev->type = IRC_EVENT_PART; ev->server = s; @@ -331,7 +323,7 @@ (void)s; (void)ev; #if 0 - struct irc_server_channel *chan; + struct irc_channel *chan; char *p, *n; if (m->argsz < 3 || !(chan = irc_server_find(s, m->args[2]))) @@ -582,6 +574,8 @@ } for (; s->aip; s->aip = s->aip->ai_next) { + int cflags; + /* We may need to close a socket that was open earlier. */ if (s->fd != 0) close(s->fd); @@ -594,8 +588,12 @@ continue; } - /* TODO: is F_GETFL required before? */ - fcntl(s->fd, F_SETFL, O_NONBLOCK); + if ((cflags = fcntl(s->fd, F_GETFL)) < 0) { + irc_log_warn("server %s: %s", s->name, strerror(errno)); + continue; + } + + fcntl(s->fd, F_SETFL, cflags | O_NONBLOCK); /* * With some luck, the connection completes immediately, @@ -834,13 +832,13 @@ return true; } -struct irc_server_channel * +struct irc_channel * irc_server_find(struct irc_server *s, const char *name) { assert(s); assert(name); - struct irc_server_channel key = {0}; + struct irc_channel key = {0}; strlcpy(key.name, name, sizeof (key.name)); @@ -890,7 +888,7 @@ assert(s); assert(name); - struct irc_server_channel *ch; + struct irc_channel *ch; bool ret = true; /*
--- a/lib/irccd/server.h Fri Jan 15 10:38:45 2021 +0100 +++ b/lib/irccd/server.h Fri Jan 15 14:18:54 2021 +0100 @@ -30,14 +30,9 @@ struct pollfd; +struct irc_channel; struct irc_event; -struct irc_server_channel { - char name[IRC_CHANNEL_MAX]; - char password[IRC_PASSWORD_MAX]; - bool joined; -}; - enum irc_server_state { IRC_SERVER_STATE_DISCONNECTED, IRC_SERVER_STATE_CONNECTING, @@ -82,10 +77,9 @@ char username[IRC_USERNAME_MAX]; char realname[IRC_REALNAME_MAX]; char ctcpversion[IRC_CTCPVERSION_MAX]; - char usermodes[IRC_USERMODES_MAX]; /* Joined channels. */ - struct irc_server_channel *channels; + struct irc_channel *channels; size_t channelsz; /* Network connectivity. */ @@ -123,7 +117,7 @@ bool irc_server_poll(struct irc_server *, struct irc_event *); -struct irc_server_channel * +struct irc_channel * irc_server_find(struct irc_server *, const char *); bool
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/set.h Fri Jan 15 14:18:54 2021 +0100 @@ -0,0 +1,62 @@ +/* + * set.h -- generic macros to insert/remove in sorted arrays + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or 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. + */ + +#ifndef IRCCD_SET_H +#define IRCCD_SET_H + +#include <string.h> +#include <stdlib.h> + +#include "util.h" + +typedef int (*irc_set_cmp)(const void *, const void *); + +#define IRC_SET_FIND(a, asz, o, f) \ + bsearch(o, a, asz, sizeof (*(o)), (irc_set_cmp)f) + +#define IRC_SET_ALLOC_PUSH(a, asz, o, f) \ +do { \ + *(a) = irc_util_reallocarray(*(a), ++(*(asz)), sizeof (*(o))); \ + memcpy(*(a) + ((*asz) - 1), o, sizeof (*o)); \ + qsort(*(a), *(asz), sizeof (*o), (irc_set_cmp)f); \ +} while (0) + +#define IRC_SET_ALLOC_REMOVE(a, asz, o) \ +do { \ + if (--(*(asz)) == 0) { \ + free(*(a)); \ + *(a) = NULL; \ + } else { \ + memmove(o, o + 1, sizeof (*(o)) * (*(asz) - ((o) - *(a)))); \ + *(a) = irc_util_reallocarray(*(a), *(asz), sizeof (*(o))); \ + } \ +} while (0) + +#define IRC_SET_PUSH(a, asz, o, f) \ +do { \ + memcpy(a + (*(asz))++, o, sizeof (*o)); \ + qsort(a, *(asz), sizeof (*o), (irc_set_cmp)f); \ +} while (0) + +#define IRC_SET_REMOVE(a, asz, o) \ +do { \ + if (--(*asz) != 0) \ + memmove(o, o + 1, sizeof (*(o)) * (*(asz) - ((o) - (a)))); \ +} while (0) + +#endif /* !IRCCD_SET_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-channel.c Fri Jan 15 14:18:54 2021 +0100 @@ -0,0 +1,129 @@ +/* + * test-channel.c -- test util.h functions + * + * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or 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. + */ + +#define GREATEST_USE_ABBREVS 0 +#include <greatest.h> + +#include <irccd/channel.h> + +GREATEST_TEST +basics_add(void) +{ + struct irc_channel ch = {0}; + + irc_channel_add(&ch, "markand", '@'); + GREATEST_ASSERT_EQ(ch.usersz, 1U); + GREATEST_ASSERT_EQ(ch.users[0].mode, '@'); + GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "markand"); + + irc_channel_add(&ch, "jean", 0); + GREATEST_ASSERT_EQ(ch.usersz, 2U); + GREATEST_ASSERT_EQ(ch.users[0].mode, 0); + GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "jean"); + GREATEST_ASSERT_EQ(ch.users[1].mode, '@'); + GREATEST_ASSERT_STR_EQ(ch.users[1].nickname, "markand"); + + irc_channel_add(&ch, "zoe", 0); + GREATEST_ASSERT_EQ(ch.usersz, 3U); + GREATEST_ASSERT_EQ(ch.users[0].mode, 0); + GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "jean"); + GREATEST_ASSERT_EQ(ch.users[1].mode, '@'); + GREATEST_ASSERT_STR_EQ(ch.users[1].nickname, "markand"); + GREATEST_ASSERT_EQ(ch.users[2].mode, 0); + GREATEST_ASSERT_STR_EQ(ch.users[2].nickname, "zoe"); + + GREATEST_PASS(); +} + +GREATEST_TEST +basics_remove(void) +{ + struct irc_channel ch = {0}; + + irc_channel_add(&ch, "markand", '@'); + irc_channel_add(&ch, "jean", 0); + irc_channel_add(&ch, "zoe", 0); + + irc_channel_remove(&ch, "jean"); + GREATEST_ASSERT_EQ(ch.usersz, 2U); + GREATEST_ASSERT_EQ(ch.users[0].mode, '@'); + GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "markand"); + GREATEST_ASSERT_EQ(ch.users[1].mode, 0); + GREATEST_ASSERT_STR_EQ(ch.users[1].nickname, "zoe"); + + irc_channel_remove(&ch, "zoe"); + GREATEST_ASSERT_EQ(ch.usersz, 1U); + GREATEST_ASSERT_EQ(ch.users[0].mode, '@'); + GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "markand"); + + irc_channel_remove(&ch, "markand"); + GREATEST_ASSERT_EQ(ch.usersz, 0U); + GREATEST_ASSERT(!ch.users); + + GREATEST_PASS(); +} + +GREATEST_TEST +basics_set_mode(void) +{ + struct irc_channel ch = {0}; + + irc_channel_add(&ch, "jean", '@'); + irc_channel_set_user_mode(&ch, "jean", '+'); + irc_channel_set_user_mode(&ch, "nobody", '+'); + + GREATEST_ASSERT_EQ(ch.usersz, 1); + GREATEST_ASSERT_EQ(ch.users[0].mode, '+'); + GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "jean"); + GREATEST_PASS(); +} + +GREATEST_TEST +basics_set_nick(void) +{ + struct irc_channel ch = {0}; + + irc_channel_add(&ch, "jean", '@'); + irc_channel_set_user_nick(&ch, "jean", "francis"); + irc_channel_set_user_nick(&ch, "nobody", "francis"); + + GREATEST_ASSERT_EQ(ch.usersz, 1); + GREATEST_ASSERT_EQ(ch.users[0].mode, '@'); + GREATEST_ASSERT_STR_EQ(ch.users[0].nickname, "francis"); + GREATEST_PASS(); +} + +GREATEST_SUITE(suite_basics) +{ + GREATEST_RUN_TEST(basics_add); + GREATEST_RUN_TEST(basics_remove); + GREATEST_RUN_TEST(basics_set_mode); + GREATEST_RUN_TEST(basics_set_nick); +} + +GREATEST_MAIN_DEFS(); + +int +main(int argc, char **argv) +{ + GREATEST_MAIN_BEGIN(); + GREATEST_RUN_SUITE(suite_basics); + GREATEST_MAIN_END(); + + return 0; +}