2 * Copyright (C) 1998 Peter Zelezny.
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
23 #include <sys/types.h>
39 #include "xchat-plugin.h"
41 #include "plugin-timer.h"
58 GSList
*popup_list
= 0;
59 GSList
*button_list
= 0;
60 GSList
*dlgbutton_list
= 0;
61 GSList
*command_list
= 0;
62 GSList
*ctcp_list
= 0;
63 GSList
*replace_list
= 0;
64 GSList
*sess_list
= 0;
66 GSList
*ignore_list
= 0;
67 GSList
*usermenu_list
= 0;
68 GSList
*urlhandler_list
= 0;
69 GSList
*tabmenu_list
= 0;
71 static int in_xchat_exit
= FALSE
;
72 int xchat_is_quitting
= FALSE
;
73 /* command-line args */
74 int arg_dont_autoconnect
= FALSE
;
75 int arg_skip_plugins
= FALSE
;
77 char *arg_command
= NULL
;
78 gint arg_existing
= FALSE
;
81 #include "dbus/dbus-client.h"
82 #include "dbus/dbus-plugin.h"
85 struct session
*current_tab
;
86 struct session
*current_sess
= 0;
87 struct xchatprefs prefs
;
90 pxProxyFactory
*libproxy_factory
;
94 is_session (session
* sess
)
96 return g_slist_find (sess_list
, sess
) ? 1 : 0;
100 find_dialog (server
*serv
, char *nick
)
102 GSList
*list
= sess_list
;
108 if (sess
->server
== serv
&& sess
->type
== SESS_DIALOG
)
110 if (!serv
->p_cmp (nick
, sess
->channel
))
119 find_channel (server
*serv
, char *chan
)
122 GSList
*list
= sess_list
;
126 if ((!serv
|| serv
== sess
->server
) && sess
->type
!= SESS_DIALOG
)
128 if (!serv
->p_cmp (chan
, sess
->channel
))
137 lagcheck_update (void)
140 GSList
*list
= serv_list
;
142 if (!prefs
.lagometer
)
149 fe_set_lag (serv
, -1);
159 GSList
*list
= serv_list
;
162 time_t now
= time (0);
165 tim
= make_ping_time ();
170 if (serv
->connected
&& serv
->end_of_motd
)
172 lag
= now
- serv
->ping_recv
;
173 if (prefs
.pingtimeout
&& lag
> prefs
.pingtimeout
&& lag
> 0)
175 sprintf (tbuf
, "%d", lag
);
176 EMIT_SIGNAL (XP_TE_PINGTIMEOUT
, serv
->server_session
, tbuf
, NULL
,
178 if (prefs
.autoreconnect
)
179 serv
->auto_reconnect (serv
, FALSE
, -1);
182 snprintf (tbuf
, sizeof (tbuf
), "LAG%lu", tim
);
183 serv
->p_ping (serv
, "", tbuf
);
184 serv
->lag_sent
= tim
;
185 fe_set_lag (serv
, -1);
197 int full
, sent
, loop
= 0;
199 if (!prefs
.away_track
|| prefs
.away_size_max
< 1)
203 /* request an update of AWAY status of 1 channel every 30 seconds */
205 sent
= 0; /* number of WHOs (users) requested */
211 if (sess
->server
->connected
&&
212 sess
->type
== SESS_CHANNEL
&&
214 sess
->total
<= prefs
.away_size_max
)
216 if (!sess
->done_away_check
)
220 /* if we're under 31 WHOs, send another channels worth */
221 if (sent
< 31 && !sess
->doing_who
)
223 sess
->done_away_check
= TRUE
;
224 sess
->doing_who
= TRUE
;
225 /* this'll send a WHO #channel */
226 sess
->server
->p_away_status (sess
->server
, sess
->channel
);
235 /* done them all, reset done_away_check to FALSE and start over unless we have away-notify */
242 if (!sess
->server
->have_awaynotify
)
243 sess
->done_away_check
= FALSE
;
255 xchat_misc_checks (void) /* this gets called every 1/2 second */
257 static int count
= 0;
259 static int count2
= 0;
264 lagcheck_update (); /* every 500ms */
267 dcc_check_timeouts (); /* every 1 second */
269 if (count
>= 60) /* every 30 seconds */
278 if (count2
>= 720) /* 720 every 6 minutes */
280 msproxy_keepalive ();
288 /* executed when the first irc window opens */
291 irc_init (session
*sess
)
293 static int done_init
= FALSE
;
301 plugin_add (sess
, NULL
, NULL
, timer_plugin_init
, NULL
, NULL
, FALSE
);
304 if (!arg_skip_plugins
)
305 plugin_auto_load (sess
); /* autoload ~/.xchat *.so */
309 plugin_add (sess
, NULL
, NULL
, dbus_plugin_init
, NULL
, NULL
, FALSE
);
312 if (prefs
.notify_timeout
)
313 notify_tag
= fe_timeout_add (prefs
.notify_timeout
* 1000,
314 notify_checklist
, 0);
316 fe_timeout_add (prefs
.away_timeout
* 1000, away_check
, 0);
317 fe_timeout_add (500, xchat_misc_checks
, 0);
321 snprintf (buf
, sizeof (buf
), "server %s", arg_url
);
322 handle_command (sess
, buf
, FALSE
);
323 g_free (arg_url
); /* from GOption */
326 if (arg_command
!= NULL
)
328 g_free (arg_command
);
331 /* load -e ~/.xchat2/startup.txt */
332 snprintf (buf
, sizeof (buf
), "%s/%s", get_xdir_fs (), "startup.txt");
333 load_perform_file (sess
, buf
);
337 session_new (server
*serv
, char *from
, int type
, int focus
)
341 sess
= malloc (sizeof (struct session
));
342 memset (sess
, 0, sizeof (struct session
));
349 sess
->alert_beep
= SET_DEFAULT
;
350 sess
->alert_taskbar
= SET_DEFAULT
;
351 sess
->alert_tray
= SET_DEFAULT
;
353 sess
->text_hidejoinpart
= SET_DEFAULT
;
354 sess
->text_logging
= SET_DEFAULT
;
355 sess
->text_scrollback
= SET_DEFAULT
;
358 safe_strcpy (sess
->channel
, from
, CHANLEN
);
360 sess_list
= g_slist_prepend (sess_list
, sess
);
362 fe_new_window (sess
, focus
);
368 new_ircwindow (server
*serv
, char *name
, int type
, int focus
)
375 serv
= server_new ();
376 if (prefs
.use_server_tab
)
377 sess
= session_new (serv
, name
, SESS_SERVER
, focus
);
379 sess
= session_new (serv
, name
, SESS_CHANNEL
, focus
);
380 serv
->server_session
= sess
;
381 serv
->front_session
= sess
;
384 sess
= session_new (serv
, name
, type
, focus
);
385 log_open_or_close (sess
);
388 /* case SESS_CHANNEL:
390 case SESS_SNOTICES:*/
391 sess
= session_new (serv
, name
, type
, focus
);
396 scrollback_load (sess
);
398 plugin_emit_dummy_print (sess
, "Open Context");
404 exec_notify_kill (session
* sess
)
407 if (sess
->running_exec
!= NULL
)
409 re
= sess
->running_exec
;
410 sess
->running_exec
= NULL
;
411 kill (re
->childpid
, SIGKILL
);
412 waitpid (re
->childpid
, NULL
, WNOHANG
);
413 fe_input_remove (re
->iotag
);
422 send_quit_or_part (session
* killsess
)
427 server
*killserv
= killsess
->server
;
429 /* check if this is the last session using this server */
433 sess
= (session
*) list
->data
;
434 if (sess
->server
== killserv
&& sess
!= killsess
)
442 if (xchat_is_quitting
)
445 if (killserv
->connected
)
449 if (!killserv
->sent_quit
)
451 killserv
->flush_queue (killserv
);
452 server_sendquit (killsess
);
453 killserv
->sent_quit
= TRUE
;
457 if (killsess
->type
== SESS_CHANNEL
&& killsess
->channel
[0] &&
458 !killserv
->sent_quit
)
460 server_sendpart (killserv
, killsess
->channel
, 0);
467 session_free (session
*killsess
)
469 server
*killserv
= killsess
->server
;
473 plugin_emit_dummy_print (killsess
, "Close Context");
475 if (current_tab
== killsess
)
478 if (killserv
->server_session
== killsess
)
479 killserv
->server_session
= NULL
;
481 if (killserv
->front_session
== killsess
)
483 /* front_session is closed, find a valid replacement */
484 killserv
->front_session
= NULL
;
488 sess
= (session
*) list
->data
;
489 if (sess
!= killsess
&& sess
->server
== killserv
)
491 killserv
->front_session
= sess
;
492 if (!killserv
->server_session
)
493 killserv
->server_session
= sess
;
500 if (!killserv
->server_session
)
501 killserv
->server_session
= killserv
->front_session
;
503 sess_list
= g_slist_remove (sess_list
, killsess
);
505 if (killsess
->type
== SESS_CHANNEL
)
506 userlist_free (killsess
);
508 exec_notify_kill (killsess
);
510 log_close (killsess
);
511 scrollback_close (killsess
);
512 chanopt_save (killsess
);
514 send_quit_or_part (killsess
);
516 history_free (&killsess
->history
);
518 free (killsess
->topic
);
519 if (killsess
->current_modes
)
520 free (killsess
->current_modes
);
522 fe_session_callback (killsess
);
524 if (current_sess
== killsess
)
528 current_sess
= sess_list
->data
;
533 if (!sess_list
&& !in_xchat_exit
)
534 xchat_exit (); /* sess_list is empty, quit! */
539 sess
= (session
*) list
->data
;
540 if (sess
->server
== killserv
)
541 return; /* this server is still being used! */
545 server_free (killserv
);
551 GSList
*list
= sess_list
;
555 fe_close_window (list
->data
);
561 static char defaultconf_ctcp
[] =
562 "NAME TIME\n" "CMD nctcp %s TIME %t\n\n"\
563 "NAME PING\n" "CMD nctcp %s PING %d\n\n";
565 static char defaultconf_replace
[] =
566 "NAME teh\n" "CMD the\n\n";
567 /* "NAME r\n" "CMD are\n\n"\
568 "NAME u\n" "CMD you\n\n"*/
570 static char defaultconf_commands
[] =
571 "NAME ACTION\n" "CMD me &2\n\n"\
572 "NAME AME\n" "CMD allchan me &2\n\n"\
573 "NAME ANICK\n" "CMD allserv nick &2\n\n"\
574 "NAME AMSG\n" "CMD allchan say &2\n\n"\
575 "NAME BANLIST\n" "CMD quote MODE %c +b\n\n"\
576 "NAME CHAT\n" "CMD dcc chat %2\n\n"\
577 "NAME DIALOG\n" "CMD query %2\n\n"\
578 "NAME DMSG\n" "CMD msg =%2 &3\n\n"\
579 "NAME EXIT\n" "CMD quit\n\n"\
580 "NAME GREP\n" "CMD lastlog -r &2\n\n"\
581 "NAME J\n" "CMD join &2\n\n"\
582 "NAME KILL\n" "CMD quote KILL %2 :&3\n\n"\
583 "NAME LEAVE\n" "CMD part &2\n\n"\
584 "NAME M\n" "CMD msg &2\n\n"\
585 "NAME ONOTICE\n" "CMD notice @%c &2\n\n"\
586 "NAME RAW\n" "CMD quote &2\n\n"\
587 "NAME SERVHELP\n" "CMD quote HELP\n\n"\
588 "NAME SPING\n" "CMD ping\n\n"\
589 "NAME SQUERY\n" "CMD quote SQUERY %2 :&3\n\n"\
590 "NAME SSLSERVER\n" "CMD server -ssl &2\n\n"\
591 "NAME SV\n" "CMD echo xchat %v %m\n\n"\
592 "NAME UMODE\n" "CMD mode %n &2\n\n"\
593 "NAME UPTIME\n" "CMD quote STATS u\n\n"\
594 "NAME VER\n" "CMD ctcp %2 VERSION\n\n"\
595 "NAME VERSION\n" "CMD ctcp %2 VERSION\n\n"\
596 "NAME WALLOPS\n" "CMD quote WALLOPS :&2\n\n"\
597 "NAME WII\n" "CMD quote WHOIS %2 %2\n\n";
599 static char defaultconf_urlhandlers
[] =
600 "NAME Open Link in Opera\n" "CMD !opera -remote 'openURL(%s)'\n\n";
603 /* Close and open log files on SIGUSR1. Usefull for log rotating */
606 sigusr1_handler (int signal
, siginfo_t
*si
, void *un
)
608 GSList
*list
= sess_list
;
614 log_open_or_close (sess
);
619 /* Execute /SIGUSR2 when SIGUSR2 received */
622 sigusr2_handler (int signal
, siginfo_t
*si
, void *un
)
624 session
*sess
= current_sess
;
627 handle_command (sess
, "SIGUSR2", FALSE
);
632 xchat_auto_connect (gpointer userdata
)
634 servlist_auto_connect (NULL
);
642 const char *cs
= NULL
;
645 struct sigaction act
;
647 /* ignore SIGPIPE's */
648 act
.sa_handler
= SIG_IGN
;
650 sigemptyset (&act
.sa_mask
);
651 sigaction (SIGPIPE
, &act
, NULL
);
653 /* Deal with SIGUSR1's & SIGUSR2's */
654 act
.sa_sigaction
= sigusr1_handler
;
656 sigemptyset (&act
.sa_mask
);
657 sigaction (SIGUSR1
, &act
, NULL
);
659 act
.sa_sigaction
= sigusr2_handler
;
661 sigemptyset (&act
.sa_mask
);
662 sigaction (SIGUSR2
, &act
, NULL
);
664 /* good enough for these old systems */
665 signal (SIGPIPE
, SIG_IGN
);
668 if (g_get_charset (&cs
))
669 prefs
.utf8_locale
= TRUE
;
676 snprintf (buf
, sizeof (buf
),
677 "NAME %s~%s~\n" "CMD query %%s\n\n"\
678 "NAME %s~%s~\n" "CMD send %%s\n\n"\
679 "NAME %s~%s~\n" "CMD whois %%s %%s\n\n"\
680 "NAME %s~%s~\n" "CMD notify -n ASK %%s\n\n"\
682 "NAME SUB\n" "CMD %s\n\n"\
683 "NAME %s\n" "CMD op %%a\n\n"\
684 "NAME %s\n" "CMD deop %%a\n\n"\
685 "NAME SEP\n" "CMD \n\n"\
686 "NAME %s\n" "CMD voice %%a\n\n"\
687 "NAME %s\n" "CMD devoice %%a\n"\
688 "NAME SEP\n" "CMD \n\n"\
689 "NAME SUB\n" "CMD %s\n\n"\
690 "NAME %s\n" "CMD kick %%s\n\n"\
691 "NAME %s\n" "CMD ban %%s\n\n"\
692 "NAME SEP\n" "CMD \n\n"\
693 "NAME %s *!*@*.host\n""CMD ban %%s 0\n\n"\
694 "NAME %s *!*@domain\n""CMD ban %%s 1\n\n"\
695 "NAME %s *!*user@*.host\n""CMD ban %%s 2\n\n"\
696 "NAME %s *!*user@domain\n""CMD ban %%s 3\n\n"\
697 "NAME SEP\n" "CMD \n\n"\
698 "NAME %s *!*@*.host\n""CMD kickban %%s 0\n\n"\
699 "NAME %s *!*@domain\n""CMD kickban %%s 1\n\n"\
700 "NAME %s *!*user@*.host\n""CMD kickban %%s 2\n\n"\
701 "NAME %s *!*user@domain\n""CMD kickban %%s 3\n\n"\
702 "NAME ENDSUB\n" "CMD \n\n"\
703 "NAME ENDSUB\n" "CMD \n\n",
705 _("_Open Dialog Window"), "xchat-dialog",
706 _("_Send a File"), "gtk-floppy",
707 _("_User Info (WhoIs)"), "gtk-info",
708 _("_Add to Friends List"), "gtk-add",
709 _("O_perator Actions"),
728 list_loadconf ("popup.conf", &popup_list
, buf
);
730 snprintf (buf
, sizeof (buf
),
731 "NAME %s\n" "CMD part\n\n"
732 "NAME %s\n" "CMD getstr # join \"%s\"\n\n"
733 "NAME %s\n" "CMD quote LINKS\n\n"
734 "NAME %s\n" "CMD ping\n\n"
735 "NAME TOGGLE %s\n" "CMD irc_hide_version\n\n",
737 _("Join Channel..."),
738 _("Enter Channel to Join:"),
742 list_loadconf ("usermenu.conf", &usermenu_list
, buf
);
744 snprintf (buf
, sizeof (buf
),
745 "NAME %s\n" "CMD op %%a\n\n"
746 "NAME %s\n" "CMD deop %%a\n\n"
747 "NAME %s\n" "CMD ban %%s\n\n"
748 "NAME %s\n" "CMD getstr \"%s\" \"kick %%s\" \"%s\"\n\n"
749 "NAME %s\n" "CMD send %%s\n\n"
750 "NAME %s\n" "CMD query %%s\n\n",
756 _("Enter reason to kick %s:"),
759 list_loadconf ("buttons.conf", &button_list
, buf
);
761 snprintf (buf
, sizeof (buf
),
762 "NAME %s\n" "CMD whois %%s %%s\n\n"
763 "NAME %s\n" "CMD send %%s\n\n"
764 "NAME %s\n" "CMD dcc chat %%s\n\n"
765 "NAME %s\n" "CMD clear\n\n"
766 "NAME %s\n" "CMD ping %%s\n\n",
772 list_loadconf ("dlgbuttons.conf", &dlgbutton_list
, buf
);
774 list_loadconf ("tabmenu.conf", &tabmenu_list
, NULL
);
775 list_loadconf ("ctcpreply.conf", &ctcp_list
, defaultconf_ctcp
);
776 list_loadconf ("commands.conf", &command_list
, defaultconf_commands
);
777 list_loadconf ("replace.conf", &replace_list
, defaultconf_replace
);
778 list_loadconf ("urlhandlers.conf", &urlhandler_list
,
779 defaultconf_urlhandlers
);
781 servlist_init (); /* load server list */
783 /* if we got a URL, don't open the server list GUI */
784 if (!prefs
.slist_skip
&& !arg_url
)
785 fe_serverlist_open (NULL
);
787 /* turned OFF via -a arg */
788 if (!arg_dont_autoconnect
)
790 /* do any auto connects */
791 if (!servlist_have_auto ()) /* if no new windows open .. */
793 /* and no serverlist gui ... */
794 if (prefs
.slist_skip
|| arg_url
)
795 /* we'll have to open one. */
796 new_ircwindow (NULL
, NULL
, SESS_SERVER
, 0);
799 fe_idle_add (xchat_auto_connect
, NULL
);
803 if (prefs
.slist_skip
|| arg_url
)
804 new_ircwindow (NULL
, NULL
, SESS_SERVER
, 0);
811 xchat_is_quitting
= TRUE
;
812 in_xchat_exit
= TRUE
;
818 if (prefs
.save_pevents
)
821 if (prefs
.autosave_url
)
833 child_handler (gpointer userdata
)
835 int pid
= GPOINTER_TO_INT (userdata
);
837 if (waitpid (pid
, 0, WNOHANG
) == pid
)
838 return 0; /* remove timeout handler */
839 return 1; /* keep the timeout handler */
843 xchat_exec (const char *cmd
)
845 int pid
= util_exec (cmd
);
847 /* zombie avoiding system. Don't ask! it has to be like this to work
848 with zvt (which overrides the default handler) */
849 fe_timeout_add (1000, child_handler
, GINT_TO_POINTER (pid
));
853 xchat_execv (char * const argv
[])
855 int pid
= util_execv (argv
);
857 /* zombie avoiding system. Don't ask! it has to be like this to work
858 with zvt (which overrides the default handler) */
859 fe_timeout_add (1000, child_handler
, GINT_TO_POINTER (pid
));
863 main (int argc
, char *argv
[])
867 srand (time (0)); /* CL: do this only once! */
873 ret
= fe_args (argc
, argv
);
884 libproxy_factory
= px_proxy_factory_new();
894 px_proxy_factory_free(libproxy_factory
);