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 */
251 sess
->done_away_check
= FALSE
;
263 xchat_misc_checks (void) /* this gets called every 1/2 second */
265 static int count
= 0;
267 static int count2
= 0;
272 lagcheck_update (); /* every 500ms */
275 dcc_check_timeouts (); /* every 1 second */
277 if (count
>= 60) /* every 30 seconds */
286 if (count2
>= 720) /* 720 every 6 minutes */
288 msproxy_keepalive ();
296 /* executed when the first irc window opens */
299 irc_init (session
*sess
)
301 static int done_init
= FALSE
;
309 plugin_add (sess
, NULL
, NULL
, timer_plugin_init
, NULL
, NULL
, FALSE
);
312 if (!arg_skip_plugins
)
313 plugin_auto_load (sess
); /* autoload ~/.xchat *.so */
317 plugin_add (sess
, NULL
, NULL
, dbus_plugin_init
, NULL
, NULL
, FALSE
);
320 if (prefs
.notify_timeout
)
321 notify_tag
= fe_timeout_add (prefs
.notify_timeout
* 1000,
322 notify_checklist
, 0);
324 fe_timeout_add (prefs
.away_timeout
* 1000, away_check
, 0);
325 fe_timeout_add (500, xchat_misc_checks
, 0);
329 snprintf (buf
, sizeof (buf
), "server %s", arg_url
);
330 handle_command (sess
, buf
, FALSE
);
331 g_free (arg_url
); /* from GOption */
334 if (arg_command
!= NULL
)
336 g_free (arg_command
);
339 /* load -e ~/.xchat2/startup.txt */
340 snprintf (buf
, sizeof (buf
), "%s/%s", get_xdir_fs (), "startup.txt");
341 load_perform_file (sess
, buf
);
345 session_new (server
*serv
, char *from
, int type
, int focus
)
349 sess
= malloc (sizeof (struct session
));
350 memset (sess
, 0, sizeof (struct session
));
357 sess
->alert_beep
= SET_DEFAULT
;
358 sess
->alert_taskbar
= SET_DEFAULT
;
359 sess
->alert_tray
= SET_DEFAULT
;
361 sess
->text_hidejoinpart
= SET_DEFAULT
;
362 sess
->text_logging
= SET_DEFAULT
;
363 sess
->text_scrollback
= SET_DEFAULT
;
366 safe_strcpy (sess
->channel
, from
, CHANLEN
);
368 sess_list
= g_slist_prepend (sess_list
, sess
);
370 fe_new_window (sess
, focus
);
376 new_ircwindow (server
*serv
, char *name
, int type
, int focus
)
383 serv
= server_new ();
384 if (prefs
.use_server_tab
)
385 sess
= session_new (serv
, name
, SESS_SERVER
, focus
);
387 sess
= session_new (serv
, name
, SESS_CHANNEL
, focus
);
388 serv
->server_session
= sess
;
389 serv
->front_session
= sess
;
392 sess
= session_new (serv
, name
, type
, focus
);
393 log_open_or_close (sess
);
396 /* case SESS_CHANNEL:
398 case SESS_SNOTICES:*/
399 sess
= session_new (serv
, name
, type
, focus
);
404 scrollback_load (sess
);
406 plugin_emit_dummy_print (sess
, "Open Context");
412 exec_notify_kill (session
* sess
)
415 if (sess
->running_exec
!= NULL
)
417 re
= sess
->running_exec
;
418 sess
->running_exec
= NULL
;
419 kill (re
->childpid
, SIGKILL
);
420 waitpid (re
->childpid
, NULL
, WNOHANG
);
421 fe_input_remove (re
->iotag
);
430 send_quit_or_part (session
* killsess
)
435 server
*killserv
= killsess
->server
;
437 /* check if this is the last session using this server */
441 sess
= (session
*) list
->data
;
442 if (sess
->server
== killserv
&& sess
!= killsess
)
450 if (xchat_is_quitting
)
453 if (killserv
->connected
)
457 if (!killserv
->sent_quit
)
459 killserv
->flush_queue (killserv
);
460 server_sendquit (killsess
);
461 killserv
->sent_quit
= TRUE
;
465 if (killsess
->type
== SESS_CHANNEL
&& killsess
->channel
[0] &&
466 !killserv
->sent_quit
)
468 server_sendpart (killserv
, killsess
->channel
, 0);
475 session_free (session
*killsess
)
477 server
*killserv
= killsess
->server
;
481 plugin_emit_dummy_print (killsess
, "Close Context");
483 if (current_tab
== killsess
)
486 if (killserv
->server_session
== killsess
)
487 killserv
->server_session
= NULL
;
489 if (killserv
->front_session
== killsess
)
491 /* front_session is closed, find a valid replacement */
492 killserv
->front_session
= NULL
;
496 sess
= (session
*) list
->data
;
497 if (sess
!= killsess
&& sess
->server
== killserv
)
499 killserv
->front_session
= sess
;
500 if (!killserv
->server_session
)
501 killserv
->server_session
= sess
;
508 if (!killserv
->server_session
)
509 killserv
->server_session
= killserv
->front_session
;
511 sess_list
= g_slist_remove (sess_list
, killsess
);
513 if (killsess
->type
== SESS_CHANNEL
)
514 userlist_free (killsess
);
516 exec_notify_kill (killsess
);
518 log_close (killsess
);
519 scrollback_close (killsess
);
520 chanopt_save (killsess
);
522 send_quit_or_part (killsess
);
524 history_free (&killsess
->history
);
526 free (killsess
->topic
);
527 if (killsess
->current_modes
)
528 free (killsess
->current_modes
);
530 fe_session_callback (killsess
);
532 if (current_sess
== killsess
)
536 current_sess
= sess_list
->data
;
541 if (!sess_list
&& !in_xchat_exit
)
542 xchat_exit (); /* sess_list is empty, quit! */
547 sess
= (session
*) list
->data
;
548 if (sess
->server
== killserv
)
549 return; /* this server is still being used! */
553 server_free (killserv
);
559 GSList
*list
= sess_list
;
563 fe_close_window (list
->data
);
569 static char defaultconf_ctcp
[] =
570 "NAME TIME\n" "CMD nctcp %s TIME %t\n\n"\
571 "NAME PING\n" "CMD nctcp %s PING %d\n\n";
573 static char defaultconf_replace
[] =
574 "NAME teh\n" "CMD the\n\n";
575 /* "NAME r\n" "CMD are\n\n"\
576 "NAME u\n" "CMD you\n\n"*/
578 static char defaultconf_commands
[] =
579 "NAME ACTION\n" "CMD me &2\n\n"\
580 "NAME AME\n" "CMD allchan me &2\n\n"\
581 "NAME ANICK\n" "CMD allserv nick &2\n\n"\
582 "NAME AMSG\n" "CMD allchan say &2\n\n"\
583 "NAME BANLIST\n" "CMD quote MODE %c +b\n\n"\
584 "NAME CHAT\n" "CMD dcc chat %2\n\n"\
585 "NAME DIALOG\n" "CMD query %2\n\n"\
586 "NAME DMSG\n" "CMD msg =%2 &3\n\n"\
587 "NAME EXIT\n" "CMD quit\n\n"\
588 "NAME GREP\n" "CMD lastlog -r &2\n\n"\
589 "NAME J\n" "CMD join &2\n\n"\
590 "NAME KILL\n" "CMD quote KILL %2 :&3\n\n"\
591 "NAME LEAVE\n" "CMD part &2\n\n"\
592 "NAME M\n" "CMD msg &2\n\n"\
593 "NAME ONOTICE\n" "CMD notice @%c &2\n\n"\
594 "NAME RAW\n" "CMD quote &2\n\n"\
595 "NAME SERVHELP\n" "CMD quote HELP\n\n"\
596 "NAME SPING\n" "CMD ping\n\n"\
597 "NAME SQUERY\n" "CMD quote SQUERY %2 :&3\n\n"\
598 "NAME SSLSERVER\n" "CMD server -ssl &2\n\n"\
599 "NAME SV\n" "CMD echo xchat %v %m\n\n"\
600 "NAME UMODE\n" "CMD mode %n &2\n\n"\
601 "NAME UPTIME\n" "CMD quote STATS u\n\n"\
602 "NAME VER\n" "CMD ctcp %2 VERSION\n\n"\
603 "NAME VERSION\n" "CMD ctcp %2 VERSION\n\n"\
604 "NAME WALLOPS\n" "CMD quote WALLOPS :&2\n\n"\
605 "NAME WII\n" "CMD quote WHOIS %2 %2\n\n";
607 static char defaultconf_urlhandlers
[] =
608 "NAME Open Link in Opera\n" "CMD !opera -remote 'openURL(%s)'\n\n";
611 /* Close and open log files on SIGUSR1. Usefull for log rotating */
614 sigusr1_handler (int signal
, siginfo_t
*si
, void *un
)
616 GSList
*list
= sess_list
;
622 log_open_or_close (sess
);
627 /* Execute /SIGUSR2 when SIGUSR2 received */
630 sigusr2_handler (int signal
, siginfo_t
*si
, void *un
)
632 session
*sess
= current_sess
;
635 handle_command (sess
, "SIGUSR2", FALSE
);
640 xchat_auto_connect (gpointer userdata
)
642 servlist_auto_connect (NULL
);
650 const char *cs
= NULL
;
653 struct sigaction act
;
655 /* ignore SIGPIPE's */
656 act
.sa_handler
= SIG_IGN
;
658 sigemptyset (&act
.sa_mask
);
659 sigaction (SIGPIPE
, &act
, NULL
);
661 /* Deal with SIGUSR1's & SIGUSR2's */
662 act
.sa_sigaction
= sigusr1_handler
;
664 sigemptyset (&act
.sa_mask
);
665 sigaction (SIGUSR1
, &act
, NULL
);
667 act
.sa_sigaction
= sigusr2_handler
;
669 sigemptyset (&act
.sa_mask
);
670 sigaction (SIGUSR2
, &act
, NULL
);
672 /* good enough for these old systems */
673 signal (SIGPIPE
, SIG_IGN
);
676 if (g_get_charset (&cs
))
677 prefs
.utf8_locale
= TRUE
;
684 snprintf (buf
, sizeof (buf
),
685 "NAME %s~%s~\n" "CMD query %%s\n\n"\
686 "NAME %s~%s~\n" "CMD send %%s\n\n"\
687 "NAME %s~%s~\n" "CMD whois %%s %%s\n\n"\
688 "NAME %s~%s~\n" "CMD notify -n ASK %%s\n\n"\
690 "NAME SUB\n" "CMD %s\n\n"\
691 "NAME %s\n" "CMD op %%a\n\n"\
692 "NAME %s\n" "CMD deop %%a\n\n"\
693 "NAME SEP\n" "CMD \n\n"\
694 "NAME %s\n" "CMD voice %%a\n\n"\
695 "NAME %s\n" "CMD devoice %%a\n"\
696 "NAME SEP\n" "CMD \n\n"\
697 "NAME SUB\n" "CMD %s\n\n"\
698 "NAME %s\n" "CMD kick %%s\n\n"\
699 "NAME %s\n" "CMD ban %%s\n\n"\
700 "NAME SEP\n" "CMD \n\n"\
701 "NAME %s *!*@*.host\n""CMD ban %%s 0\n\n"\
702 "NAME %s *!*@domain\n""CMD ban %%s 1\n\n"\
703 "NAME %s *!*user@*.host\n""CMD ban %%s 2\n\n"\
704 "NAME %s *!*user@domain\n""CMD ban %%s 3\n\n"\
705 "NAME SEP\n" "CMD \n\n"\
706 "NAME %s *!*@*.host\n""CMD kickban %%s 0\n\n"\
707 "NAME %s *!*@domain\n""CMD kickban %%s 1\n\n"\
708 "NAME %s *!*user@*.host\n""CMD kickban %%s 2\n\n"\
709 "NAME %s *!*user@domain\n""CMD kickban %%s 3\n\n"\
710 "NAME ENDSUB\n" "CMD \n\n"\
711 "NAME ENDSUB\n" "CMD \n\n",
713 _("_Open Dialog Window"), "xchat-dialog",
714 _("_Send a File"), "gtk-floppy",
715 _("_User Info (WhoIs)"), "gtk-info",
716 _("_Add to Friends List"), "gtk-add",
717 _("O_perator Actions"),
736 list_loadconf ("popup.conf", &popup_list
, buf
);
738 snprintf (buf
, sizeof (buf
),
739 "NAME %s\n" "CMD part\n\n"
740 "NAME %s\n" "CMD getstr # join \"%s\"\n\n"
741 "NAME %s\n" "CMD quote LINKS\n\n"
742 "NAME %s\n" "CMD ping\n\n"
743 "NAME TOGGLE %s\n" "CMD irc_hide_version\n\n",
745 _("Join Channel..."),
746 _("Enter Channel to Join:"),
750 list_loadconf ("usermenu.conf", &usermenu_list
, buf
);
752 snprintf (buf
, sizeof (buf
),
753 "NAME %s\n" "CMD op %%a\n\n"
754 "NAME %s\n" "CMD deop %%a\n\n"
755 "NAME %s\n" "CMD ban %%s\n\n"
756 "NAME %s\n" "CMD getstr \"%s\" \"kick %%s\" \"%s\"\n\n"
757 "NAME %s\n" "CMD send %%s\n\n"
758 "NAME %s\n" "CMD query %%s\n\n",
764 _("Enter reason to kick %s:"),
767 list_loadconf ("buttons.conf", &button_list
, buf
);
769 snprintf (buf
, sizeof (buf
),
770 "NAME %s\n" "CMD whois %%s %%s\n\n"
771 "NAME %s\n" "CMD send %%s\n\n"
772 "NAME %s\n" "CMD dcc chat %%s\n\n"
773 "NAME %s\n" "CMD clear\n\n"
774 "NAME %s\n" "CMD ping %%s\n\n",
780 list_loadconf ("dlgbuttons.conf", &dlgbutton_list
, buf
);
782 list_loadconf ("tabmenu.conf", &tabmenu_list
, NULL
);
783 list_loadconf ("ctcpreply.conf", &ctcp_list
, defaultconf_ctcp
);
784 list_loadconf ("commands.conf", &command_list
, defaultconf_commands
);
785 list_loadconf ("replace.conf", &replace_list
, defaultconf_replace
);
786 list_loadconf ("urlhandlers.conf", &urlhandler_list
,
787 defaultconf_urlhandlers
);
789 servlist_init (); /* load server list */
791 /* if we got a URL, don't open the server list GUI */
792 if (!prefs
.slist_skip
&& !arg_url
)
793 fe_serverlist_open (NULL
);
795 /* turned OFF via -a arg */
796 if (!arg_dont_autoconnect
)
798 /* do any auto connects */
799 if (!servlist_have_auto ()) /* if no new windows open .. */
801 /* and no serverlist gui ... */
802 if (prefs
.slist_skip
|| arg_url
)
803 /* we'll have to open one. */
804 new_ircwindow (NULL
, NULL
, SESS_SERVER
, 0);
807 fe_idle_add (xchat_auto_connect
, NULL
);
811 if (prefs
.slist_skip
|| arg_url
)
812 new_ircwindow (NULL
, NULL
, SESS_SERVER
, 0);
819 xchat_is_quitting
= TRUE
;
820 in_xchat_exit
= TRUE
;
826 if (prefs
.save_pevents
)
829 if (prefs
.autosave_url
)
841 child_handler (gpointer userdata
)
843 int pid
= GPOINTER_TO_INT (userdata
);
845 if (waitpid (pid
, 0, WNOHANG
) == pid
)
846 return 0; /* remove timeout handler */
847 return 1; /* keep the timeout handler */
851 xchat_exec (const char *cmd
)
853 int pid
= util_exec (cmd
);
855 /* zombie avoiding system. Don't ask! it has to be like this to work
856 with zvt (which overrides the default handler) */
857 fe_timeout_add (1000, child_handler
, GINT_TO_POINTER (pid
));
861 xchat_execv (char * const argv
[])
863 int pid
= util_execv (argv
);
865 /* zombie avoiding system. Don't ask! it has to be like this to work
866 with zvt (which overrides the default handler) */
867 fe_timeout_add (1000, child_handler
, GINT_TO_POINTER (pid
));
871 main (int argc
, char *argv
[])
875 srand (time (0)); /* CL: do this only once! */
881 ret
= fe_args (argc
, argv
);
892 libproxy_factory
= px_proxy_factory_new();
902 px_proxy_factory_free(libproxy_factory
);
907 _SSL_context_free (ctx
);