1 /*
2  irc-server.c : irssi
3 
4     Copyright (C) 1999-2000 Timo Sirainen
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "module.h"
22 
23 #include "net-sendbuffer.h"
24 #include "signals.h"
25 #include "rawlog.h"
26 #include "misc.h"
27 
28 #include "channels.h"
29 #include "queries.h"
30 
31 #include "irc-nicklist.h"
32 #include "irc-queries.h"
33 #include "irc-servers-setup.h"
34 #include "irc-servers.h"
35 #include "irc-cap.h"
36 #include "sasl.h"
37 
38 #include "channels-setup.h"
39 #include "channel-rejoin.h"
40 #include "servers-idle.h"
41 #include "servers-reconnect.h"
42 #include "servers-redirect.h"
43 #include "modes.h"
44 
45 #include "settings.h"
46 #include "recode.h"
47 
48 #define DEFAULT_MAX_KICKS 1
49 #define DEFAULT_MAX_MODES 3
50 #define DEFAULT_MAX_WHOIS 4
51 #define DEFAULT_MAX_MSGS 1
52 
53 #define DEFAULT_USER_MODE "+i"
54 #define DEFAULT_CMD_QUEUE_SPEED "2200msec"
55 #define DEFAULT_CMDS_MAX_AT_ONCE 5
56 #define DEFAULT_MAX_QUERY_CHANS 1 /* more and more IRC networks are using stupid ircds.. */
57 
58 /*
59  * 63 is the maximum hostname length defined by the protocol.  10 is a common
60  * username limit on many networks.  1 is for the `@'.
61  */
62 #define MAX_USERHOST_LEN (63 + 10 + 1)
63 
64 void irc_servers_reconnect_init(void);
65 void irc_servers_reconnect_deinit(void);
66 
67 static int cmd_tag;
68 
isnickflag_func(SERVER_REC * server,char flag)69 static int isnickflag_func(SERVER_REC *server, char flag)
70 {
71 	IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
72 
73 	return isnickflag(irc_server, flag);
74 }
75 
ischannel_func(SERVER_REC * server,const char * data)76 static int ischannel_func(SERVER_REC *server, const char *data)
77 {
78 	IRC_SERVER_REC *irc_server = (IRC_SERVER_REC *) server;
79 	char *chantypes, *statusmsg;
80 
81 	g_return_val_if_fail(data != NULL, FALSE);
82 
83 	/* empty string is no channel */
84 	if (*data == '\0')
85 		return FALSE;
86 
87 	chantypes = g_hash_table_lookup(irc_server->isupport, "chantypes");
88 	if (chantypes == NULL)
89 		chantypes = "#&!+"; /* normal, local, secure, modeless */
90 
91 	statusmsg = g_hash_table_lookup(irc_server->isupport, "statusmsg");
92 	if (statusmsg == NULL)
93 		statusmsg = "@";
94 
95 	data += strspn(data, statusmsg);
96 
97 	/* strchr(3) considers the trailing NUL as part of the string, make sure
98 	 * we didn't advance too much. */
99 	return *data != '\0' && strchr(chantypes, *data) != NULL;
100 }
101 
split_line(const SERVER_REC * server,const char * line,const char * target,int len)102 static char **split_line(const SERVER_REC *server, const char *line,
103 			 const char *target, int len)
104 {
105 	const char *start = settings_get_str("split_line_start");
106 	const char *end = settings_get_str("split_line_end");
107 	gboolean onspace = settings_get_bool("split_line_on_space");
108 	char *recoded_start = recode_out(server, start, target);
109 	char *recoded_end = recode_out(server, end, target);
110 	char **lines;
111 	int i;
112 
113 	/*
114 	 * Having the same length limit on all lines will make the first line
115 	 * shorter than necessary if `split_line_start' is set, but it makes
116 	 * the code much simpler.  It's worth it.
117 	 */
118 	len -= strlen(recoded_start) + strlen(recoded_end);
119 	g_warn_if_fail(len > 0);
120 	if (len <= 0) {
121 		/* There is no room for anything. */
122 		g_free(recoded_start);
123 		g_free(recoded_end);
124 		lines = g_new(char *, 1);
125 		lines[0] = NULL;
126 		return lines;
127 	}
128 
129 	lines = recode_split(server, line, target, len, onspace);
130 	for (i = 0; lines[i] != NULL; i++) {
131 		if (i != 0 && *start != '\0') {
132 			/* Not the first line. */
133 			char *tmp = lines[i];
134 			lines[i] = g_strconcat(start, tmp, NULL);
135 			g_free(tmp);
136 		}
137 		if (lines[i + 1] != NULL && *end != '\0') {
138 			/* Not the last line. */
139 			char *tmp = lines[i];
140 
141 			if (lines[i + 2] == NULL) {
142 				/* Next to last line.  Check if we have room
143 				 * to append the last line to the current line,
144 				 * to avoid an unnecessary line break.
145 				 */
146 				char *recoded_l = recode_out(server,
147 							     lines[i+1],
148 							     target);
149 				if (strlen(recoded_l) <= strlen(recoded_end)) {
150 					lines[i] = g_strconcat(tmp, lines[i+1],
151 							       NULL);
152 					g_free_and_null(lines[i+1]);
153 					lines = g_renew(char *, lines, i + 2);
154 
155 					g_free(recoded_l);
156 					g_free(tmp);
157 					break;
158 				}
159 				g_free(recoded_l);
160 			}
161 
162 			lines[i] = g_strconcat(tmp, end, NULL);
163 			g_free(tmp);
164 		}
165 	}
166 
167 	g_free(recoded_start);
168 	g_free(recoded_end);
169 	return lines;
170 }
171 
send_message(SERVER_REC * server,const char * target,const char * msg,int target_type)172 static void send_message(SERVER_REC *server, const char *target,
173 			 const char *msg, int target_type)
174 {
175 	IRC_SERVER_REC *ircserver;
176 	CHANNEL_REC *channel;
177 	char *str;
178 	char *recoded;
179 
180         ircserver = IRC_SERVER(server);
181 	g_return_if_fail(ircserver != NULL);
182 	g_return_if_fail(target != NULL);
183 	g_return_if_fail(msg != NULL);
184 
185 	if (*target == '!') {
186 		/* !chan -> !12345chan */
187 		channel = channel_find(server, target);
188 		if (channel != NULL &&
189 		    g_ascii_strcasecmp(channel->name, target) != 0)
190 			target = channel->name;
191 	}
192 
193 	recoded = recode_out(SERVER(server), msg, target);
194 	str = g_strdup_printf("PRIVMSG %s :%s", target, recoded);
195 	irc_send_cmd_split(ircserver, str, 2, ircserver->max_msgs_in_cmd);
196 	g_free(str);
197 	g_free(recoded);
198 }
199 
split_message(SERVER_REC * server,const char * target,const char * msg)200 static char **split_message(SERVER_REC *server, const char *target,
201 			    const char *msg)
202 {
203 	IRC_SERVER_REC *ircserver = IRC_SERVER(server);
204 
205 	g_return_val_if_fail(ircserver != NULL, NULL);
206 	g_return_val_if_fail(target != NULL, NULL);
207 	g_return_val_if_fail(msg != NULL, NULL);
208 
209 	/* length calculation shamelessly stolen from splitlong_safe.pl */
210 	return split_line(SERVER(server), msg, target,
211 			  510 - strlen(":! PRIVMSG  :") -
212 			  strlen(ircserver->nick) - MAX_USERHOST_LEN -
213 			  strlen(target));
214 }
215 
server_init(IRC_SERVER_REC * server)216 static void server_init(IRC_SERVER_REC *server)
217 {
218 	IRC_SERVER_CONNECT_REC *conn;
219 	char *address, *ptr, *username, *cmd;
220 
221 	g_return_if_fail(server != NULL);
222 
223 	conn = server->connrec;
224 
225 	if (conn->proxy != NULL && conn->proxy_password != NULL &&
226 	    *conn->proxy_password != '\0') {
227 		cmd = g_strdup_printf("PASS %s", conn->proxy_password);
228 		irc_send_cmd_now(server, cmd);
229 		g_free(cmd);
230 	}
231 
232 	if (conn->proxy != NULL && conn->proxy_string != NULL) {
233 		cmd = g_strdup_printf(conn->proxy_string, conn->address, conn->port);
234 		irc_send_cmd_now(server, cmd);
235 		g_free(cmd);
236 	}
237 
238 	if (conn->sasl_mechanism != SASL_MECHANISM_NONE)
239 		irc_cap_toggle(server, "sasl", TRUE);
240 
241 	irc_cap_toggle(server, "multi-prefix", TRUE);
242 
243 	irc_send_cmd_now(server, "CAP LS");
244 
245 	if (conn->password != NULL && *conn->password != '\0') {
246                 /* send password */
247 		cmd = g_strdup_printf("PASS %s", conn->password);
248 		irc_send_cmd_now(server, cmd);
249 		g_free(cmd);
250 	}
251 
252         /* send nick */
253 	cmd = g_strdup_printf("NICK %s", conn->nick);
254 	irc_send_cmd_now(server, cmd);
255 	g_free(cmd);
256 
257 	/* send user/realname */
258 	address = server->connrec->address;
259         ptr = strrchr(address, ':');
260 	if (ptr != NULL) {
261 		/* IPv6 address .. doesn't work here, use the string after
262 		   the last : char */
263 		address = ptr+1;
264 		if (*address == '\0')
265 			address = "x";
266 	}
267 
268 	username = g_strdup(conn->username);
269 	ptr = strchr(username, ' ');
270 	if (ptr != NULL) *ptr = '\0';
271 
272 	cmd = g_strdup_printf("USER %s %s %s :%s", username, username, address, conn->realname);
273 	irc_send_cmd_now(server, cmd);
274 	g_free(cmd);
275 	g_free(username);
276 
277 	if (conn->proxy != NULL && conn->proxy_string_after != NULL) {
278 		cmd = g_strdup_printf(conn->proxy_string_after, conn->address, conn->port);
279 		irc_send_cmd_now(server, cmd);
280 		g_free(cmd);
281 	}
282 
283 	server->isupport = g_hash_table_new((GHashFunc) g_istr_hash,
284 					    (GCompareFunc) g_istr_equal);
285 
286 	/* set the standards */
287 	g_hash_table_insert(server->isupport, g_strdup("CHANMODES"), g_strdup("beI,k,l,imnpst"));
288 	g_hash_table_insert(server->isupport, g_strdup("PREFIX"), g_strdup("(ohv)@%+"));
289 
290 	server->cmdcount = 0;
291 
292 	/* prevent the queue from sending too early, we have a max cut off of 120 secs */
293 	/* this will reset to 1 sec after we get the 001 event */
294 	g_get_current_time(&server->wait_cmd);
295 	g_time_val_add(&server->wait_cmd, 120 * G_USEC_PER_SEC);
296 }
297 
irc_server_init_connect(SERVER_CONNECT_REC * conn)298 SERVER_REC *irc_server_init_connect(SERVER_CONNECT_REC *conn)
299 {
300 	IRC_SERVER_CONNECT_REC *ircconn;
301 	IRC_SERVER_REC *server;
302 
303 	g_return_val_if_fail(IS_IRC_SERVER_CONNECT(conn), NULL);
304 	if (conn->address == NULL || *conn->address == '\0') return NULL;
305 	if (conn->nick == NULL || *conn->nick == '\0') return NULL;
306 
307 	server = g_new0(IRC_SERVER_REC, 1);
308 	server->chat_type = IRC_PROTOCOL;
309 
310 	ircconn = (IRC_SERVER_CONNECT_REC *) conn;
311 	server->connrec = ircconn;
312         server_connect_ref(conn);
313 
314 	if (server->connrec->port <= 0) {
315 		server->connrec->port =
316 			server->connrec->use_tls ? 6697 : 6667;
317 	}
318 
319 	server->cmd_queue_speed = ircconn->cmd_queue_speed > 0 ?
320 		ircconn->cmd_queue_speed : settings_get_time("cmd_queue_speed");
321 	server->max_cmds_at_once = ircconn->max_cmds_at_once > 0 ?
322 		ircconn->max_cmds_at_once : settings_get_int("cmds_max_at_once");
323 	server->max_query_chans = ircconn->max_query_chans > 0 ?
324 		ircconn->max_query_chans : DEFAULT_MAX_QUERY_CHANS;
325 
326 	server->max_kicks_in_cmd = ircconn->max_kicks > 0 ?
327 		ircconn->max_kicks : DEFAULT_MAX_KICKS;
328 	server->max_modes_in_cmd = ircconn->max_modes > 0 ?
329 		ircconn->max_modes : DEFAULT_MAX_MODES;
330 	server->max_whois_in_cmd = ircconn->max_whois > 0 ?
331 		ircconn->max_whois : DEFAULT_MAX_WHOIS;
332 	server->max_msgs_in_cmd = ircconn->max_msgs > 0 ?
333 		ircconn->max_msgs : DEFAULT_MAX_MSGS;
334 	server->connrec->use_tls = conn->use_tls;
335 
336 	modes_server_init(server);
337 
338         server_connect_init((SERVER_REC *) server);
339 	return (SERVER_REC *) server;
340 }
341 
irc_server_connect(SERVER_REC * server)342 void irc_server_connect(SERVER_REC *server)
343 {
344 	g_return_if_fail(server != NULL);
345 
346 	if (!server_start_connect(server)) {
347                 server_connect_unref(server->connrec);
348 		g_free(server);
349 	}
350 }
351 
352 /* Returns TRUE if `command' is sent to `target' */
command_has_target(const char * cmd,const char * target)353 static int command_has_target(const char *cmd, const char *target)
354 {
355 	const char *p;
356         int len;
357 
358         /* just assume the command is in form "<command> <target> <data>" */
359         p = strchr(cmd, ' ');
360 	if (p == NULL) return FALSE;
361 	p++;
362 
363         len = strlen(target);
364 	return strncmp(p, target, len) == 0 && p[len] == ' ';
365 }
366 
367 /* Purge server output, either all or for specified target */
irc_server_purge_output(IRC_SERVER_REC * server,const char * target)368 void irc_server_purge_output(IRC_SERVER_REC *server, const char *target)
369 {
370 	GSList *tmp, *next, *link;
371         REDIRECT_REC *redirect;
372 	char *cmd;
373 
374 	if (target != NULL && *target == '\0')
375                 target = NULL;
376 
377 	for (tmp = server->cmdqueue; tmp != NULL; tmp = next) {
378 		next = tmp->next->next;
379 		cmd = tmp->data;
380                 redirect = tmp->next->data;
381 
382 		if ((target == NULL || command_has_target(cmd, target)) &&
383 		    g_ascii_strncasecmp(cmd, "PONG ", 5) != 0) {
384                         /* remove the redirection */
385                         link = tmp->next;
386 			server->cmdqueue =
387 				g_slist_remove_link(server->cmdqueue, link);
388                         g_slist_free_1(link);
389 
390 			if (redirect != NULL)
391                                 server_redirect_destroy(redirect);
392 
393                         /* remove the command */
394 			server->cmdqueue =
395 				g_slist_remove(server->cmdqueue, cmd);
396                         g_free(cmd);
397                         server->cmdcount--;
398 		}
399 	}
400 }
401 
sig_connected(IRC_SERVER_REC * server)402 static void sig_connected(IRC_SERVER_REC *server)
403 {
404 	if (!IS_IRC_SERVER(server))
405 		return;
406 
407 	server->isnickflag = isnickflag_func;
408 	server->ischannel = ischannel_func;
409 	server->split_message = split_message;
410 	server->send_message = send_message;
411 	server->query_find_func =
412 		(QUERY_REC *(*)(SERVER_REC *, const char *)) irc_query_find;
413 	server->nick_comp_func = irc_nickcmp_rfc1459;
414 
415 	server->splits = g_hash_table_new((GHashFunc) g_istr_hash,
416 					  (GCompareFunc) g_istr_equal);
417 
418         if (!server->session_reconnect)
419 		server_init(server);
420 }
421 
isupport_destroy_hash(void * key,void * value)422 static void isupport_destroy_hash(void *key, void *value)
423 {
424 	g_free(key);
425 	g_free(value);
426 }
427 
sig_destroyed(IRC_SERVER_REC * server)428 static void sig_destroyed(IRC_SERVER_REC *server)
429 {
430 	GSList *tmp;
431 
432 	if (!IS_IRC_SERVER(server))
433 		return;
434 
435 	for (tmp = server->cmdqueue; tmp != NULL; tmp = tmp->next->next) {
436 		g_free(tmp->data);
437 		if (tmp->next->data != NULL)
438                         server_redirect_destroy(tmp->next->data);
439 	}
440 	g_slist_free(server->cmdqueue);
441 	server->cmdqueue = NULL;
442 
443 	gslist_free_full(server->cap_active, (GDestroyNotify) g_free);
444 	server->cap_active = NULL;
445 
446 	if (server->cap_supported) {
447 		g_hash_table_destroy(server->cap_supported);
448 		server->cap_supported = NULL;
449 	}
450 
451 	gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
452 	server->cap_queue = NULL;
453 
454 	/* was g_free_and_null, but can't use on a GString */
455 	if (server->sasl_buffer != NULL) {
456 		g_string_free(server->sasl_buffer, TRUE);
457 		server->sasl_buffer = NULL;
458 	}
459 
460 	/* these are dynamically allocated only if isupport was sent */
461 	g_hash_table_foreach(server->isupport,
462 			     (GHFunc) isupport_destroy_hash, server);
463 	g_hash_table_destroy(server->isupport);
464 	server->isupport = NULL;
465 
466 	g_free_and_null(server->wanted_usermode);
467 	g_free_and_null(server->real_address);
468 	g_free_and_null(server->usermode);
469 	g_free_and_null(server->userhost);
470 	g_free_and_null(server->last_invite);
471 }
472 
sig_server_quit(IRC_SERVER_REC * server,const char * msg)473 static void sig_server_quit(IRC_SERVER_REC *server, const char *msg)
474 {
475 	char *str;
476 	char *recoded;
477 
478 	if (!IS_IRC_SERVER(server) || !server->connected)
479 		return;
480 
481 	recoded = recode_out(SERVER(server), msg, NULL);
482 	str = g_strdup_printf("QUIT :%s", recoded);
483 	irc_send_cmd_now(server, str);
484 	g_free(str);
485 	g_free(recoded);
486 }
487 
irc_server_send_action(IRC_SERVER_REC * server,const char * target,const char * data)488 void irc_server_send_action(IRC_SERVER_REC *server, const char *target, const char *data)
489 {
490 	char *recoded;
491 
492 	recoded = recode_out(SERVER(server), data, target);
493 	irc_send_cmdv(server, "PRIVMSG %s :\001ACTION %s\001", target, recoded);
494 	g_free(recoded);
495 }
496 
irc_server_split_action(IRC_SERVER_REC * server,const char * target,const char * data)497 char **irc_server_split_action(IRC_SERVER_REC *server, const char *target,
498 			       const char *data)
499 {
500 	g_return_val_if_fail(server != NULL, NULL);
501 	g_return_val_if_fail(target != NULL, NULL);
502 	g_return_val_if_fail(data != NULL, NULL);
503 
504 	return split_line(SERVER(server), data, target,
505 			  510 - strlen(":! PRIVMSG  :\001ACTION \001") -
506 			  strlen(server->nick) - MAX_USERHOST_LEN -
507 			  strlen(target));
508 }
509 
irc_server_send_away(IRC_SERVER_REC * server,const char * reason)510 void irc_server_send_away(IRC_SERVER_REC *server, const char *reason)
511 {
512 	char *recoded = NULL;
513 
514 	if (!IS_IRC_SERVER(server))
515 		return;
516 
517 	if (*reason != '\0' || server->usermode_away) {
518 		g_free_and_null(server->away_reason);
519                 if (*reason != '\0') {
520 			server->away_reason = g_strdup(reason);
521 			reason = recoded = recode_out(SERVER(server), reason, NULL);
522 			irc_send_cmdv(server, "AWAY :%s", reason);
523 		} else {
524 			irc_send_cmdv(server, "AWAY");
525 		}
526 
527 	}
528 	g_free(recoded);
529 }
530 
irc_server_send_data(IRC_SERVER_REC * server,const char * data,int len)531 void irc_server_send_data(IRC_SERVER_REC *server, const char *data, int len)
532 {
533 	if (net_sendbuffer_send(server->handle, data, len) == -1) {
534 		/* something bad happened */
535 		server->connection_lost = TRUE;
536 		return;
537 	}
538 
539 	g_get_current_time(&server->last_cmd);
540 
541 	/* A bit kludgy way to do the flood protection. In ircnet, there
542 	   actually is 1sec / 100 bytes penalty, but we rather want to deal
543 	   with the max. 1000 bytes input buffer problem. If we send more
544 	   than that with the burst, we'll get excess flooded. */
545 	if (len < 100 || server->cmd_queue_speed <= 10)
546 		server->wait_cmd.tv_sec = 0;
547 	else {
548 		memcpy(&server->wait_cmd, &server->last_cmd, sizeof(GTimeVal));
549 		g_time_val_add(&server->wait_cmd, (2 + len/100) * G_USEC_PER_SEC);
550 	}
551 }
552 
server_cmd_timeout(IRC_SERVER_REC * server,GTimeVal * now)553 static int server_cmd_timeout(IRC_SERVER_REC *server, GTimeVal *now)
554 {
555 	REDIRECT_REC *redirect;
556         GSList *link;
557 	long usecs;
558 	char *cmd;
559 	int len;
560 
561 	if (!IS_IRC_SERVER(server))
562 		return 0;
563 
564 	if (server->cmdcount == 0 && server->cmdqueue == NULL)
565 		return 0;
566 
567 	if (g_timeval_cmp(now, &server->wait_cmd) == -1)
568 		return 1;
569 
570 	usecs = get_timeval_diff(now, &server->last_cmd);
571 	if (usecs < server->cmd_queue_speed)
572 		return 1;
573 
574 	server->cmdcount--;
575 	if (server->cmdqueue == NULL) return 1;
576 
577         /* get command */
578 	cmd = server->cmdqueue->data;
579         redirect = server->cmdqueue->next->data;
580 
581 	/* send command */
582 	len = strlen(cmd);
583 	irc_server_send_data(server, cmd, len);
584 
585 	/* add to rawlog without [CR+]LF */
586         if (len > 2 && cmd[len-2] == '\r')
587 		cmd[len-2] = '\0';
588         else if (cmd[len-1] == '\n')
589 		cmd[len-1] = '\0';
590 	rawlog_output(server->rawlog, cmd);
591 	server_redirect_command(server, cmd, redirect);
592 
593 	/* remove from queue */
594 	server->cmdqueue = g_slist_remove(server->cmdqueue, cmd);
595 	g_free(cmd);
596 
597         link = server->cmdqueue;
598 	server->cmdqueue = g_slist_remove_link(server->cmdqueue, link);
599         g_slist_free_1(link);
600 	return 1;
601 }
602 
603 /* check every now and then if there's data to be sent in command buffer */
servers_cmd_timeout(void)604 static int servers_cmd_timeout(void)
605 {
606 	GTimeVal now;
607 	GSList *tmp;
608 	int keep = 0;
609 
610 	g_get_current_time(&now);
611 	for (tmp = servers; tmp != NULL; tmp = tmp->next) {
612 		keep |= server_cmd_timeout(tmp->data, &now);
613 	}
614 	if (keep)
615 		return 1;
616 	else {
617 		cmd_tag = -1;
618 		return 0;
619 	}
620 }
621 
622 /* Start the timeout for sending data later and decreasing cmdcount again */
irc_servers_start_cmd_timeout(void)623 void irc_servers_start_cmd_timeout(void)
624 {
625 	if (cmd_tag == -1)
626 		cmd_tag = g_timeout_add(500, (GSourceFunc) servers_cmd_timeout, NULL);
627 }
628 
629 /* Return a string of all channels (and keys, if any have them) in server,
630    like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */
irc_server_get_channels(IRC_SERVER_REC * server)631 char *irc_server_get_channels(IRC_SERVER_REC *server)
632 {
633 	GSList *tmp;
634 	GString *chans, *keys;
635 	char *ret;
636 	int use_keys;
637 	int rejoin_channels_mode;
638 
639 	g_return_val_if_fail(server != NULL, FALSE);
640 
641 	rejoin_channels_mode = settings_get_choice("rejoin_channels_on_reconnect");
642 
643 	/* do we want to rejoin channels in the first place? */
644 	if(rejoin_channels_mode == 0)
645 		return g_strdup("");
646 
647 	chans = g_string_new(NULL);
648 	keys = g_string_new(NULL);
649 	use_keys = FALSE;
650 
651 	/* get currently joined channels */
652 	for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
653 		CHANNEL_REC *channel = tmp->data;
654 		CHANNEL_SETUP_REC *setup = channel_setup_find(channel->name, channel->server->connrec->chatnet);
655 		if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
656 			g_string_append_printf(chans, "%s,", channel->name);
657 			g_string_append_printf(keys, "%s,", channel->key == NULL ? "x" : channel->key);
658 			if (channel->key != NULL)
659 				use_keys = TRUE;
660 		}
661 	}
662 
663 	/* get also the channels that are in rejoin list */
664 	for (tmp = server->rejoin_channels; tmp != NULL; tmp = tmp->next) {
665 		REJOIN_REC *rec = tmp->data;
666 		CHANNEL_SETUP_REC *setup = channel_setup_find(rec->channel, server->tag);
667 
668 		if ((setup != NULL && setup->autojoin && rejoin_channels_mode == 2) || rejoin_channels_mode == 1) {
669 			g_string_append_printf(chans, "%s,", rec->channel);
670 			g_string_append_printf(keys, "%s,", rec->key == NULL ? "x" :
671 									rec->key);
672 
673 			if (rec->key != NULL) use_keys = TRUE;
674 		}
675 	}
676 
677 	if (chans->len > 0) {
678 		g_string_truncate(chans, chans->len-1);
679 		g_string_truncate(keys, keys->len-1);
680 		if (use_keys) g_string_append_printf(chans, " %s", keys->str);
681 	}
682 
683 	ret = chans->str;
684 	g_string_free(chans, FALSE);
685 	g_string_free(keys, TRUE);
686 
687 	return ret;
688 }
689 
event_connected(IRC_SERVER_REC * server,const char * data,const char * from)690 static void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
691 {
692 	char *params, *nick;
693 
694 	g_return_if_fail(server != NULL);
695 
696 	params = event_get_params(data, 1, &nick);
697 
698 	if (g_strcmp0(server->nick, nick) != 0) {
699 		/* nick changed unexpectedly .. connected via proxy, etc. */
700 		g_free(server->nick);
701 		server->nick = g_strdup(nick);
702 	}
703 
704 	/* set the server address */
705 	g_free(server->real_address);
706 	server->real_address = from == NULL ?
707 		g_strdup(server->connrec->address) : /* shouldn't happen.. */
708 		g_strdup(from);
709 
710 	/* last welcome message found - commands can be sent to server now. */
711 	server->connected = 1;
712 	server->real_connect_time = time(NULL);
713 
714 	/* let the queue send now that we are identified */
715 	g_get_current_time(&server->wait_cmd);
716 
717 	if (server->connrec->usermode != NULL) {
718 		/* Send the user mode, before the autosendcmd.
719 		 * Do not pass this through cmd_mode because it
720 		 * is not known whether the resulting MODE message
721 		 * (if any) is the initial umode or a reply to this.
722 		 */
723 		irc_send_cmdv(server, "MODE %s %s", server->nick,
724 				server->connrec->usermode);
725 		g_free_not_null(server->wanted_usermode);
726 		server->wanted_usermode = g_strdup(server->connrec->usermode);
727 	}
728 
729 	signal_emit("event connected", 1, server);
730 	g_free(params);
731 }
732 
event_server_info(IRC_SERVER_REC * server,const char * data)733 static void event_server_info(IRC_SERVER_REC *server, const char *data)
734 {
735 	char *params, *ircd_version, *usermodes, *chanmodes;
736 
737 	g_return_if_fail(server != NULL);
738 
739 	params = event_get_params(data, 5, NULL, NULL, &ircd_version, &usermodes, &chanmodes);
740 
741 	/* check if server understands I and e channel modes */
742 	if (strchr(chanmodes, 'I') && strchr(chanmodes, 'e'))
743 		server->emode_known = TRUE;
744 
745 	/* save server version */
746 	g_free_not_null(server->version);
747 	server->version = g_strdup(ircd_version);
748 
749 	g_free(params);
750 }
751 
parse_chanmodes(IRC_SERVER_REC * server,const char * sptr)752 static void parse_chanmodes(IRC_SERVER_REC *server, const char *sptr)
753 {
754 	mode_func_t *modefuncs[] = {
755 		modes_type_a,
756 		modes_type_b,
757 		modes_type_c,
758 		modes_type_d
759 	};
760 	char **item, **chanmodes;
761 	int i;
762 
763 	chanmodes = g_strsplit(sptr, ",", 5); /* ignore extras */
764 
765 	for (i = 0, item = chanmodes; *item != NULL && i < 4; item++, i++) {
766 		unsigned char *p = (unsigned char*) *item;
767 		while (*p != '\0') {
768 			server->modes[(int)*p].func = modefuncs[i];
769 			p++;
770 		}
771 	}
772 
773 	g_strfreev(chanmodes);
774 }
775 
parse_prefix(IRC_SERVER_REC * server,const char * sptr)776 static void parse_prefix(IRC_SERVER_REC *server, const char *sptr)
777 {
778 	const char *eptr;
779 
780 	if (*sptr++ != '(')
781 		return; /* Unknown prefix format */
782 
783 	eptr = strchr(sptr, ')');
784 	if (eptr == NULL)
785 		return;
786 
787 	eptr++;
788 	while (*sptr != '\0' && *eptr != '\0' && *sptr != ')' && *eptr != ' ') {
789 		server->modes[(int)(unsigned char) *sptr].func =
790 			modes_type_prefix;
791 		server->modes[(int)(unsigned char) *sptr].prefix = *eptr;
792 		server->prefix[(int)(unsigned char) *eptr] = *sptr;
793 		sptr++; eptr++;
794 	}
795 }
796 
797 
event_isupport(IRC_SERVER_REC * server,const char * data)798 static void event_isupport(IRC_SERVER_REC *server, const char *data)
799 {
800 	char **item, *sptr, *eptr;
801 	char **isupport;
802 	gpointer key, value;
803 
804 	g_return_if_fail(server != NULL);
805 
806 	server->isupport_sent = TRUE;
807 
808 	sptr = strchr(data, ' ');
809 	if (sptr == NULL)
810 		return;
811 	sptr++;
812 
813 	isupport = g_strsplit(sptr, " ", -1);
814 
815 	for(item = isupport; *item != NULL; item++) {
816 		int removed = FALSE;
817 
818 		if (**item == '\0')
819 			continue;
820 
821 		if (**item == ':')
822 			break;
823 
824 		sptr = strchr(*item, '=');
825 		if (sptr != NULL) {
826 			*sptr = '\0';
827 			sptr++;
828 		}
829 
830 		eptr = *item;
831 		if(*eptr == '-') {
832 			removed = TRUE;
833 			eptr++;
834 		}
835 
836 		key = value = NULL;
837 		if (!g_hash_table_lookup_extended(server->isupport, eptr,
838 						  &key, &value) && removed)
839 			continue;
840 
841 		g_hash_table_remove(server->isupport, eptr);
842 		if (!removed) {
843 			g_hash_table_insert(server->isupport, g_strdup(eptr),
844 					    g_strdup(sptr != NULL ? sptr : ""));
845 		}
846 
847 		g_free(key);
848 		g_free(value);
849 	}
850 	g_strfreev(isupport);
851 	irc_server_init_isupport(server);
852 
853 }
854 
event_motd(IRC_SERVER_REC * server,const char * data,const char * from)855 static void event_motd(IRC_SERVER_REC *server, const char *data, const char *from)
856 {
857 	if (server->connected)
858 		return;
859 
860 	/* Stupid broken piece of shit ircd didn't send us 001,
861 	   you'd think they could at least get that right??
862 	   But no, then I'll have to go and add these idiotic kludges
863 	   to make them work. Maybe I should instead get the users of these
864 	   servers to complain about it to their admins.
865 
866 	   Oh, and looks like it also doesn't answer anything to PINGs,
867 	   disable lag checking. */
868         server->disable_lag = TRUE;
869 	event_connected(server, data, from);
870 }
871 
event_end_of_motd(IRC_SERVER_REC * server,const char * data)872 static void event_end_of_motd(IRC_SERVER_REC *server, const char *data)
873 {
874 	server->motd_got = TRUE;
875 }
876 
event_channels_formed(IRC_SERVER_REC * server,const char * data)877 static void event_channels_formed(IRC_SERVER_REC *server, const char *data)
878 {
879 	char *params, *channels;
880 
881 	g_return_if_fail(server != NULL);
882 
883 	params = event_get_params(data, 2, NULL, &channels);
884         server->channels_formed = atoi(channels);
885 	g_free(params);
886 }
887 
event_hosthidden(IRC_SERVER_REC * server,const char * data)888 static void event_hosthidden(IRC_SERVER_REC *server, const char *data)
889 {
890 	char *params, *newhost, *p, *newuserhost;
891 
892 	g_return_if_fail(server != NULL);
893 
894 	params = event_get_params(data, 2, NULL, &newhost);
895 	/* do a sanity check */
896 	if (!strchr(newhost, '*') && !strchr(newhost, '?') &&
897 			!strchr(newhost, '!') && !strchr(newhost, '#') &&
898 			!strchr(newhost, '&') && !strchr(newhost, ' ') &&
899 			*newhost != '\0' && *newhost != '@' &&
900 			*newhost != ':' && *newhost != '-' &&
901 			newhost[strlen(newhost) - 1] != '-') {
902 		if (strchr(newhost, '@')) {
903 			newuserhost = g_strdup(newhost);
904 			g_free(server->userhost);
905 			server->userhost = newuserhost;
906 		} else if (server->userhost != NULL) {
907 			/* no user@, only process if we know the user@
908 			 * already
909 			 */
910 			p = strchr(server->userhost, '@');
911 			if (p == NULL)
912 				p = server->userhost;
913 			newuserhost = g_strdup_printf("%.*s@%s", (int)(p - server->userhost), server->userhost, newhost);
914 			g_free(server->userhost);
915 			server->userhost = newuserhost;
916 		}
917 	}
918 	g_free(params);
919 }
920 
event_server_banned(IRC_SERVER_REC * server,const char * data)921 static void event_server_banned(IRC_SERVER_REC *server, const char *data)
922 {
923 	g_return_if_fail(server != NULL);
924 
925         server->banned = TRUE;
926 }
927 
event_error(IRC_SERVER_REC * server,const char * data)928 static void event_error(IRC_SERVER_REC *server, const char *data)
929 {
930 	g_return_if_fail(server != NULL);
931 
932 	if (!server->connected && (stristr(data, "Unauthorized") != NULL ||
933 				   stristr(data, "K-lined") != NULL ||
934 				   stristr(data, "Banned") != NULL ||
935 				   stristr(data, "Bad user info") != NULL))
936 		server->banned = TRUE;
937 }
938 
event_ping(IRC_SERVER_REC * server,const char * data)939 static void event_ping(IRC_SERVER_REC *server, const char *data)
940 {
941 	char *params, *origin, *target, *str;
942 
943 	params = event_get_params(data, 2, &origin, &target);
944 	str = *target == '\0' ? g_strconcat("PONG :", origin, NULL) :
945 		g_strdup_printf("PONG %s :%s", target, origin);
946 	irc_send_cmd_now(server, str);
947         g_free(str);
948 	g_free(params);
949 }
950 
event_empty(void)951 static void event_empty(void)
952 {
953 }
954 
irc_server_init_isupport(IRC_SERVER_REC * server)955 void irc_server_init_isupport(IRC_SERVER_REC *server)
956 {
957 	char *sptr;
958 	gpointer key, value;
959 	/* chanmodes/prefix will fully override defaults */
960 	memset(server->modes, 0, sizeof(server->modes));
961 	memset(server->prefix, 0, sizeof(server->prefix));
962 
963 	if ((sptr = g_hash_table_lookup(server->isupport, "CHANMODES")))
964 		parse_chanmodes(server, sptr);
965 
966 	/* This is after chanmode because some servers define modes in both */
967 	if (g_hash_table_lookup_extended(server->isupport, "PREFIX",
968 					 &key, &value)) {
969 		sptr = value;
970 		if (*sptr != '(') {
971 			/* server incompatible with isupport draft */
972 			g_hash_table_remove(server->isupport, key);
973 			g_free(key);
974 			g_free(value);
975 			sptr = NULL;
976 		}
977 	} else {
978 		sptr = NULL;
979 	}
980 
981 	if (sptr == NULL) {
982 		sptr = g_strdup("(ohv)@%+");
983 		g_hash_table_insert(server->isupport, g_strdup("PREFIX"), sptr);
984 	}
985 	parse_prefix(server, sptr);
986 
987 	if ((sptr = g_hash_table_lookup(server->isupport, "MODES"))) {
988 		server->max_modes_in_cmd = atoi(sptr);
989 		if (server->max_modes_in_cmd < 1)
990 			server->max_modes_in_cmd = DEFAULT_MAX_MODES;
991 	}
992 
993 	if ((sptr = g_hash_table_lookup(server->isupport, "CASEMAPPING"))) {
994 		if (strstr(sptr, "rfc1459") != NULL)
995 			server->nick_comp_func = irc_nickcmp_rfc1459;
996 		else
997 			server->nick_comp_func = irc_nickcmp_ascii;
998 	}
999 
1000 	if ((sptr = g_hash_table_lookup(server->isupport, "TARGMAX"))) {
1001 		char *p = sptr;
1002 		server->max_kicks_in_cmd = 1;
1003 		server->max_msgs_in_cmd = 1;
1004 		/* Not doing WHOIS here until it is clear what it means. */
1005 		while (*p != '\0') {
1006 			if (!g_ascii_strncasecmp(p, "KICK:", 5)) {
1007 				server->max_kicks_in_cmd = atoi(p + 5);
1008 				if (server->max_kicks_in_cmd <= 0)
1009 					server->max_kicks_in_cmd = 30;
1010 			} else if (!g_ascii_strncasecmp(p, "PRIVMSG:", 8)) {
1011 				server->max_msgs_in_cmd = atoi(p + 8);
1012 				if (server->max_msgs_in_cmd <= 0)
1013 					server->max_msgs_in_cmd = 30;
1014 			}
1015 			p = strchr(p, ',');
1016 			if (p == NULL)
1017 				break;
1018 			p++;
1019 		}
1020 	} else if ((sptr = g_hash_table_lookup(server->isupport, "MAXTARGETS"))) {
1021 		server->max_msgs_in_cmd = atoi(sptr);
1022 		if (server->max_msgs_in_cmd <= 0)
1023 			server->max_msgs_in_cmd = 1;
1024 	}
1025 }
1026 
irc_servers_init(void)1027 void irc_servers_init(void)
1028 {
1029 	settings_add_choice("servers", "rejoin_channels_on_reconnect", 1, "off;on;auto");
1030 	settings_add_str("misc", "usermode", DEFAULT_USER_MODE);
1031 	settings_add_str("misc", "split_line_start", "");
1032 	settings_add_str("misc", "split_line_end", "");
1033 	settings_add_bool("misc", "split_line_on_space", TRUE);
1034 	settings_add_time("flood", "cmd_queue_speed", DEFAULT_CMD_QUEUE_SPEED);
1035 	settings_add_int("flood", "cmds_max_at_once", DEFAULT_CMDS_MAX_AT_ONCE);
1036 
1037 	cmd_tag = -1;
1038 
1039 	signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
1040 	signal_add_last("server destroyed", (SIGNAL_FUNC) sig_destroyed);
1041 	signal_add_last("server quit", (SIGNAL_FUNC) sig_server_quit);
1042 	signal_add("event 001", (SIGNAL_FUNC) event_connected);
1043 	signal_add("event 004", (SIGNAL_FUNC) event_server_info);
1044 	signal_add("event 005", (SIGNAL_FUNC) event_isupport);
1045 	signal_add("event 375", (SIGNAL_FUNC) event_motd);
1046 	signal_add_last("event 376", (SIGNAL_FUNC) event_end_of_motd);
1047 	signal_add_last("event 422", (SIGNAL_FUNC) event_end_of_motd); /* no motd */
1048 	signal_add("event 254", (SIGNAL_FUNC) event_channels_formed);
1049 	signal_add("event 396", (SIGNAL_FUNC) event_hosthidden);
1050 	signal_add("event 465", (SIGNAL_FUNC) event_server_banned);
1051 	signal_add("event error", (SIGNAL_FUNC) event_error);
1052 	signal_add("event ping", (SIGNAL_FUNC) event_ping);
1053 	signal_add("event empty", (SIGNAL_FUNC) event_empty);
1054 
1055 	irc_servers_setup_init();
1056 	irc_servers_reconnect_init();
1057 	servers_redirect_init();
1058 	servers_idle_init();
1059 }
1060 
irc_servers_deinit(void)1061 void irc_servers_deinit(void)
1062 {
1063 	if (cmd_tag != -1)
1064 		g_source_remove(cmd_tag);
1065 
1066 	signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
1067 	signal_remove("server destroyed", (SIGNAL_FUNC) sig_destroyed);
1068         signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit);
1069 	signal_remove("event 001", (SIGNAL_FUNC) event_connected);
1070 	signal_remove("event 004", (SIGNAL_FUNC) event_server_info);
1071 	signal_remove("event 005", (SIGNAL_FUNC) event_isupport);
1072 	signal_remove("event 375", (SIGNAL_FUNC) event_motd);
1073 	signal_remove("event 376", (SIGNAL_FUNC) event_end_of_motd);
1074 	signal_remove("event 422", (SIGNAL_FUNC) event_end_of_motd); /* no motd */
1075 	signal_remove("event 254", (SIGNAL_FUNC) event_channels_formed);
1076 	signal_remove("event 396", (SIGNAL_FUNC) event_hosthidden);
1077 	signal_remove("event 465", (SIGNAL_FUNC) event_server_banned);
1078 	signal_remove("event error", (SIGNAL_FUNC) event_error);
1079 	signal_remove("event ping", (SIGNAL_FUNC) event_ping);
1080 	signal_remove("event empty", (SIGNAL_FUNC) event_empty);
1081 
1082 	irc_servers_setup_deinit();
1083 	irc_servers_reconnect_deinit();
1084 	servers_redirect_deinit();
1085 	servers_idle_deinit();
1086 }
1087