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 <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <time.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 
26 #define WANTSOCKET
27 #include "inet.h"
28 
29 #ifdef WIN32
30 #include <windows.h>
31 #else
32 #include <sys/wait.h>
33 #include <signal.h>
34 #include <unistd.h>
35 #endif
36 
37 #include "hexchat.h"
38 #include "fe.h"
39 #include "util.h"
40 #include "cfgfiles.h"
41 #include "chanopt.h"
42 #include "ignore.h"
43 #include "hexchat-plugin.h"
44 #include "inbound.h"
45 #include "plugin.h"
46 #include "plugin-identd.h"
47 #include "plugin-timer.h"
48 #include "notify.h"
49 #include "server.h"
50 #include "servlist.h"
51 #include "outbound.h"
52 #include "text.h"
53 #include "url.h"
54 #include "hexchatc.h"
55 
56 #if ! GLIB_CHECK_VERSION (2, 36, 0)
57 #include <glib-object.h>			/* for g_type_init() */
58 #endif
59 
60 GSList *popup_list = 0;
61 GSList *button_list = 0;
62 GSList *dlgbutton_list = 0;
63 GSList *command_list = 0;
64 GSList *ctcp_list = 0;
65 GSList *replace_list = 0;
66 GSList *sess_list = 0;
67 GSList *dcc_list = 0;
68 GSList *ignore_list = 0;
69 GSList *usermenu_list = 0;
70 GSList *urlhandler_list = 0;
71 GSList *tabmenu_list = 0;
72 
73 /*
74  * This array contains 5 double linked lists, one for each priority in the
75  * "interesting session" queue ("channel" stands for everything but
76  * SESS_DIALOG):
77  *
78  * [0] queries with hilight
79  * [1] queries
80  * [2] channels with hilight
81  * [3] channels with dialogue
82  * [4] channels with other data
83  *
84  * Each time activity happens the corresponding session is put at the
85  * beginning of one of the lists.  The aim is to be able to switch to the
86  * session with the most important/recent activity.
87  */
88 GList *sess_list_by_lastact[5] = {NULL, NULL, NULL, NULL, NULL};
89 
90 
91 static int in_hexchat_exit = FALSE;
92 int hexchat_is_quitting = FALSE;
93 /* command-line args */
94 int arg_dont_autoconnect = FALSE;
95 int arg_skip_plugins = FALSE;
96 char *arg_url = NULL;
97 char **arg_urls = NULL;
98 char *arg_command = NULL;
99 gint arg_existing = FALSE;
100 
101 #ifdef USE_DBUS
102 #include "dbus/dbus-client.h"
103 #include "dbus/dbus-plugin.h"
104 #endif /* USE_DBUS */
105 
106 struct session *current_tab;
107 struct session *current_sess = 0;
108 struct hexchatprefs prefs;
109 
110 /*
111  * Update the priority queue of the "interesting sessions"
112  * (sess_list_by_lastact).
113  */
114 void
lastact_update(session * sess)115 lastact_update(session *sess)
116 {
117 	int oldidx = sess->lastact_idx;
118 	int newidx = LACT_NONE;
119 	int dia = (sess->type == SESS_DIALOG);
120 
121 	if (sess->tab_state & TAB_STATE_NEW_HILIGHT)
122 		newidx = dia? LACT_QUERY_HI: LACT_CHAN_HI;
123 	else if (sess->tab_state & TAB_STATE_NEW_MSG)
124 		newidx = dia? LACT_QUERY: LACT_CHAN;
125 	else if (sess->tab_state & TAB_STATE_NEW_DATA)
126 		newidx = dia? LACT_QUERY: LACT_CHAN_DATA;
127 
128 	/* If already first at the right position, just return */
129 	if (oldidx == newidx &&
130 		 (newidx == LACT_NONE || g_list_index(sess_list_by_lastact[newidx], sess) == 0))
131 		return;
132 
133 	/* Remove from the old position */
134 	if (oldidx != LACT_NONE)
135 		sess_list_by_lastact[oldidx] = g_list_remove(sess_list_by_lastact[oldidx], sess);
136 
137 	/* Add at the new position */
138 	sess->lastact_idx = newidx;
139 	if (newidx != LACT_NONE)
140 		sess_list_by_lastact[newidx] = g_list_prepend(sess_list_by_lastact[newidx], sess);
141 	return;
142 }
143 
144 /*
145  * Extract the first session from the priority queue of sessions with recent
146  * activity. Return NULL if no such session can be found.
147  *
148  * If filter is specified, skip a session if filter(session) returns 0. This
149  * can be used for UI-specific needs, e.g. in fe-gtk we want to filter out
150  * detached sessions.
151  */
152 session *
lastact_getfirst(int (* filter)(session * sess))153 lastact_getfirst(int (*filter) (session *sess))
154 {
155 	int i;
156 	session *sess = NULL;
157 	GList *curitem;
158 
159 	/* 5 is the number of priority classes LACT_ */
160 	for (i = 0; i < 5 && !sess; i++)
161 	{
162 		curitem = sess_list_by_lastact[i];
163 		while (curitem && !sess)
164 		{
165 			sess = g_list_nth_data(curitem, 0);
166 			if (!sess || (filter && !filter(sess)))
167 			{
168 				sess = NULL;
169 				curitem = g_list_next(curitem);
170 			}
171 		}
172 
173 		if (sess)
174 		{
175 			sess_list_by_lastact[i] = g_list_remove(sess_list_by_lastact[i], sess);
176 			sess->lastact_idx = LACT_NONE;
177 		}
178 	}
179 
180 	return sess;
181 }
182 
183 int
is_session(session * sess)184 is_session (session * sess)
185 {
186 	return g_slist_find (sess_list, sess) ? 1 : 0;
187 }
188 
189 session *
find_dialog(server * serv,char * nick)190 find_dialog (server *serv, char *nick)
191 {
192 	GSList *list = sess_list;
193 	session *sess;
194 
195 	while (list)
196 	{
197 		sess = list->data;
198 		if (sess->server == serv && sess->type == SESS_DIALOG)
199 		{
200 			if (!serv->p_cmp (nick, sess->channel))
201 				return (sess);
202 		}
203 		list = list->next;
204 	}
205 	return NULL;
206 }
207 
208 session *
find_channel(server * serv,char * chan)209 find_channel (server *serv, char *chan)
210 {
211 	session *sess;
212 	GSList *list = sess_list;
213 	while (list)
214 	{
215 		sess = list->data;
216 		if ((serv == sess->server) && sess->type == SESS_CHANNEL)
217 		{
218 			if (!serv->p_cmp (chan, sess->channel))
219 				return sess;
220 		}
221 		list = list->next;
222 	}
223 	return NULL;
224 }
225 
226 static void
lagcheck_update(void)227 lagcheck_update (void)
228 {
229 	server *serv;
230 	GSList *list = serv_list;
231 
232 	if (!prefs.hex_gui_lagometer)
233 		return;
234 
235 	while (list)
236 	{
237 		serv = list->data;
238 		if (serv->lag_sent)
239 			fe_set_lag (serv, -1);
240 
241 		list = list->next;
242 	}
243 }
244 
245 void
lag_check(void)246 lag_check (void)
247 {
248 	server *serv;
249 	GSList *list = serv_list;
250 	unsigned long tim;
251 	char tbuf[128];
252 	time_t now = time (0);
253 	time_t lag;
254 
255 	tim = make_ping_time ();
256 
257 	while (list)
258 	{
259 		serv = list->data;
260 		if (serv->connected && serv->end_of_motd)
261 		{
262 			lag = now - serv->ping_recv;
263 			if (prefs.hex_net_ping_timeout != 0 && lag > prefs.hex_net_ping_timeout && lag > 0)
264 			{
265 				sprintf (tbuf, "%" G_GINT64_FORMAT, (gint64) lag);
266 				EMIT_SIGNAL (XP_TE_PINGTIMEOUT, serv->server_session, tbuf, NULL,
267 								 NULL, NULL, 0);
268 				if (prefs.hex_net_auto_reconnect)
269 					serv->auto_reconnect (serv, FALSE, -1);
270 			}
271 			else
272 			{
273 				g_snprintf (tbuf, sizeof (tbuf), "LAG%lu", tim);
274 				serv->p_ping (serv, "", tbuf);
275 
276 				if (!serv->lag_sent)
277 				{
278 					serv->lag_sent = tim;
279 					fe_set_lag (serv, -1);
280 				}
281 			}
282 		}
283 		list = list->next;
284 	}
285 }
286 
287 static int
away_check(void)288 away_check (void)
289 {
290 	session *sess;
291 	GSList *list;
292 	int full, sent, loop = 0;
293 
294 	if (!prefs.hex_away_track)
295 		return 1;
296 
297 doover:
298 	/* request an update of AWAY status of 1 channel every 30 seconds */
299 	full = TRUE;
300 	sent = 0;	/* number of WHOs (users) requested */
301 	list = sess_list;
302 	while (list)
303 	{
304 		sess = list->data;
305 
306 		if (sess->server->connected &&
307 			 sess->type == SESS_CHANNEL &&
308 			 sess->channel[0] &&
309 			 (sess->total <= prefs.hex_away_size_max || !prefs.hex_away_size_max))
310 		{
311 			if (!sess->done_away_check)
312 			{
313 				full = FALSE;
314 
315 				/* if we're under 31 WHOs, send another channels worth */
316 				if (sent < 31 && !sess->doing_who)
317 				{
318 					sess->done_away_check = TRUE;
319 					sess->doing_who = TRUE;
320 					/* this'll send a WHO #channel */
321 					sess->server->p_away_status (sess->server, sess->channel);
322 					sent += sess->total;
323 				}
324 			}
325 		}
326 
327 		list = list->next;
328 	}
329 
330 	/* done them all, reset done_away_check to FALSE and start over unless we have away-notify */
331 	if (full)
332 	{
333 		list = sess_list;
334 		while (list)
335 		{
336 			sess = list->data;
337 			if (!sess->server->have_awaynotify)
338 				sess->done_away_check = FALSE;
339 			list = list->next;
340 		}
341 		loop++;
342 		if (loop < 2)
343 			goto doover;
344 	}
345 
346 	return 1;
347 }
348 
349 /* these are only run if the lagometer is enabled */
350 static int
hexchat_lag_check(void)351 hexchat_lag_check (void)   /* this gets called every 30 seconds */
352 {
353 	lag_check ();
354 	return 1;
355 }
356 
357 static int
hexchat_lag_check_update(void)358 hexchat_lag_check_update (void)   /* this gets called every 0.5 seconds */
359 {
360 	lagcheck_update ();
361 	return 1;
362 }
363 
364 /* call whenever timeout intervals change */
365 void
hexchat_reinit_timers(void)366 hexchat_reinit_timers (void)
367 {
368 	static int lag_check_update_tag = 0;
369 	static int lag_check_tag = 0;
370 	static int away_tag = 0;
371 
372 	/* notify timeout */
373 	if (prefs.hex_notify_timeout && notify_tag == 0)
374 	{
375 		notify_tag = fe_timeout_add_seconds (prefs.hex_notify_timeout,
376 						     notify_checklist, NULL);
377 	}
378 	else if (!prefs.hex_notify_timeout && notify_tag != 0)
379 	{
380 		fe_timeout_remove (notify_tag);
381 		notify_tag = 0;
382 	}
383 
384 	/* away status tracking */
385 	if (prefs.hex_away_track && away_tag == 0)
386 	{
387 		away_tag = fe_timeout_add_seconds (prefs.hex_away_timeout, away_check, NULL);
388 	}
389 	else if (!prefs.hex_away_track && away_tag != 0)
390 	{
391 		fe_timeout_remove (away_tag);
392 		away_tag = 0;
393 	}
394 
395 	/* lag-o-meter */
396 	if (prefs.hex_gui_lagometer && lag_check_update_tag == 0)
397 	{
398 		lag_check_update_tag = fe_timeout_add (500, hexchat_lag_check_update, NULL);
399 	}
400 	else if (!prefs.hex_gui_lagometer && lag_check_update_tag != 0)
401 	{
402 		fe_timeout_remove (lag_check_update_tag);
403 		lag_check_update_tag = 0;
404 	}
405 
406 	/* network timeouts and lag-o-meter */
407 	if ((prefs.hex_net_ping_timeout != 0 || prefs.hex_gui_lagometer)
408 	    && lag_check_tag == 0)
409 	{
410 		lag_check_tag = fe_timeout_add_seconds (30, hexchat_lag_check, NULL);
411 	}
412 	else if ((!prefs.hex_net_ping_timeout && !prefs.hex_gui_lagometer)
413 					 && lag_check_tag != 0)
414 	{
415 		fe_timeout_remove (lag_check_tag);
416 		lag_check_tag = 0;
417 	}
418 }
419 
420 /* executed when the first irc window opens */
421 
422 static void
irc_init(session * sess)423 irc_init (session *sess)
424 {
425 	static int done_init = FALSE;
426 	char *buf;
427 
428 	if (done_init)
429 		return;
430 
431 	done_init = TRUE;
432 
433 	plugin_add (sess, NULL, NULL, timer_plugin_init, NULL, NULL, FALSE);
434 	plugin_add (sess, NULL, NULL, identd_plugin_init, identd_plugin_deinit, NULL, FALSE);
435 
436 #ifdef USE_PLUGIN
437 	if (!arg_skip_plugins)
438 		plugin_auto_load (sess);	/* autoload ~/.xchat *.so */
439 #endif
440 
441 #ifdef USE_DBUS
442 	plugin_add (sess, NULL, NULL, dbus_plugin_init, NULL, NULL, FALSE);
443 #endif
444 
445 	hexchat_reinit_timers ();
446 
447 	if (arg_url != NULL)
448 	{
449 		buf = g_strdup_printf ("server %s", arg_url);
450 		g_free (arg_url);	/* from GOption */
451 		handle_command (sess, buf, FALSE);
452 		g_free (buf);
453 	}
454 
455 	if (arg_urls != NULL)
456 	{
457 		guint i;
458 		for (i = 0; i < g_strv_length (arg_urls); i++)
459 		{
460 			buf = g_strdup_printf ("%s %s", i==0? "server" : "newserver", arg_urls[i]);
461 			handle_command (sess, buf, FALSE);
462 			g_free (buf);
463 		}
464 		g_strfreev (arg_urls);
465 	}
466 
467 	if (arg_command != NULL)
468 	{
469 		handle_command (sess, arg_command, FALSE);
470 		g_free (arg_command);
471 	}
472 
473 	/* load -e <xdir>/startup.txt */
474 	load_perform_file (sess, "startup.txt");
475 }
476 
477 static session *
session_new(server * serv,char * from,int type,int focus)478 session_new (server *serv, char *from, int type, int focus)
479 {
480 	session *sess;
481 
482 	sess = g_new0 (struct session, 1);
483 
484 	sess->server = serv;
485 	sess->logfd = -1;
486 	sess->type = type;
487 
488 	sess->alert_balloon = SET_DEFAULT;
489 	sess->alert_beep = SET_DEFAULT;
490 	sess->alert_taskbar = SET_DEFAULT;
491 	sess->alert_tray = SET_DEFAULT;
492 
493 	sess->text_hidejoinpart = SET_DEFAULT;
494 	sess->text_logging = SET_DEFAULT;
495 	sess->text_scrollback = SET_DEFAULT;
496 	sess->text_strip = SET_DEFAULT;
497 
498 	sess->lastact_idx = LACT_NONE;
499 
500 	if (from != NULL)
501 	{
502 		safe_strcpy(sess->channel, from, CHANLEN);
503 		safe_strcpy(sess->session_name, from, CHANLEN);
504 	}
505 
506 	sess_list = g_slist_prepend (sess_list, sess);
507 
508 	fe_new_window (sess, focus);
509 
510 	return sess;
511 }
512 
513 session *
new_ircwindow(server * serv,char * name,int type,int focus)514 new_ircwindow (server *serv, char *name, int type, int focus)
515 {
516 	session *sess;
517 
518 	switch (type)
519 	{
520 	case SESS_SERVER:
521 		serv = server_new ();
522 		if (prefs.hex_gui_tab_server)
523 			sess = session_new (serv, name, SESS_SERVER, focus);
524 		else
525 			sess = session_new (serv, name, SESS_CHANNEL, focus);
526 		serv->server_session = sess;
527 		serv->front_session = sess;
528 		break;
529 	case SESS_DIALOG:
530 		sess = session_new (serv, name, type, focus);
531 		break;
532 	default:
533 /*	case SESS_CHANNEL:
534 	case SESS_NOTICES:
535 	case SESS_SNOTICES:*/
536 		sess = session_new (serv, name, type, focus);
537 		break;
538 	}
539 
540 	irc_init (sess);
541 	chanopt_load (sess);
542 	scrollback_load (sess);
543 	if (sess->scrollwritten && sess->scrollback_replay_marklast)
544 		sess->scrollback_replay_marklast (sess);
545 	if (type == SESS_DIALOG)
546 	{
547 		struct User *user;
548 
549 		log_open_or_close (sess);
550 
551 		user = userlist_find_global (serv, name);
552 		if (user && user->hostname)
553 			set_topic (sess, user->hostname, user->hostname);
554 	}
555 	plugin_emit_dummy_print (sess, "Open Context");
556 
557 	return sess;
558 }
559 
560 static void
exec_notify_kill(session * sess)561 exec_notify_kill (session * sess)
562 {
563 #ifndef WIN32
564 	struct nbexec *re;
565 	if (sess->running_exec != NULL)
566 	{
567 		re = sess->running_exec;
568 		sess->running_exec = NULL;
569 		kill (re->childpid, SIGKILL);
570 		waitpid (re->childpid, NULL, WNOHANG);
571 		fe_input_remove (re->iotag);
572 		close (re->myfd);
573 		g_free (re->linebuf);
574 		g_free (re);
575 	}
576 #endif
577 }
578 
579 static void
send_quit_or_part(session * killsess)580 send_quit_or_part (session * killsess)
581 {
582 	int willquit = TRUE;
583 	GSList *list;
584 	session *sess;
585 	server *killserv = killsess->server;
586 
587 	/* check if this is the last session using this server */
588 	list = sess_list;
589 	while (list)
590 	{
591 		sess = (session *) list->data;
592 		if (sess->server == killserv && sess != killsess)
593 		{
594 			willquit = FALSE;
595 			list = 0;
596 		} else
597 			list = list->next;
598 	}
599 
600 	if (hexchat_is_quitting)
601 		willquit = TRUE;
602 
603 	if (killserv->connected)
604 	{
605 		if (willquit)
606 		{
607 			if (!killserv->sent_quit)
608 			{
609 				killserv->flush_queue (killserv);
610 				server_sendquit (killsess);
611 				killserv->sent_quit = TRUE;
612 			}
613 		} else
614 		{
615 			if (killsess->type == SESS_CHANNEL && killsess->channel[0] &&
616 				 !killserv->sent_quit)
617 			{
618 				server_sendpart (killserv, killsess->channel, 0);
619 			}
620 		}
621 	}
622 }
623 
624 void
session_free(session * killsess)625 session_free (session *killsess)
626 {
627 	server *killserv = killsess->server;
628 	session *sess;
629 	GSList *list;
630 	int oldidx;
631 
632 	plugin_emit_dummy_print (killsess, "Close Context");
633 
634 	if (current_tab == killsess)
635 		current_tab = NULL;
636 
637 	if (killserv->server_session == killsess)
638 		killserv->server_session = NULL;
639 
640 	if (killserv->front_session == killsess)
641 	{
642 		/* front_session is closed, find a valid replacement */
643 		killserv->front_session = NULL;
644 		list = sess_list;
645 		while (list)
646 		{
647 			sess = (session *) list->data;
648 			if (sess != killsess && sess->server == killserv)
649 			{
650 				killserv->front_session = sess;
651 				if (!killserv->server_session)
652 					killserv->server_session = sess;
653 				break;
654 			}
655 			list = list->next;
656 		}
657 	}
658 
659 	if (!killserv->server_session)
660 		killserv->server_session = killserv->front_session;
661 
662 	sess_list = g_slist_remove (sess_list, killsess);
663 
664 	if (killsess->type == SESS_CHANNEL)
665 		userlist_free (killsess);
666 
667 	oldidx = killsess->lastact_idx;
668 	if (oldidx != LACT_NONE)
669 		sess_list_by_lastact[oldidx] = g_list_remove(sess_list_by_lastact[oldidx], killsess);
670 
671 	exec_notify_kill (killsess);
672 
673 	log_close (killsess);
674 	scrollback_close (killsess);
675 	chanopt_save (killsess);
676 
677 	send_quit_or_part (killsess);
678 
679 	history_free (&killsess->history);
680 	g_free (killsess->topic);
681 	g_free (killsess->current_modes);
682 
683 	fe_session_callback (killsess);
684 
685 	if (current_sess == killsess)
686 	{
687 		current_sess = NULL;
688 		if (sess_list)
689 			current_sess = sess_list->data;
690 	}
691 
692 	g_free (killsess);
693 
694 	if (!sess_list && !in_hexchat_exit)
695 		hexchat_exit ();						/* sess_list is empty, quit! */
696 
697 	list = sess_list;
698 	while (list)
699 	{
700 		sess = (session *) list->data;
701 		if (sess->server == killserv)
702 			return;					  /* this server is still being used! */
703 		list = list->next;
704 	}
705 
706 	server_free (killserv);
707 }
708 
709 static void
free_sessions(void)710 free_sessions (void)
711 {
712 	GSList *list = sess_list;
713 
714 	while (list)
715 	{
716 		fe_close_window (list->data);
717 		list = sess_list;
718 	}
719 }
720 
721 
722 static char defaultconf_ctcp[] =
723 	"NAME TIME\n"				"CMD nctcp %s TIME %t\n\n"\
724 	"NAME PING\n"				"CMD nctcp %s PING %d\n\n";
725 
726 static char defaultconf_replace[] =
727 	"NAME teh\n"				"CMD the\n\n";
728 /*	"NAME r\n"					"CMD are\n\n"\
729 	"NAME u\n"					"CMD you\n\n"*/
730 
731 static char defaultconf_commands[] =
732 	"NAME ACTION\n"		"CMD me &2\n\n"\
733 	"NAME AME\n"			"CMD allchan me &2\n\n"\
734 	"NAME ANICK\n"			"CMD allserv nick &2\n\n"\
735 	"NAME AMSG\n"			"CMD allchan say &2\n\n"\
736 	"NAME BANLIST\n"		"CMD quote MODE %c +b\n\n"\
737 	"NAME CHAT\n"			"CMD dcc chat %2\n\n"\
738 	"NAME DIALOG\n"		"CMD query %2\n\n"\
739 	"NAME DMSG\n"			"CMD msg =%2 &3\n\n"\
740 	"NAME EXIT\n"			"CMD quit\n\n"\
741 	"NAME GREP\n"			"CMD lastlog -r -- &2\n\n"\
742 	"NAME IGNALL\n"			"CMD ignore %2!*@* ALL\n\n"\
743 	"NAME J\n"				"CMD join &2\n\n"\
744 	"NAME KILL\n"			"CMD quote KILL %2 :&3\n\n"\
745 	"NAME LEAVE\n"			"CMD part &2\n\n"\
746 	"NAME M\n"				"CMD msg &2\n\n"\
747 	"NAME OMSG\n"			"CMD msg @%c &2\n\n"\
748 	"NAME ONOTICE\n"		"CMD notice @%c &2\n\n"\
749 	"NAME RAW\n"			"CMD quote &2\n\n"\
750 	"NAME SERVHELP\n"		"CMD quote HELP\n\n"\
751 	"NAME SPING\n"			"CMD ping\n\n"\
752 	"NAME SQUERY\n"		"CMD quote SQUERY %2 :&3\n\n"\
753 	"NAME SSLSERVER\n"	"CMD server -ssl &2\n\n"\
754 	"NAME SV\n"				"CMD echo HexChat %v %m\n\n"\
755 	"NAME UMODE\n"			"CMD mode %n &2\n\n"\
756 	"NAME UPTIME\n"		"CMD quote STATS u\n\n"\
757 	"NAME VER\n"			"CMD ctcp %2 VERSION\n\n"\
758 	"NAME VERSION\n"		"CMD ctcp %2 VERSION\n\n"\
759 	"NAME WALLOPS\n"		"CMD quote WALLOPS :&2\n\n"\
760         "NAME WI\n"                     "CMD quote WHOIS %2\n\n"\
761 	"NAME WII\n"			"CMD quote WHOIS %2 %2\n\n";
762 
763 static char defaultconf_urlhandlers[] =
764 		"NAME Open Link in a new Firefox Window\n"		"CMD !firefox -new-window %s\n\n";
765 
766 #ifdef USE_SIGACTION
767 /* Close and open log files on SIGUSR1. Usefull for log rotating */
768 
769 static void
sigusr1_handler(int signal,siginfo_t * si,void * un)770 sigusr1_handler (int signal, siginfo_t *si, void *un)
771 {
772 	GSList *list = sess_list;
773 	session *sess;
774 
775 	while (list)
776 	{
777 		sess = list->data;
778 		log_open_or_close (sess);
779 		list = list->next;
780 	}
781 }
782 
783 /* Execute /SIGUSR2 when SIGUSR2 received */
784 
785 static void
sigusr2_handler(int signal,siginfo_t * si,void * un)786 sigusr2_handler (int signal, siginfo_t *si, void *un)
787 {
788 	session *sess = current_sess;
789 
790 	if (sess)
791 		handle_command (sess, "SIGUSR2", FALSE);
792 }
793 #endif
794 
795 static gint
xchat_auto_connect(gpointer userdata)796 xchat_auto_connect (gpointer userdata)
797 {
798 	servlist_auto_connect (NULL);
799 	return 0;
800 }
801 
802 static void
xchat_init(void)803 xchat_init (void)
804 {
805 	char buf[3068];
806 
807 #ifdef WIN32
808 	WSADATA wsadata;
809 
810 	if (WSAStartup(0x0202, &wsadata) != 0)
811 	{
812 		MessageBox (NULL, "Cannot find winsock 2.2+", "Error", MB_OK);
813 		exit (0);
814 	}
815 #endif	/* !WIN32 */
816 
817 #ifdef USE_SIGACTION
818 	struct sigaction act;
819 
820 	/* ignore SIGPIPE's */
821 	act.sa_handler = SIG_IGN;
822 	act.sa_flags = 0;
823 	sigemptyset (&act.sa_mask);
824 	sigaction (SIGPIPE, &act, NULL);
825 
826 	/* Deal with SIGUSR1's & SIGUSR2's */
827 	act.sa_sigaction = sigusr1_handler;
828 	act.sa_flags = 0;
829 	sigemptyset (&act.sa_mask);
830 	sigaction (SIGUSR1, &act, NULL);
831 
832 	act.sa_sigaction = sigusr2_handler;
833 	act.sa_flags = 0;
834 	sigemptyset (&act.sa_mask);
835 	sigaction (SIGUSR2, &act, NULL);
836 #else
837 #ifndef WIN32
838 	/* good enough for these old systems */
839 	signal (SIGPIPE, SIG_IGN);
840 #endif
841 #endif
842 
843 	load_text_events ();
844 	sound_load ();
845 	notify_load ();
846 	ignore_load ();
847 
848 	g_snprintf (buf, sizeof (buf),
849 		"NAME %s~%s~\n"				"CMD query %%s\n\n"\
850 		"NAME %s~%s~\n"				"CMD send %%s\n\n"\
851 		"NAME %s~%s~\n"				"CMD whois %%s %%s\n\n"\
852 		"NAME %s~%s~\n"				"CMD notify -n ASK %%s\n\n"\
853 		"NAME %s~%s~\n"				"CMD ignore %%s!*@* ALL\n\n"\
854 
855 		"NAME SUB\n"					"CMD %s\n\n"\
856 			"NAME %s\n"					"CMD op %%a\n\n"\
857 			"NAME %s\n"					"CMD deop %%a\n\n"\
858 			"NAME SEP\n"				"CMD \n\n"\
859 			"NAME %s\n"					"CMD voice %%a\n\n"\
860 			"NAME %s\n"					"CMD devoice %%a\n"\
861 			"NAME SEP\n"				"CMD \n\n"\
862 			"NAME SUB\n"				"CMD %s\n\n"\
863 				"NAME %s\n"				"CMD kick %%s\n\n"\
864 				"NAME %s\n"				"CMD ban %%s\n\n"\
865 				"NAME SEP\n"			"CMD \n\n"\
866 				"NAME %s *!*@*.host\n""CMD ban %%s 0\n\n"\
867 				"NAME %s *!*@domain\n""CMD ban %%s 1\n\n"\
868 				"NAME %s *!*user@*.host\n""CMD ban %%s 2\n\n"\
869 				"NAME %s *!*user@domain\n""CMD ban %%s 3\n\n"\
870 				"NAME SEP\n"			"CMD \n\n"\
871 				"NAME %s *!*@*.host\n""CMD kickban %%s 0\n\n"\
872 				"NAME %s *!*@domain\n""CMD kickban %%s 1\n\n"\
873 				"NAME %s *!*user@*.host\n""CMD kickban %%s 2\n\n"\
874 				"NAME %s *!*user@domain\n""CMD kickban %%s 3\n\n"\
875 			"NAME ENDSUB\n"			"CMD \n\n"\
876 		"NAME ENDSUB\n"				"CMD \n\n",
877 
878 		_("_Open Dialog Window"), "gtk-go-up",
879 		_("_Send a File" ELLIPSIS), "gtk-floppy",
880 		_("_User Info (WhoIs)"), "gtk-info",
881 		_("_Add to Friends List" ELLIPSIS), "gtk-add",
882 		_("_Ignore"), "gtk-stop",
883 		_("O_perator Actions"),
884 
885 		_("Give Ops"),
886 		_("Take Ops"),
887 		_("Give Voice"),
888 		_("Take Voice"),
889 
890 		_("Kick/Ban"),
891 		_("Kick"),
892 		_("Ban"),
893 		_("Ban"),
894 		_("Ban"),
895 		_("Ban"),
896 		_("Ban"),
897 		_("KickBan"),
898 		_("KickBan"),
899 		_("KickBan"),
900 		_("KickBan"));
901 
902 	list_loadconf ("popup.conf", &popup_list, buf);
903 
904 	g_snprintf (buf, sizeof (buf),
905 		"NAME %s\n"				"CMD part\n\n"
906 		"NAME %s\n"				"CMD getstr # join \"%s\"\n\n"
907 		"NAME %s\n"				"CMD quote LINKS\n\n"
908 		"NAME %s\n"				"CMD ping\n\n"
909 		"NAME TOGGLE %s\n"	"CMD irc_hide_version\n\n",
910 				_("Leave Channel"),
911 				_("Join Channel..."),
912 				_("Enter Channel to Join:"),
913 				_("Server Links"),
914 				_("Ping Server"),
915 				_("Hide Version"));
916 	list_loadconf ("usermenu.conf", &usermenu_list, buf);
917 
918 	g_snprintf (buf, sizeof (buf),
919 		"NAME %s\n"		"CMD op %%a\n\n"
920 		"NAME %s\n"		"CMD deop %%a\n\n"
921 		"NAME %s\n"		"CMD ban %%s\n\n"
922 		"NAME %s\n"		"CMD getstr \"%s\" \"kick %%s\" \"%s\"\n\n"
923 		"NAME %s\n"		"CMD send %%s\n\n"
924 		"NAME %s\n"		"CMD query %%s\n\n",
925 				_("Op"),
926 				_("DeOp"),
927 				_("Ban"),
928 				_("Kick"),
929 				_("bye"),
930 				_("Enter reason to kick %s:"),
931 				_("Send File"),
932 				_("Dialog"));
933 	list_loadconf ("buttons.conf", &button_list, buf);
934 
935 	g_snprintf (buf, sizeof (buf),
936 		"NAME %s\n"				"CMD whois %%s %%s\n\n"
937 		"NAME %s\n"				"CMD send %%s\n\n"
938 		"NAME %s\n"				"CMD dcc chat %%s\n\n"
939 		"NAME %s\n"				"CMD clear\n\n"
940 		"NAME %s\n"				"CMD ping %%s\n\n",
941 				_("WhoIs"),
942 				_("Send"),
943 				_("Chat"),
944 				_("Clear"),
945 				_("Ping"));
946 	list_loadconf ("dlgbuttons.conf", &dlgbutton_list, buf);
947 
948 	list_loadconf ("tabmenu.conf", &tabmenu_list, NULL);
949 	list_loadconf ("ctcpreply.conf", &ctcp_list, defaultconf_ctcp);
950 	list_loadconf ("commands.conf", &command_list, defaultconf_commands);
951 	list_loadconf ("replace.conf", &replace_list, defaultconf_replace);
952 	list_loadconf ("urlhandlers.conf", &urlhandler_list,
953 						defaultconf_urlhandlers);
954 
955 	servlist_init ();							/* load server list */
956 
957 	/* if we got a URL, don't open the server list GUI */
958 	if (!prefs.hex_gui_slist_skip && !arg_url && !arg_urls)
959 		fe_serverlist_open (NULL);
960 
961 	/* turned OFF via -a arg or by passing urls */
962 	if (!arg_dont_autoconnect && !arg_urls && !arg_url)
963 	{
964 		/* do any auto connects */
965 		if (!servlist_have_auto ())	/* if no new windows open .. */
966 		{
967 			/* and no serverlist gui ... */
968 			if (prefs.hex_gui_slist_skip || arg_url || arg_urls)
969 				/* we'll have to open one. */
970 				new_ircwindow (NULL, NULL, SESS_SERVER, 0);
971 		} else
972 		{
973 			fe_idle_add (xchat_auto_connect, NULL);
974 		}
975 	} else
976 	{
977 		if (prefs.hex_gui_slist_skip || arg_url || arg_urls)
978 			new_ircwindow (NULL, NULL, SESS_SERVER, 0);
979 	}
980 }
981 
982 void
hexchat_exit(void)983 hexchat_exit (void)
984 {
985 	hexchat_is_quitting = TRUE;
986 	in_hexchat_exit = TRUE;
987 	plugin_kill_all ();
988 	fe_cleanup ();
989 
990 	save_config ();
991 	if (prefs.save_pevents)
992 	{
993 		pevent_save (NULL);
994 	}
995 
996 	sound_save ();
997 	notify_save ();
998 	ignore_save ();
999 	free_sessions ();
1000 	chanopt_save_all (TRUE);
1001 	servlist_cleanup ();
1002 	fe_exit ();
1003 }
1004 
1005 void
hexchat_exec(const char * cmd)1006 hexchat_exec (const char *cmd)
1007 {
1008 	util_exec (cmd);
1009 }
1010 
1011 
1012 static void
set_locale(void)1013 set_locale (void)
1014 {
1015 #ifdef WIN32
1016 	char hexchat_lang[13];	/* LC_ALL= plus 5 chars of hex_gui_lang and trailing \0 */
1017 
1018 	strcpy (hexchat_lang, "LC_ALL=");
1019 
1020 	if (0 <= prefs.hex_gui_lang && prefs.hex_gui_lang < LANGUAGES_LENGTH)
1021 		strcat (hexchat_lang, languages[prefs.hex_gui_lang]);
1022 	else
1023 		strcat (hexchat_lang, "en");
1024 
1025 	putenv (hexchat_lang);
1026 #endif
1027 }
1028 
1029 int
main(int argc,char * argv[])1030 main (int argc, char *argv[])
1031 {
1032 	int i;
1033 	int ret;
1034 
1035 #ifdef WIN32
1036 	HRESULT coinit_result;
1037 #endif
1038 
1039 	srand ((unsigned int) time (NULL)); /* CL: do this only once! */
1040 
1041 	/* We must check for the config dir parameter, otherwise load_config() will behave incorrectly.
1042 	 * load_config() must come before fe_args() because fe_args() calls gtk_init() which needs to
1043 	 * know the language which is set in the config. The code below is copy-pasted from fe_args()
1044 	 * for the most part. */
1045 	if (argc >= 2)
1046 	{
1047 		for (i = 1; i < argc; i++)
1048 		{
1049 			if ((strcmp (argv[i], "-d") == 0 || strcmp (argv[i], "--cfgdir") == 0)
1050 				&& i + 1 < argc)
1051 			{
1052 				xdir = g_strdup (argv[i + 1]);
1053 			}
1054 			else if (strncmp (argv[i], "--cfgdir=", 9) == 0)
1055 			{
1056 				xdir = g_strdup (argv[i] + 9);
1057 			}
1058 
1059 			if (xdir != NULL)
1060 			{
1061 				if (xdir[strlen (xdir) - 1] == G_DIR_SEPARATOR)
1062 				{
1063 					xdir[strlen (xdir) - 1] = 0;
1064 				}
1065 				break;
1066 			}
1067 		}
1068 	}
1069 
1070 #if ! GLIB_CHECK_VERSION (2, 36, 0)
1071 	g_type_init ();
1072 #endif
1073 
1074 	if (check_config_dir () == 0)
1075 	{
1076 		if (load_config () != 0)
1077 			load_default_config ();
1078 	} else
1079 	{
1080 		/* this is probably the first run */
1081 		load_default_config ();
1082 		make_config_dirs ();
1083 		make_dcc_dirs ();
1084 	}
1085 
1086 	/* we MUST do this after load_config () AND before fe_init (thus gtk_init) otherwise it will fail */
1087 	set_locale ();
1088 
1089 	ret = fe_args (argc, argv);
1090 	if (ret != -1)
1091 		return ret;
1092 
1093 #ifdef USE_DBUS
1094 	hexchat_remote ();
1095 #endif
1096 
1097 #ifdef WIN32
1098 	coinit_result = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
1099 	if (SUCCEEDED (coinit_result))
1100 	{
1101 		CoInitializeSecurity (NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
1102 	}
1103 #endif
1104 
1105 	fe_init ();
1106 
1107 	/* This is done here because cfgfiles.c is too early in
1108 	* the startup process to use gtk functions. */
1109 	if (g_access (get_xdir (), W_OK) != 0)
1110 	{
1111 		char buf[2048];
1112 
1113 		g_snprintf (buf, sizeof(buf),
1114 			_("You do not have write access to %s. Nothing from this session can be saved."),
1115 			get_xdir ());
1116 		fe_message (buf, FE_MSG_ERROR);
1117 	}
1118 
1119 #ifndef WIN32
1120 #ifndef __EMX__
1121 	/* OS/2 uses UID 0 all the time */
1122 	if (getuid () == 0)
1123 		fe_message (_("* Running IRC as root is stupid! You should\n"
1124 			      "  create a User Account and use that to login.\n"), FE_MSG_WARN|FE_MSG_WAIT);
1125 #endif
1126 #endif /* !WIN32 */
1127 
1128 	xchat_init ();
1129 
1130 	fe_main ();
1131 
1132 #ifdef WIN32
1133 	if (SUCCEEDED (coinit_result))
1134 	{
1135 		CoUninitialize ();
1136 	}
1137 #endif
1138 
1139 #ifdef WIN32
1140 	WSACleanup ();
1141 #endif
1142 
1143 	return 0;
1144 }
1145