added session manager and support for an external login system
[TransparentIRC.git] / src / UserSession.c
1 /* UserSession.c - TransparentIRC 0.1
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #include "UserSession.h"
18 #include "IOHandler.h"
19 #include "UserClient.h"
20 #include "ConfigParser.h"
21 #include "tools.h"
22
23 static struct UserSession *usersessions = NULL;
24
25 static struct UserSession *usersession_add(char *username, char *password, char *nick) {
26     struct UserSession *session;
27     session = calloc(1, sizeof(*session));
28     session->username = strdup(username);
29     session->password = strdup(password);
30     session->nick = strdup(nick);
31     
32     //add UserSession to the list
33     session->prev = NULL;
34     session->next = usersessions;
35     if(usersessions)
36         usersessions->prev = session;
37     usersessions = session;
38     
39     return session;
40 }
41
42 /*
43 static void usersession_close(struct UserSession *session) {
44     if(session->client)
45         userclient_close(session->client);
46     
47     if(session->prev)
48         session->prev->next = session->next;
49     else
50         usersessions = session->next;
51     if(session->next)
52         session->next->prev = session->prev;
53     free(session->username);
54     free(session->password);
55     free(session->nick);
56     free(session);
57 }
58 */
59
60 /* ****************** SESSION FUNCTIONS ****************** */
61
62 static void usersession_initialize_session(struct UserSession *session) {
63     
64 }
65
66 /* ****************** EXTERNAL EVENTS ****************** */
67
68 void usersession_client_close(struct UserSession *session) {
69     session->client = NULL;
70     session->idle_since = time(0);
71     
72 }
73
74 void usersession_client_notification(struct UserSession *session, char *notification) {
75     if(session->client) {
76         iohandler_printf(session->client->iofd, ":*TransparentIRC!TransIRC@TransparentIRC.system.notification NOTICE %s :[TransparentIRC] %s", session->nick, notification);
77     }
78 }
79
80 /* ****************** LOGIN FUNCTIONS ****************** */
81
82 static void usersession_login_accept(struct UserLogin *login);
83 static void usersession_login_callback(struct IOEvent *event);
84
85 void usersession_login(struct UserLogin *login) {
86     if(get_int_field("auth.external.enabled")) {
87         char *execute = get_string_field("auth.external.execute");
88         char *parameters = get_string_field("auth.external.parameters");
89         
90         struct variable_replace_map map[] = {
91             {'U', login->username},
92             {'P', login->password},
93             {'N', login->nick},
94             {0, NULL}
95         };
96         char paramstr[CMDLEN+1];
97         build_var_string(paramstr, parameters, map);
98         char *argv[MAXNUMPARAMS];
99         int argc = parse_line(paramstr, argv+1, 0);
100         argv[0] = execute;
101         argv[argc+1] = NULL;
102         int fp = run_external_process(execute, argv);
103         if(fp < 0) {
104             userclient_login_failed(login, "Login Script error.");
105             return;
106         }
107         struct IODescriptor *iofd = iohandler_add(fp, IOTYPE_CLIENT, usersession_login_callback);
108         if(iofd) {
109             iofd->read_lines = 1;
110             iofd->state = IO_CONNECTED;
111             int timeout = get_int_field("auth.external.timeout");
112             if(timeout) {
113                 gettimeofday(&iofd->timeout, NULL);
114                 iofd->timeout.tv_sec += timeout;
115             }
116             iofd->data = login;
117             login->login_iofd = iofd;
118             login->client->flags |= USERCLIENT_LOGIN_PROCESSING;
119         } else
120             userclient_login_failed(login, "Internal error.");
121     } else 
122         usersession_login_accept(login);
123 }
124
125 static void usersession_login_accept(struct UserLogin *login) {
126     //search session for user (or create a new one)
127     struct UserSession *session, *active_session = NULL;
128     int sessioncount = 0;
129     for(session = usersessions; session; session = session->next) {
130         if(!stricmp(login->username, session->username)) {
131             sessioncount++;
132             if(!strcmp(login->password, session->password) && !stricmp(login->nick, session->nick)) {
133                 active_session = session;
134             }
135         }
136     }
137     if(active_session) {
138         if(active_session->client) {
139             iohandler_printf(active_session->client->iofd, "ERROR :[TransparentIRC] Another client logged in.");
140             userclient_close(active_session->client);
141         } //active_session->client is now NULL
142         active_session->client = login->client;
143         userclient_login_successful(login, active_session, 1);
144     } else {
145         int sessionlimit = get_int_field("auth.session_limit");
146         if(sessionlimit && sessioncount >= sessionlimit) {
147             userclient_login_failed(login, "Session limit reached.");
148             return;
149         }
150         active_session = usersession_add(login->username, login->password, login->nick);
151         if(!active_session) {
152             userclient_login_failed(login, "Could not create Session.");
153             return;
154         }
155         userclient_login_successful(login, active_session, 0);
156         usersession_initialize_session(active_session);
157     }
158 }
159
160 void usersession_login_abort(struct UserLogin *login) {
161     struct IODescriptor *iofd = login->login_iofd;
162     iohandler_close(iofd);
163 }
164
165 static void usersession_login_callback(struct IOEvent *event) {
166     struct UserLogin *login = event->iofd->data;
167     login->client->flags &= ~USERCLIENT_LOGIN_PROCESSING;
168     char *reply;
169     int login_finished = 0;
170     switch(event->type) {
171         case IOEVENT_RECV:
172             reply = event->data.recv_str;
173             if(!stricmplen(reply, "REJECT", 6)) {
174                 if((reply = strchr(reply, ' '))) {
175                     char reason[LINELEN];
176                     sprintf(reason, "Access denied: %s", reply + 7);
177                     login->reject_reason = strdup(reason);
178                 } else
179                     login->reject_reason = strdup("Access denied.");
180             } else if(!stricmplen(reply, "ACCEPT", 6)) {
181                 if((reply = strchr(reply, ' '))) {
182                     char *passwd = strchr(reply, ' ');
183                     if(passwd) {
184                         *passwd = '\0';
185                         passwd++;
186                         free(login->password);
187                         login->password = strdup(passwd);
188                     }
189                     free(login->username);
190                     login->username = strdup(reply);
191                 }
192                 login->login_accepted = 1;
193             } else {
194                 char *argv[MAXNUMPARAMS];
195                 int argc = parse_line(reply, argv, 0);
196                 if(argc < 2) return;
197                 if(!stricmp(argv[0], "CLASS")) {
198                     login->session_class = strdup(argv[1]);
199                 }
200                 
201             }
202             break;
203         case IOEVENT_TIMEOUT:
204             login->reject_reason = strdup("Login Script timeout.");
205             login_finished = 1;
206             break;
207         case IOEVENT_CLOSED:
208             login->reject_reason = strdup("Login Script error (no reply).");
209             login_finished = 1;
210             break;
211         default:
212             return;
213     }
214     if(login_finished) {
215         iohandler_close(event->iofd);
216         if(login->login_accepted)
217             usersession_login_accept(login);
218         else
219             userclient_login_failed(login, login->reject_reason);
220     }
221 }