first commit

This commit is contained in:
Your Name 2024-08-12 15:14:11 +03:00
commit f9cc013c8d
3 changed files with 304 additions and 0 deletions

14
LICENSE Normal file
View File

@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
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.

27
README Normal file
View File

@ -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^
^^

263
main.c Normal file
View File

@ -0,0 +1,263 @@
#define VERSION "1.0"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#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;
}