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