1 /* X-Chat
2  * Copyright (C) 1998 Peter Zelezny.
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 2 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, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <string.h>
20 #include <ctype.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <time.h>
25 
26 #ifdef WIN32
27 #include <io.h>
28 #else
29 #include <unistd.h>
30 #endif
31 
32 #define WANTARPA
33 #define WANTDNS
34 #include "inet.h"
35 
36 #include "hexchat.h"
37 #include "util.h"
38 #include "ignore.h"
39 #include "fe.h"
40 #include "modes.h"
41 #include "notify.h"
42 #include "outbound.h"
43 #include "inbound.h"
44 #include "server.h"
45 #include "servlist.h"
46 #include "text.h"
47 #include "ctcp.h"
48 #include "hexchatc.h"
49 #include "chanopt.h"
50 
51 
52 void
clear_channel(session * sess)53 clear_channel (session *sess)
54 {
55 	if (sess->channel[0])
56 		strcpy (sess->waitchannel, sess->channel);
57 	sess->channel[0] = 0;
58 	sess->doing_who = FALSE;
59 	sess->done_away_check = FALSE;
60 
61 	log_close (sess);
62 
63 	if (sess->current_modes)
64 	{
65 		g_free (sess->current_modes);
66 		sess->current_modes = NULL;
67 	}
68 
69 	if (sess->mode_timeout_tag)
70 	{
71 		fe_timeout_remove (sess->mode_timeout_tag);
72 		sess->mode_timeout_tag = 0;
73 	}
74 
75 	fe_clear_channel (sess);
76 	userlist_clear (sess);
77 	fe_set_nonchannel (sess, FALSE);
78 	fe_set_title (sess);
79 }
80 
81 void
set_topic(session * sess,char * topic,char * stripped_topic)82 set_topic (session *sess, char *topic, char *stripped_topic)
83 {
84 	/* The topic of dialogs are the users hostname which is logged is new */
85 	if (sess->type == SESS_DIALOG && (!sess->topic || strcmp(sess->topic, stripped_topic))
86 		&& sess->logfd != -1)
87 	{
88 		char tbuf[1024];
89 		g_snprintf (tbuf, sizeof (tbuf), "[%s has address %s]\n", sess->channel, stripped_topic);
90 		write (sess->logfd, tbuf, strlen (tbuf));
91 	}
92 
93 	g_free (sess->topic);
94 	sess->topic = g_strdup (stripped_topic);
95 	fe_set_topic (sess, topic, stripped_topic);
96 }
97 
98 static session *
find_session_from_nick(char * nick,server * serv)99 find_session_from_nick (char *nick, server *serv)
100 {
101 	session *sess;
102 	GSList *list = sess_list;
103 
104 	sess = find_dialog (serv, nick);
105 	if (sess)
106 		return sess;
107 
108 	if (serv->front_session)
109 	{
110 		// If we are here for ChanServ, then it is usually a reply for the user
111 		if (!g_ascii_strcasecmp(nick, "ChanServ") || userlist_find (serv->front_session, nick))
112 			return serv->front_session;
113 	}
114 
115 	if (current_sess && current_sess->server == serv)
116 	{
117 		if (userlist_find (current_sess, nick))
118 			return current_sess;
119 	}
120 
121 	while (list)
122 	{
123 		sess = list->data;
124 		if (sess->server == serv)
125 		{
126 			if (userlist_find (sess, nick))
127 				return sess;
128 		}
129 		list = list->next;
130 	}
131 	return NULL;
132 }
133 
134 static session *
inbound_open_dialog(server * serv,char * from,const message_tags_data * tags_data)135 inbound_open_dialog (server *serv, char *from,
136 							const message_tags_data *tags_data)
137 {
138 	session *sess;
139 
140 	sess = new_ircwindow (serv, from, SESS_DIALOG, 0);
141 	/* for playing sounds */
142 	EMIT_SIGNAL_TIMESTAMP (XP_TE_OPENDIALOG, sess, NULL, NULL, NULL, NULL, 0,
143 								  tags_data->timestamp);
144 
145 	return sess;
146 }
147 
148 static void
inbound_make_idtext(server * serv,char * idtext,int max,int id)149 inbound_make_idtext (server *serv, char *idtext, int max, int id)
150 {
151 	idtext[0] = 0;
152 	if (serv->have_idmsg || serv->have_accnotify)
153 	{
154 		if (id)
155 		{
156 			safe_strcpy (idtext, prefs.hex_irc_id_ytext, max);
157 		} else
158 		{
159 			safe_strcpy (idtext, prefs.hex_irc_id_ntext, max);
160 		}
161 		/* convert codes like %C,%U to the proper ones */
162 		check_special_chars (idtext, TRUE);
163 	}
164 }
165 
166 void
inbound_privmsg(server * serv,char * from,char * ip,char * text,int id,const message_tags_data * tags_data)167 inbound_privmsg (server *serv, char *from, char *ip, char *text, int id,
168 					  const message_tags_data *tags_data)
169 {
170 	session *sess;
171 	struct User *user;
172 	char idtext[64];
173 	gboolean nodiag = FALSE;
174 
175 	sess = find_dialog (serv, from);
176 
177 	if (sess || prefs.hex_gui_autoopen_dialog)
178 	{
179 		/*0=ctcp  1=priv will set hex_gui_autoopen_dialog=0 here is flud detected */
180 		if (!sess)
181 		{
182 			if (flood_check (from, ip, serv, current_sess, 1))
183 				/* Create a dialog session */
184 				sess = inbound_open_dialog (serv, from, tags_data);
185 			else
186 				sess = serv->server_session;
187 			if (!sess)
188 				return; /* ?? */
189 		}
190 
191 		if (ip && ip[0])
192 			set_topic (sess, ip, ip);
193 		inbound_chanmsg (serv, NULL, NULL, from, text, FALSE, tags_data->identified, tags_data);
194 		return;
195 	}
196 
197 	sess = find_session_from_nick (from, serv);
198 	if (!sess)
199 	{
200 		sess = serv->front_session;
201 		nodiag = TRUE; /* We don't want it to look like a normal message in front sess */
202 	}
203 
204 	user = userlist_find (sess, from);
205 	if (user)
206 	{
207 		user->lasttalk = time (0);
208 		if (user->account)
209 			id = TRUE;
210 	}
211 
212 	inbound_make_idtext (serv, idtext, sizeof (idtext), id);
213 
214 	if (sess->type == SESS_DIALOG && !nodiag)
215 		EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0,
216 									  tags_data->timestamp);
217 	else
218 		EMIT_SIGNAL_TIMESTAMP (XP_TE_PRIVMSG, sess, from, text, idtext, NULL, 0,
219 									  tags_data->timestamp);
220 }
221 
222 /* used for Alerts section. Masks can be separated by commas and spaces. */
223 
224 gboolean
alert_match_word(char * word,char * masks)225 alert_match_word (char *word, char *masks)
226 {
227 	char *p = masks;
228 	char endchar;
229 	int res;
230 
231 	if (masks[0] == 0)
232 		return FALSE;
233 
234 	while (1)
235 	{
236 		/* if it's a 0, space or comma, the word has ended. */
237 		if (*p == 0 || *p == ' ' || *p == ',')
238 		{
239 			endchar = *p;
240 			*p = 0;
241 			res = match (g_strchug (masks), word);
242 			*p = endchar;
243 
244 			if (res)
245 				return TRUE;	/* yes, matched! */
246 
247 			masks = p + 1;
248 			if (*p == 0)
249 				return FALSE;
250 		}
251 		p++;
252 	}
253 }
254 
255 gboolean
alert_match_text(char * text,char * masks)256 alert_match_text (char *text, char *masks)
257 {
258 	unsigned char *p = text;
259 	unsigned char endchar;
260 	int res;
261 
262 	if (masks[0] == 0)
263 		return FALSE;
264 
265 	while (1)
266 	{
267 		if (*p >= '0' && *p <= '9')
268 		{
269 			p++;
270 			continue;
271 		}
272 
273 		/* if it's RFC1459 <special>, it can be inside a word */
274 		switch (*p)
275 		{
276 		case '-': case '[': case ']': case '\\':
277 		case '`': case '^': case '{': case '}':
278 		case '_': case '|':
279 			p++;
280 			continue;
281 		}
282 
283 		/* if it's a 0, space or comma, the word has ended. */
284 		if (*p == 0 || *p == ' ' || *p == ',' ||
285 			/* if it's anything BUT a letter, the word has ended. */
286 			 (!g_unichar_isalpha (g_utf8_get_char (p))))
287 		{
288 			endchar = *p;
289 			*p = 0;
290 			res = alert_match_word (text, masks);
291 			*p = endchar;
292 
293 			if (res)
294 				return TRUE;	/* yes, matched! */
295 
296 			text = p + g_utf8_skip [p[0]];
297 			if (*p == 0)
298 				return FALSE;
299 		}
300 
301 		p += g_utf8_skip [p[0]];
302 	}
303 }
304 
305 static int
is_hilight(char * from,char * text,session * sess,server * serv)306 is_hilight (char *from, char *text, session *sess, server *serv)
307 {
308 	if (alert_match_word (from, prefs.hex_irc_no_hilight))
309 		return 0;
310 
311 	text = strip_color (text, -1, STRIP_ALL);
312 
313 	if (alert_match_text (text, serv->nick) ||
314 		 alert_match_text (text, prefs.hex_irc_extra_hilight) ||
315 		 alert_match_word (from, prefs.hex_irc_nick_hilight))
316 	{
317 		g_free (text);
318 		if (sess != current_tab)
319 		{
320 			sess->tab_state |= TAB_STATE_NEW_HILIGHT;
321 			lastact_update (sess);
322 		}
323 		return 1;
324 	}
325 
326 	g_free (text);
327 	return 0;
328 }
329 
330 void
inbound_action(session * sess,char * chan,char * from,char * ip,char * text,int fromme,int id,const message_tags_data * tags_data)331 inbound_action (session *sess, char *chan, char *from, char *ip, char *text,
332 					 int fromme, int id, const message_tags_data *tags_data)
333 {
334 	session *def = sess;
335 	server *serv = sess->server;
336 	struct User *user;
337 	char nickchar[2] = "\000";
338 	char idtext[64];
339 	int privaction = FALSE;
340 
341 	if (!fromme)
342 	{
343 		if (is_channel (serv, chan))
344 		{
345 			sess = find_channel (serv, chan);
346 		} else
347 		{
348 			/* it's a private action! */
349 			privaction = TRUE;
350 			/* find a dialog tab for it */
351 			sess = find_dialog (serv, from);
352 			/* if non found, open a new one */
353 			if (!sess && prefs.hex_gui_autoopen_dialog)
354 			{
355 				/* but only if it wouldn't flood */
356 				if (flood_check (from, ip, serv, current_sess, 1))
357 					sess = inbound_open_dialog (serv, from, tags_data);
358 				else
359 					sess = serv->server_session;
360 			}
361 			if (!sess)
362 			{
363 				sess = find_session_from_nick (from, serv);
364 				/* still not good? */
365 				if (!sess)
366 					sess = serv->front_session;
367 			}
368 		}
369 	}
370 
371 	if (!sess)
372 		sess = def;
373 
374 	if (sess != current_tab)
375 	{
376 		if (fromme)
377 			sess->tab_state |= TAB_STATE_NEW_DATA;
378 		else
379 			sess->tab_state |= TAB_STATE_NEW_MSG;
380 		lastact_update (sess);
381 	}
382 
383 	user = userlist_find (sess, from);
384 	if (user)
385 	{
386 		nickchar[0] = user->prefix[0];
387 		user->lasttalk = time (0);
388 		if (user->account)
389 			id = TRUE;
390 		if (user->me)
391 			fromme = TRUE;
392 	}
393 
394 	inbound_make_idtext (serv, idtext, sizeof (idtext), id);
395 
396 	if (!fromme && !privaction)
397 	{
398 		if (is_hilight (from, text, sess, serv))
399 		{
400 			EMIT_SIGNAL_TIMESTAMP (XP_TE_HCHANACTION, sess, from, text, nickchar,
401 										  idtext, 0, tags_data->timestamp);
402 			return;
403 		}
404 	}
405 
406 	if (fromme)
407 		EMIT_SIGNAL_TIMESTAMP (XP_TE_UACTION, sess, from, text, nickchar, idtext,
408 									  0, tags_data->timestamp);
409 	else if (!privaction)
410 		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANACTION, sess, from, text, nickchar,
411 									  idtext, 0, tags_data->timestamp);
412 	else if (sess->type == SESS_DIALOG)
413 		EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVACTION, sess, from, text, idtext, NULL,
414 									  0, tags_data->timestamp);
415 	else
416 		EMIT_SIGNAL_TIMESTAMP (XP_TE_PRIVACTION, sess, from, text, idtext, NULL, 0,
417 									  tags_data->timestamp);
418 }
419 
420 void
inbound_chanmsg(server * serv,session * sess,char * chan,char * from,char * text,char fromme,int id,const message_tags_data * tags_data)421 inbound_chanmsg (server *serv, session *sess, char *chan, char *from,
422 					  char *text, char fromme, int id,
423 					  const message_tags_data *tags_data)
424 {
425 	struct User *user;
426 	int hilight = FALSE;
427 	char nickchar[2] = "\000";
428 	char idtext[64];
429 
430 	if (!sess)
431 	{
432 		if (chan)
433 		{
434 			sess = find_channel (serv, chan);
435 			if (!sess && !is_channel (serv, chan))
436 				sess = find_dialog (serv, chan);
437 		} else
438 		{
439 			sess = find_dialog (serv, from);
440 		}
441 		if (!sess)
442 			return;
443 	}
444 
445 	if (sess != current_tab)
446 	{
447 		sess->tab_state |= TAB_STATE_NEW_MSG;
448 		lastact_update (sess);
449 	}
450 
451 	user = userlist_find (sess, from);
452 	if (user)
453 	{
454 		if (user->account)
455 			id = TRUE;
456 		nickchar[0] = user->prefix[0];
457 		user->lasttalk = time (0);
458 		if (user->me)
459 			fromme = TRUE;
460 	}
461 
462 	if (fromme)
463 	{
464 		if (prefs.hex_away_auto_unmark && serv->is_away && !tags_data->timestamp)
465 			sess->server->p_set_back (sess->server);
466 		EMIT_SIGNAL_TIMESTAMP (XP_TE_UCHANMSG, sess, from, text, nickchar, NULL,
467 									  0, tags_data->timestamp);
468 		return;
469 	}
470 
471 	inbound_make_idtext (serv, idtext, sizeof (idtext), id);
472 
473 	if (is_hilight (from, text, sess, serv))
474 		hilight = TRUE;
475 
476 	if (sess->type == SESS_DIALOG)
477 		EMIT_SIGNAL_TIMESTAMP (XP_TE_DPRIVMSG, sess, from, text, idtext, NULL, 0,
478 									  tags_data->timestamp);
479 	else if (hilight)
480 		EMIT_SIGNAL_TIMESTAMP (XP_TE_HCHANMSG, sess, from, text, nickchar, idtext,
481 									  0, tags_data->timestamp);
482 	else
483 		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANMSG, sess, from, text, nickchar, idtext,
484 									  0, tags_data->timestamp);
485 }
486 
487 void
inbound_newnick(server * serv,char * nick,char * newnick,int quiet,const message_tags_data * tags_data)488 inbound_newnick (server *serv, char *nick, char *newnick, int quiet,
489 					  const message_tags_data *tags_data)
490 {
491 	int me = FALSE;
492 	session *sess;
493 	GSList *list = sess_list;
494 
495 	if (!serv->p_cmp (nick, serv->nick))
496 	{
497 		me = TRUE;
498 		safe_strcpy (serv->nick, newnick, NICKLEN);
499 	}
500 
501 	while (list)
502 	{
503 		sess = list->data;
504 		if (sess->server == serv)
505 		{
506 			if (userlist_change (sess, nick, newnick) || (me && sess->type == SESS_SERVER))
507 			{
508 				if (!quiet)
509 				{
510 					if (me)
511 						EMIT_SIGNAL_TIMESTAMP (XP_TE_UCHANGENICK, sess, nick,
512 													  newnick, NULL, NULL, 0,
513 													  tags_data->timestamp);
514 					else
515 						EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANGENICK, sess, nick,
516 													  newnick, NULL, NULL, 0, tags_data->timestamp);
517 				}
518 			}
519 			if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
520 			{
521 				safe_strcpy (sess->channel, newnick, CHANLEN);
522 				fe_set_channel (sess);
523 			}
524 			fe_set_title (sess);
525 		}
526 		list = list->next;
527 	}
528 
529 	dcc_change_nick (serv, nick, newnick);
530 
531 	if (me)
532 		fe_set_nick (serv, newnick);
533 }
534 
535 /* find a "<none>" tab */
536 static session *
find_unused_session(server * serv)537 find_unused_session (server *serv)
538 {
539 	session *sess;
540 	GSList *list = sess_list;
541 	while (list)
542 	{
543 		sess = (session *) list->data;
544 		if (sess->type == SESS_CHANNEL && sess->channel[0] == 0 &&
545 			 sess->server == serv)
546 		{
547 			if (sess->waitchannel[0] == 0)
548 				return sess;
549 		}
550 		list = list->next;
551 	}
552 	return NULL;
553 }
554 
555 static session *
find_session_from_waitchannel(char * chan,struct server * serv)556 find_session_from_waitchannel (char *chan, struct server *serv)
557 {
558 	session *sess;
559 	GSList *list = sess_list;
560 	while (list)
561 	{
562 		sess = (session *) list->data;
563 		if (sess->server == serv && sess->channel[0] == 0 && sess->type == SESS_CHANNEL)
564 		{
565 			if (!serv->p_cmp (chan, sess->waitchannel))
566 				return sess;
567 		}
568 		list = list->next;
569 	}
570 	return NULL;
571 }
572 
573 void
inbound_ujoin(server * serv,char * chan,char * nick,char * ip,const message_tags_data * tags_data)574 inbound_ujoin (server *serv, char *chan, char *nick, char *ip,
575 					const message_tags_data *tags_data)
576 {
577 	session *sess;
578 	int found_unused = FALSE;
579 
580 	/* already joined? probably a bnc */
581 	sess = find_channel (serv, chan);
582 	if (!sess)
583 	{
584 		/* see if a window is waiting to join this channel */
585 		sess = find_session_from_waitchannel (chan, serv);
586 		if (!sess)
587 		{
588 			/* find a "<none>" tab and use that */
589 			sess = find_unused_session (serv);
590 			found_unused = sess != NULL;
591 			if (!sess)
592 				/* last resort, open a new tab/window */
593 				sess = new_ircwindow (serv, chan, SESS_CHANNEL, 1);
594 		}
595 	}
596 
597 	safe_strcpy (sess->channel, chan, CHANLEN);
598 	if (found_unused)
599 	{
600 		chanopt_load (sess);
601 		scrollback_load (sess);
602 		if (sess->scrollwritten && sess->scrollback_replay_marklast)
603 			sess->scrollback_replay_marklast (sess);
604 	}
605 
606 	fe_set_channel (sess);
607 	fe_set_title (sess);
608 	fe_set_nonchannel (sess, TRUE);
609 	userlist_clear (sess);
610 
611 	log_open_or_close (sess);
612 
613 	sess->waitchannel[0] = 0;
614 	sess->ignore_date = TRUE;
615 	sess->ignore_mode = TRUE;
616 	sess->ignore_names = TRUE;
617 	sess->end_of_names = FALSE;
618 
619 	/* sends a MODE */
620 	serv->p_join_info (sess->server, chan);
621 
622 	EMIT_SIGNAL_TIMESTAMP (XP_TE_UJOIN, sess, nick, chan, ip, NULL, 0,
623 								  tags_data->timestamp);
624 
625 	if (prefs.hex_irc_who_join)
626 	{
627 		/* sends WHO #channel */
628 		serv->p_user_list (sess->server, chan);
629 		sess->doing_who = TRUE;
630 	}
631 }
632 
633 void
inbound_ukick(server * serv,char * chan,char * kicker,char * reason,const message_tags_data * tags_data)634 inbound_ukick (server *serv, char *chan, char *kicker, char *reason,
635 					const message_tags_data *tags_data)
636 {
637 	session *sess = find_channel (serv, chan);
638 	if (sess)
639 	{
640 		EMIT_SIGNAL_TIMESTAMP (XP_TE_UKICK, sess, serv->nick, chan, kicker,
641 									  reason, 0, tags_data->timestamp);
642 		clear_channel (sess);
643 		if (prefs.hex_irc_auto_rejoin)
644 		{
645 			serv->p_join (serv, chan, sess->channelkey);
646 			safe_strcpy (sess->waitchannel, chan, CHANLEN);
647 		}
648 	}
649 }
650 
651 void
inbound_upart(server * serv,char * chan,char * ip,char * reason,const message_tags_data * tags_data)652 inbound_upart (server *serv, char *chan, char *ip, char *reason,
653 					const message_tags_data *tags_data)
654 {
655 	session *sess = find_channel (serv, chan);
656 	if (sess)
657 	{
658 		if (*reason)
659 			EMIT_SIGNAL_TIMESTAMP (XP_TE_UPARTREASON, sess, serv->nick, ip, chan,
660 										  reason, 0, tags_data->timestamp);
661 		else
662 			EMIT_SIGNAL_TIMESTAMP (XP_TE_UPART, sess, serv->nick, ip, chan, NULL,
663 										  0, tags_data->timestamp);
664 		clear_channel (sess);
665 	}
666 }
667 
668 void
inbound_nameslist(server * serv,char * chan,char * names,const message_tags_data * tags_data)669 inbound_nameslist (server *serv, char *chan, char *names,
670 						 const message_tags_data *tags_data)
671 {
672 	session *sess;
673 	char **name_list;
674 	char *host, *nopre_name;
675 	char name[NICKLEN];
676 	int i;
677 	size_t offset;
678 
679 	sess = find_channel (serv, chan);
680 	if (!sess)
681 	{
682 		EMIT_SIGNAL_TIMESTAMP (XP_TE_USERSONCHAN, serv->server_session, chan,
683 									  names, NULL, NULL, 0, tags_data->timestamp);
684 		return;
685 	}
686 	if (!sess->ignore_names)
687 		EMIT_SIGNAL_TIMESTAMP (XP_TE_USERSONCHAN, sess, chan, names, NULL, NULL,
688 									  0, tags_data->timestamp);
689 
690 	if (sess->end_of_names)
691 	{
692 		sess->end_of_names = FALSE;
693 		userlist_clear (sess);
694 	}
695 
696 	name_list = g_strsplit (names, " ", -1);
697 	for (i = 0; name_list[i]; i++)
698 	{
699 		host = NULL;
700 		offset = sizeof(name);
701 
702 		if (name_list[i][0] == 0)
703 			continue;
704 
705 		if (serv->have_uhnames)
706 		{
707 			offset = 0;
708 			nopre_name = name_list[i];
709 
710 			/* Ignore prefixes so '!' won't cause issues */
711 			while (strchr (serv->nick_prefixes, *nopre_name) != NULL)
712 			{
713 				nopre_name++;
714 				offset++;
715 			}
716 
717 			offset += strcspn (nopre_name, "!");
718 			if (offset++ < strlen (name_list[i]))
719 				host = name_list[i] + offset;
720 		}
721 
722 		g_strlcpy (name, name_list[i], MIN(offset, sizeof(name)));
723 
724 		userlist_add (sess, name, host, NULL, NULL, tags_data);
725 	}
726 	g_strfreev (name_list);
727 }
728 
729 void
inbound_topic(server * serv,char * chan,char * topic_text,const message_tags_data * tags_data)730 inbound_topic (server *serv, char *chan, char *topic_text,
731 					const message_tags_data *tags_data)
732 {
733 	session *sess = find_channel (serv, chan);
734 	char *stripped_topic;
735 
736 	if (sess)
737 	{
738 		stripped_topic = strip_color (topic_text, -1, STRIP_ALL);
739 		set_topic (sess, topic_text, stripped_topic);
740 		g_free (stripped_topic);
741 	} else
742 		sess = serv->server_session;
743 
744 	EMIT_SIGNAL_TIMESTAMP (XP_TE_TOPIC, sess, chan, topic_text, NULL, NULL, 0,
745 								  tags_data->timestamp);
746 }
747 
748 void
inbound_topicnew(server * serv,char * nick,char * chan,char * topic,const message_tags_data * tags_data)749 inbound_topicnew (server *serv, char *nick, char *chan, char *topic,
750 						const message_tags_data *tags_data)
751 {
752 	session *sess;
753 	char *stripped_topic;
754 
755 	sess = find_channel (serv, chan);
756 	if (sess)
757 	{
758 		EMIT_SIGNAL_TIMESTAMP (XP_TE_NEWTOPIC, sess, nick, topic, chan, NULL, 0,
759 									  tags_data->timestamp);
760 		stripped_topic = strip_color (topic, -1, STRIP_ALL);
761 		set_topic (sess, topic, stripped_topic);
762 		g_free (stripped_topic);
763 	}
764 }
765 
766 void
inbound_join(server * serv,char * chan,char * user,char * ip,char * account,char * realname,const message_tags_data * tags_data)767 inbound_join (server *serv, char *chan, char *user, char *ip, char *account,
768 				  char *realname, const message_tags_data *tags_data)
769 {
770 	session *sess = find_channel (serv, chan);
771 	if (sess)
772 	{
773 		EMIT_SIGNAL_TIMESTAMP (XP_TE_JOIN, sess, user, chan, ip, account, 0,
774 									  tags_data->timestamp);
775 		userlist_add (sess, user, ip, account, realname, tags_data);
776 	}
777 }
778 
779 void
inbound_kick(server * serv,char * chan,char * user,char * kicker,char * reason,const message_tags_data * tags_data)780 inbound_kick (server *serv, char *chan, char *user, char *kicker, char *reason,
781 				  const message_tags_data *tags_data)
782 {
783 	session *sess = find_channel (serv, chan);
784 	if (sess)
785 	{
786 		EMIT_SIGNAL_TIMESTAMP (XP_TE_KICK, sess, kicker, user, chan, reason, 0,
787 									  tags_data->timestamp);
788 		userlist_remove (sess, user);
789 	}
790 }
791 
792 void
inbound_part(server * serv,char * chan,char * user,char * ip,char * reason,const message_tags_data * tags_data)793 inbound_part (server *serv, char *chan, char *user, char *ip, char *reason,
794 				  const message_tags_data *tags_data)
795 {
796 	session *sess = find_channel (serv, chan);
797 	if (sess)
798 	{
799 		if (*reason)
800 			EMIT_SIGNAL_TIMESTAMP (XP_TE_PARTREASON, sess, user, ip, chan, reason,
801 										  0, tags_data->timestamp);
802 		else
803 			EMIT_SIGNAL_TIMESTAMP (XP_TE_PART, sess, user, ip, chan, NULL, 0,
804 										  tags_data->timestamp);
805 		userlist_remove (sess, user);
806 	}
807 }
808 
809 void
inbound_topictime(server * serv,char * chan,char * nick,time_t stamp,const message_tags_data * tags_data)810 inbound_topictime (server *serv, char *chan, char *nick, time_t stamp,
811 						 const message_tags_data *tags_data)
812 {
813 	char *tim = ctime (&stamp);
814 	session *sess = find_channel (serv, chan);
815 
816 	if (!sess)
817 		sess = serv->server_session;
818 
819 	if (tim != NULL)
820 		tim[24] = 0;	/* get rid of the \n */
821 
822 	EMIT_SIGNAL_TIMESTAMP (XP_TE_TOPICDATE, sess, chan, nick, tim, NULL, 0,
823 								  tags_data->timestamp);
824 }
825 
826 void
inbound_quit(server * serv,char * nick,char * ip,char * reason,const message_tags_data * tags_data)827 inbound_quit (server *serv, char *nick, char *ip, char *reason,
828 				  const message_tags_data *tags_data)
829 {
830 	GSList *list = sess_list;
831 	session *sess;
832 	struct User *user;
833 	int was_on_front_session = FALSE;
834 
835 	while (list)
836 	{
837 		sess = (session *) list->data;
838 		if (sess->server == serv)
839 		{
840  			if (sess == current_sess)
841  				was_on_front_session = TRUE;
842 			if ((user = userlist_find (sess, nick)))
843 			{
844 				EMIT_SIGNAL_TIMESTAMP (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0,
845 											  tags_data->timestamp);
846 				userlist_remove_user (sess, user);
847 			} else if (sess->type == SESS_DIALOG && !serv->p_cmp (sess->channel, nick))
848 			{
849 				EMIT_SIGNAL_TIMESTAMP (XP_TE_QUIT, sess, nick, reason, ip, NULL, 0,
850 											  tags_data->timestamp);
851 			}
852 		}
853 		list = list->next;
854 	}
855 
856 	notify_set_offline (serv, nick, was_on_front_session, tags_data);
857 }
858 
859 void
inbound_account(server * serv,char * nick,char * account,const message_tags_data * tags_data)860 inbound_account (server *serv, char *nick, char *account,
861 					  const message_tags_data *tags_data)
862 {
863 	session *sess = NULL;
864 	GSList *list;
865 
866 	list = sess_list;
867 	while (list)
868 	{
869 		sess = list->data;
870 		if (sess->server == serv)
871 			userlist_set_account (sess, nick, account);
872 		list = list->next;
873 	}
874 }
875 
876 void
inbound_ping_reply(session * sess,char * timestring,char * from,const message_tags_data * tags_data)877 inbound_ping_reply (session *sess, char *timestring, char *from,
878 						  const message_tags_data *tags_data)
879 {
880 	unsigned long tim, nowtim, dif;
881 	int lag = 0;
882 	char outbuf[64];
883 
884 	if (strncmp (timestring, "LAG", 3) == 0)
885 	{
886 		timestring += 3;
887 		lag = 1;
888 	}
889 
890 	tim = strtoul (timestring, NULL, 10);
891 	nowtim = make_ping_time ();
892 	dif = nowtim - tim;
893 
894 	sess->server->ping_recv = time (0);
895 
896 	if (lag)
897 	{
898 		sess->server->lag_sent = 0;
899 		sess->server->lag = dif;
900 		fe_set_lag (sess->server, dif);
901 		return;
902 	}
903 
904 	if (atol (timestring) == 0)
905 	{
906 		if (sess->server->lag_sent)
907 			sess->server->lag_sent = 0;
908 		else
909 			EMIT_SIGNAL_TIMESTAMP (XP_TE_PINGREP, sess, from, "?", NULL, NULL, 0,
910 										  tags_data->timestamp);
911 	} else
912 	{
913 		g_snprintf (outbuf, sizeof (outbuf), "%ld.%03ld", dif / 1000, dif % 1000);
914 		EMIT_SIGNAL_TIMESTAMP (XP_TE_PINGREP, sess, from, outbuf, NULL, NULL, 0,
915 									  tags_data->timestamp);
916 	}
917 }
918 
919 static session *
find_session_from_type(int type,server * serv)920 find_session_from_type (int type, server *serv)
921 {
922 	session *sess;
923 	GSList *list = sess_list;
924 	while (list)
925 	{
926 		sess = list->data;
927 		if (sess->type == type && serv == sess->server)
928 			return sess;
929 		list = list->next;
930 	}
931 	return NULL;
932 }
933 
934 void
inbound_notice(server * serv,char * to,char * nick,char * msg,char * ip,int id,const message_tags_data * tags_data)935 inbound_notice (server *serv, char *to, char *nick, char *msg, char *ip, int id,
936 					 const message_tags_data *tags_data)
937 {
938 	char *ptr = to;
939 	session *sess = 0;
940 	int server_notice = FALSE;
941 
942 	if (is_channel (serv, ptr))
943 		sess = find_channel (serv, ptr);
944 
945 	/* /notice [mode-prefix]#channel should end up in that channel */
946 	if (!sess && ptr[0] && strchr(serv->nick_prefixes, ptr[0]) != NULL)
947 	{
948 		ptr++;
949 		sess = find_channel (serv, ptr);
950 	}
951 
952 	if (strcmp (nick, ip) == 0)
953 		server_notice = TRUE;
954 
955 	if (!sess)
956 	{
957 		ptr = 0;
958 		if (prefs.hex_irc_notice_pos == 0)
959 		{
960 											/* paranoia check */
961 			if (msg[0] == '[' && (!serv->have_idmsg || id))
962 			{
963 				/* guess where chanserv meant to post this -sigh- */
964 				if (!g_ascii_strcasecmp (nick, "ChanServ") && !find_dialog (serv, nick))
965 				{
966 					char *dest = g_strdup (msg + 1);
967 					char *end = strchr (dest, ']');
968 					if (end)
969 					{
970 						*end = 0;
971 						sess = find_channel (serv, dest);
972 					}
973 					g_free (dest);
974 				}
975 			}
976 			if (!sess)
977 				sess = find_session_from_nick (nick, serv);
978 		} else if (prefs.hex_irc_notice_pos == 1)
979 		{
980 			int stype = server_notice ? SESS_SNOTICES : SESS_NOTICES;
981 			sess = find_session_from_type (stype, serv);
982 			if (!sess)
983 			{
984 				if (stype == SESS_NOTICES)
985 					sess = new_ircwindow (serv, "(notices)", SESS_NOTICES, 0);
986 				else
987 					sess = new_ircwindow (serv, "(snotices)", SESS_SNOTICES, 0);
988 				fe_set_channel (sess);
989 				fe_set_title (sess);
990 				fe_set_nonchannel (sess, FALSE);
991 				userlist_clear (sess);
992 				log_open_or_close (sess);
993 			}
994 			/* Avoid redundancy with some Undernet notices */
995 			if (!strncmp (msg, "*** Notice -- ", 14))
996 				msg += 14;
997 		} else
998 		{
999 			sess = serv->front_session;
1000 		}
1001 
1002 		if (!sess)
1003 		{
1004 			if (server_notice)
1005 				sess = serv->server_session;
1006 			else
1007 				sess = serv->front_session;
1008 		}
1009 	}
1010 
1011 	if (msg[0] == '\001')
1012 	{
1013 		size_t len;
1014 
1015 		msg++;
1016 		if (!strncmp (msg, "PING", 4))
1017 		{
1018 			inbound_ping_reply (sess, msg + 5, nick, tags_data);
1019 			return;
1020 		}
1021 
1022 		len = strlen(msg);
1023 		if (msg[len - 1] == '\001')
1024 			msg[len - 1] = '\000';
1025 	}
1026 
1027 	if (server_notice)
1028 		EMIT_SIGNAL_TIMESTAMP (XP_TE_SERVNOTICE, sess, msg, nick, NULL, NULL, 0,
1029 									  tags_data->timestamp);
1030 	else if (ptr)
1031 		EMIT_SIGNAL_TIMESTAMP (XP_TE_CHANNOTICE, sess, nick, to, msg, NULL, 0,
1032 									  tags_data->timestamp);
1033 	else
1034 		EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTICE, sess, nick, msg, NULL, NULL, 0,
1035 									  tags_data->timestamp);
1036 }
1037 
1038 void
inbound_away(server * serv,char * nick,char * msg,const message_tags_data * tags_data)1039 inbound_away (server *serv, char *nick, char *msg,
1040 				  const message_tags_data *tags_data)
1041 {
1042 	struct away_msg *away = server_away_find_message (serv, nick);
1043 	session *sess = NULL;
1044 	GSList *list;
1045 
1046 	if (away && !strcmp (msg, away->message))	/* Seen the msg before? */
1047 	{
1048 		if (prefs.hex_away_show_once && !serv->inside_whois)
1049 			return;
1050 	} else
1051 	{
1052 		server_away_save_message (serv, nick, msg);
1053 	}
1054 
1055 	if (prefs.hex_irc_whois_front)
1056 		sess = serv->front_session;
1057 	else
1058 	{
1059 		if (!serv->inside_whois)
1060 			sess = find_session_from_nick (nick, serv);
1061 		if (!sess)
1062 			sess = serv->server_session;
1063 	}
1064 
1065 	/* possibly hide the output */
1066 	if (!serv->inside_whois || !serv->skip_next_whois)
1067 		EMIT_SIGNAL_TIMESTAMP (XP_TE_WHOIS5, sess, nick, msg, NULL, NULL, 0,
1068 									  tags_data->timestamp);
1069 
1070 	list = sess_list;
1071 	while (list)
1072 	{
1073 		sess = list->data;
1074 		if (sess->server == serv)
1075 			userlist_set_away (sess, nick, TRUE);
1076 		list = list->next;
1077 	}
1078 }
1079 
1080 void
inbound_away_notify(server * serv,char * nick,char * reason,const message_tags_data * tags_data)1081 inbound_away_notify (server *serv, char *nick, char *reason,
1082 							const message_tags_data *tags_data)
1083 {
1084 	session *sess = NULL;
1085 	GSList *list;
1086 
1087 	list = sess_list;
1088 	while (list)
1089 	{
1090 		sess = list->data;
1091 		if (sess->server == serv)
1092 		{
1093 			userlist_set_away (sess, nick, reason ? TRUE : FALSE);
1094 			if (sess == serv->front_session && notify_is_in_list (serv, nick))
1095 			{
1096 				if (reason)
1097 					EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYAWAY, sess, nick, reason, NULL,
1098 												  NULL, 0, tags_data->timestamp);
1099 				else
1100 					EMIT_SIGNAL_TIMESTAMP (XP_TE_NOTIFYBACK, sess, nick, NULL, NULL,
1101 												  NULL, 0, tags_data->timestamp);
1102 			}
1103 		}
1104 		list = list->next;
1105 	}
1106 }
1107 
1108 int
inbound_nameslist_end(server * serv,char * chan,const message_tags_data * tags_data)1109 inbound_nameslist_end (server *serv, char *chan,
1110 							  const message_tags_data *tags_data)
1111 {
1112 	session *sess;
1113 	GSList *list;
1114 
1115 	if (!strcmp (chan, "*"))
1116 	{
1117 		list = sess_list;
1118 		while (list)
1119 		{
1120 			sess = list->data;
1121 			if (sess->server == serv)
1122 			{
1123 				sess->end_of_names = TRUE;
1124 				sess->ignore_names = FALSE;
1125 				fe_userlist_numbers (sess);
1126 			}
1127 			list = list->next;
1128 		}
1129 		return TRUE;
1130 	}
1131 	sess = find_channel (serv, chan);
1132 	if (sess)
1133 	{
1134 		sess->end_of_names = TRUE;
1135 		sess->ignore_names = FALSE;
1136 		fe_userlist_numbers (sess);
1137 		return TRUE;
1138 	}
1139 	return FALSE;
1140 }
1141 
1142 static gboolean
check_autojoin_channels(server * serv)1143 check_autojoin_channels (server *serv)
1144 {
1145 	int i = 0;
1146 	session *sess;
1147 	GSList *list = sess_list;
1148 	GSList *sess_channels = NULL;			/* joined channels that are not in the favorites list */
1149 	favchannel *fav;
1150 
1151 	/* shouldn't really happen, the io tag is destroyed in server.c */
1152 	if (!is_server (serv))
1153 	{
1154 		return FALSE;
1155 	}
1156 
1157 	/* If there's a session (i.e. this is a reconnect), autojoin to everything that was open previously. */
1158 	while (list)
1159 	{
1160 		sess = list->data;
1161 
1162 		if (sess->server == serv)
1163 		{
1164 			if (sess->willjoinchannel[0] != 0)
1165 			{
1166 				strcpy (sess->waitchannel, sess->willjoinchannel);
1167 				sess->willjoinchannel[0] = 0;
1168 
1169 				fav = servlist_favchan_find (serv->network, sess->waitchannel, NULL);	/* Is this channel in our favorites? */
1170 
1171 				/* session->channelkey is initially unset for channels joined from the favorites. You have to fill them up manually from favorites settings. */
1172 				if (fav)
1173 				{
1174 					/* session->channelkey is set if there was a key change during the session. In that case, use the session key, not the one from favorites. */
1175 					if (fav->key && !strlen (sess->channelkey))
1176 					{
1177 						safe_strcpy (sess->channelkey, fav->key, sizeof (sess->channelkey));
1178 					}
1179 				}
1180 
1181 				/* for easier checks, ensure that favchannel->key is just NULL when session->channelkey is empty i.e. '' */
1182 				if (strlen (sess->channelkey))
1183 				{
1184 					sess_channels = servlist_favchan_listadd (sess_channels, sess->waitchannel, sess->channelkey);
1185 				}
1186 				else
1187 				{
1188 					sess_channels = servlist_favchan_listadd (sess_channels, sess->waitchannel, NULL);
1189 				}
1190 				i++;
1191 			}
1192 		}
1193 
1194 		list = list->next;
1195 	}
1196 
1197 	if (sess_channels)
1198 	{
1199 		serv->p_join_list (serv, sess_channels);
1200 		g_slist_free_full (sess_channels, (GDestroyNotify) servlist_favchan_free);
1201 	}
1202 	else
1203 	{
1204 		/* If there's no session, just autojoin to favorites. */
1205 		if (serv->favlist)
1206 		{
1207 			serv->p_join_list (serv, serv->favlist);
1208 			i++;
1209 
1210 			/* FIXME this is not going to work and is not needed either. server_free() does the job already. */
1211 			/* g_slist_free_full (serv->favlist, (GDestroyNotify) servlist_favchan_free); */
1212 		}
1213 	}
1214 
1215 	serv->joindelay_tag = 0;
1216 	fe_server_event (serv, FE_SE_LOGGEDIN, i);
1217 	return FALSE;
1218 }
1219 
1220 void
inbound_next_nick(session * sess,char * nick,int error,const message_tags_data * tags_data)1221 inbound_next_nick (session *sess, char *nick, int error,
1222 						 const message_tags_data *tags_data)
1223 {
1224 	char *newnick;
1225 	server *serv = sess->server;
1226 	ircnet *net;
1227 
1228 	serv->nickcount++;
1229 
1230 	switch (serv->nickcount)
1231 	{
1232 	case 2:
1233 		newnick = prefs.hex_irc_nick2;
1234 		net = serv->network;
1235 		/* use network specific "Second choice"? */
1236 		if (net && !(net->flags & FLAG_USE_GLOBAL) && net->nick2)
1237 		{
1238 			newnick = net->nick2;
1239 		}
1240 		serv->p_change_nick (serv, newnick);
1241 		if (error)
1242 		{
1243 			EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKERROR, sess, nick, newnick, NULL, NULL,
1244 										  0, tags_data->timestamp);
1245 		}
1246 		else
1247 		{
1248 			EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKCLASH, sess, nick, newnick, NULL, NULL,
1249 										  0, tags_data->timestamp);
1250 		}
1251 		break;
1252 
1253 	case 3:
1254 		serv->p_change_nick (serv, prefs.hex_irc_nick3);
1255 		if (error)
1256 		{
1257 			EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKERROR, sess, nick, prefs.hex_irc_nick3,
1258 										  NULL, NULL, 0, tags_data->timestamp);
1259 		}
1260 		else
1261 		{
1262 			EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKCLASH, sess, nick, prefs.hex_irc_nick3,
1263 										  NULL, NULL, 0, tags_data->timestamp);
1264 		}
1265 		break;
1266 
1267 	default:
1268 		EMIT_SIGNAL_TIMESTAMP (XP_TE_NICKFAIL, sess, NULL, NULL, NULL, NULL, 0,
1269 									  tags_data->timestamp);
1270 	}
1271 }
1272 
1273 
1274 static void
dns_addr_callback(GObject * obj,GAsyncResult * result,gpointer user_data)1275 dns_addr_callback (GObject *obj, GAsyncResult *result, gpointer user_data)
1276 {
1277 	GResolver *resolver = G_RESOLVER(obj);
1278 	session *sess = (session*)user_data;
1279 	gchar *addr;
1280 
1281 	g_return_if_fail (is_session(sess));
1282 
1283 	addr = g_resolver_lookup_by_address_finish (resolver, result, NULL);
1284 	if (addr)
1285 		PrintTextf (sess, _("Resolved to %s"), addr);
1286 	else
1287 		PrintText (sess, _("Not found"));
1288 }
1289 
1290 static void
dns_name_callback(GObject * obj,GAsyncResult * result,gpointer user_data)1291 dns_name_callback (GObject *obj, GAsyncResult *result, gpointer user_data)
1292 {
1293 	GResolver *resolver = G_RESOLVER(obj);
1294 	session *sess = (session*)user_data;
1295 	GList* addrs;
1296 	gchar* addr;
1297 	GList* list;
1298 
1299 	g_return_if_fail (is_session (sess));
1300 
1301 	addrs = g_resolver_lookup_by_name_finish (resolver, result, NULL);
1302 	if (addrs)
1303 	{
1304 		PrintText (sess, _("Resolved to:"));
1305 
1306 		for (list = g_list_first (addrs); list; list = g_list_next (list))
1307 		{
1308 			addr = g_inet_address_to_string (list->data);
1309 			PrintTextf (sess, "    %s", addr);
1310 		}
1311 
1312 		g_resolver_free_addresses (addrs);
1313 	}
1314 	else
1315 		PrintText (sess, _("Not found"));
1316 }
1317 
1318 void
do_dns(session * sess,char * nick,char * host,const message_tags_data * tags_data)1319 do_dns (session *sess, char *nick, char *host,
1320 		const message_tags_data *tags_data)
1321 {
1322 	GResolver *res = g_resolver_get_default ();
1323 	GInetAddress *addr;
1324 	char *po;
1325 
1326 	po = strrchr (host, '@');
1327 	if (po)
1328 		host = po + 1;
1329 
1330 	if (nick)
1331 		EMIT_SIGNAL_TIMESTAMP (XP_TE_RESOLVINGUSER, sess, nick, host, NULL, NULL, 0,
1332 								tags_data->timestamp);
1333 
1334 	PrintTextf (sess, _("Looking up %s..."), host);
1335 
1336 	addr = g_inet_address_new_from_string (host);
1337 	if (addr)
1338 		g_resolver_lookup_by_address_async (res, addr, NULL, dns_addr_callback, sess);
1339 	else
1340 		g_resolver_lookup_by_name_async (res, host, NULL, dns_name_callback, sess);
1341 }
1342 
1343 static void
set_default_modes(server * serv)1344 set_default_modes (server *serv)
1345 {
1346 	char modes[8];
1347 
1348 	modes[0] = '+';
1349 	modes[1] = '\0';
1350 
1351 	if (prefs.hex_irc_wallops)
1352 		strcat (modes, "w");
1353 	if (prefs.hex_irc_servernotice)
1354 		strcat (modes, "s");
1355 	if (prefs.hex_irc_invisible)
1356 		strcat (modes, "i");
1357 	if (prefs.hex_irc_hidehost)
1358 		strcat (modes, "x");
1359 
1360 	if (modes[1] != '\0')
1361 	{
1362 		serv->p_mode (serv, serv->nick, modes);
1363 	}
1364 }
1365 
1366 void
inbound_login_start(session * sess,char * nick,char * servname,const message_tags_data * tags_data)1367 inbound_login_start (session *sess, char *nick, char *servname,
1368 							const message_tags_data *tags_data)
1369 {
1370 	inbound_newnick (sess->server, sess->server->nick, nick, TRUE, tags_data);
1371 	server_set_name (sess->server, servname);
1372 	if (sess->type == SESS_SERVER)
1373 		log_open_or_close (sess);
1374 	/* reset our away status */
1375 	if (sess->server->reconnect_away)
1376 	{
1377 		handle_command (sess->server->server_session, "away", FALSE);
1378 		sess->server->reconnect_away = FALSE;
1379 	}
1380 }
1381 
1382 static void
inbound_set_all_away_status(server * serv,char * nick,unsigned int status)1383 inbound_set_all_away_status (server *serv, char *nick, unsigned int status)
1384 {
1385 	GSList *list;
1386 	session *sess;
1387 
1388 	list = sess_list;
1389 	while (list)
1390 	{
1391 		sess = list->data;
1392 		if (sess->server == serv)
1393 			userlist_set_away (sess, nick, status);
1394 		list = list->next;
1395 	}
1396 }
1397 
1398 void
inbound_uaway(server * serv,const message_tags_data * tags_data)1399 inbound_uaway (server *serv, const message_tags_data *tags_data)
1400 {
1401 	serv->is_away = TRUE;
1402 	serv->away_time = time (NULL);
1403 	fe_set_away (serv);
1404 
1405 	inbound_set_all_away_status (serv, serv->nick, 1);
1406 }
1407 
1408 void
inbound_uback(server * serv,const message_tags_data * tags_data)1409 inbound_uback (server *serv, const message_tags_data *tags_data)
1410 {
1411 	serv->is_away = FALSE;
1412 	serv->reconnect_away = FALSE;
1413 	fe_set_away (serv);
1414 
1415 	inbound_set_all_away_status (serv, serv->nick, 0);
1416 }
1417 
1418 void
inbound_foundip(session * sess,char * ip,const message_tags_data * tags_data)1419 inbound_foundip (session *sess, char *ip, const message_tags_data *tags_data)
1420 {
1421 	struct hostent *HostAddr;
1422 
1423 	HostAddr = gethostbyname (ip);
1424 	if (HostAddr)
1425 	{
1426 		sess->server->dcc_ip = ((struct in_addr *) HostAddr->h_addr)->s_addr;
1427 		EMIT_SIGNAL_TIMESTAMP (XP_TE_FOUNDIP, sess->server->server_session,
1428 									  inet_ntoa (*((struct in_addr *) HostAddr->h_addr)),
1429 									  NULL, NULL, NULL, 0, tags_data->timestamp);
1430 	}
1431 }
1432 
1433 void
inbound_user_info_start(session * sess,char * nick,const message_tags_data * tags_data)1434 inbound_user_info_start (session *sess, char *nick,
1435 								 const message_tags_data *tags_data)
1436 {
1437 	/* set away to FALSE now, 301 may turn it back on */
1438 	inbound_set_all_away_status (sess->server, nick, 0);
1439 }
1440 
1441 /* reporting new information found about this user. chan may be NULL.
1442  * away may be 0xff to indicate UNKNOWN. */
1443 
1444 void
inbound_user_info(session * sess,char * chan,char * user,char * host,char * servname,char * nick,char * realname,char * account,unsigned int away,const message_tags_data * tags_data)1445 inbound_user_info (session *sess, char *chan, char *user, char *host,
1446 						 char *servname, char *nick, char *realname,
1447 						 char *account, unsigned int away,
1448 						 const message_tags_data *tags_data)
1449 {
1450 	server *serv = sess->server;
1451 	session *who_sess;
1452 	GSList *list;
1453 	char *uhost = NULL;
1454 
1455 	if (user && host)
1456 	{
1457 		uhost = g_strdup_printf ("%s@%s", user, host);
1458 	}
1459 
1460 	if (chan)
1461 	{
1462 		who_sess = find_channel (serv, chan);
1463 		if (who_sess)
1464 			userlist_add_hostname (who_sess, nick, uhost, realname, servname, account, away);
1465 		else
1466 		{
1467 			if (serv->doing_dns && nick && host)
1468 				do_dns (sess, nick, host, tags_data);
1469 		}
1470 	}
1471 	else
1472 	{
1473 		/* came from WHOIS, not channel specific */
1474 		for (list = sess_list; list; list = list->next)
1475 		{
1476 			sess = list->data;
1477 			if (sess->type == SESS_CHANNEL && sess->server == serv)
1478 			{
1479 				userlist_add_hostname (sess, nick, uhost, realname, servname, account, away);
1480 			}
1481 		}
1482 	}
1483 
1484 	g_free (uhost);
1485 }
1486 
1487 int
inbound_banlist(session * sess,time_t stamp,char * chan,char * mask,char * banner,int rplcode,const message_tags_data * tags_data)1488 inbound_banlist (session *sess, time_t stamp, char *chan, char *mask,
1489 					  char *banner, int rplcode, const message_tags_data *tags_data)
1490 {
1491 	char *time_str = ctime (&stamp);
1492 	server *serv = sess->server;
1493 	char *nl;
1494 
1495 	if (stamp <= 0 || time_str == NULL)
1496 	{
1497 		time_str = "";
1498 	}
1499 	else
1500 	{
1501 		if ((nl = strchr (time_str, '\n')))
1502 			*nl = 0;
1503 	}
1504 
1505 	sess = find_channel (serv, chan);
1506 	if (!sess)
1507 	{
1508 		sess = serv->front_session;
1509 		goto nowindow;
1510 	}
1511 
1512 	if (!fe_add_ban_list (sess, mask, banner, time_str, rplcode))
1513 	{
1514 nowindow:
1515 
1516 		EMIT_SIGNAL_TIMESTAMP (XP_TE_BANLIST, sess, chan, mask, banner, time_str,
1517 									  0, tags_data->timestamp);
1518 		return TRUE;
1519 	}
1520 
1521 	return TRUE;
1522 }
1523 
1524 /* execute 1 end-of-motd command */
1525 
1526 static int
inbound_exec_eom_cmd(char * str,void * sess)1527 inbound_exec_eom_cmd (char *str, void *sess)
1528 {
1529 	char *cmd;
1530 
1531 	cmd = command_insert_vars ((session*)sess, (str[0] == '/') ? str + 1 : str);
1532 	handle_command ((session*)sess, cmd, TRUE);
1533 	g_free (cmd);
1534 
1535 	return 1;
1536 }
1537 
1538 static int
inbound_nickserv_login(server * serv)1539 inbound_nickserv_login (server *serv)
1540 {
1541 	/* this could grow ugly, but let's hope there won't be new NickServ types */
1542 	switch (serv->loginmethod)
1543 	{
1544 		case LOGIN_MSG_NICKSERV:
1545 		case LOGIN_NICKSERV:
1546 		case LOGIN_CHALLENGEAUTH:
1547 #if 0
1548 		case LOGIN_NS:
1549 		case LOGIN_MSG_NS:
1550 		case LOGIN_AUTH:
1551 #endif
1552 			return 1;
1553 		default:
1554 			return 0;
1555 	}
1556 }
1557 
1558 void
inbound_login_end(session * sess,char * text,const message_tags_data * tags_data)1559 inbound_login_end (session *sess, char *text, const message_tags_data *tags_data)
1560 {
1561 	GSList *cmdlist;
1562 	commandentry *cmd;
1563 	server *serv = sess->server;
1564 	ircnet *net = serv->network;
1565 
1566 	if (!serv->end_of_motd)
1567 	{
1568 		if (prefs.hex_dcc_ip_from_server && serv->use_who)
1569 		{
1570 			serv->skip_next_userhost = TRUE;
1571 			serv->p_get_ip_uh (serv, serv->nick);	/* sends USERHOST mynick */
1572 		}
1573 		set_default_modes (serv);
1574 
1575 		if (net)
1576 		{
1577 			/* there may be more than 1, separated by \n */
1578 
1579 			cmdlist = net->commandlist;
1580 			while (cmdlist)
1581 			{
1582 				cmd = cmdlist->data;
1583 				inbound_exec_eom_cmd (cmd->command, sess);
1584 				cmdlist = cmdlist->next;
1585 			}
1586 		}
1587 		/* The previously ran commands can alter the state of the server */
1588 		if (serv->network != net)
1589 			return;
1590 
1591 		/* send nickserv password */
1592 		if (net && net->pass && inbound_nickserv_login (serv))
1593 		{
1594 			serv->p_ns_identify (serv, net->pass);
1595 		}
1596 
1597 		/* wait for join if command or nickserv set */
1598 		if (net && prefs.hex_irc_join_delay
1599 			&& ((net->pass && inbound_nickserv_login (serv))
1600 				|| net->commandlist))
1601 		{
1602 			serv->joindelay_tag = fe_timeout_add_seconds (prefs.hex_irc_join_delay, check_autojoin_channels, serv);
1603 		}
1604 		else
1605 		{
1606 			check_autojoin_channels (serv);
1607 		}
1608 
1609 		if (serv->supports_watch || serv->supports_monitor)
1610 		{
1611 			notify_send_watches (serv);
1612 		}
1613 
1614 		serv->end_of_motd = TRUE;
1615 	}
1616 
1617 	if (prefs.hex_irc_skip_motd && !serv->motd_skipped)
1618 	{
1619 		serv->motd_skipped = TRUE;
1620 		EMIT_SIGNAL_TIMESTAMP (XP_TE_MOTDSKIP, serv->server_session, NULL, NULL,
1621 									  NULL, NULL, 0, tags_data->timestamp);
1622 		return;
1623 	}
1624 
1625 	EMIT_SIGNAL_TIMESTAMP (XP_TE_MOTD, serv->server_session, text, NULL, NULL,
1626 								  NULL, 0, tags_data->timestamp);
1627 }
1628 
1629 void
inbound_identified(server * serv)1630 inbound_identified (server *serv)	/* 'MODE +e MYSELF' on freenode */
1631 {
1632 	if (serv->joindelay_tag)
1633 	{
1634 		/* stop waiting, just auto JOIN now */
1635 		fe_timeout_remove (serv->joindelay_tag);
1636 		serv->joindelay_tag = 0;
1637 		check_autojoin_channels (serv);
1638 	}
1639 }
1640 
1641 static const char *sasl_mechanisms[] =
1642 {
1643 	"PLAIN",
1644 	"EXTERNAL"
1645 };
1646 
1647 static void
inbound_toggle_caps(server * serv,const char * extensions_str,gboolean enable)1648 inbound_toggle_caps (server *serv, const char *extensions_str, gboolean enable)
1649 {
1650 	char **extensions;
1651 	gsize i;
1652 
1653 	extensions = g_strsplit (extensions_str, " ", 0);
1654 
1655 	for (i = 0; extensions[i]; i++)
1656 	{
1657 		const char *extension = extensions[i];
1658 
1659 		if (!strcmp (extension, "solanum.chat/identify-msg"))
1660 			serv->have_idmsg = enable;
1661 		else if (!strcmp (extension, "multi-prefix"))
1662 			serv->have_namesx = enable;
1663 		else if (!strcmp (extension, "account-notify"))
1664 			serv->have_accnotify = enable;
1665 		else if (!strcmp (extension, "extended-join"))
1666 			serv->have_extjoin = enable;
1667 		else if (!strcmp (extension, "userhost-in-names"))
1668 			serv->have_uhnames = enable;
1669 		else if (!strcmp (extension, "server-time")
1670 				|| !strcmp (extension, "znc.in/server-time")
1671 				|| !strcmp (extension, "znc.in/server-time-iso"))
1672 			serv->have_server_time = enable;
1673 		else if (!strcmp (extension, "away-notify"))
1674 			serv->have_awaynotify = enable;
1675 		else if (!strcmp (extension, "account-tag"))
1676 			serv->have_account_tag = enable;
1677 		else if (!strcmp (extension, "sasl"))
1678 		{
1679 			serv->have_sasl = enable;
1680 			if (enable)
1681 			{
1682 #ifdef USE_OPENSSL
1683 				if (serv->loginmethod == LOGIN_SASLEXTERNAL)
1684 					serv->sasl_mech = MECH_EXTERNAL;
1685 #endif
1686 				/* Mechanism either defaulted to PLAIN or server gave us list */
1687 				tcp_sendf (serv, "AUTHENTICATE %s\r\n", sasl_mechanisms[serv->sasl_mech]);
1688 			}
1689 		}
1690 	}
1691 
1692 	g_strfreev (extensions);
1693 }
1694 
1695 void
inbound_cap_ack(server * serv,char * nick,char * extensions,const message_tags_data * tags_data)1696 inbound_cap_ack (server *serv, char *nick, char *extensions,
1697 					  const message_tags_data *tags_data)
1698 {
1699 	EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions,
1700 								  NULL, NULL, 0, tags_data->timestamp);
1701 
1702 	inbound_toggle_caps (serv, extensions, TRUE);
1703 }
1704 
1705 void
inbound_cap_del(server * serv,char * nick,char * extensions,const message_tags_data * tags_data)1706 inbound_cap_del (server *serv, char *nick, char *extensions,
1707 					 const message_tags_data *tags_data)
1708 {
1709 	EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPDEL, serv->server_session, nick, extensions,
1710 								  NULL, NULL, 0, tags_data->timestamp);
1711 
1712 	inbound_toggle_caps (serv, extensions, FALSE);
1713 }
1714 
1715 static const char * const supported_caps[] = {
1716 	/* IRCv3.1 */
1717 	"multi-prefix",
1718 	"away-notify",
1719 	"account-notify",
1720 	"extended-join",
1721 	/* "sasl", Handled manually */
1722 
1723 	/* IRCv3.2 */
1724 	"server-time",
1725 	"userhost-in-names",
1726 	"cap-notify",
1727 	"chghost",
1728 	"setname",
1729 	"invite-notify",
1730 	"account-tag",
1731 
1732 	/* ZNC */
1733 	"znc.in/server-time-iso",
1734 	"znc.in/server-time",
1735 
1736 	/* Twitch */
1737 	"twitch.tv/membership",
1738 
1739 	/* Solanum */
1740 	"solanum.chat/identify-msg",
1741 };
1742 
1743 static int
get_supported_mech(server * serv,const char * list)1744 get_supported_mech (server *serv, const char *list)
1745 {
1746 	char **mechs = g_strsplit (list, ",", 0);
1747 	gsize i;
1748 	int ret = -1;
1749 
1750 	for (i = 0; mechs[i]; ++i)
1751 	{
1752 #ifdef USE_OPENSSL
1753 		if (serv->loginmethod == LOGIN_SASLEXTERNAL)
1754 		{
1755 			if (!strcmp (mechs[i], "EXTERNAL"))
1756 			{
1757 				ret = MECH_EXTERNAL;
1758 				break;
1759 			}
1760 		}
1761 		else
1762 #endif
1763 		if (!strcmp (mechs[i], "PLAIN"))
1764 		{
1765 			ret = MECH_PLAIN;
1766 			break;
1767 		}
1768 	}
1769 
1770 	g_strfreev (mechs);
1771 	return ret;
1772 }
1773 
1774 void
inbound_cap_ls(server * serv,char * nick,char * extensions_str,const message_tags_data * tags_data)1775 inbound_cap_ls (server *serv, char *nick, char *extensions_str,
1776 					 const message_tags_data *tags_data)
1777 {
1778 	char buffer[500];	/* buffer for requesting capabilities and emitting the signal */
1779 	gboolean want_cap = FALSE; /* format the CAP REQ string based on previous capabilities being requested or not */
1780 	char **extensions;
1781 	int i;
1782 
1783 	if (g_str_has_prefix (extensions_str, "* "))
1784 	{
1785 		serv->waiting_on_cap = TRUE;
1786 		extensions_str += 2;
1787 		extensions_str += extensions_str[0] == ':' ? 1 : 0;
1788 	}
1789 	else
1790 	{
1791 		serv->waiting_on_cap = FALSE;
1792 	}
1793 
1794 	EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPLIST, serv->server_session, nick,
1795 								  extensions_str, NULL, NULL, 0, tags_data->timestamp);
1796 
1797 	extensions = g_strsplit (extensions_str, " ", 0);
1798 
1799 	strcpy (buffer, "CAP REQ :");
1800 
1801 	for (i=0; extensions[i]; i++)
1802 	{
1803 		char *extension = extensions[i];
1804 		char *value;
1805 		gsize x;
1806 
1807 		/* CAP 3.2 can provide values */
1808 		if ((value = strchr (extension, '=')))
1809 		{
1810 			*value = '\0';
1811 			value++;
1812 		}
1813 
1814 		/* if the SASL password is set AND auth mode is set to SASL, request SASL auth */
1815 		if (!g_strcmp0 (extension, "sasl") &&
1816 			((serv->loginmethod == LOGIN_SASL && strlen (serv->password) != 0)
1817 				|| serv->loginmethod == LOGIN_SASLEXTERNAL))
1818 		{
1819 			if (value)
1820 			{
1821 				int sasl_mech = get_supported_mech (serv, value);
1822 				if (sasl_mech == -1) /* No supported mech */
1823 					continue;
1824 				serv->sasl_mech = sasl_mech;
1825 			}
1826 			want_cap = TRUE;
1827 			serv->waiting_on_sasl = TRUE;
1828 			g_strlcat (buffer, "sasl ", sizeof(buffer));
1829 			continue;
1830 		}
1831 
1832 		for (x = 0; x < G_N_ELEMENTS(supported_caps); ++x)
1833 		{
1834 			if (!g_strcmp0 (extension, supported_caps[x]))
1835 			{
1836 				g_strlcat (buffer, extension, sizeof(buffer));
1837 				g_strlcat (buffer, " ", sizeof(buffer));
1838 				want_cap = TRUE;
1839 			}
1840 		}
1841 	}
1842 
1843 	g_strfreev (extensions);
1844 
1845 	if (want_cap)
1846 	{
1847 		/* buffer + 9 = emit buffer without "CAP REQ :" */
1848 		EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPREQ, serv->server_session,
1849 									  buffer + 9, NULL, NULL, NULL, 0,
1850 									  tags_data->timestamp);
1851 		tcp_sendf (serv, "%s\r\n", g_strchomp (buffer));
1852 	}
1853 	if (!serv->waiting_on_sasl && !serv->waiting_on_cap)
1854 	{
1855 		/* if we use SASL, CAP END is dealt via raw numerics */
1856 		serv->sent_capend = TRUE;
1857 		tcp_send_len (serv, "CAP END\r\n", 9);
1858 	}
1859 }
1860 
1861 void
inbound_cap_nak(server * serv,char * extensions_str,const message_tags_data * tags_data)1862 inbound_cap_nak (server *serv, char *extensions_str, const message_tags_data *tags_data)
1863 {
1864 	char **extensions;
1865 	int i;
1866 
1867 	extensions = g_strsplit (extensions_str, " ", 0);
1868 	for (i=0; extensions[i]; i++)
1869 	{
1870 		if (!g_strcmp0 (extensions[i], "sasl"))
1871 			serv->waiting_on_sasl = FALSE;
1872 	}
1873 
1874 	if (!serv->waiting_on_cap && !serv->waiting_on_sasl && !serv->sent_capend)
1875 	{
1876 		serv->sent_capend = TRUE;
1877 		tcp_send_len (serv, "CAP END\r\n", 9);
1878 	}
1879 
1880 	g_strfreev (extensions);
1881 }
1882 
1883 void
inbound_cap_list(server * serv,char * nick,char * extensions,const message_tags_data * tags_data)1884 inbound_cap_list (server *serv, char *nick, char *extensions,
1885 						const message_tags_data *tags_data)
1886 {
1887 	if (g_str_has_prefix (extensions, "* "))
1888 	{
1889 		extensions += 2;
1890 		extensions += extensions[0] == ':' ? 1 : 0;
1891 	}
1892 	EMIT_SIGNAL_TIMESTAMP (XP_TE_CAPACK, serv->server_session, nick, extensions,
1893 								  NULL, NULL, 0, tags_data->timestamp);
1894 }
1895 
1896 void
inbound_sasl_authenticate(server * serv,char * data)1897 inbound_sasl_authenticate (server *serv, char *data)
1898 {
1899 		ircnet *net = (ircnet*)serv->network;
1900 		char *user, *pass = NULL;
1901 		const char *mech = sasl_mechanisms[serv->sasl_mech];
1902 
1903 		/* Got a list of supported mechanisms from outdated inspircd
1904 		 * just ignore it as it goes against spec */
1905 		if (strchr (data, ',') != NULL)
1906 			return;
1907 
1908 		if (net->user && !(net->flags & FLAG_USE_GLOBAL))
1909 			user = net->user;
1910 		else
1911 			user = prefs.hex_irc_user_name;
1912 
1913 		switch (serv->sasl_mech)
1914 		{
1915 		case MECH_PLAIN:
1916 			pass = encode_sasl_pass_plain (user, serv->password);
1917 			break;
1918 #ifdef USE_OPENSSL
1919 		case MECH_EXTERNAL:
1920 			pass = g_strdup ("+");
1921 			break;
1922 #endif
1923 		}
1924 
1925 		if (pass == NULL)
1926 		{
1927 			/* something went wrong abort */
1928 			tcp_sendf (serv, "AUTHENTICATE *\r\n");
1929 			return;
1930 		}
1931 
1932 		tcp_sendf (serv, "AUTHENTICATE %s\r\n", pass);
1933 		g_free (pass);
1934 
1935 
1936 		EMIT_SIGNAL_TIMESTAMP (XP_TE_SASLAUTH, serv->server_session, user, (char*)mech,
1937 								NULL,	NULL,	0,	0);
1938 }
1939 
1940 void
inbound_sasl_error(server * serv)1941 inbound_sasl_error (server *serv)
1942 {
1943 	/* Just abort, not much we can do */
1944 	tcp_sendf (serv, "AUTHENTICATE *\r\n");
1945 }
1946