From f9cc013c8dc8d580569a11441ca0b411f8024cba Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 12 Aug 2024 15:14:11 +0300 Subject: [PATCH] first commit --- LICENSE | 14 +++ README | 27 ++++++ main.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+) create mode 100644 LICENSE create mode 100644 README create mode 100644 main.c diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1691ec3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + +Version 2, December 2004 + +Copyright (C) 2004 Sam Hocevar + +Everyone is permitted to copy and distribute verbatim or modified +copies of this license document, and changing it is allowed as long +as the name is changed. + +DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README b/README new file mode 100644 index 0000000..4e7fb41 --- /dev/null +++ b/README @@ -0,0 +1,27 @@ +> irclibs + git.macaw.me/8nl/irclibs + + md5 irc.c c43a8faf4ad199a30a1148b847867022 + md5 irc.h bd6a06cb02d75da9896233db6ea20cd8 + +> gemini://any-key.press/tt/welcome.gmi + Сделано в соответствии с духом Тривиальных Технологий + +> Лицензия: WTFPL + + + _.oo. + _.u[[/;:,. .odMMMMMM' + .o888UU[[[/;:-. .o@P^ MMM^ + oN88888UU[[[/;::-. dP^ + dNMMNN888UU[[[/;:--. .o@P^ + ,MMMMMMN888UU[[/;::-. o@^ + NNMMMNN888UU[[[/~.o@P^ + 888888888UU[[[/o@^-.. + oI8888UU[[[/o@P^:--.. + .@^ YUU[[[/o@^;::---.. + oMP ^/o@P^;:::---.. + .dMMM .o@^ ^;::---... + dMMMMMMM@^` `^^^^ +YMMMUP^ + ^^ diff --git a/main.c b/main.c new file mode 100644 index 0000000..6d0731d --- /dev/null +++ b/main.c @@ -0,0 +1,263 @@ +#define VERSION "1.0" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_SSL +#include "irc.h" + +#define bool char +typedef struct { + /* config */ + int port; + char *ip; + char *nick; + bool use_ssl; + bool debug; + + /* Irc socket */ + IRCC_client client; +} CFG; +static CFG cfg = {.nick = "gem2irc", .ip = "127.0.0.1", .port = 6667}; + +typedef struct { + SSL_CTX *ctx; + SSL *ssl; +} GEM_CFG; +static GEM_CFG gem_cfg; + +int irc_client(const CFG cfg, IRCC_client *client, char **channels, const int channels_size, char **error_msg) { + if (IRCC_connect(client, cfg.ip, cfg.port) == IRCC_ERROR) { + *error_msg = strerror(errno); + return 1; + } + + if (cfg.use_ssl) { + if (IRCC_initssl(client) == IRCC_ERROR) { + *error_msg = ERR_error_string(ERR_get_error(), NULL); + return 1; + } + } + + int status = IRCC_register(client, cfg.nick); + if (status == IRCC_ERROR || status == IRCC_DISCONNECTED) { + *error_msg = "disconnected"; + return 1; + } + + for (int i = 0; i < channels_size; i++) { + char *key = ""; + const char *channel = channels[i]; + + char *p = strchr(channel, ':'); + if (p != NULL) { + p[0] = '\0'; + key = p + 1; + } + + IRCC_join(client, channel, key); + } + + return 0; +} + +void run_gemini(IRCC_client *client) { + char *uri = client->irc_msg + 4; + if (strlen(uri) <= 1) + return; + + char *nick = strdup(client->irc_nick); + if (nick == NULL) + return; + + int ret = 1; + + /* Connect to the gemini server */ + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) + goto RG_CLOSE; + + /* Get domainname from uri */ + char domain[1024]; + snprintf(domain, sizeof(domain), "%.*s", (int)strcspn(uri, ":/\0"), uri); + + struct hostent *he; + if ((he = gethostbyname(domain)) == 0) + goto RG_CLOSE_SOCK; + + /* Connect */ + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(1965); + addr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)he->h_addr)); + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) + goto RG_CLOSE_SOCK; + + /* TLS */ + if ((gem_cfg.ssl = SSL_new(gem_cfg.ctx)) == 0) + goto RG_CLOSE_SOCK; + + if (!SSL_set_tlsext_host_name(gem_cfg.ssl, domain)) + goto RG_CLOSE_TLS; + + if (!SSL_set_fd(gem_cfg.ssl, sock)) + goto RG_CLOSE_TLS; + + if (SSL_connect(gem_cfg.ssl) < 1) + goto RG_CLOSE_TLS; + + /* Send response */ + ssize_t size = snprintf(domain, sizeof(domain), "gemini://%s\r\n", uri); + if (SSL_write(gem_cfg.ssl, domain, size) < 1) + goto RG_CLOSE_TLS; + + bool exit_flag = 0; + while (1) { + char buf[128]; + char *p = buf; + + /* Read line */ + while ((size = SSL_read(gem_cfg.ssl, p, 1)) > 0) { + if (*p == '\n' || p == buf + sizeof(buf)) { + *p = '\0'; + break; + } + + p++; + } + + if (size <= 0) { + if (exit_flag) + break; + + exit_flag = 1; + continue; + } + + *p = '\0'; + IRCC_send(client, nick, buf); + exit_flag = 0; + } + + ret = 0; + +RG_CLOSE_TLS: + SSL_free(gem_cfg.ssl); + gem_cfg.ssl = NULL; + +RG_CLOSE_SOCK: + close(sock); + +RG_CLOSE: + free(nick); + if (ret) + IRCC_send(client, nick, "Internal error. Maybe invaild uri?"); +} + +void sig_handler(int sig) { + IRCC_close(&cfg.client); + + /* Close ssl for all forked processes */ + if (gem_cfg.ssl) { + SSL_shutdown(gem_cfg.ssl); + SSL_free(gem_cfg.ssl); + } + + if (gem_cfg.ctx) + SSL_CTX_free(gem_cfg.ctx); + + if (sig == -1) + exit(1); + + fprintf(stderr, "gem2irc: received signal %d\n", sig); + exit(0); +} + +int main(int argc, char **argv) { + signal(SIGINT, sig_handler); + signal(SIGKILL, sig_handler); + signal(SIGTERM, sig_handler); + signal(SIGCHLD, SIG_IGN); + + int opt; + while ((opt = getopt(argc, argv, "n:p:h:tVD")) != -1) { + switch (opt) { + case 'n': + cfg.nick = optarg; + break; + + case 'h': + cfg.ip = optarg; + break; + + case 'p': + cfg.port = atoi(optarg); + break; + + case 't': + cfg.use_ssl = 1; + break; + + case 'V': + printf("License: WTFPL\nVersion: %s\n", VERSION); + return 0; + + case 'D': + cfg.debug = 1; + break; + + default: + printf("gem2irc - simple bridge between irc and gemini\ngem2irc -[n:p:h:tVD] [e.p CHANNEL1:PASSWORD CHANNEL2...]\n\t-n NICK\tdefault: %s\n\t-h HOST\tdefault: %s\n\t-p PORT\tdefault: %d\n\t-t Use ssl\tdefault: %s\n\t-D Print raw messages\n\t-V Version\n", cfg.nick, cfg.ip, cfg.port, (cfg.use_ssl) ? "true" : "false"); + return 0; + } + } + + argv += optind; + argc -= optind; + + /* TLS FOR GEMINI */ + SSL_library_init(); + gem_cfg.ctx = SSL_CTX_new(TLS_client_method()); + if (!gem_cfg.ctx) { + fprintf(stderr, "gem2irc: ssl: %s\n", ERR_error_string(ERR_get_error(), NULL)); + return 1; + } + + /* IRC */ + char *error_msg = NULL; + if (irc_client(cfg, &cfg.client, argv, argc, &error_msg)) { + fprintf(stderr, "gem2irc: irc: %s\n", (error_msg) ? error_msg : "error"); + sig_handler(-1); + } + + while (1) { + int status = IRCC_recv(&cfg.client); + if (status == IRCC_DISCONNECTED) { + fprintf(stderr, "gem2irc: irc: disconnected\n"); + sig_handler(-1); + } + + if (cfg.debug) + printf("\033[32m%s\033[0m", cfg.client.irc_raw); + + status = IRCC_parse(&cfg.client); + if (status == IRCC_PRIVMSG && cfg.client.irc_nick != NULL) + if (!strncmp(cfg.client.irc_msg, "!go ", 4)) + if (fork() == 0) { + run_gemini(&cfg.client); + exit(0); + } + } + + return 0; +}