summary refs log blame commit diff stats
path: root/plugins/perl/lib/Xchat.pm
blob: cb1dc3d65b7839eda2e81f4c66ef5d910cb664c5 (plain) (tree)










































































































































                                                                                             
                                                               






















                                                                              
                                                                           













































                                                                                                                                                           

                                                

                                                 





                                                                            


                                                                                     

                                                                                  



                                                                                               
                                                                                   









                                                          
                                                             
































                                                                                       
                                               



















                                                                                   

                        




















































































































































































































                                                                                      
$SIG{__WARN__} = sub {
	my $message = shift @_;
	my ($package) = caller;
	
	# redirect Gtk/Glib errors and warnings back to STDERR
	my $message_levels =	qr/ERROR|CRITICAL|WARNING|MESSAGE|INFO|DEBUG/i;
	if( $message =~ /^(?:Gtk|GLib|Gdk)(?:-\w+)?-$message_levels/i ) {
		print STDERR $message;
	} else {

		if( defined &Xchat::Internal::print ) {
			Xchat::print( $message );
		} else {
			warn $message;
		}
	}
};

use File::Spec ();
use File::Basename ();
use File::Glob ();
use List::Util ();
use Symbol();
use Time::HiRes ();
use Carp ();

package Xchat;
use base qw(Exporter);
use strict;
use warnings;

sub PRI_HIGHEST ();
sub PRI_HIGH ();
sub PRI_NORM ();
sub PRI_LOW ();
sub PRI_LOWEST ();

sub EAT_NONE ();
sub EAT_XCHAT ();
sub EAT_PLUIN ();
sub EAT_ALL ();

sub KEEP ();
sub REMOVE ();
sub FD_READ ();
sub FD_WRITE ();
sub FD_EXCEPTION ();
sub FD_NOTSOCKET ();

sub get_context;
sub Xchat::Internal::context_info;
sub Xchat::Internal::print;

our %EXPORT_TAGS = (
	constants => [
		qw(PRI_HIGHEST PRI_HIGH PRI_NORM PRI_LOW PRI_LOWEST), # priorities
		qw(EAT_NONE EAT_XCHAT EAT_PLUGIN EAT_ALL), # callback return values
		qw(FD_READ FD_WRITE FD_EXCEPTION FD_NOTSOCKET), # fd flags
		qw(KEEP REMOVE), # timers
	],
	hooks => [
		qw(hook_server hook_command hook_print hook_timer hook_fd unhook),
	],
	util => [
		qw(register nickcmp strip_code send_modes), # misc
		qw(print prnt printf prntf command commandf emit_print), # output
		qw(find_context get_context set_context), # context
		qw(get_info get_prefs get_list context_info user_info), # input
	],
);

$EXPORT_TAGS{all} = [ map { @{$_} } @EXPORT_TAGS{qw(constants hooks util)}];
our @EXPORT = @{$EXPORT_TAGS{constants}};
our @EXPORT_OK = @{$EXPORT_TAGS{all}};

sub register {
	my $package = Xchat::Embed::find_pkg();
	my $pkg_info = Xchat::Embed::pkg_info( $package );
	my $filename = $pkg_info->{filename};
	my ($name, $version, $description, $callback) = @_;
	
	if( defined $pkg_info->{gui_entry} ) {
		Xchat::print( "Xchat::register called more than once in "
			. $pkg_info->{filename} );
		return ();
	}
	
	$description = "" unless defined $description;
	$pkg_info->{shutdown} = $callback;
	unless( $name && $name =~ /[[:print:]\w]/ ) {
		$name = "Not supplied";
	}
	unless( $version && $version =~ /\d+(?:\.\d+)?/ ) {
		$version = "NaN";
	}
	$pkg_info->{gui_entry} =
		Xchat::Internal::register( $name, $version, $description, $filename );
	# keep with old behavior
	return ();
}

sub _process_hook_options {
	my ($options, $keys, $store) = @_;

	unless( @$keys == @$store ) {
		die 'Number of keys must match the size of the store';
	}

	my @results;

	if( ref( $options ) eq 'HASH' ) {
		for my $index ( 0 .. @$keys - 1 ) {
			my $key = $keys->[$index];
			if( exists( $options->{ $key } ) && defined( $options->{ $key } ) ) {
				${$store->[$index]} = $options->{ $key };
			}
		}
	}

}

sub hook_server {
	return undef unless @_ >= 2;
	my $message = shift;
	my $callback = shift;
	my $options = shift;
	my $package = Xchat::Embed::find_pkg();
	
	$callback = Xchat::Embed::fix_callback( $package, $callback );
	
	my ($priority, $data) = ( Xchat::PRI_NORM, undef );
	_process_hook_options(
		$options,
		[qw(priority data)],
		[\($priority, $data)],
	);
	
	my $pkg_info = Xchat::Embed::pkg_info( $package );
	my $hook = Xchat::Internal::hook_server(
		$message, $priority, $callback, $data, $package
	);
	push @{$pkg_info->{hooks}}, $hook if defined $hook;
	return $hook;
}

sub hook_command {
	return undef unless @_ >= 2;
	my $command = shift;
	my $callback = shift;
	my $options = shift;
	my $package = Xchat::Embed::find_pkg();

	$callback = Xchat::Embed::fix_callback( $package, $callback );
	
	my ($priority, $help_text, $data) = ( Xchat::PRI_NORM, undef, undef );
	_process_hook_options(
		$options,
		[qw(priority help_text data)],
		[\($priority, $help_text, $data)],
	);
	
	my $pkg_info = Xchat::Embed::pkg_info( $package );
	my $hook = Xchat::Internal::hook_command(
		$command, $priority, $callback, $help_text, $data, $package
	);
	push @{$pkg_info->{hooks}}, $hook if defined $hook;
	return $hook;
}

sub hook_print {
	return undef unless @_ >= 2;
	my $event = shift;
	my $callback = shift;
	my $options = shift;
	my $package = Xchat::Embed::find_pkg();

	$callback = Xchat::Embed::fix_callback( $package, $callback );
	
	my ($priority, $run_after, $filter, $data) = ( Xchat::PRI_NORM, 0, 0, undef );
	_process_hook_options(
		$options,
		[qw(priority run_after_event filter data)],
		[\($priority, $run_after, $filter, $data)],
	);
	
	if( $run_after and $filter ) {
		Carp::carp( "Xchat::hook_print's run_after_event and filter options are mutually exclusive, you can only use of them at a time per hook" );
		return;
	}

	if( $run_after ) {
		my $cb = $callback;
		$callback = sub {
			my @args = @_;
			hook_timer( 0, sub {
				$cb->( @args );

				if( ref $run_after eq 'CODE' ) {
					$run_after->( @args );
				}
				return REMOVE;
			});
			return EAT_NONE;
		};
	}

	if( $filter ) {
		my $cb = $callback;
		$callback = sub {
			my @args = @{$_[0]};
			my $event_data = $_[1];
			my $event_name = $event;
			my $last_arg = @args - 1;

			my @new = $cb->( \@args, $event_data, $event_name );

			# allow changing event by returning the new value
			if( @new > @args ) {
				$event_name = pop @new;
			}

			# a filter can either return the new results or it can modify
			# @_ in place. 
			if( @new == @args ) {
				emit_print( $event_name, @new[ 0 .. $last_arg ] );
				return EAT_ALL;
			} elsif(
				join( "\0", @{$_[0]} ) ne join( "\0", @args[ 0 .. $last_arg ] )
			) {
				emit_print( $event_name, @args[ 0 .. $last_arg ] );
				return EAT_ALL;
			}

			return EAT_NONE;
		};

	}

	my $pkg_info = Xchat::Embed::pkg_info( $package );
	my $hook = Xchat::Internal::hook_print(
		$event, $priority, $callback, $data, $package
	);
	push @{$pkg_info->{hooks}}, $hook if defined $hook;
	return $hook;
}

sub hook_timer {
	return undef unless @_ >= 2;
	my ($timeout, $callback, $data) = @_;
	my $package = Xchat::Embed::find_pkg();

	$callback = Xchat::Embed::fix_callback( $package, $callback );

	if(
		ref( $data ) eq 'HASH' && exists( $data->{data} )
		&& defined( $data->{data} )
	) {
		$data = $data->{data};
	}
	
	my $pkg_info = Xchat::Embed::pkg_info( $package );
	my $hook = Xchat::Internal::hook_timer( $timeout, $callback, $data, $package );
	push @{$pkg_info->{hooks}}, $hook if defined $hook;
	return $hook;
}

sub hook_fd {
	return undef unless @_ >= 2;
	my ($fd, $callback, $options) = @_;
	return undef unless defined $fd && defined $callback;

	my $fileno = fileno $fd;
	return undef unless defined $fileno; # no underlying fd for this handle
	
	my $package = Xchat::Embed::find_pkg();
	$callback = Xchat::Embed::fix_callback( $package, $callback );
	
	my ($flags, $data) = (Xchat::FD_READ, undef);
	_process_hook_options(
		$options,
		[qw(flags data)],
		[\($flags, $data)],
	);
	
	my $cb = sub {
		my $userdata = shift;
		return $userdata->{CB}->(
			$userdata->{FD}, $userdata->{FLAGS}, $userdata->{DATA},
		);
	};
	
	my $pkg_info = Xchat::Embed::pkg_info( $package );
	my $hook = Xchat::Internal::hook_fd(
		$fileno, $cb, $flags, {
			DATA => $data, FD => $fd, CB => $callback, FLAGS => $flags,
		},
		$package
	);
	push @{$pkg_info->{hooks}}, $hook if defined $hook;
	return $hook;
}

sub unhook {
	my $hook = shift @_;
	my $package = shift @_;
	($package) = caller unless $package;
	my $pkg_info = Xchat::Embed::pkg_info( $package );

	if( defined( $hook )
		&& $hook =~ /^\d+$/
		&& grep { $_ == $hook } @{$pkg_info->{hooks}} ) {
		$pkg_info->{hooks} = [grep { $_ != $hook } @{$pkg_info->{hooks}}];
		return Xchat::Internal::unhook( $hook );
	}
	return ();
}

sub _do_for_each {
	my ($cb, $channels, $servers) = @_;

	# not specifying any channels or servers is not the same as specifying
	# undef for both
	# - not specifying either results in calling the callback inthe current ctx
	# - specifying undef for for both results in calling the callback in the
	#   front/currently selected tab
	if( @_ == 3 && !($channels || $servers) ) { 
		$channels = [ undef ];
		$servers = [ undef ];
	} elsif( !($channels || $servers) ) {
		$cb->();
		return 1;
	}

	$channels = [ $channels ] unless ref( $channels ) eq 'ARRAY';

	if( $servers ) {
		$servers = [ $servers ] unless ref( $servers ) eq 'ARRAY';
	} else {
		$servers = [ undef ];
	}

	my $num_done = 0;
	my $old_ctx = Xchat::get_context();
	for my $server ( @$servers ) {
		for my $channel ( @$channels ) {
			if( Xchat::set_context( $channel, $server ) ) {
				$cb->();
				$num_done++
			}
		}
	}
	Xchat::set_context( $old_ctx );
	return $num_done;
}

sub print {
	my $text = shift @_;
	return "" unless defined $text;
	if( ref( $text ) eq 'ARRAY' ) {
		if( $, ) {
			$text = join $, , @$text;
		} else {
			$text = join "", @$text;
		}
	}
	
	return _do_for_each(
		sub { Xchat::Internal::print( $text ); },
		@_
	);
}

sub printf {
	my $format = shift;
	Xchat::print( sprintf( $format, @_ ) );
}

# make Xchat::prnt() and Xchat::prntf() as aliases for Xchat::print() and 
# Xchat::printf(), mainly useful when these functions are exported
sub prnt {
	goto &Xchat::print;
}

sub prntf {
	goto &Xchat::printf;
}

sub command {
	my $command = shift;
	return "" unless defined $command;
	my @commands;
	
	if( ref( $command ) eq 'ARRAY' ) {
		@commands = @$command;
	} else {
		@commands = ($command);
	}
	
	return _do_for_each(
		sub { Xchat::Internal::command( $_ ) foreach @commands },
		@_
	);
}

sub commandf {
	my $format = shift;
	Xchat::command( sprintf( $format, @_ ) );
}

sub set_context {
	my $context;
	if( @_ == 2 ) {
		my ($channel, $server) = @_;
		$context = Xchat::find_context( $channel, $server );
	} elsif( @_ == 1 ) {
		if( defined $_[0] && $_[0] =~ /^\d+$/ ) {
			$context = $_[0];
		} else {
			$context = Xchat::find_context( $_[0] );
		}
	} elsif( @_ == 0 ) {
		$context = Xchat::find_context();
	}
	return $context ? Xchat::Internal::set_context( $context ) : 0;
}

sub get_info {
	my $id = shift;
	my $info;
	
	if( defined( $id ) ) {
		if( grep { $id eq $_ } qw(state_cursor id) ) {
			$info = Xchat::get_prefs( $id );
		} else {
			$info = Xchat::Internal::get_info( $id );
		}
	}
	return $info;
}

sub user_info {
	my $nick = Xchat::strip_code(shift @_ || Xchat::get_info( "nick" ));
	my $user;
	for (Xchat::get_list( "users" ) ) {
		if ( Xchat::nickcmp( $_->{nick}, $nick ) == 0 ) {
			$user = $_;
			last;
		}
	}
	return $user;
}

sub context_info {
	my $ctx = shift @_ || Xchat::get_context;
	my $old_ctx = Xchat::get_context;
	my @fields = (
		qw(away channel charset host id inputbox libdirfs modes network),
		qw(nick nickserv server topic version win_ptr win_status),
		qw(xchatdir xchatdirfs state_cursor),
	);

	if( Xchat::set_context( $ctx ) ) {
		my %info;
		for my $field ( @fields ) {
			$info{$field} = Xchat::get_info( $field );
		}
		
		my $ctx_info = Xchat::Internal::context_info;
		@info{keys %$ctx_info} = values %$ctx_info;
		
		Xchat::set_context( $old_ctx );
		return %info if wantarray;
		return \%info;
	} else {
		return undef;
	}
}

sub get_list {
	unless( grep { $_[0] eq $_ } qw(channels dcc ignore notify users networks) ) {
		Carp::carp( "'$_[0]' does not appear to be a valid list name" );
	}
	if( $_[0] eq 'networks' ) {
		return Xchat::List::Network->get();
	} else {
		return Xchat::Internal::get_list( $_[0] );
	}
}

sub strip_code {
	my $pattern = qr<
		\cB| #Bold
		\cC\d{0,2}(?:,\d{1,2})?| #Color
		\e\[(?:\d{1,2}(?:;\d{1,2})*)?m| # ANSI color code
		\cG| #Beep
		\cO| #Reset
		\cV| #Reverse
		\c_  #Underline
	>x;
		
	if( defined wantarray ) {
		my $msg = shift;
		$msg =~ s/$pattern//g;
		return $msg;
	} else {
		$_[0] =~ s/$pattern//g if defined $_[0];
	}
}

1
n> = NULL; kill (re->childpid, SIGKILL); waitpid (re->childpid, NULL, WNOHANG); fe_input_remove (re->iotag); close (re->myfd); g_free (re->linebuf); g_free (re); } #endif } static void send_quit_or_part (session * killsess) { int willquit = TRUE; GSList *list; session *sess; server *killserv = killsess->server; /* check if this is the last session using this server */ list = sess_list; while (list) { sess = (session *) list->data; if (sess->server == killserv && sess != killsess) { willquit = FALSE; list = 0; } else list = list->next; } if (hexchat_is_quitting) willquit = TRUE; if (killserv->connected) { if (willquit) { if (!killserv->sent_quit) { killserv->flush_queue (killserv); server_sendquit (killsess); killserv->sent_quit = TRUE; } } else { if (killsess->type == SESS_CHANNEL && killsess->channel[0] && !killserv->sent_quit) { server_sendpart (killserv, killsess->channel, 0); } } } } void session_free (session *killsess) { server *killserv = killsess->server; session *sess; GSList *list; int oldidx; plugin_emit_dummy_print (killsess, "Close Context"); if (current_tab == killsess) current_tab = NULL; if (killserv->server_session == killsess) killserv->server_session = NULL; if (killserv->front_session == killsess) { /* front_session is closed, find a valid replacement */ killserv->front_session = NULL; list = sess_list; while (list) { sess = (session *) list->data; if (sess != killsess && sess->server == killserv) { killserv->front_session = sess; if (!killserv->server_session) killserv->server_session = sess; break; } list = list->next; } } if (!killserv->server_session) killserv->server_session = killserv->front_session; sess_list = g_slist_remove (sess_list, killsess); if (killsess->type == SESS_CHANNEL) userlist_free (killsess); oldidx = killsess->lastact_idx; if (oldidx != LACT_NONE) sess_list_by_lastact[oldidx] = g_list_remove(sess_list_by_lastact[oldidx], killsess); exec_notify_kill (killsess); log_close (killsess); scrollback_close (killsess); chanopt_save (killsess); send_quit_or_part (killsess); history_free (&killsess->history); g_free (killsess->topic); g_free (killsess->current_modes); fe_session_callback (killsess); if (current_sess == killsess) { current_sess = NULL; if (sess_list) current_sess = sess_list->data; } g_free (killsess); if (!sess_list && !in_hexchat_exit) hexchat_exit (); /* sess_list is empty, quit! */ list = sess_list; while (list) { sess = (session *) list->data; if (sess->server == killserv) return; /* this server is still being used! */ list = list->next; } server_free (killserv); } static void free_sessions (void) { GSList *list = sess_list; while (list) { fe_close_window (list->data); list = sess_list; } } static char defaultconf_ctcp[] = "NAME TIME\n" "CMD nctcp %s TIME %t\n\n"\ "NAME PING\n" "CMD nctcp %s PING %d\n\n"; static char defaultconf_replace[] = "NAME teh\n" "CMD the\n\n"; /* "NAME r\n" "CMD are\n\n"\ "NAME u\n" "CMD you\n\n"*/ static char defaultconf_commands[] = "NAME ACTION\n" "CMD me &2\n\n"\ "NAME AME\n" "CMD allchan me &2\n\n"\ "NAME ANICK\n" "CMD allserv nick &2\n\n"\ "NAME AMSG\n" "CMD allchan say &2\n\n"\ "NAME BANLIST\n" "CMD quote MODE %c +b\n\n"\ "NAME CHAT\n" "CMD dcc chat %2\n\n"\ "NAME DIALOG\n" "CMD query %2\n\n"\ "NAME DMSG\n" "CMD msg =%2 &3\n\n"\ "NAME EXIT\n" "CMD quit\n\n"\ "NAME GREP\n" "CMD lastlog -r -- &2\n\n"\ "NAME IGNALL\n" "CMD ignore %2!*@* ALL\n\n"\ "NAME J\n" "CMD join &2\n\n"\ "NAME KILL\n" "CMD quote KILL %2 :&3\n\n"\ "NAME LEAVE\n" "CMD part &2\n\n"\ "NAME M\n" "CMD msg &2\n\n"\ "NAME OMSG\n" "CMD msg @%c &2\n\n"\ "NAME ONOTICE\n" "CMD notice @%c &2\n\n"\ "NAME RAW\n" "CMD quote &2\n\n"\ "NAME SERVHELP\n" "CMD quote HELP\n\n"\ "NAME SPING\n" "CMD ping\n\n"\ "NAME SQUERY\n" "CMD quote SQUERY %2 :&3\n\n"\ "NAME SSLSERVER\n" "CMD server -ssl &2\n\n"\ "NAME SV\n" "CMD echo HexChat %v %m\n\n"\ "NAME UMODE\n" "CMD mode %n &2\n\n"\ "NAME UPTIME\n" "CMD quote STATS u\n\n"\ "NAME VER\n" "CMD ctcp %2 VERSION\n\n"\ "NAME VERSION\n" "CMD ctcp %2 VERSION\n\n"\ "NAME WALLOPS\n" "CMD quote WALLOPS :&2\n\n"\ "NAME WI\n" "CMD quote WHOIS %2\n\n"\ "NAME WII\n" "CMD quote WHOIS %2 %2\n\n"; static char defaultconf_urlhandlers[] = "NAME Open Link in a new Firefox Window\n" "CMD !firefox -new-window %s\n\n"; #ifdef USE_SIGACTION /* Close and open log files on SIGUSR1. Usefull for log rotating */ static void sigusr1_handler (int signal, siginfo_t *si, void *un) { GSList *list = sess_list; session *sess; while (list) { sess = list->data; log_open_or_close (sess); list = list->next; } } /* Execute /SIGUSR2 when SIGUSR2 received */ static void sigusr2_handler (int signal, siginfo_t *si, void *un) { session *sess = current_sess; if (sess) handle_command (sess, "SIGUSR2", FALSE); } #endif static gint xchat_auto_connect (gpointer userdata) { servlist_auto_connect (NULL); return 0; } static void xchat_init (void) { char buf[3068]; #ifdef WIN32 WSADATA wsadata; if (WSAStartup(0x0202, &wsadata) != 0) { MessageBox (NULL, "Cannot find winsock 2.2+", "Error", MB_OK); exit (0); } #endif /* !WIN32 */ #ifdef USE_SIGACTION struct sigaction act; /* ignore SIGPIPE's */ act.sa_handler = SIG_IGN; act.sa_flags = 0; sigemptyset (&act.sa_mask); sigaction (SIGPIPE, &act, NULL); /* Deal with SIGUSR1's & SIGUSR2's */ act.sa_sigaction = sigusr1_handler; act.sa_flags = 0; sigemptyset (&act.sa_mask); sigaction (SIGUSR1, &act, NULL); act.sa_sigaction = sigusr2_handler; act.sa_flags = 0; sigemptyset (&act.sa_mask); sigaction (SIGUSR2, &act, NULL); #else #ifndef WIN32 /* good enough for these old systems */ signal (SIGPIPE, SIG_IGN); #endif #endif load_text_events (); sound_load (); notify_load (); ignore_load (); g_snprintf (buf, sizeof (buf), "NAME %s~%s~\n" "CMD query %%s\n\n"\ "NAME %s~%s~\n" "CMD send %%s\n\n"\ "NAME %s~%s~\n" "CMD whois %%s %%s\n\n"\ "NAME %s~%s~\n" "CMD notify -n ASK %%s\n\n"\ "NAME %s~%s~\n" "CMD ignore %%s!*@* ALL\n\n"\ "NAME SUB\n" "CMD %s\n\n"\ "NAME %s\n" "CMD op %%a\n\n"\ "NAME %s\n" "CMD deop %%a\n\n"\ "NAME SEP\n" "CMD \n\n"\ "NAME %s\n" "CMD voice %%a\n\n"\ "NAME %s\n" "CMD devoice %%a\n"\ "NAME SEP\n" "CMD \n\n"\ "NAME SUB\n" "CMD %s\n\n"\ "NAME %s\n" "CMD kick %%s\n\n"\ "NAME %s\n" "CMD ban %%s\n\n"\ "NAME SEP\n" "CMD \n\n"\ "NAME %s *!*@*.host\n""CMD ban %%s 0\n\n"\ "NAME %s *!*@domain\n""CMD ban %%s 1\n\n"\ "NAME %s *!*user@*.host\n""CMD ban %%s 2\n\n"\ "NAME %s *!*user@domain\n""CMD ban %%s 3\n\n"\ "NAME SEP\n" "CMD \n\n"\ "NAME %s *!*@*.host\n""CMD kickban %%s 0\n\n"\ "NAME %s *!*@domain\n""CMD kickban %%s 1\n\n"\ "NAME %s *!*user@*.host\n""CMD kickban %%s 2\n\n"\ "NAME %s *!*user@domain\n""CMD kickban %%s 3\n\n"\ "NAME ENDSUB\n" "CMD \n\n"\ "NAME ENDSUB\n" "CMD \n\n", _("_Open Dialog Window"), "gtk-go-up", _("_Send a File" ELLIPSIS), "gtk-floppy", _("_User Info (WhoIs)"), "gtk-info", _("_Add to Friends List" ELLIPSIS), "gtk-add", _("_Ignore"), "gtk-stop", _("O_perator Actions"), _("Give Ops"), _("Take Ops"), _("Give Voice"), _("Take Voice"), _("Kick/Ban"), _("Kick"), _("Ban"), _("Ban"), _("Ban"), _("Ban"), _("Ban"), _("KickBan"), _("KickBan"), _("KickBan"), _("KickBan")); list_loadconf ("popup.conf", &popup_list, buf); g_snprintf (buf, sizeof (buf), "NAME %s\n" "CMD part\n\n" "NAME %s\n" "CMD getstr # join \"%s\"\n\n" "NAME %s\n" "CMD quote LINKS\n\n" "NAME %s\n" "CMD ping\n\n" "NAME TOGGLE %s\n" "CMD irc_hide_version\n\n", _("Leave Channel"), _("Join Channel..."), _("Enter Channel to Join:"), _("Server Links"), _("Ping Server"), _("Hide Version")); list_loadconf ("usermenu.conf", &usermenu_list, buf); g_snprintf (buf, sizeof (buf), "NAME %s\n" "CMD op %%a\n\n" "NAME %s\n" "CMD deop %%a\n\n" "NAME %s\n" "CMD ban %%s\n\n" "NAME %s\n" "CMD getstr \"%s\" \"kick %%s\" \"%s\"\n\n" "NAME %s\n" "CMD send %%s\n\n" "NAME %s\n" "CMD query %%s\n\n", _("Op"), _("DeOp"), _("Ban"), _("Kick"), _("bye"), _("Enter reason to kick %s:"), _("Send File"), _("Dialog")); list_loadconf ("buttons.conf", &button_list, buf); g_snprintf (buf, sizeof (buf), "NAME %s\n" "CMD whois %%s %%s\n\n" "NAME %s\n" "CMD send %%s\n\n" "NAME %s\n" "CMD dcc chat %%s\n\n" "NAME %s\n" "CMD clear\n\n" "NAME %s\n" "CMD ping %%s\n\n", _("WhoIs"), _("Send"), _("Chat"), _("Clear"), _("Ping")); list_loadconf ("dlgbuttons.conf", &dlgbutton_list, buf); list_loadconf ("tabmenu.conf", &tabmenu_list, NULL); list_loadconf ("ctcpreply.conf", &ctcp_list, defaultconf_ctcp); list_loadconf ("commands.conf", &command_list, defaultconf_commands); list_loadconf ("replace.conf", &replace_list, defaultconf_replace); list_loadconf ("urlhandlers.conf", &urlhandler_list, defaultconf_urlhandlers); servlist_init (); /* load server list */ /* if we got a URL, don't open the server list GUI */ if (!prefs.hex_gui_slist_skip && !arg_url && !arg_urls) fe_serverlist_open (NULL); /* turned OFF via -a arg or by passing urls */ if (!arg_dont_autoconnect && !arg_urls && !arg_url) { /* do any auto connects */ if (!servlist_have_auto ()) /* if no new windows open .. */ { /* and no serverlist gui ... */ if (prefs.hex_gui_slist_skip || arg_url || arg_urls) /* we'll have to open one. */ new_ircwindow (NULL, NULL, SESS_SERVER, 0); } else { fe_idle_add (xchat_auto_connect, NULL); } } else { if (prefs.hex_gui_slist_skip || arg_url || arg_urls) new_ircwindow (NULL, NULL, SESS_SERVER, 0); } } void hexchat_exit (void) { hexchat_is_quitting = TRUE; in_hexchat_exit = TRUE; plugin_kill_all (); fe_cleanup (); save_config (); if (prefs.save_pevents) { pevent_save (NULL); } sound_save (); notify_save (); ignore_save (); free_sessions (); chanopt_save_all (TRUE); servlist_cleanup (); fe_exit (); } void hexchat_exec (const char *cmd) { util_exec (cmd); } static void set_locale (void) { #ifdef WIN32 char hexchat_lang[13]; /* LC_ALL= plus 5 chars of hex_gui_lang and trailing \0 */ strcpy (hexchat_lang, "LC_ALL="); if (0 <= prefs.hex_gui_lang && prefs.hex_gui_lang < LANGUAGES_LENGTH) strcat (hexchat_lang, languages[prefs.hex_gui_lang]); else strcat (hexchat_lang, "en"); putenv (hexchat_lang); #endif } int main (int argc, char *argv[]) { int i; int ret; #ifdef WIN32 HRESULT coinit_result; #endif srand ((unsigned int) time (NULL)); /* CL: do this only once! */ /* We must check for the config dir parameter, otherwise load_config() will behave incorrectly. * load_config() must come before fe_args() because fe_args() calls gtk_init() which needs to * know the language which is set in the config. The code below is copy-pasted from fe_args() * for the most part. */ if (argc >= 2) { for (i = 1; i < argc; i++) { if ((strcmp (argv[i], "-d") == 0 || strcmp (argv[i], "--cfgdir") == 0) && i + 1 < argc) { xdir = g_strdup (argv[i + 1]); } else if (strncmp (argv[i], "--cfgdir=", 9) == 0) { xdir = g_strdup (argv[i] + 9); } if (xdir != NULL) { if (xdir[strlen (xdir) - 1] == G_DIR_SEPARATOR) { xdir[strlen (xdir) - 1] = 0; } break; } } } #if ! GLIB_CHECK_VERSION (2, 36, 0) g_type_init (); #endif if (check_config_dir () == 0) { if (load_config () != 0) load_default_config (); } else { /* this is probably the first run */ load_default_config (); make_config_dirs (); make_dcc_dirs (); } /* we MUST do this after load_config () AND before fe_init (thus gtk_init) otherwise it will fail */ set_locale (); ret = fe_args (argc, argv); if (ret != -1) return ret; #ifdef USE_DBUS hexchat_remote (); #endif #ifdef USE_LIBPROXY libproxy_factory = px_proxy_factory_new (); #endif #ifdef WIN32 coinit_result = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); if (SUCCEEDED (coinit_result)) { CoInitializeSecurity (NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL); } #endif fe_init (); /* This is done here because cfgfiles.c is too early in * the startup process to use gtk functions. */ if (g_access (get_xdir (), W_OK) != 0) { char buf[2048]; g_snprintf (buf, sizeof(buf), _("You do not have write access to %s. Nothing from this session can be saved."), get_xdir ()); fe_message (buf, FE_MSG_ERROR); } #ifndef WIN32 #ifndef __EMX__ /* OS/2 uses UID 0 all the time */ if (getuid () == 0) fe_message (_("* Running IRC as root is stupid! You should\n" " create a User Account and use that to login.\n"), FE_MSG_WARN|FE_MSG_WAIT); #endif #endif /* !WIN32 */ xchat_init (); fe_main (); #ifdef WIN32 if (SUCCEEDED (coinit_result)) { CoUninitialize (); } #endif #ifdef USE_LIBPROXY px_proxy_factory_free (libproxy_factory); #endif #ifdef WIN32 WSACleanup (); #endif return 0; }