added SSL backend for IOMultiplexer
[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 #include "IRCClient.h"
23
24 static struct UserSession *usersessions = NULL;
25
26 static struct UserSession *usersession_add(char *username, char *password, char *nick) {
27     struct UserSession *session;
28     session = calloc(1, sizeof(*session));
29     session->username = strdup(username);
30     session->password = strdup(password);
31     session->nick = strdup(nick);
32     
33     //add UserSession to the list
34     session->prev = NULL;
35     session->next = usersessions;
36     if(usersessions)
37         usersessions->prev = session;
38     usersessions = session;
39     
40     return session;
41 }
42
43 static void usersession_close(struct UserSession *session) {
44     if(session->client) {
45         session->client->user = NULL;
46         userclient_close(session->client);
47     }
48     if(session->irc)
49         ircclient_close(session->irc);
50     if(session->timer)
51         iohandler_close(session->timer);
52     
53     if(session->prev)
54         session->prev->next = session->next;
55     else
56         usersessions = session->next;
57     if(session->next)
58         session->next->prev = session->prev;
59     free(session->username);
60     free(session->password);
61     free(session->nick);
62     if(session->realname)
63         free(session->realname);
64     free(session);
65 }
66
67 /* ****************** EXTERNAL EVENTS ****************** */
68
69 static void usersession_timer_callback(struct IOEvent *event);
70
71 void usersession_error(struct UserSession *session, char *error) {
72     if(session->client) {
73         iohandler_printf(session->client->iofd, ":*TransparentIRC!TransIRC@TransparentIRC.system.notification NOTICE %s :[TransparentIRC] ERROR: %s", session->nick, error);
74     }
75     usersession_close(session);
76 }
77
78 void usersession_client_close(struct UserSession *session) {
79     session->client = NULL;
80     if(!session->irc || !session->irc->fully_connected) {
81         usersession_close(session);
82         return;
83     }
84     session->idle_since = time(0);
85     if(!session->timer) {
86         int timeout_sec = get_int_field("session.timeout");
87         if(timeout_sec) {
88             struct timeval timeout;
89             gettimeofday(&timeout, NULL);
90             timeout.tv_sec += timeout_sec;
91             session->timer = iohandler_timer(timeout, usersession_timer_callback);
92             session->timer->data = session;
93         }
94     }
95 }
96
97 void usersession_client_raw(struct UserSession *session, char *raw) {
98     if(session->client) {
99         iohandler_printf(session->client->iofd, "%s", raw);
100     }
101 }
102
103 void usersession_client_notification(struct UserSession *session, char *notification) {
104     if(session->client) {
105         iohandler_printf(session->client->iofd, ":*TransparentIRC!TransIRC@TransparentIRC.system.notification NOTICE %s :[TransparentIRC] %s", session->nick, notification);
106     }
107 }
108
109 static void usersession_timer_callback(struct IOEvent *event) {
110     struct UserSession *session = event->iofd->data;
111     switch(event->type) {
112         case IOEVENT_TIMEOUT:
113             usersession_close(session);
114             break;
115         default:
116             break;
117     }
118 }
119
120 /* ****************** LOGIN FUNCTIONS ****************** */
121
122 static void usersession_login_accept(struct UserLogin *login);
123 static void usersession_login_callback(struct IOEvent *event);
124
125 void usersession_login(struct UserLogin *login) {
126     if(get_int_field("auth.external.enabled")) {
127         char *execute = get_string_field("auth.external.execute");
128         char *parameters = get_string_field("auth.external.parameters");
129         
130         struct variable_replace_map map[] = {
131             {'U', login->username},
132             {'P', login->password},
133             {'N', login->nick},
134             {0, NULL}
135         };
136         char paramstr[CMDLEN+1];
137         build_var_string(paramstr, parameters, map);
138         char *argv[MAXNUMPARAMS];
139         int argc = parse_line(paramstr, argv+1, 0);
140         argv[0] = execute;
141         argv[argc+1] = NULL;
142         int fp = run_external_process(execute, argv);
143         if(fp < 0) {
144             userclient_login_failed(login, "Login Script error.");
145             return;
146         }
147         struct IODescriptor *iofd = iohandler_add(fp, IOTYPE_CLIENT, NULL, usersession_login_callback);
148         if(iofd) {
149             iofd->read_lines = 1;
150             iofd->state = IO_CONNECTED;
151             int timeout = get_int_field("auth.external.timeout");
152             if(timeout) {
153                 struct timeval tv_timeout;
154                 gettimeofday(&tv_timeout, NULL);
155                 tv_timeout.tv_sec += timeout;
156                 iohandler_set_timeout(iofd, &tv_timeout);
157             }
158             iofd->data = login;
159             login->login_iofd = iofd;
160             login->client->flags |= USERCLIENT_LOGIN_PROCESSING;
161         } else
162             userclient_login_failed(login, "Internal error.");
163     } else 
164         usersession_login_accept(login);
165 }
166
167 static void usersession_login_accept(struct UserLogin *login) {
168     //search session for user (or create a new one)
169     struct UserSession *session, *active_session = NULL;
170     int sessioncount = 0;
171     for(session = usersessions; session; session = session->next) {
172         if(!stricmp(login->username, session->username)) {
173             sessioncount++;
174             if(!strcmp(login->password, session->password) && !stricmp(login->nick, session->nick)) {
175                 active_session = session;
176             }
177         }
178     }
179     if(active_session) {
180         if(active_session->client) {
181             iohandler_printf(active_session->client->iofd, "ERROR :[TransparentIRC] Another client logged in.");
182             userclient_close(active_session->client);
183         } //active_session->client is now NULL
184         active_session->client = login->client;
185         if(active_session->timer) {
186             iohandler_close(active_session->timer);
187             active_session->timer = NULL;
188         }
189         userclient_login_successful(login, active_session, 1);
190     } else {
191         int sessionlimit = get_int_field("auth.session_limit");
192         if(sessionlimit && sessioncount >= sessionlimit) {
193             userclient_login_failed(login, "Session limit reached.");
194             return;
195         }
196         active_session = usersession_add(login->username, login->password, login->nick);
197         if(!active_session) {
198             userclient_login_failed(login, "Could not create Session.");
199             return;
200         }
201         active_session->client = login->client;
202         active_session->realname = strdup(login->realname);
203         ircclient_initialize(active_session, login);
204         userclient_login_successful(login, active_session, 0);
205     }
206 }
207
208 void usersession_login_abort(struct UserLogin *login) {
209     struct IODescriptor *iofd = login->login_iofd;
210     iohandler_close(iofd);
211 }
212
213 static void usersession_login_callback(struct IOEvent *event) {
214     struct UserLogin *login = event->iofd->data;
215     login->client->flags &= ~USERCLIENT_LOGIN_PROCESSING;
216     char *reply;
217     int login_finished = 0;
218     switch(event->type) {
219         case IOEVENT_RECV:
220             reply = event->data.recv_str;
221             if(!stricmplen(reply, "REJECT", 6)) {
222                 if((reply = strchr(reply, ' '))) {
223                     char reason[LINELEN];
224                     sprintf(reason, "Access denied: %s", reply + 7);
225                     login->reject_reason = strdup(reason);
226                 } else
227                     login->reject_reason = strdup("Access denied.");
228             } else if(!stricmplen(reply, "ACCEPT", 6)) {
229                 if((reply = strchr(reply, ' '))) {
230                     char *passwd = strchr(reply, ' ');
231                     if(passwd) {
232                         *passwd = '\0';
233                         passwd++;
234                         free(login->password);
235                         login->password = strdup(passwd);
236                     }
237                     free(login->username);
238                     login->username = strdup(reply);
239                 }
240                 login->login_accepted = 1;
241             } else {
242                 char *argv[MAXNUMPARAMS];
243                 int argc = parse_line(reply, argv, 0);
244                 if(argc < 2) return;
245                 if(!stricmp(argv[0], "CLASS")) {
246                     login->session_class = strdup(argv[1]);
247                 } else if(!stricmp(argv[0], "SERVER")) {
248                     login->server_address = strdup(argv[1]);
249                     if(argc > 2) {
250                         login->server_override_port = 1;
251                         if(argv[2][0] == '+') {
252                             argv[2]++;
253                             login->server_ssl = 1;
254                         }
255                         login->server_port = atoi(argv[2]);
256                     }
257                 } else if(!stricmp(argv[0], "BIND")) {
258                     login->bind_address = strdup(argv[1]);
259                 }
260                 
261             }
262             break;
263         case IOEVENT_TIMEOUT:
264             login->reject_reason = strdup("Login Script timeout.");
265             login_finished = 1;
266             break;
267         case IOEVENT_CLOSED:
268             login->reject_reason = strdup("Login Script error (no reply).");
269             login_finished = 1;
270             break;
271         default:
272             return;
273     }
274     if(login_finished) {
275         iohandler_close(event->iofd);
276         if(login->login_accepted)
277             usersession_login_accept(login);
278         else
279             userclient_login_failed(login, login->reject_reason);
280     }
281 }