From 8889b0aee1dc430e735e2a0df462fd0aeee63847 Mon Sep 17 00:00:00 2001 From: pk910 Date: Thu, 10 May 2012 13:00:35 +0200 Subject: [PATCH] added IRC Client and RAW pass-through --- Makefile.am | 3 +- src/IOEngine_select.c | 1 + src/IOHandler.c | 8 +-- src/IOHandler.h | 5 +- src/IRCClient.c | 116 ++++++++++++++++++++++++++++++++++++++++++ src/IRCClient.h | 40 +++++++++++++++ src/ServerSocket.c | 4 +- src/ServerSocket.h | 2 +- src/UserClient.c | 18 ++++++- src/UserClient.h | 6 +++ src/UserSession.c | 40 ++++++++++++--- src/UserSession.h | 4 ++ src/main.c | 2 +- src/tools.c | 24 +++++++++ src/tools.h | 2 + 15 files changed, 255 insertions(+), 20 deletions(-) create mode 100644 src/IRCClient.c create mode 100644 src/IRCClient.h diff --git a/Makefile.am b/Makefile.am index 7d04a23..225077c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,8 @@ transirc_SOURCES = src/version.c \ src/ServerSocket.c \ src/tools.c \ src/UserSession.c \ - src/UserClient.c + src/UserClient.c \ + src/IRCClient.c transirc_LDADD = $(MYSQL_LIBS) $(SYSTEM_LIBS) diff --git a/src/IOEngine_select.c b/src/IOEngine_select.c index 2f32bf0..e37b104 100644 --- a/src/IOEngine_select.c +++ b/src/IOEngine_select.c @@ -96,6 +96,7 @@ static void engine_select_loop(struct timeval *timeout) { if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN) { if(FD_ISSET(iofd->fd, &read_fds) || FD_ISSET(iofd->fd, &write_fds)) { iohandler_events(iofd, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds)); + continue; } } if(iofd->type == IOTYPE_TIMER || iofd->timeout.tv_sec || iofd->timeout.tv_usec) { diff --git a/src/IOHandler.c b/src/IOHandler.c index 375bcea..1843acc 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -106,7 +106,7 @@ struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback return descriptor; } -struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, const char *bindhost, iohandler_callback *callback) { +struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback) { //non-blocking connect int sockfd; struct addrinfo hints, *res, *freeres; @@ -216,7 +216,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, return descriptor; } -struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) { +struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, int ssl, iohandler_callback *callback) { int sockfd; struct addrinfo hints, *res, *freeres; struct sockaddr_in *ip4 = NULL; @@ -434,8 +434,10 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { if(used_bytes) { if(used_bytes == iofd->readbuf.bufpos) iofd->readbuf.bufpos = 0; - else + else { memmove(iofd->readbuf.buffer, iofd->readbuf.buffer + used_bytes, iofd->readbuf.bufpos - used_bytes); + iofd->readbuf.bufpos -= used_bytes; + } } callback_event.type = IOEVENT_IGNORE; } diff --git a/src/IOHandler.h b/src/IOHandler.h index 8bc2a37..3e9e705 100644 --- a/src/IOHandler.h +++ b/src/IOHandler.h @@ -66,6 +66,7 @@ struct IODescriptor { struct IOBuffer writebuf; void *data; int read_lines : 1; + int ssl : 1; struct IODescriptor *next, *prev; }; @@ -83,8 +84,8 @@ struct IOEvent { struct IODescriptor *iohandler_add(int sockfd, enum IOType type, iohandler_callback *callback); struct IODescriptor *iohandler_file(FILE *file, iohandler_callback *callback); struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback); -struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, const char *bind, iohandler_callback *callback); -struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback); +struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bind, iohandler_callback *callback); +struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, int ssl, iohandler_callback *callback); void iohandler_write(struct IODescriptor *iofd, const char *line); void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen); void iohandler_printf(struct IODescriptor *iofd, const char *text, ...); diff --git a/src/IRCClient.c b/src/IRCClient.c new file mode 100644 index 0000000..46e030e --- /dev/null +++ b/src/IRCClient.c @@ -0,0 +1,116 @@ +/* IRCClient.c - TransparentIRC 0.1 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "IRCClient.h" +#include "IOHandler.h" +#include "UserSession.h" +#include "UserClient.h" +#include "tools.h" +#include "ConfigParser.h" + +static void ircclient_callback(struct IOEvent *event); + +void ircclient_initialize(struct UserSession *session, struct UserLogin *login) { + struct IRCClient *client = calloc(1, sizeof(*client)); + if(!client) { + usersession_error(session, "Could not initialize IRC connection."); + return; + } + client->session = session; + session->irc = client; + + char *server_address = (login->server_address ? login->server_address : get_string_field("server.host")); + int server_port = (login->server_override_port ? login->server_port : get_int_field("server.port")); + int server_ssl = (login->server_override_port ? login->server_ssl : get_int_field("server.ssl")); + char *bind_address = (login->bind_address ? login->bind_address : get_string_field("server.bind")); + + char notification[LINELEN]; + sprintf(notification, "Connecting to %s (%s%d)", server_address, (server_ssl ? "+" : ""), server_port); + usersession_client_notification(session, notification); + + client->iofd = iohandler_connect(server_address, server_port, server_ssl, bind_address, ircclient_callback); + if(!client->iofd) { + usersession_error(session, "Could not initialize IRC connection."); + return; + } + client->iofd->data = client; +} + +void ircclient_close(struct IRCClient *client) { + client->session->irc = NULL; + iohandler_printf(client->iofd, "QUIT :[TransparentIRC] Quit"); + iohandler_close(client->iofd); + free(client); +} + +static void ircclient_handshake(struct IRCClient *client) { + struct UserSession *session = client->session; + struct IODescriptor *iofd = client->iofd; + iohandler_printf(iofd, "PASS %s:%s", session->username, session->password); + iohandler_printf(iofd, "NICK %s", session->nick); + iohandler_printf(iofd, "USER %s 0 * :%s", session->username, session->realname); + free(session->realname); + session->realname = NULL; +} + +static void ircclient_recv(struct IRCClient *client, char *line) { + struct UserSession *session = client->session; + char *argv[MAXNUMPARAMS]; + char tmp[LINELEN]; + strcpy(tmp, line); + int argc = parse_line(tmp, argv, 1); + int pass_to_client = 1; + if(argc > 2 && !stricmp(argv[1], "PING")) { + iohandler_printf(client->iofd, "PONG :%s", argv[2]); + pass_to_client = 0; + } + if(!client->auth_confirmed) { + if(argc == 4 && !stricmp(argv[1], "NOTICE") && !stricmp(argv[2], "AUTH") && !stricmp(argv[3], "*** Login accepted")) { + client->auth_confirmed = 1; + } else if(argc > 1 && !stricmp(argv[1], "001")) { + usersession_error(session, "IRC Session closed (Please use Login on Connect)"); + pass_to_client = 0; + } + } else { + + } + if(pass_to_client) + usersession_client_raw(session, line); +} + +static void ircclient_callback(struct IOEvent *event) { + struct IRCClient *client = event->iofd->data; + switch(event->type) { + case IOEVENT_NOTCONNECTED: + usersession_error(client->session, "Could not connect to the Server."); + break; + case IOEVENT_CONNECTED: + ircclient_handshake(client); + break; + case IOEVENT_RECV: + ircclient_recv(client, event->data.recv_str); + break; + case IOEVENT_CLOSED: + usersession_error(client->session, "Disconnected from the IRC Server."); + break; + default: + break; + } +} + +void ircclient_send(struct IRCClient *client, char *line) { + iohandler_printf(client->iofd, "%s", line); +} diff --git a/src/IRCClient.h b/src/IRCClient.h new file mode 100644 index 0000000..a6b2f4c --- /dev/null +++ b/src/IRCClient.h @@ -0,0 +1,40 @@ +/* IRCClient.h - TransparentIRC 0.1 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef _IRCClient_h +#define _IRCClient_h +#include "overall.h" + +struct IODescriptor; +struct UserSession; +struct UserLogin; + +struct IRCClient { + struct IODescriptor *iofd; + + struct UserSession *session; + + int auth_confirmed : 1; + + struct IRCClient *next, *prev; +}; + +void ircclient_initialize(struct UserSession *session, struct UserLogin *login); +void ircclient_close(struct IRCClient *client); +void ircclient_send(struct IRCClient *client, char *line); + +#endif diff --git a/src/ServerSocket.c b/src/ServerSocket.c index b09e36c..943228b 100644 --- a/src/ServerSocket.c +++ b/src/ServerSocket.c @@ -47,8 +47,8 @@ static void serversocket_delete(struct ServerSocket *server) { free(server); } -struct ServerSocket *serversocket_listen(char *hostname, int port) { - struct IODescriptor *iofd = iohandler_listen(hostname, port, serversocket_callback); +struct ServerSocket *serversocket_listen(char *hostname, int port, int ssl) { + struct IODescriptor *iofd = iohandler_listen(hostname, port, ssl, serversocket_callback); if(!iofd) return NULL; struct ServerSocket *server = serversocket_create(iofd); return server; diff --git a/src/ServerSocket.h b/src/ServerSocket.h index 5e77db1..5ca4d21 100644 --- a/src/ServerSocket.h +++ b/src/ServerSocket.h @@ -29,7 +29,7 @@ struct ServerSocket { struct ServerSocket *next, *prev; }; -struct ServerSocket *serversocket_listen(char *hostname, int port); +struct ServerSocket *serversocket_listen(char *hostname, int port, int ssl); void serversocket_close(struct ServerSocket *server, int keep_clients); #endif diff --git a/src/UserClient.c b/src/UserClient.c index e64b8a0..d6c0ca5 100644 --- a/src/UserClient.c +++ b/src/UserClient.c @@ -19,6 +19,7 @@ #include "ServerSocket.h" #include "UserSession.h" #include "tools.h" +#include "IRCClient.h" static void userclient_callback(struct IOEvent *event); @@ -63,6 +64,8 @@ void userclient_close(struct UserClient *client) { } if(login->username) free(login->username); + if(login->realname) + free(login->realname); if(login->password) free(login->password); if(login->nick) @@ -71,6 +74,10 @@ void userclient_close(struct UserClient *client) { free(login->reject_reason); if(login->session_class) free(login->session_class); + if(login->bind_address) + free(login->bind_address); + if(login->server_address) + free(login->server_address); free(login); } iohandler_close(client->iofd); @@ -123,9 +130,10 @@ static void userclient_recv(struct UserClient *client, char *line) { login->password = strdup(delimiter); } else login->password = strdup(argv[2]); - } else if(!stricmp(argv[1], "USER")) { + } else if(!stricmp(argv[1], "USER") && argc >= 6) { if(!login->username) login->username = strdup(argv[2]); + login->realname = strdup(argv[5]); } else if(!stricmp(argv[1], "NICK")) { if(login->nick) free(login->nick); @@ -134,13 +142,18 @@ static void userclient_recv(struct UserClient *client, char *line) { iohandler_printf(client->iofd, "NOTICE AUTH :*** [TransparentIRC] You need to send your LOC data. Try /quote PASS :"); } } - if(login->username && login->password && login->nick && !(client->flags & USERCLIENT_LOGIN_PROCESSING)) { + if(login->username && login->password && login->nick && login->realname && !(client->flags & USERCLIENT_LOGIN_PROCESSING)) { //try to login iohandler_printf(client->iofd, "NOTICE AUTH :*** [TransparentIRC] Checking login..."); usersession_login(login); } } else { + struct UserSession *session = client->user; + if(!stricmplen(line, "QUIT", 4)) + return; + if(session->irc) + ircclient_send(session->irc, line); } } @@ -160,6 +173,7 @@ void userclient_login_successful(struct UserLogin *login, struct UserSession *se free(login->nick); free(login); client->user = session; + client->flags |= USERCLIENT_LOGGED_IN; if(recover) { iohandler_printf(client->iofd, "NOTICE AUTH :*** [TransparentIRC] Recovering previous link (Nick: %s).", session->nick); diff --git a/src/UserClient.h b/src/UserClient.h index bde6001..1da0717 100644 --- a/src/UserClient.h +++ b/src/UserClient.h @@ -31,11 +31,17 @@ struct UserLogin { char *username; char *password; char *nick; + char *realname; int login_accepted : 1; char *reject_reason; char *session_class; + char *bind_address; + char *server_address; + int server_override_port : 1; + int server_port : 17; + int server_ssl : 1; struct IODescriptor *login_iofd; }; diff --git a/src/UserSession.c b/src/UserSession.c index 136b767..8b90299 100644 --- a/src/UserSession.c +++ b/src/UserSession.c @@ -19,6 +19,7 @@ #include "UserClient.h" #include "ConfigParser.h" #include "tools.h" +#include "IRCClient.h" static struct UserSession *usersessions = NULL; @@ -39,10 +40,11 @@ static struct UserSession *usersession_add(char *username, char *password, char return session; } -/* static void usersession_close(struct UserSession *session) { if(session->client) userclient_close(session->client); + if(session->irc) + ircclient_close(session->irc); if(session->prev) session->prev->next = session->next; @@ -53,24 +55,32 @@ static void usersession_close(struct UserSession *session) { free(session->username); free(session->password); free(session->nick); + if(session->realname) + free(session->realname); free(session); } -*/ -/* ****************** SESSION FUNCTIONS ****************** */ +/* ****************** EXTERNAL EVENTS ****************** */ -static void usersession_initialize_session(struct UserSession *session) { - +void usersession_error(struct UserSession *session, char *error) { + if(session->client) { + iohandler_printf(session->client->iofd, ":*TransparentIRC!TransIRC@TransparentIRC.system.notification NOTICE %s :[TransparentIRC] ERROR: %s", session->nick, error); + } + usersession_close(session); } -/* ****************** EXTERNAL EVENTS ****************** */ - void usersession_client_close(struct UserSession *session) { session->client = NULL; session->idle_since = time(0); } +void usersession_client_raw(struct UserSession *session, char *raw) { + if(session->client) { + iohandler_printf(session->client->iofd, "%s", raw); + } +} + void usersession_client_notification(struct UserSession *session, char *notification) { if(session->client) { iohandler_printf(session->client->iofd, ":*TransparentIRC!TransIRC@TransparentIRC.system.notification NOTICE %s :[TransparentIRC] %s", session->nick, notification); @@ -152,8 +162,10 @@ static void usersession_login_accept(struct UserLogin *login) { userclient_login_failed(login, "Could not create Session."); return; } + active_session->client = login->client; + active_session->realname = strdup(login->realname); + ircclient_initialize(active_session, login); userclient_login_successful(login, active_session, 0); - usersession_initialize_session(active_session); } } @@ -196,6 +208,18 @@ static void usersession_login_callback(struct IOEvent *event) { if(argc < 2) return; if(!stricmp(argv[0], "CLASS")) { login->session_class = strdup(argv[1]); + } else if(!stricmp(argv[0], "SERVER")) { + login->server_address = strdup(argv[1]); + if(argc > 2) { + login->server_override_port = 1; + if(argv[2][0] == '+') { + argv[2]++; + login->server_ssl = 1; + } + login->server_port = atoi(argv[2]); + } + } else if(!stricmp(argv[0], "BIND")) { + login->bind_address = strdup(argv[1]); } } diff --git a/src/UserSession.h b/src/UserSession.h index deb9f59..534a302 100644 --- a/src/UserSession.h +++ b/src/UserSession.h @@ -26,14 +26,18 @@ struct UserSession { char *username; char *password; char *nick; + char *realname; //only valid till IRC handshake struct UserClient *client; + struct IRCClient *irc; time_t idle_since; time_t connected_since; struct UserSession *next, *prev; }; +void usersession_error(struct UserSession *session, char *error); +void usersession_client_raw(struct UserSession *session, char *raw); void usersession_client_notification(struct UserSession *session, char *notification); void usersession_login(struct UserLogin *login); void usersession_login_abort(struct UserLogin *login); diff --git a/src/main.c b/src/main.c index ddf5568..07df53b 100644 --- a/src/main.c +++ b/src/main.c @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { parseParameters(argc, argv); loadConfig(configFile); - serversocket_listen("0.0.0.0", 9001); + serversocket_listen("0.0.0.0", 9001, 0); while(1) { iohandler_poll(); diff --git a/src/tools.c b/src/tools.c index 5a26b19..8316e01 100644 --- a/src/tools.c +++ b/src/tools.c @@ -138,3 +138,27 @@ int run_external_process(char *command, char **parameters) { //win32 incompatibl } } +char* merge_argv(char **argv, int start, int end) { + return merge_argv_char(argv, start, end, ' '); +} + +char* merge_argv_char(char **argv, int start, int end, char seperator) { + int i; + char *p = NULL; + while(!argv[start]) start++; + if(start >= end) + return NULL; + for(i = start; i < end; i++) { + p = argv[i]; + if(!p) continue; + while(*p) p++; + if(i < end-1) { + while(p != argv[i+1]) { + *p++ = seperator; + } + } else + *p = seperator; + } + if(p) *p = '\0'; + return argv[start]; +} diff --git a/src/tools.h b/src/tools.h index 33f4f02..673d341 100644 --- a/src/tools.h +++ b/src/tools.h @@ -29,5 +29,7 @@ int stricmplen(const char *s1, const char *s2, int len); int parse_line(char *line, char **argv, int irc_raw); void build_var_string(char *buffer, char *format, struct variable_replace_map *map); int run_external_process(char *command, char **parameters); +char *merge_argv(char **argv, int start, int end); +char *merge_argv_char(char **argv, int start, int end, char seperator); #endif -- 2.20.1