Mercurial > irccd
changeset 940:94cae3129870
irccd: add begin of transports
author | David Demelier <markand@malikania.fr> |
---|---|
date | Thu, 14 Jan 2021 10:46:41 +0100 |
parents | a62c56c8b5ca |
children | e43ccb1f0ace |
files | .hgignore Makefile config.mk irccd/main.c lib/irccd/config.h.in lib/irccd/peer.c lib/irccd/peer.h lib/irccd/plugin.c lib/irccd/server.c lib/irccd/transport.c lib/irccd/transport.h |
diffstat | 11 files changed, 687 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Wed Jan 13 17:18:35 2021 +0100 +++ b/.hgignore Thu Jan 14 10:46:41 2021 +0100 @@ -21,6 +21,7 @@ irccd/irccd # temporary files. +^lib/irccd/config\.h$ \.a$ \.o$ \.d$
--- a/Makefile Wed Jan 13 17:18:35 2021 +0100 +++ b/Makefile Thu Jan 14 10:46:41 2021 +0100 @@ -21,6 +21,10 @@ include config.mk +MAJOR= 4 +MINOR= 0 +PATCH= 0 + IRCCD= irccd/irccd IRCCD_SRCS= irccd/main.c IRCCD_OBJS= ${IRCCD_SRCS:.c=.o} @@ -36,9 +40,11 @@ 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 LIBIRCCD_SRCS+= lib/irccd/plugin.c LIBIRCCD_SRCS+= lib/irccd/server.c LIBIRCCD_SRCS+= lib/irccd/subst.c +LIBIRCCD_SRCS+= lib/irccd/transport.c LIBIRCCD_SRCS+= lib/irccd/util.c ifeq (${WITH_JS},yes) @@ -86,6 +92,10 @@ endif LIBS+= -l irccd +ifeq (${WITH_SSL},yes) +LIBS+= -l ssl -l crypto +endif + all: ${IRCCD} .c.o: @@ -102,7 +112,21 @@ ${MAKE} CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" -C extern/libduktape endif -${LIBIRCCD_OBJS}: ${LIBCOMPAT} +ifneq (${WITH_JS},yes) +EXTRA_SEDS+= -e "/IRCCD_WITH_JS/d" +endif + +ifneq (${WITH_SSL},yes) +EXTRA_SEDS+= -e "/IRCCD_WITH_SSL/d" +endif + +lib/irccd/config.h: lib/irccd/config.h.in Makefile config.mk + sed -e "s/@IRCCD_VERSION_MAJOR@/${MAJOR}/" \ + -e "s/@IRCCD_VERSION_MINOR@/${MINOR}/" \ + -e "s/@IRCCD_VERSION_PATCH@/${PATCH}/" \ + ${EXTRA_SEDS} < $< > $@ + +${LIBIRCCD_OBJS}: ${LIBCOMPAT} lib/irccd/config.h ${LIBIRCCD}: ${LIBIRCCD_OBJS} ${CMD.ar}
--- a/config.mk Wed Jan 13 17:18:35 2021 +0100 +++ b/config.mk Thu Jan 14 10:46:41 2021 +0100 @@ -29,6 +29,7 @@ # User options. WITH_JS= yes +WITH_SSL= no # System dependant macros. OS:= $(shell uname -s)
--- a/irccd/main.c Wed Jan 13 17:18:35 2021 +0100 +++ b/irccd/main.c Thu Jan 14 10:46:41 2021 +0100 @@ -25,6 +25,7 @@ #include <irccd/plugin.h> #include <irccd/log.h> #include <irccd/server.h> +#include <irccd/transport.h> static struct irc_plugin js = { .name = "example" @@ -53,11 +54,13 @@ irc_init(); irc_log_set_verbose(true); irc_server_join(&s, "#test", NULL); - //irc_server_join(&freenode, "#irccd", NULL); + irc_transport_bind("/tmp/irccd.sock"); irc_add_server(&s); irc_add_server(&freenode); +#if 0 if (!irc_js_plugin_open(&js, "/Users/markand/Dev/irccd-4/test.js")) return 1; irc_add_plugin(&js); +#endif irc_run(); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/config.h.in Thu Jan 14 10:46:41 2021 +0100 @@ -0,0 +1,11 @@ +#ifndef IRCCD_CONFIG_H +#define IRCCD_CONFIG_H + +#define IRCCD_VERSION_MAJOR @IRCCD_VERSION_MAJOR@ +#define IRCCD_VERSION_MINOR @IRCCD_VERSION_MINOR@ +#define IRCCD_VERSION_PATCH @IRCCD_VERSION_PATCH@ + +#define IRCCD_WITH_JS +#define IRCCD_WITH_SSL + +#endif /* !IRCCD_CONFIG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/peer.c Thu Jan 14 10:46:41 2021 +0100 @@ -0,0 +1,421 @@ +/* + * peer.c -- client connected to irccd + * + * 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 <ctype.h> +#include <errno.h> +#include <poll.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <assert.h> + +#include "irccd.h" +#include "log.h" +#include "peer.h" +#include "server.h" +#include "util.h" + +static size_t +parse(char *line, const char **args, size_t max) +{ + size_t idx; + + /* Skip command. */ + while (*line && !isspace(*line++)) + continue; + + if (!*line) + return 0; + + for (idx = 0; idx < max; ++idx) { + char *sp = strchr(line, ' '); + + if (!sp || idx + 1 >= max) { + args[idx++] = line; + break; + } + + *sp = '\0'; + args[idx] = line; + line = sp + 1; + } + + return idx; +} + +static struct irc_server * +require_server(struct irc_peer *p, const char *id) +{ + struct irc_server *s; + + if (!(s = irc_find_server(id))) { + irc_peer_send(p, "server %s not found", id); + return NULL; + } + + return s; +} + +/* + * MESSAGE server channel message + */ +static int +cmd_message(struct irc_peer *p, char *line) +{ + const char *args[3] = {0}; + struct irc_server *s; + + if (parse(line, args, 3) != 3) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_message(s, args[1], args[2]); + + return 0; +} + +/* + * ME server channel message + */ +static int +cmd_me(struct irc_peer *p, char *line) +{ + const char *args[3] = {0}; + struct irc_server *s; + + if (parse(line, args, 3) != 3) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_me(s, args[1], args[2]); + + return 0; +} + +/* + * MODE server channel mode [limit] [user] [mask] + */ +static int +cmd_mode(struct irc_peer *p, char *line) +{ + const char *args[6] = {0}; + struct irc_server *s; + + if (parse(line, args, 6) < 3) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_mode(s, args[1], args[2], + args[3][0] ? args[3] : NULL, + args[4][0] ? args[4] : NULL, + args[5][0] ? args[5] : NULL + ); + + return 0; +} + +/* + * NOTICE server channel message + */ +static int +cmd_notice(struct irc_peer *p, char *line) +{ + const char *args[3] = {0}; + struct irc_server *s; + + if (parse(line, args, 3) != 3) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_notice(s, args[1], args[2]); + + return 0; +} + +/* + * INVITE server channel target + */ +static int +cmd_invite(struct irc_peer *p, char *line) +{ + const char *args[3] = {0}; + struct irc_server *s; + + if (parse(line, args, 3) != 3) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_invite(s, args[1], args[2]); + + return 0; +} + +/* + * JOIN server channel [password] + */ +static int +cmd_join(struct irc_peer *p, char *line) +{ + const char *args[3] = {0}; + struct irc_server *s; + + if (parse(line, args, 3) < 2) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_join(s, args[1], args[2][0] ? args[2] : NULL); + + return 0; +} + +/* + * KICK server channel target [reason] + */ +static int +cmd_kick(struct irc_peer *p, char *line) +{ + const char *args[4] = {0}; + struct irc_server *s; + + if (parse(line, args, 4) < 3) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_kick(s, args[1], args[2], args[3][0] ? args[3] : NULL); + + return 0; +} + +/* + * PART server channel [reason] + */ +static int +cmd_part(struct irc_peer *p, char *line) +{ + const char *args[3] = {0}; + struct irc_server *s; + + if (parse(line, args, 3) < 2) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return -10; + + irc_server_part(s, args[1], args[2][0] ? args[2] : NULL); + + return 0; +} + +/* + * TOPIC server channel topic + */ +static int +cmd_topic(struct irc_peer *p, char *line) +{ + const char *args[3] = {0}; + struct irc_server *s; + + if (parse(line, args, 3) != 3) + return EINVAL; + if (!(s = require_server(p, args[0]))) + return 0; + + irc_server_topic(s, args[1], args[2]); + + return 0; +} + +static const struct cmd { + const char *name; + int (*call)(struct irc_peer *, char *); +} cmds[] = { + { "INVITE", cmd_invite }, + { "JOIN", cmd_join }, + { "KICK", cmd_kick }, + { "ME", cmd_me }, + { "MESSAGE", cmd_message }, + { "MODE", cmd_mode }, + { "NOTICE", cmd_notice }, + { "PART", cmd_part }, + { "TOPIC", cmd_topic }, +}; + +static int +cmp_cmd(const void *d1, const void *d2) +{ + const char *key = d1; + const struct cmd *cmd = d2; + + return strncmp(cmd->name, key, strlen(cmd->name)); +} + +static const struct cmd * +find(const char *line) +{ + return bsearch(line, cmds, IRC_UTIL_SIZE(cmds), sizeof (struct cmd), cmp_cmd); +} + +static void +invoke(struct irc_peer *p, char *line) +{ + const struct cmd *c = find(line); + int er; + + if (!c) + irc_peer_send(p, "command not found"); + else if ((er = c->call(p, line)) != 0 && er != -1) + irc_peer_send(p, "%s", strerror(errno)); + else + irc_peer_send(p, "OK"); +} + +static void +dispatch(struct irc_peer *p) +{ + char *pos; + size_t length; + + while ((pos = strstr(p->in, "\n"))) { + /* Turn end of the string at delimiter. */ + *pos = 0; + length = pos - p->in; + + if (length > 0) + invoke(p, p->in); + + memmove(p->in, pos + 1, sizeof (p->in) - (length + 1)); + } +} + +static bool +input(struct irc_peer *p) +{ + char buf[BUFSIZ + 1]; + ssize_t nr; + + if ((nr = recv(p->fd, buf, BUFSIZ, 0)) <= 0) { + irc_log_info("transport: client disconnect"); + return false; + } + + buf[nr] = '\0'; + + if (strlcat(p->in, buf, sizeof (p->in)) >= sizeof (p->in)) { + errno = EMSGSIZE; + return false; + } + + dispatch(p); + + return true; +} + +static bool +output(struct irc_peer *p) +{ + ssize_t ns; + size_t len = strlen(p->out); + + if ((ns = send(p->fd, p->out, len, 0)) < 0) + return false; + + if (ns >= len) + memset(p->out, 0, sizeof (p->out)); + else + memmove(p->out, p->out + ns, sizeof (p->out) - ns); + + return true; +} + +bool +irc_peer_send(struct irc_peer *p, const char *fmt, ...) +{ + assert(p); + assert(fmt); + + char buf[IRC_BUF_MAX]; + va_list ap; + size_t len, avail, required; + + va_start(ap, fmt); + required = vsnprintf(buf, sizeof (buf), fmt, ap); + va_end(ap); + + len = strlen(p->out); + avail = sizeof (p->out) - len; + + /* Don't forget \n. */ + if (required + 1 >= avail) + return false; + + strlcat(p->out, buf, sizeof (p->out)); + strlcat(p->out, "\n", sizeof (p->out)); + + return true; +} + +void +irc_peer_prepare(struct irc_peer *p, struct pollfd *fd) +{ + assert(p); + assert(fd); + + fd->fd = p->fd;; + fd->events = POLLIN; + + if (p->out[0]) + fd->events |= POLLOUT; +} + +bool +irc_peer_flush(struct irc_peer *p, const struct pollfd *fd) +{ + assert(p); + assert(fd); + + char buf[IRC_BUF_MAX]; + + if (fd->fd != p->fd) + return true; + + if (fd->revents & POLLIN && !input(p)) + return false; + if (fd->revents & POLLOUT && !output(p)) + return false; + + return true; +} + +void +irc_peer_finish(struct irc_peer *p) +{ + assert(p); + + close(p->fd); + memset(p, 0, sizeof (*p)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/peer.h Thu Jan 14 10:46:41 2021 +0100 @@ -0,0 +1,46 @@ +/* + * peer.h -- client connected to irccd + * + * 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_PEER_H +#define IRCCD_PEER_H + +#include <stdbool.h> + +#include "limits.h" + +struct pollfd; + +struct irc_peer { + int fd; + char in[IRC_BUF_MAX]; + char out[IRC_BUF_MAX]; +}; + +bool +irc_peer_send(struct irc_peer *, const char *, ...); + +void +irc_peer_prepare(struct irc_peer *, struct pollfd *); + +bool +irc_peer_flush(struct irc_peer *, const struct pollfd *); + +void +irc_peer_finish(struct irc_peer *); + +#endif /* !IRCCD_PEER_H */
--- a/lib/irccd/plugin.c Wed Jan 13 17:18:35 2021 +0100 +++ b/lib/irccd/plugin.c Thu Jan 14 10:46:41 2021 +0100 @@ -18,6 +18,7 @@ #include <assert.h> #include <stddef.h> +#include <string.h> #include "plugin.h" @@ -167,4 +168,6 @@ if (plg->finish) plg->finish(plg); + + memset(plg, 0, sizeof (*plg)); }
--- a/lib/irccd/server.c Wed Jan 13 17:18:35 2021 +0100 +++ b/lib/irccd/server.c Thu Jan 14 10:46:41 2021 +0100 @@ -437,7 +437,6 @@ static void clear(struct irc_server *s) { - puts("clear..."); s->state = IRC_SERVER_STATE_DISCONNECTED; if (s->fd != 0) { @@ -784,7 +783,6 @@ { assert(s); - puts("HERE?!"); clear(s); } @@ -855,7 +853,7 @@ assert(s); assert(fmt); - char buf[sizeof (s->out)]; + char buf[IRC_BUF_MAX]; va_list ap; size_t len, avail, required; @@ -877,11 +875,11 @@ } bool -irc_server_invite(struct irc_server *s, const char *target, const char *channel) +irc_server_invite(struct irc_server *s, const char *channel, const char *target) { assert(s); + assert(channel); assert(target); - assert(channel); return irc_server_send(s, "INVITE %s %s", target, channel); } @@ -914,11 +912,11 @@ } bool -irc_server_kick(struct irc_server *s, const char *target, const char *channel, const char *reason) +irc_server_kick(struct irc_server *s, const char *channel, const char *target, const char *reason) { assert(s); + assert(channel); assert(target); - assert(channel); bool ret; @@ -931,29 +929,29 @@ } bool -irc_server_part(struct irc_server *s, const char *name, const char *reason) +irc_server_part(struct irc_server *s, const char *channel, const char *reason) { assert(s); - assert(name); + assert(channel); bool ret; if (reason && strlen(reason) > 0) - ret = irc_server_send(s, "PART %s :%s", name, reason); + ret = irc_server_send(s, "PART %s :%s", channel, reason); else - ret = irc_server_send(s, "PART %s", name); + ret = irc_server_send(s, "PART %s", channel); return ret; } bool -irc_server_topic(struct irc_server *s, const char *name, const char *topic) +irc_server_topic(struct irc_server *s, const char *channel, const char *topic) { assert(s); - assert(name); + assert(channel); assert(topic); - return irc_server_send(s, "TOPIC %s :%s", name, topic); + return irc_server_send(s, "TOPIC %s :%s", channel, topic); } bool @@ -995,13 +993,13 @@ } bool -irc_server_notice(struct irc_server *s, const char *target, const char *message) +irc_server_notice(struct irc_server *s, const char *channel, const char *message) { assert(s); - assert(target); + assert(channel); assert(message); - return irc_server_send(s, "NOTICE %s: %s", target, message); + return irc_server_send(s, "NOTICE %s: %s", channel, message); } void
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/transport.c Thu Jan 14 10:46:41 2021 +0100 @@ -0,0 +1,118 @@ +/* + * transport.c -- remote command support + * + * 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 <sys/types.h> +#include <assert.h> +#include <errno.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/un.h> +#include <unistd.h> + +#include "config.h" +#include "log.h" +#include "transport.h" +#include "peer.h" + +static struct sockaddr_un addr; +static int fd = -1; + +bool +irc_transport_bind(const char *path) +{ + assert(path); + + /* Silently remove the file first. */ + if (strlcpy(addr.sun_path, path, sizeof (addr.sun_path)) >= sizeof (addr.sun_path)) { + errno = ENAMETOOLONG; + goto err; + } + + unlink(addr.sun_path); + + if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) + goto err; + if (bind(fd, (const struct sockaddr *)&addr, sizeof (addr)) < 0) + goto err; + if (listen(fd, 16) < 0) + goto err; + + irc_log_info("transport: listening on %s", path); + irc_log_debug("transport: file descriptor %d", fd); + + return true; + +err: + irc_log_warn("transport: %s: %s", path, strerror(errno)); + + if (fd != -1) { + close(fd); + fd = -1; + } + + return -1; +} + +void +irc_transport_prepare(struct pollfd *pfd) +{ + assert(pfd); + + if (fd < 0) + return; + + pfd->fd = fd; + pfd->events = POLLIN; +} + +bool +irc_transport_flush(const struct pollfd *pfd, struct irc_peer *peer) +{ + assert(pfd); + assert(peer); + + if (fd < 0 || pfd->fd != fd || !(pfd->revents & POLLIN)) + return false; + + memset(peer, 0, sizeof (*peer)); + + if ((peer->fd = accept(fd, NULL, NULL)) < 0) { + irc_log_warn("transport: %s", strerror(errno)); + return false; + } + + irc_log_info("transport: new client connected"); + irc_peer_send(peer, "IRCCD %d.%d.%d", IRCCD_VERSION_MAJOR, + IRCCD_VERSION_MINOR, IRCCD_VERSION_PATCH); + + return true; +} + +void +irc_transport_finish(void) +{ + /* Connection socket. */ + if (fd != -1) + close(fd); + + unlink(addr.sun_path); + memset(&addr, 0, sizeof (addr)); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/transport.h Thu Jan 14 10:46:41 2021 +0100 @@ -0,0 +1,42 @@ +/* + * transport.h -- remote command support + * + * 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_TRANSPORT_H +#define IRCCD_TRANSPORT_H + +#include <stdbool.h> + +#include "limits.h" + +struct pollfd; + +struct irc_peer; + +bool +irc_transport_bind(const char *); + +void +irc_transport_prepare(struct pollfd *); + +bool +irc_transport_flush(const struct pollfd *, struct irc_peer *); + +void +irc_transport_finish(void); + +#endif /* !IRCCD_TRANSPORT_H */