Mercurial > irccd
changeset 959:0d6e2a89fee5
irccd: split network code from server into conn.[ch]
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sun, 24 Jan 2021 18:31:50 +0100 |
parents | 533639ec5e9c |
children | 4af3140f234d |
files | irccd/main.c lib/CMakeLists.txt lib/irccd/channel.h lib/irccd/conn.c lib/irccd/conn.h lib/irccd/event.c lib/irccd/js-plugin.c lib/irccd/jsapi-server.c lib/irccd/limits.h lib/irccd/server.c lib/irccd/server.h |
diffstat | 11 files changed, 799 insertions(+), 661 deletions(-) [+] |
line wrap: on
line diff
--- a/irccd/main.c Thu Jan 21 23:15:20 2021 +0100 +++ b/irccd/main.c Sun Jan 24 18:31:50 2021 +0100 @@ -34,4 +34,12 @@ (void)argc; (void)argv; + struct irc_server *s; + + irc_bot_init(); + + s = irc_server_new("mlk", "circ", "circ", "circ", "malikania.fr", 6667); + irc_server_join(s, "#test", NULL); + irc_bot_server_add(s); + irc_bot_run(); }
--- a/lib/CMakeLists.txt Thu Jan 21 23:15:20 2021 +0100 +++ b/lib/CMakeLists.txt Sun Jan 24 18:31:50 2021 +0100 @@ -24,6 +24,8 @@ irccd/channel.c irccd/channel.h irccd/config.h.in + irccd/conn.c + irccd/conn.h irccd/dl-plugin.c irccd/dl-plugin.h irccd/event.c
--- a/lib/irccd/channel.h Thu Jan 21 23:15:20 2021 +0100 +++ b/lib/irccd/channel.h Sun Jan 24 18:31:50 2021 +0100 @@ -40,6 +40,8 @@ LIST_ENTRY(irc_channel) link; }; +LIST_HEAD(irc_channel_list, irc_channel); + struct irc_channel * irc_channel_new(const char *, const char *, bool);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/conn.c Sun Jan 24 18:31:50 2021 +0100 @@ -0,0 +1,453 @@ +/* + * conn.c -- 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 <sys/socket.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <poll.h> +#include <string.h> +#include <unistd.h> + +#include "conn.h" +#include "util.h" + +static void +cleanup(struct irc_conn *conn) +{ + if (conn->fd != 0) + close(conn->fd); + +#if defined(IRCCD_WITH_SSL) + if (conn->ssl) + SSL_free(conn->ssl); + if (conn->ctx) + SSL_CTX_free(conn->ctx); + + conn->ssl = NULL; + conn->ctx = NULL; +#endif + + conn->fd = -1; +} + +static inline void +scan(char **line, char **str) +{ + char *p = strchr(*line, ' '); + + if (p) + *p = '\0'; + + *str = *line; + *line = p ? p + 1 : strchr(*line, '\0'); +} + +static bool +parse(struct irc_conn_msg *msg, const char *line) +{ + char *ptr = msg->buf; + size_t a; + + memset(msg, 0, sizeof (*msg)); + strlcpy(msg->buf, line, sizeof (msg->buf)); + + /* + * IRC message is defined as following: + * + * [:prefix] command arg1 arg2 [:last-argument] + */ + if (*ptr == ':') + scan((++ptr, &ptr), &msg->prefix); /* prefix */ + + scan(&ptr, &msg->cmd); /* command */ + + /* And finally arguments. */ + for (a = 0; *ptr && a < IRC_UTIL_SIZE(msg->args); ++a) { + if (*ptr == ':') { + msg->args[a] = ptr + 1; + ptr = strchr(ptr, '\0'); + } else + scan(&ptr, &msg->args[a]); + } + + if (a >= IRC_UTIL_SIZE(msg->args)) + return errno = EMSGSIZE, false; + if (msg->cmd == NULL) + return errno = EBADMSG, false; + + return true; +} + +static bool +create(struct irc_conn *conn) +{ + struct addrinfo *ai = conn->aip; + int cflags = 0; + + cleanup(conn); + + if ((conn->fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) + return false; + if ((cflags = fcntl(conn->fd, F_GETFL)) < 0) + return false; + if (fcntl(conn->fd, F_SETFL, cflags | O_NONBLOCK) < 0) + return false; + + return true; +} + +static inline bool +update_ssl_state(struct irc_conn *conn, int ret) +{ + switch (SSL_get_error(conn->ssl, ret)) { + case SSL_ERROR_WANT_READ: + conn->ssl_state = IRC_CONN_SSL_STATE_NEED_READ; + break; + case SSL_ERROR_WANT_WRITE: + conn->ssl_state = IRC_CONN_SSL_STATE_NEED_WRITE; + break; + case SSL_ERROR_SSL: + return irc_conn_disconnect(conn), -1; + default: + break; + } + + return true; +} + +static ssize_t +input_ssl(struct irc_conn *conn, char *dst, size_t dstsz) +{ + int nr; + + if ((nr = SSL_read(conn->ssl, dst, dstsz)) <= 0) + return update_ssl_state(conn, nr); + + conn->ssl_state = IRC_CONN_SSL_STATE_NONE; + + return nr; +} + +static inline ssize_t +input_clear(struct irc_conn *conn, char *buf, size_t bufsz) +{ + ssize_t nr; + + if ((nr = recv(conn->fd, buf, bufsz, 0)) <= 0) + return irc_conn_disconnect(conn), -1; + + return nr; +} + +static bool +input(struct irc_conn *conn) +{ + size_t len = strlen(conn->in); + size_t cap = sizeof (conn->in) - len - 1; + ssize_t nr = 0; + + if (conn->flags & IRC_CONN_SSL) + nr = input_ssl(conn, conn->in + len, cap); + else + nr = input_clear(conn, conn->in + len, cap); + + if (nr > 0) + conn->in[len + nr] = '\0'; + + return nr > 0; +} + +static inline ssize_t +output_ssl(struct irc_conn *conn) +{ + int ns; + + if ((ns = SSL_write(conn->ssl, conn->out, strlen(conn->out))) <= 0) + return update_ssl_state(conn, ns); + + return ns; +} + +static inline ssize_t +output_clear(struct irc_conn *conn) +{ + ssize_t ns; + + if ((ns = send(conn->fd, conn->out, strlen(conn->out), 0)) < 0) + return irc_conn_disconnect(conn), -1; + + return ns; +} + +static bool +output(struct irc_conn *conn) +{ + ssize_t ns = 0; + + if (conn->flags & IRC_CONN_SSL) + ns = output_ssl(conn); + else + ns = output_clear(conn); + + if (ns > 0) { + /* Optimize if everything was sent. */ + if ((size_t)ns >= sizeof (conn->out) - 1) + conn->out[0] = '\0'; + else + memmove(conn->out, conn->out + ns, sizeof (conn->out) - ns); + } + + return ns != -1; +} + +static bool +handshake(struct irc_conn *conn) +{ + if (conn->flags & IRC_CONN_SSL) { +#if defined(IRCCD_WITH_SSL) + int r; + + conn->state = IRC_CONN_STATE_HANDSHAKING; + + /* + * This function is called several time until it completes so we + * must keep the same context/ssl stuff once it has been + * created. + */ + if (!conn->ctx) + conn->ctx = SSL_CTX_new(TLS_method()); + if (!conn->ssl) { + conn->ssl = SSL_new(conn->ctx); + + SSL_set_fd(conn->ssl, conn->fd); + SSL_set_connect_state(conn->ssl); + } + + if ((r = SSL_do_handshake(conn->ssl)) <= 0) + return update_ssl_state(conn, r); + + conn->state = IRC_CONN_STATE_READY; +#endif + } else + conn->state = IRC_CONN_STATE_READY; + + return true; +} + +static bool +dial(struct irc_conn *conn) +{ + /* No more address available. */ + if (conn->aip == NULL) + return irc_conn_disconnect(conn), false; + + for (; conn->aip; conn->aip = conn->aip->ai_next) { + if (!create(conn)) { + // irc_log_warn("server %s: %s", s->name, strerror(errno)); + continue; + } + + /* + * With some luck, the connection completes immediately, + * otherwise we will need to wait until the socket is writable. + */ + if (connect(conn->fd, conn->aip->ai_addr, conn->aip->ai_addrlen) == 0) + return handshake(conn); + + /* Connect "succeeds" but isn't complete yet. */ + if (errno == EINPROGRESS || errno == EAGAIN) + return true; + } + + return false; +} + +static bool +lookup(struct irc_conn *conn) +{ + struct addrinfo hints = { + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_NUMERICSERV + }; + char service[16]; + int ret; + + snprintf(service, sizeof (service), "%hu", conn->port); + + if ((ret = getaddrinfo(conn->hostname, service, &hints, &conn->ai)) != 0) { + // irc_log_warn gai_strerror(ret) + return false; + } + + conn->aip = conn->ai; + + return true; +} + +static bool +check_connect(struct irc_conn *conn) +{ + int res, err = -1; + socklen_t len = sizeof (int); + + /* Determine if the non blocking connect(2) call succeeded. */ + if ((res = getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &err, &len)) < 0 || err) + return dial(conn); + + return handshake(conn); +} + +bool +irc_conn_connect(struct irc_conn *conn) +{ + assert(conn); + + conn->state = IRC_CONN_STATE_CONNECTING; + + if (!lookup(conn)) + return irc_conn_disconnect(conn), false; + + return dial(conn), true; +} + +void +irc_conn_disconnect(struct irc_conn *conn) +{ + assert(conn); + + cleanup(conn); + conn->state = IRC_CONN_STATE_NONE; +} + +static inline void +prepare_ssl(const struct irc_conn *conn, struct pollfd *pfd) +{ +#if defined(IRCCD_WITH_SSL) + switch (conn->ssl_state) { + case IRC_CONN_SSL_STATE_NEED_READ: + pfd->events |= POLLIN; + break; + case IRC_CONN_SSL_STATE_NEED_WRITE: + pfd->events |= POLLOUT; + break; + default: + break; + } +#else + (void)conn; +#endif +} + +void +irc_conn_prepare(const struct irc_conn *conn, struct pollfd *pfd) +{ + assert(conn); + assert(pfd); + + pfd->fd = conn->fd; + + if (conn->ssl_state) + prepare_ssl(conn, pfd); + else { + switch (conn->state) { + case IRC_CONN_STATE_CONNECTING: + pfd->events = POLLOUT; + break; + case IRC_CONN_STATE_READY: + pfd->events = POLLIN; + + if (conn->out[0]) + pfd->events |= POLLOUT; + break; + default: + break; + } + } +} + +bool +irc_conn_flush(struct irc_conn *conn, const struct pollfd *pfd) +{ + assert(conn); + assert(pfd); + + switch (conn->state) { + case IRC_CONN_STATE_CONNECTING: + return check_connect(conn); + case IRC_CONN_STATE_HANDSHAKING: + return handshake(conn); + case IRC_CONN_STATE_READY: + if (pfd->revents & (POLLERR | POLLHUP)) + return irc_conn_disconnect(conn), false; + if (pfd->revents & POLLIN && !input(conn)) + return irc_conn_disconnect(conn), false; + if (pfd->revents & POLLOUT && !output(conn)) + return irc_conn_disconnect(conn), false; + break; + default: + break; + } + + return true; +} + +bool +irc_conn_poll(struct irc_conn *conn, struct irc_conn_msg *msg) +{ + assert(conn); + assert(msg); + + char *pos; + size_t length; + + if (!(pos = strstr(conn->in, "\r\n"))) + return false; + + /* Turn end of the string at delimiter. */ + *pos = 0; + length = pos - conn->in; + + if (length > 0) + parse(msg, conn->in); + + memmove(conn->in, pos + 2, sizeof (conn->in) - (length + 2)); + + return true; +} + +bool +irc_conn_send(struct irc_conn *conn, const char *data) +{ + assert(conn); + assert(data); + + if (strlcat(conn->out, data, sizeof (conn->out)) >= sizeof (conn->out)) + return errno = EMSGSIZE, false; + if (strlcat(conn->out, "\r\n", sizeof (conn->out)) >= sizeof (conn->out)) + return errno = EMSGSIZE, false; + + return true; +} + +void +irc_conn_finish(struct irc_conn *conn) +{ + assert(conn); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/conn.h Sun Jan 24 18:31:50 2021 +0100 @@ -0,0 +1,102 @@ +/* + * conn.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_CONN_H +#define IRCCD_CONN_H + +#include <stdbool.h> + +#include "config.h" + +#if defined(IRCCD_WITH_SSL) +# include <openssl/ssl.h> +#endif + +#include "limits.h" + +struct addrinfo; +struct pollfd; + +enum irc_conn_state { + IRC_CONN_STATE_NONE, /* Nothing, default. */ + IRC_CONN_STATE_CONNECTING, /* Pending connect(2) call. */ + IRC_CONN_STATE_HANDSHAKING, /* SSL connect+handshake. */ + IRC_CONN_STATE_READY /* Ready for I/O. */ +}; + +enum irc_conn_flags { + IRC_CONN_SSL = (1 << 0) +}; + +#if defined(IRCCD_WITH_SSL) + +enum irc_conn_ssl_state { + IRC_CONN_SSL_STATE_NONE, + IRC_CONN_SSL_STATE_NEED_READ, + IRC_CONN_SSL_STATE_NEED_WRITE, +}; + +#endif + +struct irc_conn { + char hostname[IRC_HOST_LEN]; + unsigned short port; + int fd; + struct addrinfo *ai; + struct addrinfo *aip; + char in[IRC_BUF_LEN]; + char out[IRC_BUF_LEN]; + enum irc_conn_state state; + enum irc_conn_flags flags; + +#if defined(IRCCD_WITH_SSL) + SSL_CTX *ctx; + SSL *ssl; + enum irc_conn_ssl_state ssl_state; +#endif +}; + +struct irc_conn_msg { + char *prefix; + char *cmd; + char *args[IRC_ARGS_MAX]; + char buf[IRC_MESSAGE_LEN]; +}; + +bool +irc_conn_connect(struct irc_conn *); + +void +irc_conn_disconnect(struct irc_conn *); + +void +irc_conn_prepare(const struct irc_conn *, struct pollfd *); + +bool +irc_conn_flush(struct irc_conn *, const struct pollfd *); + +bool +irc_conn_poll(struct irc_conn *, struct irc_conn_msg *); + +bool +irc_conn_send(struct irc_conn *, const char *); + +void +irc_conn_finish(struct irc_conn *); + +#endif /* !IRCCD_CONN_H */
--- a/lib/irccd/event.c Thu Jan 21 23:15:20 2021 +0100 +++ b/lib/irccd/event.c Sun Jan 24 18:31:50 2021 +0100 @@ -19,6 +19,7 @@ #include <assert.h> #include <errno.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> #include "event.h"
--- a/lib/irccd/js-plugin.c Thu Jan 21 23:15:20 2021 +0100 +++ b/lib/irccd/js-plugin.c Sun Jan 24 18:31:50 2021 +0100 @@ -87,12 +87,14 @@ static void push_names(duk_context *ctx, const struct irc_event *ev) { - char *token, *p = ev->names.names; + const char *token; + char *p = ev->names.names; duk_push_array(ctx); for (size_t i = 0; (token = strtok_r(p, " ", &p)); ++i) { - duk_push_string(ctx, irc_server_strip(ev->server, token).name); + irc_server_strip(ev->server, &token, NULL, NULL); + duk_push_string(ctx, token); duk_put_prop_index(ctx, -2, i); } } @@ -100,8 +102,8 @@ static void push_whois(duk_context *ctx, const struct irc_event *ev) { - char *token, *p = ev->whois.channels; - struct irc_server_namemode nm; + const char *token; + char *p = ev->whois.channels; duk_push_object(ctx); duk_push_string(ctx, ev->whois.nickname); @@ -114,12 +116,16 @@ duk_put_prop_string(ctx, -2, "hostname"); duk_push_array(ctx); for (size_t i = 0; (token = strtok_r(p, " ", &p)); ++i) { - nm = irc_server_strip(ev->server, token); + char mode = 0, prefix = 0; + + irc_server_strip(ev->server, &token, &mode, &prefix); duk_push_object(ctx); - duk_push_string(ctx, nm.name); + duk_push_string(ctx, token); duk_put_prop_string(ctx, -2, "channel"); - duk_push_sprintf(ctx, "%c", nm.mode); + duk_push_sprintf(ctx, "%c", mode); duk_put_prop_string(ctx, -2, "mode"); + duk_push_sprintf(ctx, "%c", prefix); + duk_put_prop_string(ctx, -2, "prefix"); duk_put_prop_index(ctx, -2, i); } duk_put_prop_string(ctx, -2, "channels");
--- a/lib/irccd/jsapi-server.c Thu Jan 21 23:15:20 2021 +0100 +++ b/lib/irccd/jsapi-server.c Sun Jan 24 18:31:50 2021 +0100 @@ -60,16 +60,36 @@ return sv; } -static inline void -get_port(duk_context *ctx, struct irc_server *s) +static inline unsigned short +get_port(duk_context *ctx) { + unsigned short port; + duk_get_prop_string(ctx, 0, "port"); if (!duk_is_number(ctx, -1)) duk_error(ctx, DUK_ERR_ERROR, "invalid 'port' property"); - s->port = duk_to_int(ctx, -1); + port = duk_to_int(ctx, -1); duk_pop(ctx); + + return port; +} + +static inline const char * +get_string(duk_context *ctx, const char *n) +{ + const char *ret; + + duk_get_prop_string(ctx, 0, n); + + if (!duk_is_string(ctx, -1)) + duk_error(ctx, DUK_ERR_ERROR, "invalid or missing '%s' property", n); + + ret = duk_to_string(ctx, -1); + duk_pop(ctx); + + return ret; } static inline void @@ -102,19 +122,6 @@ } static inline void -get_string(duk_context *ctx, const char *n, bool required, char *dst, size_t dstsz) -{ - duk_get_prop_string(ctx, 0, n); - - if (duk_is_string(ctx, -1)) - strlcpy(dst, duk_to_string(ctx, -1), dstsz); - else if (required) - duk_error(ctx, DUK_ERR_ERROR, "invalid or missing '%s' property", n); - - duk_pop(ctx); -} - -static inline void get_channels(duk_context *ctx, struct irc_server *s) { duk_get_prop_string(ctx, 0, "channels"); @@ -144,19 +151,19 @@ duk_push_object(ctx); duk_push_string(ctx, s->name); duk_put_prop_string(ctx, -2, "name"); - duk_push_string(ctx, s->hostname); + duk_push_string(ctx, s->conn.hostname); duk_put_prop_string(ctx, -2, "hostname"); - duk_push_uint(ctx, s->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"); duk_push_string(ctx, s->commandchar); duk_put_prop_string(ctx, -2, "commandChar"); - duk_push_string(ctx, s->realname); + duk_push_string(ctx, s->ident.realname); duk_put_prop_string(ctx, -2, "realname"); - duk_push_string(ctx, s->nickname); + duk_push_string(ctx, s->ident.nickname); duk_put_prop_string(ctx, -2, "nickname"); - duk_push_string(ctx, s->username); + duk_push_string(ctx, s->ident.username); duk_put_prop_string(ctx, -2, "username"); duk_push_array(ctx); @@ -441,26 +448,27 @@ static duk_ret_t Server_constructor(duk_context *ctx) { - struct irc_server s = {0}, *p; + struct irc_server *s; duk_require_object(ctx, 0); - get_string(ctx, "name", true, s.name, sizeof (s.name)); - get_string(ctx, "hostname", true, s.hostname, sizeof (s.hostname)); - get_port(ctx, &s); - get_ip(ctx, &s); - get_ssl(ctx, &s); - get_string(ctx, "nickname", false, s.nickname, sizeof (s.nickname)); - get_string(ctx, "username", false, s.username, sizeof (s.username)); - get_string(ctx, "realname", false, s.realname, sizeof (s.realname)); - get_string(ctx, "commandChar", false, s.commandchar, sizeof (s.commandchar)); - get_channels(ctx, &s); + s = irc_server_new( + get_string(ctx, "name"), + get_string(ctx, "nickname"), + get_string(ctx, "username"), + get_string(ctx, "realname"), + get_string(ctx, "hostname"), + get_port(ctx) + ); - p = irc_util_memdup(&s, sizeof (s)); - irc_server_incref(p); + get_ip(ctx, s); + get_ssl(ctx, s); + get_channels(ctx, s); + + irc_server_incref(s); duk_push_this(ctx); - duk_push_pointer(ctx, p); + duk_push_pointer(ctx, s); duk_put_prop_string(ctx, -2, SIGNATURE); duk_pop(ctx);
--- a/lib/irccd/limits.h Thu Jan 21 23:15:20 2021 +0100 +++ b/lib/irccd/limits.h Sun Jan 24 18:31:50 2021 +0100 @@ -27,7 +27,10 @@ #define IRC_PASSWORD_LEN 64 /* Password length. */ #define IRC_CTCPVERSION_LEN 64 /* Custom CTCP version answer. */ #define IRC_USERMODES_LEN 8 /* Number of modes (e.g. ohv). */ +#define IRC_CHANTYPES_LEN 8 #define IRC_CMDCHAR_LEN 4 /* Prefix for plugin commands (e.g. !). */ +#define IRC_MESSAGE_LEN 512 /* Official length per message. */ +#define IRC_ARGS_MAX 32 /* Own supported number of arguments per message. */ /* Network limits. */ #define IRC_HOST_LEN 64 /* Hostname length.. */
--- a/lib/irccd/server.c Thu Jan 21 23:15:20 2021 +0100 +++ b/lib/irccd/server.c Sun Jan 24 18:31:50 2021 +0100 @@ -16,19 +16,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/types.h> -#include <sys/socket.h> #include <assert.h> -#include <ctype.h> #include <errno.h> -#include <fcntl.h> -#include <netdb.h> #include <poll.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include "config.h" @@ -42,85 +36,49 @@ #include "server.h" #include "util.h" +static inline void +clear_channels(struct irc_server *s, bool free) +{ + struct irc_channel *c, *tmp; + + LIST_FOREACH_SAFE(c, &s->channels, link, tmp) { + if (free) + irc_channel_finish(c); + else + irc_channel_clear(c); + } + + if (free) + LIST_INIT(&s->channels); +} + +static inline void +clear_server(struct irc_server *s) +{ + free(s->bufwhois.nickname); + free(s->bufwhois.username); + free(s->bufwhois.realname); + free(s->bufwhois.hostname); + free(s->bufwhois.channels); + + memset(&s->params, 0, sizeof (s->params)); + memset(&s->bufwhois, 0, sizeof (s->bufwhois)); +} + +static inline bool +is_self(const struct irc_server *s, const char *nick) +{ + return strncmp(s->ident.nickname, nick, strlen(s->ident.nickname)) == 0; +} + +#if 0 + struct origin { char nickname[IRC_NICKNAME_LEN]; char username[IRC_USERNAME_LEN]; char host[IRC_HOST_LEN]; }; -struct message { - char *prefix; - char *cmd; - char *args[32]; - char buf[512]; -}; - -static inline void -scan(char **line, char **str) -{ - char *p = strchr(*line, ' '); - - if (p) - *p = '\0'; - - *str = *line; - *line = p ? p + 1 : strchr(*line, '\0'); -} - -static bool -parse(struct message *msg, const char *line) -{ - char *ptr = msg->buf; - size_t a; - - memset(msg, 0, sizeof (*msg)); - strlcpy(msg->buf, line, sizeof (msg->buf)); - - /* - * IRC message is defined as following: - * - * [:prefix] command arg1 arg2 [:last-argument] - */ - if (*ptr == ':') - scan((++ptr, &ptr), &msg->prefix); /* prefix */ - - scan(&ptr, &msg->cmd); /* command */ - - /* And finally arguments. */ - for (a = 0; *ptr && a < IRC_UTIL_SIZE(msg->args); ++a) { - if (*ptr == ':') { - msg->args[a] = ptr + 1; - ptr = strchr(ptr, '\0'); - } else - scan(&ptr, &msg->args[a]); - } - - if (a >= IRC_UTIL_SIZE(msg->args)) - return errno = EMSGSIZE, false; - if (msg->cmd == NULL) - return errno = EBADMSG, false; - - return true; -} - -static inline char -sym(const struct irc_server *s, char mode) -{ - for (size_t i = 0; i < sizeof (s->prefixes); ++i) - if (s->prefixes[i].mode == mode) - return s->prefixes[i].token; - - return '?'; -} - -static inline bool -is_self(const struct irc_server *s, const char *nick) -{ - return strncmp(s->nickname, nick, strlen(s->nickname)) == 0; -} - -#if 0 - static const struct origin * parse_origin(const char *prefix) { @@ -142,18 +100,10 @@ static void add_nick(const struct irc_server *s, struct irc_channel *ch, const char *nick) { - char mode = 0, symbol = 0; + char mode = 0, prefix = 0; - for (size_t i = 0; i < IRC_UTIL_SIZE(s->prefixes); ++i) { - if (nick[0] == s->prefixes[i].token) { - mode = s->prefixes[i].mode; - symbol = s->prefixes[i].token; - ++nick; - break; - } - } - - irc_channel_add(ch, nick, mode, symbol); + irc_server_strip(s, &nick, &mode, &prefix); + irc_channel_add(ch, nick, mode, prefix); } static struct irc_channel * @@ -218,16 +168,20 @@ static void read_support_prefix(struct irc_server *s, const char *value) { - char modes[16 + 1] = {0}; - char tokens[16 + 1] = {0}; + char modes[IRC_UTIL_SIZE(s->params.prefixes) + 1] = {0}; + char tokens[IRC_UTIL_SIZE(s->params.prefixes) + 1] = {0}; + char fmt[32] = {0}; - if (sscanf(value, "(%16[^)])%16s", modes, tokens) == 2) { + snprintf(fmt, sizeof (fmt), "(%%%zu[^)])%%%zus", + sizeof (modes) - 1, sizeof (tokens) - 1); + + if (sscanf(value, fmt, modes, tokens) == 2) { char *pm = modes; char *tk = tokens; - for (size_t i = 0; i < 16 && *pm && *tk; ++i) { - s->prefixes[i].mode = *pm++; - s->prefixes[i].token = *tk++; + for (size_t i = 0; i < IRC_UTIL_SIZE(s->params.prefixes) && *pm && *tk; ++i) { + s->params.prefixes[i].mode = *pm++; + s->params.prefixes[i].token = *tk++; } } } @@ -235,11 +189,11 @@ static void read_support_chantypes(struct irc_server *s, const char *value) { - strlcpy(s->chantypes, value, sizeof (s->chantypes)); + strlcpy(s->params.chantypes, value, sizeof (s->params.chantypes)); } static void -handle_connect(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_connect(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)msg; @@ -255,7 +209,15 @@ } static void -handle_support(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_disconnect(struct irc_server *s, struct irc_event *ev) +{ + s->state = IRC_SERVER_STATE_NONE; + ev->type = IRC_EVENT_DISCONNECT; + ev->server = s; +} + +static void +handle_support(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)ev; @@ -274,7 +236,7 @@ } static void -handle_invite(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_invite(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { ev->type = IRC_EVENT_INVITE; ev->invite.origin = strdup(msg->args[0]); @@ -286,7 +248,7 @@ } static void -handle_join(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_join(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { ev->type = IRC_EVENT_JOIN; ev->join.origin = strdup(msg->prefix); @@ -296,7 +258,7 @@ } static void -handle_kick(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_kick(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { ev->type = IRC_EVENT_KICK; ev->kick.origin = strdup(msg->prefix); @@ -321,7 +283,7 @@ } static void -handle_mode(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_mode(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)s; (void)ev; @@ -339,7 +301,7 @@ } static void -handle_part(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_part(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { struct irc_channel *ch; @@ -357,7 +319,7 @@ } static void -handle_msg(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_msg(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)s; @@ -380,7 +342,7 @@ } static void -handle_nick(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_nick(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { ev->type = IRC_EVENT_NICK; ev->nick.origin = strdup(msg->prefix); @@ -388,11 +350,11 @@ /* Update nickname if it is myself. */ if (is_self(s, ev->nick.origin) == 0) - strlcpy(s->nickname, ev->nick.nickname, sizeof (s->nickname)); + strlcpy(s->ident.nickname, ev->nick.nickname, sizeof (s->ident.nickname)); } static void -handle_notice(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_notice(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)s; @@ -403,7 +365,7 @@ } static void -handle_topic(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_topic(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)s; @@ -414,17 +376,17 @@ } static void -handle_ping(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_ping(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)s; (void)ev; (void)msg; - //irc_server_send(s, "PONG %s", args[1]); + irc_server_send(s, "PONG %s", msg->args[1]); } static void -handle_names(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_names(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)s; (void)ev; @@ -442,7 +404,7 @@ } static void -handle_endofnames(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_endofnames(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)s; (void)ev; @@ -461,8 +423,8 @@ fp = open_memstream(&ev->names.names, &length); LIST_FOREACH(u, &ch->users, link) { - if (u->mode) - fprintf(fp, "%c", sym(s, u->mode)); + if (u->symbol) + fprintf(fp, "%c", u->symbol); fprintf(fp, "%s", u->nickname); @@ -474,7 +436,7 @@ } static void -handle_whoisuser(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_whoisuser(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)s; (void)ev; @@ -487,7 +449,7 @@ } static void -handle_whoischannels(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_whoischannels(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)ev; @@ -514,7 +476,7 @@ } static void -handle_endofwhois(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle_endofwhois(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { (void)msg; @@ -526,7 +488,7 @@ static const struct handler { const char *command; - void (*handle)(struct irc_server *, struct irc_event *, struct message *); + void (*handle)(struct irc_server *, struct irc_event *, struct irc_conn_msg *); } handlers[] = { /* Must be kept ordered. */ { "001", handle_connect }, @@ -562,7 +524,7 @@ } static void -handle(struct irc_server *s, struct irc_event *ev, struct message *msg) +handle(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg) { const struct handler *h; @@ -576,380 +538,18 @@ } static void -clear(struct irc_server *s) -{ - struct irc_channel *ch, *tmp; - - s->state = IRC_SERVER_STATE_DISCONNECTED; - - if (s->fd != 0) { - close(s->fd); - s->fd = 0; - } - - if (s->ai) { - freeaddrinfo(s->ai); - s->ai = NULL; - s->aip = NULL; - } - -#if defined(IRCCD_WITH_SSL) - if (s->ssl) { - SSL_free(s->ssl); - s->ssl = NULL; - } - if (s->ctx) { - SSL_CTX_free(s->ctx); - s->ctx = NULL; - } -#endif - - LIST_FOREACH_SAFE(ch, &s->channels, link, tmp) { - irc_channel_finish(ch); - free(ch); - } - LIST_INIT(&s->channels); -} - -static bool -lookup(struct irc_server *s) -{ - struct addrinfo hints = { - .ai_socktype = SOCK_STREAM, - }; - char service[16]; - int ret; - - snprintf(service, sizeof (service), "%hu", s->port); - - if ((ret = getaddrinfo(s->hostname, service, &hints, &s->ai)) != 0) - irc_log_warn("server %s: %s", s->name, gai_strerror(ret)); - - s->aip = s->ai; - - return true; -} - -static void auth(struct irc_server *s) { s->state = IRC_SERVER_STATE_CONNECTED; - if (s->password[0]) - irc_server_send(s, "PASS %s", s->password); - - irc_server_send(s, "NICK %s", s->nickname); - irc_server_send(s, "USER %s %s %s :%s", s->username, - s->username, s->username, s->realname); -} - -#if defined(IRCCD_WITH_SSL) - -static void -update(struct irc_server *s, int ret) -{ - (void)s; - (void)ret; - - assert(s); - - if (!(s->flags & IRC_SERVER_FLAGS_SSL)) - return; - - switch (SSL_get_error(s->ssl, ret)) { - case SSL_ERROR_WANT_READ: - s->ssl_state = IRC_SERVER_SSL_NEED_READ; - break; - case SSL_ERROR_WANT_WRITE: - s->ssl_state = IRC_SERVER_SSL_NEED_WRITE; - break; - case SSL_ERROR_SSL: - clear(s); - break; - default: - s->ssl_state = IRC_SERVER_SSL_NONE; - break; - } -} - -#endif - -static void -handshake(struct irc_server *s) -{ - assert(s); - - if (!(s->flags & IRC_SERVER_FLAGS_SSL)) - auth(s); - else { -#if defined(IRCCD_WITH_SSL) - int r; - - s->state = IRC_SERVER_STATE_HANDSHAKING; - - if ((r = SSL_do_handshake(s->ssl)) > 0) - auth(s); - - update(s, r); -#endif - } -} - -static void -try_connect(struct irc_server *s) -{ - assert(s); - - if (!(s->flags & IRC_SERVER_FLAGS_SSL)) - handshake(s); - else { -#if defined(IRCCD_WITH_SSL) - if (!s->ctx) - s->ctx = SSL_CTX_new(TLS_method()); - if (!s->ssl) { - s->ssl = SSL_new(s->ctx); - SSL_set_fd(s->ssl, s->fd); - } - - SSL_set_connect_state(s->ssl); - handshake(s); -#endif - } -} - -static bool -set_nonblock(struct irc_server *s) -{ - int cflags = 0; - - if ((cflags = fcntl(s->fd, F_GETFL)) < 0 || - fcntl(s->fd, F_SETFL, cflags | O_NONBLOCK) < 0) - return false; - - return true; -} - -static bool -create(struct irc_server *s) -{ - s->fd = socket(s->aip->ai_family, s->aip->ai_socktype, - s->aip->ai_protocol); - - return set_nonblock(s); -} - -static void -dial(struct irc_server *s) -{ - /* No more address available. */ - if (s->aip == NULL) { - clear(s); - return; - } - - for (; s->aip; s->aip = s->aip->ai_next) { - /* We may need to close a socket that was open earlier. */ - if (s->fd != 0) - close(s->fd); - - if (!create(s)) { - irc_log_warn("server %s: %s", s->name, strerror(errno)); - continue; - } - - /* - * With some luck, the connection completes immediately, - * otherwise we will need to wait until the socket is writable. - */ - if (connect(s->fd, s->aip->ai_addr, s->aip->ai_addrlen) == 0) { - try_connect(s); - break; - } - - /* Connect failed, check why. */ - switch (errno) { - case EINPROGRESS: - case EAGAIN: - /* Let the writable state to determine. */ - return; - default: - irc_log_warn("server %s: %s", s->name, strerror(errno)); - break; - } - } -} - -static size_t -input_ssl(struct irc_server *s, char *dst, size_t dstsz) -{ - int nr; - - if ((nr = SSL_read(s->ssl, dst, dstsz)) <= 0) { - update(s, nr); - return 0; - } - - s->ssl_state = IRC_SERVER_SSL_NONE; + if (s->ident.password[0]) + irc_server_send(s, "PASS %s", s->ident.password); - return nr; -} - -static size_t -input_clear(struct irc_server *s, char *buf, size_t bufsz) -{ - ssize_t nr; - - if ((nr = recv(s->fd, buf, bufsz, 0)) <= 0) { - clear(s); - return 0; - } - - return nr; -} - -static void -input(struct irc_server *s) -{ - size_t len = strlen(s->in); - size_t cap = sizeof (s->in) - len - 1; - size_t nr = 0; - - if (s->flags & IRC_SERVER_FLAGS_SSL) { -#if defined(IRCCD_WITH_SSL) - nr = input_ssl(s, s->in + len, cap); -#endif - } else - nr = input_clear(s, s->in + len, cap); - - if (nr > 0) - s->in[len + nr] = '\0'; -} - -static void -output(struct irc_server *s) -{ - ssize_t ns = 0; - - if (s->flags & IRC_SERVER_FLAGS_SSL) { -#if defined(IRCCD_WITH_SSL) - ns = SSL_write(s->ssl, s->out, strlen(s->out)); - update(s, ns); -#endif - } else if ((ns = send(s->fd, s->out, strlen(s->out), 0)) <= 0) - clear(s); - - if (ns > 0) { - /* Optimize if everything was sent. */ - if ((size_t)ns >= sizeof (s->out)) - s->out[0] = '\0'; - else - memmove(s->out, s->out + ns, sizeof (s->out) - ns); - } -} - -static void -prepare_connecting(const struct irc_server *s, struct pollfd *pfd) -{ - (void)s; - -#if defined(IRCCD_WITH_SSL) - if (s->flags & IRC_SERVER_FLAGS_SSL && s->ssl && s->ctx) { - switch (s->ssl_state) { - case IRC_SERVER_SSL_NEED_READ: - pfd->events |= POLLIN; - break; - case IRC_SERVER_SSL_NEED_WRITE: - pfd->events |= POLLOUT; - break; - default: - break; - } - } else -#endif - pfd->events |= POLLOUT; + irc_server_send(s, "USER %s %s %s :%s", s->ident.username, + s->ident.username, s->ident.username, s->ident.realname); + irc_server_send(s, "NICK %s", s->ident.nickname); } -static void -prepare_ready(const struct irc_server *s, struct pollfd *pfd) -{ -#if defined(IRCCD_WITH_SSL) - if (s->flags & IRC_SERVER_FLAGS_SSL && s->ssl_state) { - switch (s->ssl_state) { - case IRC_SERVER_SSL_NEED_READ: - pfd->events |= POLLIN; - break; - case IRC_SERVER_SSL_NEED_WRITE: - pfd->events |= POLLOUT; - break; - default: - break; - } - } else { -#endif - pfd->events |= POLLIN; - - if (s->out[0]) - pfd->events |= POLLOUT; -#if defined(IRCCD_WITH_SSL) - } -#endif -} - -static void -flush_connecting(struct irc_server *s, const struct pollfd *pfd) -{ - (void)pfd; - - int res, err = -1; - socklen_t len = sizeof (int); - - if ((res = getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &err, &len)) < 0 || err) { - irc_log_warn("server %s: %s", s->name, strerror(res ? err : errno)); - dial(s); - } else - try_connect(s); -} - -static void -flush_handshaking(struct irc_server *s, const struct pollfd *pfd) -{ - (void)pfd; - - handshake(s); -} - -static void -flush_ready(struct irc_server *s, const struct pollfd *pfd) -{ - if (pfd->revents & POLLERR || pfd->revents & POLLHUP) { - clear(s); - return; - } - - if (pfd->revents & POLLIN) - input(s); - if (pfd->revents & POLLOUT) - output(s); -} - -static const struct { - void (*prepare)(const struct irc_server *, struct pollfd *); - void (*flush)(struct irc_server *, const struct pollfd *); -} io_table[] = { - [IRC_SERVER_STATE_CONNECTING] = { - prepare_connecting, - flush_connecting - }, - [IRC_SERVER_STATE_HANDSHAKING] = { - prepare_ready, - flush_handshaking - }, - [IRC_SERVER_STATE_CONNECTED] = { - prepare_ready, - flush_ready - }, -}; - struct irc_server * irc_server_new(const char *name, const char *nickname, @@ -967,14 +567,18 @@ struct irc_server *s; s = irc_util_calloc(1, sizeof (*s)); - s->port = port; + + /* Connection. */ + s->conn.port = port; + strlcpy(s->conn.hostname, hostname, sizeof (s->conn.hostname)); + /* Identity. */ + strlcpy(s->ident.nickname, nickname, sizeof (s->ident.nickname)); + strlcpy(s->ident.username, username, sizeof (s->ident.username)); + strlcpy(s->ident.realname, realname, sizeof (s->ident.realname)); + + /* Server itslf. */ strlcpy(s->name, name, sizeof (s->name)); - strlcpy(s->nickname, nickname, sizeof (s->nickname)); - strlcpy(s->username, username, sizeof (s->username)); - strlcpy(s->realname, realname, sizeof (s->realname)); - strlcpy(s->hostname, hostname, sizeof (s->hostname)); - LIST_INIT(&s->channels); return s; @@ -985,12 +589,10 @@ { assert(s); - s->state = IRC_SERVER_STATE_CONNECTING; - - if (!lookup(s)) - clear(s); + if (irc_conn_connect(&s->conn)) + s->state = IRC_SERVER_STATE_CONNECTING; else - dial(s); + s->state = IRC_SERVER_STATE_DISCONNECTED; } void @@ -998,27 +600,45 @@ { assert(s); - clear(s); + s->state = IRC_SERVER_STATE_DISCONNECTED; + + irc_conn_disconnect(&s->conn); + + clear_channels(s, false); + clear_server(s); } void irc_server_prepare(const struct irc_server *s, struct pollfd *pfd) { - pfd->fd = s->fd; - pfd->events = 0; + assert(s); + assert(pfd); - if (io_table[s->state].prepare) - io_table[s->state].prepare(s, pfd); + irc_conn_prepare(&s->conn, pfd); } void irc_server_flush(struct irc_server *s, const struct pollfd *pfd) { - if (pfd->fd != s->fd) + assert(s); + assert(pfd); + + if (!irc_conn_flush(&s->conn, pfd)) + return irc_server_disconnect(s); + if (s->conn.state != IRC_CONN_STATE_READY) return; - if (io_table[s->state].flush) - io_table[s->state].flush(s, pfd); + switch (s->state) { + case IRC_SERVER_STATE_CONNECTING: + /* + * Now the conn object is ready which means the server has + * to authenticate. + */ + auth(s); + break; + default: + break; + } } bool @@ -1027,23 +647,14 @@ assert(s); assert(ev); - struct message msg; - char *pos; - size_t length; - - if (!(pos = strstr(s->in, "\r\n"))) - return false; + struct irc_conn_msg msg = {0}; - /* Turn end of the string at delimiter. */ - *pos = 0; - length = pos - s->in; + if (irc_conn_poll(&s->conn, &msg)) + return handle(s, ev, &msg), true; + if (s->state == IRC_SERVER_STATE_DISCONNECTED) + return handle_disconnect(s, ev), true; - if (length > 0 && parse(&msg, s->in)) - handle(s, ev, &msg); - - memmove(s->in, pos + 2, sizeof (s->in) - (length + 2)); - - return true; + return false; } struct irc_channel * @@ -1069,23 +680,12 @@ char buf[IRC_BUF_LEN]; va_list ap; - size_t len, avail, required; va_start(ap, fmt); - required = vsnprintf(buf, sizeof (buf), fmt, ap); + vsnprintf(buf, sizeof (buf), fmt, ap); va_end(ap); - len = strlen(s->out); - avail = sizeof (s->out) - len; - - /* Don't forget \r\n. */ - if (required + 2 >= avail) - return false; - - strlcat(s->out, buf, sizeof (s->out)); - strlcat(s->out, "\r\n", sizeof (s->out)); - - return true; + return irc_conn_send(&s->conn, buf); } bool @@ -1218,8 +818,8 @@ assert(s); assert(nick); - if (s->state == IRC_SERVER_STATE_DISCONNECTED) { - strlcpy(s->nickname, nick, sizeof (s->nickname)); + if (s->state <= IRC_SERVER_STATE_DISCONNECTED) { + strlcpy(s->ident.nickname, nick, sizeof (s->ident.nickname)); return true; } @@ -1242,34 +842,28 @@ assert(s); assert(target); -#if 0 - /* Cleanup previous result. */ - free(s->whois.channels); - memset(&s->whois, 0, sizeof (s->whois)); -#endif - return irc_server_send(s, "WHOIS %s", target); } -struct irc_server_namemode -irc_server_strip(const struct irc_server *s, const char *item) +void +irc_server_strip(const struct irc_server *s, const char **nick, char *mode, char *prefix) { assert(s); - assert(item); - - struct irc_server_namemode ret = {0}; + assert(*nick); - for (size_t i = 0; i < IRC_UTIL_SIZE(s->prefixes); ++i) { - if (item[0] == s->prefixes[i].token) { - ret.mode = s->prefixes[i].mode; - ++item; + if (mode) + *mode = 0; + if (prefix) + *mode = 0; + + for (size_t i = 0; i < IRC_UTIL_SIZE(s->params.prefixes); ++i) { + if (**nick == s->params.prefixes[i].token) { + *mode = s->params.prefixes[i].mode; + *prefix = s->params.prefixes[i].token; + *nick += 1; break; } } - - ret.name = (char *)item; - - return ret; } void @@ -1287,7 +881,7 @@ assert(s->refc >= 1); if (--s->refc == 0) { - clear(s); + clear_channels(s, true); free(s); } }
--- a/lib/irccd/server.h Thu Jan 21 23:15:20 2021 +0100 +++ b/lib/irccd/server.h Sun Jan 24 18:31:50 2021 +0100 @@ -23,12 +23,8 @@ #include <stdbool.h> #include <stddef.h> -#include "config.h" - -#if defined(IRCCD_WITH_SSL) -# include <openssl/ssl.h> -#endif - +#include "channel.h" +#include "conn.h" #include "event.h" #include "limits.h" @@ -37,9 +33,9 @@ struct irc_channel; enum irc_server_state { + IRC_SERVER_STATE_NONE, IRC_SERVER_STATE_DISCONNECTED, IRC_SERVER_STATE_CONNECTING, - IRC_SERVER_STATE_HANDSHAKING, IRC_SERVER_STATE_CONNECTED, IRC_SERVER_STATE_WAITING, IRC_SERVER_STATE_NUM @@ -53,70 +49,33 @@ IRC_SERVER_FLAGS_IPV6 = (1 << 4) }; -struct irc_server_prefix { - char mode; - char token; +struct irc_server_ident { + char nickname[IRC_NICKNAME_LEN]; + char username[IRC_USERNAME_LEN]; + char realname[IRC_REALNAME_LEN]; + char password[IRC_PASSWORD_LEN]; + char ctcpversion[IRC_CTCPVERSION_LEN]; }; -#if defined(IRCCD_WITH_SSL) - -enum irc_server_ssl_state { - IRC_SERVER_SSL_NONE, - IRC_SERVER_SSL_NEED_READ, - IRC_SERVER_SSL_NEED_WRITE, -}; - -#endif - -struct irc_server_namemode { - char mode; - char sym; - char *name; +struct irc_server_params { + char chantypes[IRC_CHANTYPES_LEN]; + struct { + char mode; /* Mode (e.g. ov). */ + char token; /* Symbol used (e.g. @+). */ + } prefixes[IRC_USERMODES_LEN]; }; struct irc_server { - /* Connection settings. */ char name[IRC_ID_LEN]; - char hostname[IRC_HOST_LEN]; - char password[IRC_PASSWORD_LEN]; - unsigned short port; - enum irc_server_flags flags; - - /* Plugin prefix. */ char commandchar[IRC_CMDCHAR_LEN]; - - /* IRC identity. */ - char nickname[IRC_NICKNAME_LEN]; - char username[IRC_USERNAME_LEN]; - char realname[IRC_REALNAME_LEN]; - char ctcpversion[IRC_CTCPVERSION_LEN]; - - LIST_HEAD(, irc_channel) channels; - - /* Network connectivity. */ - int fd; - struct addrinfo *ai; - struct addrinfo *aip; - char in[IRC_BUF_LEN]; - char out[IRC_BUF_LEN]; + struct irc_server_ident ident; + struct irc_server_params params; enum irc_server_state state; - - /* OpenSSL support. */ -#if defined(IRCCD_WITH_SSL) - SSL_CTX *ctx; - SSL *ssl; - enum irc_server_ssl_state ssl_state; -#endif - + enum irc_server_flags flags; + struct irc_channel_list channels; struct irc_event_whois bufwhois; - - /* Reference count. */ + struct irc_conn conn; size_t refc; - - /* IRC server settings. */ - char chantypes[8]; - struct irc_server_prefix prefixes[16]; - LIST_ENTRY(irc_server) link; }; @@ -192,8 +151,8 @@ bool irc_server_whois(struct irc_server *, const char *); -struct irc_server_namemode -irc_server_strip(const struct irc_server *, const char *); +void +irc_server_strip(const struct irc_server *, const char **, char *, char *); void irc_server_incref(struct irc_server *);