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