diff options
Diffstat (limited to '')
-rw-r--r-- | client.c | 140 |
1 files changed, 136 insertions, 4 deletions
diff --git a/client.c b/client.c index d012b28..0bba8d3 100644 --- a/client.c +++ b/client.c @@ -14,23 +14,155 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <assert.h> #include <err.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> #include <stdlib.h> +#include <string.h> #include <sysexits.h> +#include <tls.h> #include "bounce.h" -struct Client *clientAlloc(void) { - struct Client *client = calloc(1, sizeof(*client)); - if (!client) err(EX_OSERR, "calloc"); +enum Need { + NeedNick = 1 << 0, + NeedUser = 1 << 1, + NeedPass = 1 << 2, + NeedCapEnd = 1 << 3, +}; + +struct Client { + bool close; + struct tls *tls; + enum Need need; + char buf[4096]; + size_t len; +}; + +struct Client *clientAlloc(struct tls *tls) { + struct Client *client = malloc(sizeof(*client)); + if (!client) err(EX_OSERR, "malloc"); + + client->close = false; + client->tls = tls; + client->need = NeedNick | NeedUser | (clientPass ? NeedPass : 0); + client->len = 0; + return client; } void clientFree(struct Client *client) { + tls_close(client->tls); tls_free(client->tls); free(client); } -void clientRecv(struct Client *client) { +bool clientClose(const struct Client *client) { + return client->close; +} + +static void clientSend(struct Client *client, const char *ptr, size_t len) { + if (verbose) fprintf(stderr, "\x1B[34m%.*s\x1B[m", (int)len, ptr); + while (len) { + ssize_t ret = tls_write(client->tls, ptr, len); + // FIXME: Handle non-blocking? + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue; + if (ret < 0) { + warnx("tls_write: %s", tls_error(client->tls)); + client->close = true; + return; + } + ptr += ret; + len -= ret; + } +} + +static void format(struct Client *client, const char *format, ...) { + char buf[513]; + va_list ap; + va_start(ap, format); + int len = vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + assert(len > 0 && (size_t)len < sizeof(buf)); + clientSend(client, buf, len); +} + +typedef void Handler(struct Client *client, struct Command cmd); + +static void handleNick(struct Client *client, struct Command cmd) { + (void)cmd; + client->need &= ~NeedNick; +} + +static void handleUser(struct Client *client, struct Command cmd) { + (void)cmd; + // TODO: Identify client by username. + client->need &= ~NeedUser; +} + +static void handlePass(struct Client *client, struct Command cmd) { + if (!cmd.params[0] || strcmp(clientPass, cmd.params[0])) { + format(client, ":invalid 464 * :Password incorrect\r\n"); + client->close = true; + } else { + client->need &= ~NeedPass; + } +} + +static void handleCap(struct Client *client, struct Command cmd) { // TODO... } + +static const struct { + const char *cmd; + Handler *fn; +} Handlers[] = { + { "CAP", handleCap }, + { "NICK", handleNick }, + { "PASS", handlePass }, + { "USER", handleUser }, +}; + +static void clientParse(struct Client *client, char *line) { + struct Command cmd = parse(line); + if (!cmd.name) { + // FIXME: Identify client in message. + warnx("no command"); + client->close = true; + return; + } + for (size_t i = 0; i < ARRAY_LEN(Handlers); ++i) { + if (strcmp(cmd.name, Handlers[i].cmd)) continue; + Handlers[i].fn(client, cmd); + break; + } +} + +void clientRecv(struct Client *client) { + ssize_t read = tls_read( + client->tls, + &client->buf[client->len], sizeof(client->buf) - client->len + ); + if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) return; + if (read < 0) warnx("tls_read: %s", tls_error(client->tls)); + if (read < 1) { + client->close = true; + return; + } + client->len += read; + + char *crlf; + char *line = client->buf; + for (;;) { + crlf = memmem(line, &client->buf[client->len] - line, "\r\n", 2); + if (!crlf) break; + crlf[0] = '\0'; + if (verbose) fprintf(stderr, "\x1B[33m%s\x1B[m\n", line); + clientParse(client, line); + line = crlf + 2; + } + client->len -= line - client->buf; + memmove(client->buf, line, client->len); +} |