diff options
author | June McEnroe <june@causal.agency> | 2019-10-23 00:16:31 -0400 |
---|---|---|
committer | June McEnroe <june@causal.agency> | 2019-10-23 00:16:31 -0400 |
commit | e0d292cb2fd1b535d601cfc45c370533919c8420 (patch) | |
tree | d58f25c659ca993aa7c3c7716010f2505f120985 | |
parent | Add README.7 (diff) | |
download | pounce-e0d292cb2fd1b535d601cfc45c370533919c8420.tar.gz pounce-e0d292cb2fd1b535d601cfc45c370533919c8420.zip |
Add state
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | bounce.c | 6 | ||||
-rw-r--r-- | bounce.h | 7 | ||||
-rw-r--r-- | server.c | 32 | ||||
-rw-r--r-- | state.c | 138 |
5 files changed, 183 insertions, 1 deletions
diff --git a/Makefile b/Makefile index 2a78fab..72d0f05 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,7 @@ LDLIBS = -ltls OBJS += bounce.o OBJS += listen.o OBJS += server.o +OBJS += state.o all: tags linger diff --git a/bounce.c b/bounce.c index 43d3ef8..ec89945 100644 --- a/bounce.c +++ b/bounce.c @@ -95,7 +95,11 @@ int main(int argc, char *argv[]) { int server = serverConnect(host, port); serverLogin(pass, auth, nick, user, real); - // TODO: Wait for successful login before listening. + while (!stateReady()) { + serverRecv(); + } + if (join) serverJoin(join); + for (size_t i = 0; i < bindLen; ++i) { int error = listen(bind[i], 1); if (error) err(EX_IOERR, "listen"); diff --git a/bounce.h b/bounce.h index 999180f..e442e47 100644 --- a/bounce.h +++ b/bounce.h @@ -14,6 +14,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <stdbool.h> #include <stdlib.h> #include <tls.h> @@ -34,4 +35,10 @@ void serverLogin( const char *pass, const char *auth, const char *nick, const char *user, const char *real ); +void serverAuth(void); +void serverJoin(const char *join); void serverSend(const char *ptr, size_t len); +void serverRecv(void); + +bool stateReady(void); +void stateParse(char *line); diff --git a/server.c b/server.c index 4c22287..628ced7 100644 --- a/server.c +++ b/server.c @@ -148,3 +148,35 @@ void serverLogin( } format("NICK %s\r\nUSER %s 0 * :%s\r\n", nick, user, real); } + +void serverAuth(void) { + format("AUTHENTICATE PLAIN\r\nAUTHENTICATE %s\r\nCAP END\r\n", authBase64); +} + +void serverJoin(const char *join) { + format("JOIN :%s\r\n", join); +} + +void serverRecv(void) { + static char buf[4096]; + static size_t len; + + ssize_t read = tls_read(client, &buf[len], sizeof(buf) - len); + if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) return; + if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client)); + if (!read) errx(EX_DATAERR, "tls_read: eof"); + len += read; + + char *crlf; + char *line = buf; + for (;;) { + crlf = memmem(line, &buf[len] - line, "\r\n", 2); + if (!crlf) break; + crlf[0] = '\0'; + // TODO: Add line to ring if stateReady(). + stateParse(line); + line = crlf + 2; + } + len -= line - buf; + memmove(buf, line, len); +} diff --git a/state.c b/state.c new file mode 100644 index 0000000..7b96a0b --- /dev/null +++ b/state.c @@ -0,0 +1,138 @@ +/* Copyright (C) 2019 C. McEnroe <june@causal.agency> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> + +#include "bounce.h" + +enum { ISupportCap = 32 }; +static struct { + char *origin; + char *nick; + char *welcome; + char *yourHost; + char *created; + char *myInfo[4]; + struct { + char *values[ISupportCap]; + size_t len; + } iSupport; +} state; + +bool stateReady(void) { + return state.origin + && state.nick + && state.welcome + && state.yourHost + && state.created + && state.myInfo[0] + && state.iSupport.len; +} + +static void set(char **field, const char *value) { + if (*field) free(*field); + *field = NULL; + if (value) { + *field = strdup(value); + if (!*field) err(EX_OSERR, "strdup"); + } +} + +enum { ParamCap = 15 }; +struct Command { + const char *origin; + const char *name; + const char *target; + const char *params[ParamCap]; +}; +typedef void Handler(struct Command); + +static void cap(struct Command cmd) { + bool ack = cmd.params[0] && !strcmp(cmd.params[0], "ACK"); + bool sasl = cmd.params[1] && !strcmp(cmd.params[1], "sasl"); + if (!ack || !sasl) errx(EX_CONFIG, "server does not support SASL"); + serverAuth(); +} + +static void replyWelcome(struct Command cmd) { + set(&state.origin, cmd.origin); + set(&state.nick, cmd.target); + set(&state.welcome, cmd.params[0]); +} + +static void replyYourHost(struct Command cmd) { + set(&state.yourHost, cmd.params[0]); +} + +static void replyCreated(struct Command cmd) { + set(&state.created, cmd.params[0]); +} + +static void replyMyInfo(struct Command cmd) { + set(&state.myInfo[0], cmd.params[0]); + set(&state.myInfo[1], cmd.params[1]); + set(&state.myInfo[2], cmd.params[2]); + set(&state.myInfo[3], cmd.params[3]); +} + +static void replyISupport(struct Command cmd) { + for (size_t i = 0; i < ParamCap; ++i) { + if (!cmd.params[i] || strchr(cmd.params[i], ' ')) break; + if (state.iSupport.len == ISupportCap) break; + set(&state.iSupport.values[state.iSupport.len++], cmd.params[i]); + } +} + +static const struct { + const char *cmd; + Handler *fn; +} Handlers[] = { + { "001", replyWelcome }, + { "002", replyYourHost }, + { "003", replyCreated }, + { "004", replyMyInfo }, + { "005", replyISupport }, + { "CAP", cap }, +}; +static const size_t HandlersLen = sizeof(Handlers) / sizeof(Handlers[0]); + +void stateParse(char *line) { + struct Command cmd = {0}; + if (line[0] == ':') { + cmd.origin = 1 + strsep(&line, " "); + if (!line) errx(EX_PROTOCOL, "eof after origin"); + } + + cmd.name = strsep(&line, " "); + cmd.target = strsep(&line, " "); + for (size_t i = 0; line && i < ParamCap; ++i) { + if (line[0] == ':') { + cmd.params[i] = line; + break; + } + cmd.params[i] = strsep(&line, " "); + } + + for (size_t i = 0; i < HandlersLen; ++i) { + if (strcmp(cmd.name, Handlers[i].cmd)) continue; + Handlers[i].fn(cmd); + break; + } +} |