added session manager and support for an external login system
[TransparentIRC.git] / src / UserSession.c
diff --git a/src/UserSession.c b/src/UserSession.c
new file mode 100644 (file)
index 0000000..136b767
--- /dev/null
@@ -0,0 +1,221 @@
+/* UserSession.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 <http://www.gnu.org/licenses/>. 
+ */
+#include "UserSession.h"
+#include "IOHandler.h"
+#include "UserClient.h"
+#include "ConfigParser.h"
+#include "tools.h"
+
+static struct UserSession *usersessions = NULL;
+
+static struct UserSession *usersession_add(char *username, char *password, char *nick) {
+    struct UserSession *session;
+    session = calloc(1, sizeof(*session));
+    session->username = strdup(username);
+    session->password = strdup(password);
+    session->nick = strdup(nick);
+    
+    //add UserSession to the list
+    session->prev = NULL;
+    session->next = usersessions;
+    if(usersessions)
+        usersessions->prev = session;
+    usersessions = session;
+    
+    return session;
+}
+
+/*
+static void usersession_close(struct UserSession *session) {
+    if(session->client)
+        userclient_close(session->client);
+    
+    if(session->prev)
+        session->prev->next = session->next;
+    else
+        usersessions = session->next;
+    if(session->next)
+        session->next->prev = session->prev;
+    free(session->username);
+    free(session->password);
+    free(session->nick);
+    free(session);
+}
+*/
+
+/* ****************** SESSION FUNCTIONS ****************** */
+
+static void usersession_initialize_session(struct UserSession *session) {
+    
+}
+
+/* ****************** EXTERNAL EVENTS ****************** */
+
+void usersession_client_close(struct UserSession *session) {
+    session->client = NULL;
+    session->idle_since = time(0);
+    
+}
+
+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);
+    }
+}
+
+/* ****************** LOGIN FUNCTIONS ****************** */
+
+static void usersession_login_accept(struct UserLogin *login);
+static void usersession_login_callback(struct IOEvent *event);
+
+void usersession_login(struct UserLogin *login) {
+    if(get_int_field("auth.external.enabled")) {
+        char *execute = get_string_field("auth.external.execute");
+        char *parameters = get_string_field("auth.external.parameters");
+        
+        struct variable_replace_map map[] = {
+            {'U', login->username},
+            {'P', login->password},
+            {'N', login->nick},
+            {0, NULL}
+        };
+        char paramstr[CMDLEN+1];
+        build_var_string(paramstr, parameters, map);
+        char *argv[MAXNUMPARAMS];
+        int argc = parse_line(paramstr, argv+1, 0);
+        argv[0] = execute;
+        argv[argc+1] = NULL;
+        int fp = run_external_process(execute, argv);
+        if(fp < 0) {
+            userclient_login_failed(login, "Login Script error.");
+            return;
+        }
+        struct IODescriptor *iofd = iohandler_add(fp, IOTYPE_CLIENT, usersession_login_callback);
+        if(iofd) {
+            iofd->read_lines = 1;
+            iofd->state = IO_CONNECTED;
+            int timeout = get_int_field("auth.external.timeout");
+            if(timeout) {
+                gettimeofday(&iofd->timeout, NULL);
+                iofd->timeout.tv_sec += timeout;
+            }
+            iofd->data = login;
+            login->login_iofd = iofd;
+            login->client->flags |= USERCLIENT_LOGIN_PROCESSING;
+        } else
+            userclient_login_failed(login, "Internal error.");
+    } else 
+        usersession_login_accept(login);
+}
+
+static void usersession_login_accept(struct UserLogin *login) {
+    //search session for user (or create a new one)
+    struct UserSession *session, *active_session = NULL;
+    int sessioncount = 0;
+    for(session = usersessions; session; session = session->next) {
+        if(!stricmp(login->username, session->username)) {
+            sessioncount++;
+            if(!strcmp(login->password, session->password) && !stricmp(login->nick, session->nick)) {
+                active_session = session;
+            }
+        }
+    }
+    if(active_session) {
+        if(active_session->client) {
+            iohandler_printf(active_session->client->iofd, "ERROR :[TransparentIRC] Another client logged in.");
+            userclient_close(active_session->client);
+        } //active_session->client is now NULL
+        active_session->client = login->client;
+        userclient_login_successful(login, active_session, 1);
+    } else {
+        int sessionlimit = get_int_field("auth.session_limit");
+        if(sessionlimit && sessioncount >= sessionlimit) {
+            userclient_login_failed(login, "Session limit reached.");
+            return;
+        }
+        active_session = usersession_add(login->username, login->password, login->nick);
+        if(!active_session) {
+            userclient_login_failed(login, "Could not create Session.");
+            return;
+        }
+        userclient_login_successful(login, active_session, 0);
+        usersession_initialize_session(active_session);
+    }
+}
+
+void usersession_login_abort(struct UserLogin *login) {
+    struct IODescriptor *iofd = login->login_iofd;
+    iohandler_close(iofd);
+}
+
+static void usersession_login_callback(struct IOEvent *event) {
+    struct UserLogin *login = event->iofd->data;
+    login->client->flags &= ~USERCLIENT_LOGIN_PROCESSING;
+    char *reply;
+    int login_finished = 0;
+    switch(event->type) {
+        case IOEVENT_RECV:
+            reply = event->data.recv_str;
+            if(!stricmplen(reply, "REJECT", 6)) {
+                if((reply = strchr(reply, ' '))) {
+                    char reason[LINELEN];
+                    sprintf(reason, "Access denied: %s", reply + 7);
+                    login->reject_reason = strdup(reason);
+                } else
+                    login->reject_reason = strdup("Access denied.");
+            } else if(!stricmplen(reply, "ACCEPT", 6)) {
+                if((reply = strchr(reply, ' '))) {
+                    char *passwd = strchr(reply, ' ');
+                    if(passwd) {
+                        *passwd = '\0';
+                        passwd++;
+                        free(login->password);
+                        login->password = strdup(passwd);
+                    }
+                    free(login->username);
+                    login->username = strdup(reply);
+                }
+                login->login_accepted = 1;
+            } else {
+                char *argv[MAXNUMPARAMS];
+                int argc = parse_line(reply, argv, 0);
+                if(argc < 2) return;
+                if(!stricmp(argv[0], "CLASS")) {
+                    login->session_class = strdup(argv[1]);
+                }
+                
+            }
+            break;
+        case IOEVENT_TIMEOUT:
+            login->reject_reason = strdup("Login Script timeout.");
+            login_finished = 1;
+            break;
+        case IOEVENT_CLOSED:
+            login->reject_reason = strdup("Login Script error (no reply).");
+            login_finished = 1;
+            break;
+        default:
+            return;
+    }
+    if(login_finished) {
+        iohandler_close(event->iofd);
+        if(login->login_accepted)
+            usersession_login_accept(login);
+        else
+            userclient_login_failed(login, login->reject_reason);
+    }
+}