diff options
author | phoebos <phoebos@tilde.institute> | 2023-05-27 16:29:03 +0000 |
---|---|---|
committer | phoebos <phoebos@tilde.institute> | 2023-05-27 16:29:03 +0000 |
commit | 191856c5a614e9765dfe0308125bf395557bea21 (patch) | |
tree | 5809d9553dbb9e886c7061993c822005670eb179 | |
parent | 6139188d408ef5e107c1afaec1495c42d942d113 (diff) | |
download | cbot-191856c5a614e9765dfe0308125bf395557bea21.tar.gz |
do irc
and other misc (test, sig handler)
-rw-r--r-- | receive.c | 174 |
1 files changed, 168 insertions, 6 deletions
diff --git a/receive.c b/receive.c index d1568d3..d85a1ac 100644 --- a/receive.c +++ b/receive.c @@ -1,19 +1,30 @@ #include <err.h> #include <errno.h> +#include <netdb.h> #include <poll.h> +#include <signal.h> #include <stdio.h> #include <string.h> +#include <stdarg.h> +#include <stdlib.h> #include <sys/socket.h> +#include <sys/types.h> #include <sys/un.h> #include <unistd.h> #define SOCK_PATH "/tmp/kissbot" +#define IRC_HOST "irc.libera.chat" +#define IRC_PORT "6667" +#define IRC_NICK "_kissbot_" + +int sock_write(int s, char *msg, ...); int read_print(int s) { - char buf[100]; + char buf[0x1000]; ssize_t n; - while ((n = recv(s, buf, sizeof buf, 0)) > 0) + n = recv(s, buf, sizeof buf, 0); + if (n > 0) write(1, buf, n); if (n < 0) perror("recv"); @@ -21,6 +32,52 @@ read_print(int s) { } int +parse_irc(int s, char *p, size_t len) { + /* look for the beginning of lines and handle some commands */ + fprintf(stderr, "parse_irc got '%s'\n", p); + while (1) { + char *e = memchr(p, '\n', len); + size_t off = len; + /* If found a '\n', great; otherwise off = len is the whole rest of + * the line. */ + if (e != NULL) + off = e - p; + if (off <= 6) + break; + if (memcmp(p, "PING", 4) == 0) { + p[1] = 'O'; + char *r = p; + if (e == NULL) { + r = malloc(len+1); + if (r == NULL) + err(1, "malloc"); + strlcpy(r, p, len+1); + } else + *e = '\0'; + + sock_write(s, "%s\n", r); + + if (!e) + free(r); + } + if (e == NULL || len < off + 1) + break; + len -= off + 1; + p = e + 1; + } + return 0; +} + +int +handle_irc(int s) { + char buf[0x1000]; + ssize_t n = recv(s, buf, sizeof buf, 0); + if (n <= 0) + return 1; + return parse_irc(s, buf, n); +} + +int start_listening(void) { struct sockaddr_un my_addr = {.sun_family = AF_UNIX}; int s = socket(AF_UNIX, SOCK_STREAM, 0); @@ -45,11 +102,75 @@ accept_one(int s) { } int +sock_write(int s, char *msg, ...) { + va_list ap; + va_start(ap, msg); + if (vdprintf(s, msg, ap) < 0) { + perror("sock_write"); + va_end(ap); + return -1; + } + va_end(ap); + return 0; +} + +int +irc_identify(int s) { + return sock_write(s, "NICK %s\r\nUSER %s 8 x :%s\r\n", IRC_NICK, IRC_NICK, IRC_NICK); +} + +int +connect_irc(void) { + int s = -1; + struct addrinfo hints = {0}, *result, *rp; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + hints.ai_protocol = 0; + + int e = getaddrinfo(IRC_HOST, IRC_PORT, &hints, &result); + if (e != 0) + errx(1, "%s", gai_strerror(e)); + + for (rp = result; rp != NULL; rp = rp->ai_next) { + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == -1) + continue; + if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0) + break; + + close(s); + s = -1; + } + freeaddrinfo(result); + + if (s == -1 || irc_identify(s) != 0) + errx(1, "couldn't connect to %s:%s", IRC_HOST, IRC_PORT); + return s; +} + +void +cleanup(int sig) { + unlink(SOCK_PATH); + _exit(1); +} + +#ifndef TEST +int main(void) { + struct sigaction act = { + .sa_handler = cleanup, + .sa_flags = 0, + .sa_mask = 0, + }; + if (sigaction(SIGINT, &act, NULL) == -1) { + err(1, "sigaction"); + } struct pollfd fds[3]; /* UNIX socket, IRC socket, client on UNIX socket */ fds[0].fd = start_listening(); fds[0].events = POLLIN; - fds[1].fd = -1; + fds[1].fd = connect_irc(); + fds[1].events = POLLIN ; fds[2].fd = -1; while (1) { @@ -72,17 +193,58 @@ main(void) { fds[2].events = POLLIN; } } + if (fds[0].revents & POLLHUP) { + close(fds[1].fd); + if (fds[2].fd >= 0) + close(fds[2].fd); + errx(1, "unexpected error, quitting\n"); + } if (fds[2].revents & POLLIN) { read_print(fds[2].fd); - close(fds[2].fd); - fds[2].fd = -1; - fds[0].events = POLLIN; } if (fds[2].revents & POLLHUP) { close(fds[2].fd); fds[2].fd = -1; fds[0].events = POLLIN; + fds[1].events = POLLIN; + } + if (fds[1].revents & POLLIN) { + handle_irc(fds[1].fd); + } + if (fds[1].revents & POLLHUP) { + fprintf(stderr, "IRC disconnected; trying to reconnect\n"); + close(fds[1].fd); + fds[1].fd = connect_irc(); + /* could exit(3) in connect_irc */ } } close(fds[0].fd); } +#else +int main(void) { + struct slen { + char *s; size_t len; + }; + struct slen s[] = { + {strdup("PING :hi\n"), 9}, + {strdup("PING :ih"), 8}, + {strdup(""), 0}, + {strdup("PING no colon\n"), 14}, + {strdup("PING :too short len"), 10}, + {strdup("PING :too long len"), 20}, + }; + printf("%s", s[0].s); + for (size_t i = 0; i < sizeof(s)/sizeof(s[0]); i++) { + parse_irc(1, s[i].s, s[i].len); + free(s[i].s); + fflush(stdout); + } + + char str[100]; + while (fgets(str, sizeof str, stdin) != NULL) { + parse_irc(1, str, strlen(str)); + fflush(stdout); + } + return 0; +} +#endif |