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"
51 #include <openssl/ssl.h> /* SSL_() */
63 GSList
*popup_list
= 0;
64 GSList
*button_list
= 0;
65 GSList
*dlgbutton_list
= 0;
66 GSList
*command_list
= 0;
67 GSList
*ctcp_list
= 0;
68 GSList
*replace_list
= 0;
69 GSList
*sess_list
= 0;
71 GSList
*ignore_list
= 0;
72 GSList
*usermenu_list
= 0;
73 GSList
*urlhandler_list
= 0;
74 GSList
*tabmenu_list
= 0;
76 static int in_xchat_exit
= FALSE
;
77 int xchat_is_quitting
= FALSE
;
78 /* command-line args */
79 int arg_dont_autoconnect
= FALSE
;
80 int arg_skip_plugins
= FALSE
;
82 char *arg_command
= NULL
;
83 gint arg_existing
= FALSE
;
86 #include "dbus/dbus-client.h"
87 #include "dbus/dbus-plugin.h"
90 struct session
*current_tab
;
91 struct session
*current_sess
= 0;
92 struct xchatprefs prefs
;
99 pxProxyFactory
*libproxy_factory
;
103 is_session (session
* sess
)
105 return g_slist_find (sess_list
, sess
) ? 1 : 0;
109 find_dialog (server
*serv
, char *nick
)
111 GSList
*list
= sess_list
;
117 if (sess
->server
== serv
&& sess
->type
== SESS_DIALOG
)
119 if (!serv
->p_cmp (nick
, sess
->channel
))
128 find_channel (server
*serv
, char *chan
)
131 GSList
*list
= sess_list
;
135 if ((!serv
|| serv
== sess
->server
) && sess
->type
!= SESS_DIALOG
)
137 if (!serv
->p_cmp (chan
, sess
->channel
))
146 lagcheck_update (void)
149 GSList
*list
= serv_list
;
151 if (!prefs
.lagometer
)
158 fe_set_lag (serv
, -1);
168 GSList
*list
= serv_list
;
171 time_t now
= time (0);
174 tim
= make_ping_time ();
179 if (serv
->connected
&& serv
->end_of_motd
)
181 lag
= now
- serv
->ping_recv
;
182 if (prefs
.pingtimeout
&& lag
> prefs
.pingtimeout
&& lag
> 0)
184 sprintf (tbuf
, "%d", lag
);
185 EMIT_SIGNAL (XP_TE_PINGTIMEOUT
, serv
->server_session
, tbuf
, NULL
,
187 if (prefs
.autoreconnect
)
188 serv
->auto_reconnect (serv
, FALSE
, -1);
191 snprintf (tbuf
, sizeof (tbuf
), "LAG%lu", tim
);
192 serv
->p_ping (serv
, "", tbuf
);
193 serv
->lag_sent
= tim
;
194 fe_set_lag (serv
, -1);
206 int full
, sent
, loop
= 0;
208 if (!prefs
.away_track
|| prefs
.away_size_max
< 1)
212 /* request an update of AWAY status of 1 channel every 30 seconds */
214 sent
= 0; /* number of WHOs (users) requested */
220 if (sess
->server
->connected
&&
221 sess
->type
== SESS_CHANNEL
&&
223 sess
->total
<= prefs
.away_size_max
)
225 if (!sess
->done_away_check
)
229 /* if we're under 31 WHOs, send another channels worth */
230 if (sent
< 31 && !sess
->doing_who
)
232 sess
->done_away_check
= TRUE
;
233 sess
->doing_who
= TRUE
;
234 /* this'll send a WHO #channel */
235 sess
->server
->p_away_status (sess
->server
, sess
->channel
);
244 /* done them all, reset done_away_check to FALSE and start over unless we have away-notify */
251 if (!sess
->server
->have_awaynotify
)
252 sess
->done_away_check
= FALSE
;
264 xchat_misc_checks (void) /* this gets called every 1/2 second */
266 static int count
= 0;
268 static int count2
= 0;
273 lagcheck_update (); /* every 500ms */
276 dcc_check_timeouts (); /* every 1 second */
278 if (count
>= 60) /* every 30 seconds */
287 if (count2
>= 720) /* 720 every 6 minutes */
289 msproxy_keepalive ();
297 /* executed when the first irc window opens */
300 irc_init (session
*sess
)
302 static int done_init
= FALSE
;
310 plugin_add (sess
, NULL
, NULL
, timer_plugin_init
, NULL
, NULL
, FALSE
);
313 if (!arg_skip_plugins
)
314 plugin_auto_load (sess
); /* autoload ~/.xchat *.so */
318 plugin_add (sess
, NULL
, NULL
, dbus_plugin_init
, NULL
, NULL
, FALSE
);
321 if (prefs
.notify_timeout
)
322 notify_tag
= fe_timeout_add (prefs
.notify_timeout
* 1000,
323 notify_checklist
, 0);
325 fe_timeout_add (prefs
.away_timeout
* 1000, away_check
, 0);
326 fe_timeout_add (500, xchat_misc_checks
, 0);
330 snprintf (buf
, sizeof (buf
), "server %s", arg_url
);
331 handle_command (sess
, buf
, FALSE
);
332 g_free (arg_url
); /* from GOption */
335 if (arg_command
!= NULL
)
337 g_free (arg_command
);
340 /* load -e ~/.xchat2/startup.txt */
341 snprintf (buf
, sizeof (buf
), "%s/%s", get_xdir_fs (), "startup.txt");
342 load_perform_file (sess
, buf
);
346 session_new (server
*serv
, char *from
, int type
, int focus
)
350 sess
= malloc (sizeof (struct session
));
351 memset (sess
, 0, sizeof (struct session
));
358 sess
->alert_beep
= SET_DEFAULT
;
359 sess
->alert_taskbar
= SET_DEFAULT
;
360 sess
->alert_tray
= SET_DEFAULT
;
362 sess
->text_hidejoinpart
= SET_DEFAULT
;
363 sess
->text_logging
= SET_DEFAULT
;
364 sess
->text_scrollback
= SET_DEFAULT
;
367 safe_strcpy (sess
->channel
, from
, CHANLEN
);
369 sess_list
= g_slist_prepend (sess_list
, sess
);
371 fe_new_window (sess
, focus
);
377 new_ircwindow (server
*serv
, char *name
, int type
, int focus
)
384 serv
= server_new ();
385 if (prefs
.use_server_tab
)
386 sess
= session_new (serv
, name
, SESS_SERVER
, focus
);
388 sess
= session_new (serv
, name
, SESS_CHANNEL
, focus
);
389 serv
->server_session
= sess
;
390 serv
->front_session
= sess
;
393 sess
= session_new (serv
, name
, type
, focus
);
394 log_open_or_close (sess
);
397 /* case SESS_CHANNEL:
399 case SESS_SNOTICES:*/
400 sess
= session_new (serv
, name
, type
, focus
);
405 scrollback_load (sess
);
407 plugin_emit_dummy_print (sess
, "Open Context");
413 exec_notify_kill (session
* sess
)
416 if (sess
->running_exec
!= NULL
)
418 re
= sess
->running_exec
;
419 sess
->running_exec
= NULL
;
420 kill (re
->childpid
, SIGKILL
);
421 waitpid (re
->childpid
, NULL
, WNOHANG
);
422 fe_input_remove (re
->iotag
);
431 send_quit_or_part (session
* killsess
)
436 server
*killserv
= killsess
->server
;
438 /* check if this is the last session using this server */
442 sess
= (session
*) list
->data
;
443 if (sess
->server
== killserv
&& sess
!= killsess
)
451 if (xchat_is_quitting
)
454 if (killserv
->connected
)
458 if (!killserv
->sent_quit
)
460 killserv
->flush_queue (killserv
);
461 server_sendquit (killsess
);
462 killserv
->sent_quit
= TRUE
;
466 if (killsess
->type
== SESS_CHANNEL
&& killsess
->channel
[0] &&
467 !killserv
->sent_quit
)
469 server_sendpart (killserv
, killsess
->channel
, 0);
476 session_free (session
*killsess
)
478 server
*killserv
= killsess
->server
;
482 plugin_emit_dummy_print (killsess
, "Close Context");
484 if (current_tab
== killsess
)
487 if (killserv
->server_session
== killsess
)
488 killserv
->server_session
= NULL
;
490 if (killserv
->front_session
== killsess
)
492 /* front_session is closed, find a valid replacement */
493 killserv
->front_session
= NULL
;
497 sess
= (session
*) list
->data
;
498 if (sess
!= killsess
&& sess
->server
== killserv
)
500 killserv
->front_session
= sess
;
501 if (!killserv
->server_session
)
502 killserv
->server_session
= sess
;
509 if (!killserv
->server_session
)
510 killserv
->server_session
= killserv
->front_session
;
512 sess_list
= g_slist_remove (sess_list
, killsess
);
514 if (killsess
->type
== SESS_CHANNEL
)
515 userlist_free (killsess
);
517 exec_notify_kill (killsess
);
519 log_close (killsess
);
520 scrollback_close (killsess
);
521 chanopt_save (killsess
);
523 send_quit_or_part (killsess
);
525 history_free (&killsess
->history
);
527 free (killsess
->topic
);
528 if (killsess
->current_modes
)
529 free (killsess
->current_modes
);
531 fe_session_callback (killsess
);
533 if (current_sess
== killsess
)
537 current_sess
= sess_list
->data
;
542 if (!sess_list
&& !in_xchat_exit
)
543 xchat_exit (); /* sess_list is empty, quit! */
548 sess
= (session
*) list
->data
;
549 if (sess
->server
== killserv
)
550 return; /* this server is still being used! */
554 server_free (killserv
);
560 GSList
*list
= sess_list
;
564 fe_close_window (list
->data
);
570 static char defaultconf_ctcp
[] =
571 "NAME TIME\n" "CMD nctcp %s TIME %t\n\n"\
572 "NAME PING\n" "CMD nctcp %s PING %d\n\n";
574 static char defaultconf_replace
[] =
575 "NAME teh\n" "CMD the\n\n";
576 /* "NAME r\n" "CMD are\n\n"\
577 "NAME u\n" "CMD you\n\n"*/
579 static char defaultconf_commands
[] =
580 "NAME ACTION\n" "CMD me &2\n\n"\
581 "NAME AME\n" "CMD allchan me &2\n\n"\
582 "NAME ANICK\n" "CMD allserv nick &2\n\n"\
583 "NAME AMSG\n" "CMD allchan say &2\n\n"\
584 "NAME BANLIST\n" "CMD quote MODE %c +b\n\n"\
585 "NAME CHAT\n" "CMD dcc chat %2\n\n"\
586 "NAME DIALOG\n" "CMD query %2\n\n"\
587 "NAME DMSG\n" "CMD msg =%2 &3\n\n"\
588 "NAME EXIT\n" "CMD quit\n\n"\
589 "NAME GREP\n" "CMD lastlog -r &2\n\n"\
590 "NAME J\n" "CMD join &2\n\n"\
591 "NAME KILL\n" "CMD quote KILL %2 :&3\n\n"\
592 "NAME LEAVE\n" "CMD part &2\n\n"\
593 "NAME M\n" "CMD msg &2\n\n"\
594 "NAME ONOTICE\n" "CMD notice @%c &2\n\n"\
595 "NAME RAW\n" "CMD quote &2\n\n"\
596 "NAME SERVHELP\n" "CMD quote HELP\n\n"\
597 "NAME SPING\n" "CMD ping\n\n"\
598 "NAME SQUERY\n" "CMD quote SQUERY %2 :&3\n\n"\
599 "NAME SSLSERVER\n" "CMD server -ssl &2\n\n"\
600 "NAME SV\n" "CMD echo xchat %v %m\n\n"\
601 "NAME UMODE\n" "CMD mode %n &2\n\n"\
602 "NAME UPTIME\n" "CMD quote STATS u\n\n"\
603 "NAME VER\n" "CMD ctcp %2 VERSION\n\n"\
604 "NAME VERSION\n" "CMD ctcp %2 VERSION\n\n"\
605 "NAME WALLOPS\n" "CMD quote WALLOPS :&2\n\n"\
606 "NAME WII\n" "CMD quote WHOIS %2 %2\n\n";
608 static char defaultconf_urlhandlers
[] =
609 "NAME Open Link in Opera\n" "CMD !opera -remote 'openURL(%s)'\n\n";
612 /* Close and open log files on SIGUSR1. Usefull for log rotating */
615 sigusr1_handler (int signal
, siginfo_t
*si
, void *un
)
617 GSList
*list
= sess_list
;
623 log_open_or_close (sess
);
628 /* Execute /SIGUSR2 when SIGUSR2 received */
631 sigusr2_handler (int signal
, siginfo_t
*si
, void *un
)
633 session
*sess
= current_sess
;
636 handle_command (sess
, "SIGUSR2", FALSE
);
641 xchat_auto_connect (gpointer userdata
)
643 servlist_auto_connect (NULL
);
651 const char *cs
= NULL
;
654 struct sigaction act
;
656 /* ignore SIGPIPE's */
657 act
.sa_handler
= SIG_IGN
;
659 sigemptyset (&act
.sa_mask
);
660 sigaction (SIGPIPE
, &act
, NULL
);
662 /* Deal with SIGUSR1's & SIGUSR2's */
663 act
.sa_sigaction
= sigusr1_handler
;
665 sigemptyset (&act
.sa_mask
);
666 sigaction (SIGUSR1
, &act
, NULL
);
668 act
.sa_sigaction
= sigusr2_handler
;
670 sigemptyset (&act
.sa_mask
);
671 sigaction (SIGUSR2
, &act
, NULL
);
673 /* good enough for these old systems */
674 signal (SIGPIPE
, SIG_IGN
);
677 if (g_get_charset (&cs
))
678 prefs
.utf8_locale
= TRUE
;
685 snprintf (buf
, sizeof (buf
),
686 "NAME %s~%s~\n" "CMD query %%s\n\n"\
687 "NAME %s~%s~\n" "CMD send %%s\n\n"\
688 "NAME %s~%s~\n" "CMD whois %%s %%s\n\n"\
689 "NAME %s~%s~\n" "CMD notify -n ASK %%s\n\n"\
691 "NAME SUB\n" "CMD %s\n\n"\
692 "NAME %s\n" "CMD op %%a\n\n"\
693 "NAME %s\n" "CMD deop %%a\n\n"\
694 "NAME SEP\n" "CMD \n\n"\
695 "NAME %s\n" "CMD voice %%a\n\n"\
696 "NAME %s\n" "CMD devoice %%a\n"\
697 "NAME SEP\n" "CMD \n\n"\
698 "NAME SUB\n" "CMD %s\n\n"\
699 "NAME %s\n" "CMD kick %%s\n\n"\
700 "NAME %s\n" "CMD ban %%s\n\n"\
701 "NAME SEP\n" "CMD \n\n"\
702 "NAME %s *!*@*.host\n""CMD ban %%s 0\n\n"\
703 "NAME %s *!*@domain\n""CMD ban %%s 1\n\n"\
704 "NAME %s *!*user@*.host\n""CMD ban %%s 2\n\n"\
705 "NAME %s *!*user@domain\n""CMD ban %%s 3\n\n"\
706 "NAME SEP\n" "CMD \n\n"\
707 "NAME %s *!*@*.host\n""CMD kickban %%s 0\n\n"\
708 "NAME %s *!*@domain\n""CMD kickban %%s 1\n\n"\
709 "NAME %s *!*user@*.host\n""CMD kickban %%s 2\n\n"\
710 "NAME %s *!*user@domain\n""CMD kickban %%s 3\n\n"\
711 "NAME ENDSUB\n" "CMD \n\n"\
712 "NAME ENDSUB\n" "CMD \n\n",
714 _("_Open Dialog Window"), "xchat-dialog",
715 _("_Send a File"), "gtk-floppy",
716 _("_User Info (WhoIs)"), "gtk-info",
717 _("_Add to Friends List"), "gtk-add",
718 _("O_perator Actions"),
737 list_loadconf ("popup.conf", &popup_list
, buf
);
739 snprintf (buf
, sizeof (buf
),
740 "NAME %s\n" "CMD part\n\n"
741 "NAME %s\n" "CMD getstr # join \"%s\"\n\n"
742 "NAME %s\n" "CMD quote LINKS\n\n"
743 "NAME %s\n" "CMD ping\n\n"
744 "NAME TOGGLE %s\n" "CMD irc_hide_version\n\n",
746 _("Join Channel..."),
747 _("Enter Channel to Join:"),
751 list_loadconf ("usermenu.conf", &usermenu_list
, buf
);
753 snprintf (buf
, sizeof (buf
),
754 "NAME %s\n" "CMD op %%a\n\n"
755 "NAME %s\n" "CMD deop %%a\n\n"
756 "NAME %s\n" "CMD ban %%s\n\n"
757 "NAME %s\n" "CMD getstr \"%s\" \"kick %%s\" \"%s\"\n\n"
758 "NAME %s\n" "CMD send %%s\n\n"
759 "NAME %s\n" "CMD query %%s\n\n",
765 _("Enter reason to kick %s:"),
768 list_loadconf ("buttons.conf", &button_list
, buf
);
770 snprintf (buf
, sizeof (buf
),
771 "NAME %s\n" "CMD whois %%s %%s\n\n"
772 "NAME %s\n" "CMD send %%s\n\n"
773 "NAME %s\n" "CMD dcc chat %%s\n\n"
774 "NAME %s\n" "CMD clear\n\n"
775 "NAME %s\n" "CMD ping %%s\n\n",
781 list_loadconf ("dlgbuttons.conf", &dlgbutton_list
, buf
);
783 list_loadconf ("tabmenu.conf", &tabmenu_list
, NULL
);
784 list_loadconf ("ctcpreply.conf", &ctcp_list
, defaultconf_ctcp
);
785 list_loadconf ("commands.conf", &command_list
, defaultconf_commands
);
786 list_loadconf ("replace.conf", &replace_list
, defaultconf_replace
);
787 list_loadconf ("urlhandlers.conf", &urlhandler_list
,
788 defaultconf_urlhandlers
);
790 servlist_init (); /* load server list */
792 /* if we got a URL, don't open the server list GUI */
793 if (!prefs
.slist_skip
&& !arg_url
)
794 fe_serverlist_open (NULL
);
796 /* turned OFF via -a arg */
797 if (!arg_dont_autoconnect
)
799 /* do any auto connects */
800 if (!servlist_have_auto ()) /* if no new windows open .. */
802 /* and no serverlist gui ... */
803 if (prefs
.slist_skip
|| arg_url
)
804 /* we'll have to open one. */
805 new_ircwindow (NULL
, NULL
, SESS_SERVER
, 0);
808 fe_idle_add (xchat_auto_connect
, NULL
);
812 if (prefs
.slist_skip
|| arg_url
)
813 new_ircwindow (NULL
, NULL
, SESS_SERVER
, 0);
820 xchat_is_quitting
= TRUE
;
821 in_xchat_exit
= TRUE
;
827 if (prefs
.save_pevents
)
830 if (prefs
.autosave_url
)
842 child_handler (gpointer userdata
)
844 int pid
= GPOINTER_TO_INT (userdata
);
846 if (waitpid (pid
, 0, WNOHANG
) == pid
)
847 return 0; /* remove timeout handler */
848 return 1; /* keep the timeout handler */
852 xchat_exec (const char *cmd
)
854 int pid
= util_exec (cmd
);
856 /* zombie avoiding system. Don't ask! it has to be like this to work
857 with zvt (which overrides the default handler) */
858 fe_timeout_add (1000, child_handler
, GINT_TO_POINTER (pid
));
862 xchat_execv (char * const argv
[])
864 int pid
= util_execv (argv
);
866 /* zombie avoiding system. Don't ask! it has to be like this to work
867 with zvt (which overrides the default handler) */
868 fe_timeout_add (1000, child_handler
, GINT_TO_POINTER (pid
));
872 main (int argc
, char *argv
[])
876 srand (time (0)); /* CL: do this only once! */
882 ret
= fe_args (argc
, argv
);
893 libproxy_factory
= px_proxy_factory_new();
903 px_proxy_factory_free(libproxy_factory
);
908 _SSL_context_free (ctx
);