summary refs log tree commit diff stats
path: root/plugins/perl/lib/Xchat
diff options
context:
space:
mode:
authorberkeviktor@aol.com <berkeviktor@aol.com>2011-02-24 04:14:30 +0100
committerberkeviktor@aol.com <berkeviktor@aol.com>2011-02-24 04:14:30 +0100
commit4a6ceffb98a0b785494f680d3776c4bfc4052f9e (patch)
tree850703c1c841ccd99f58d0b06084615aaebe782c /plugins/perl/lib/Xchat
parentf16af8be941b596dedac3bf4e371ee2d21f4b598 (diff)
add xchat r1489
Diffstat (limited to 'plugins/perl/lib/Xchat')
-rw-r--r--plugins/perl/lib/Xchat/Embed.pm253
-rw-r--r--plugins/perl/lib/Xchat/List/Network.pm32
-rw-r--r--plugins/perl/lib/Xchat/List/Network/AutoJoin.pm82
-rw-r--r--plugins/perl/lib/Xchat/List/Network/Entry.pm105
4 files changed, 472 insertions, 0 deletions
diff --git a/plugins/perl/lib/Xchat/Embed.pm b/plugins/perl/lib/Xchat/Embed.pm
new file mode 100644
index 00000000..dffbaf5e
--- /dev/null
+++ b/plugins/perl/lib/Xchat/Embed.pm
@@ -0,0 +1,253 @@
+package Xchat::Embed;
+use strict;
+use warnings;
+# list of loaded scripts keyed by their package names
+our %scripts;
+
+sub load {
+	my $file = expand_homedir( shift @_ );
+	my $package = file2pkg( $file );
+	
+	if( exists $scripts{$package} ) {
+		my $pkg_info = pkg_info( $package );
+		my $filename = File::Basename::basename( $pkg_info->{filename} );
+		Xchat::printf(
+			qq{'%s' already loaded from '%s'.\n},
+			$filename, $pkg_info->{filename}
+		);
+		Xchat::print(
+			'If this is a different script then it rename and try '.
+			'loading it again.'
+		);
+		return 2;
+	}
+	
+	if( open my $source_handle, $file ) {
+		my $source = do {local $/; <$source_handle>};
+		close $source_handle;
+		# we shouldn't care about things after __END__
+		$source =~ s/^__END__.*//ms;
+		
+		if(
+			my @replacements = $source =~
+				m/^\s*package ((?:[^\W:]+(?:::)?)+)\s*?;/mg
+		) {
+			
+			if ( @replacements > 1 ) {
+				Xchat::print(
+					"Too many package defintions, only 1 is allowed\n"
+				);
+				return 1;
+			}
+			
+			my $original_package = shift @replacements;
+			
+			# remove original package declaration
+			$source =~ s/^(package $original_package\s*;)/#$1/m;
+			
+			# fixes things up for code calling subs with fully qualified names
+			$source =~ s/${original_package}:://g;
+		}
+		
+		# this must come before the eval or the filename will not be found in
+		# Xchat::register
+		$scripts{$package}{filename} = $file;
+		$scripts{$package}{loaded_at} = Time::HiRes::time();
+
+		my $full_path = File::Spec->rel2abs( $file );
+		$source =~ s/^/#line 1 "$full_path"\n\x7Bpackage $package;/;
+
+		# make sure we add the closing } even if the last line is a comment
+		if( $source =~ /^#.*\Z/m ) {
+			$source =~ s/^(?=#.*\Z)/\x7D/m;
+		} else {
+			$source =~ s/\Z/\x7D/;
+		}
+
+		_do_eval( $source );
+
+		unless( exists $scripts{$package}{gui_entry} ) {
+			$scripts{$package}{gui_entry} =
+				Xchat::Internal::register(
+					"", "unknown", "", $file
+				);
+		}
+		
+		if( $@ ) {
+			# something went wrong
+			$@ =~ s/\(eval \d+\)/$file/g;
+			Xchat::print( "Error loading '$file':\n$@\n" );
+			# make sure the script list doesn't contain false information
+			unload( $scripts{$package}{filename} );
+			return 1;
+		}
+	} else {
+		Xchat::print( "Error opening '$file': $!\n" );
+		return 2;
+	}
+
+	return 0;
+}
+
+sub _do_eval {
+	no strict;
+	no warnings;
+	eval $_[0];
+}
+
+sub unload {
+	my $file = shift @_;
+	my $package = file2pkg( $file );
+	my $pkg_info = pkg_info( $package );
+
+	if( $pkg_info ) {	
+		# take care of the shutdown callback
+		if( exists $pkg_info->{shutdown} ) {
+			# allow incorrectly written scripts to be unloaded
+			eval {
+				if( ref $pkg_info->{shutdown} eq 'CODE' ) {
+					$pkg_info->{shutdown}->();
+				} elsif ( $pkg_info->{shutdown} ) {
+					no strict 'refs';
+					&{$pkg_info->{shutdown}};
+				}
+			};
+		}
+
+		if( exists $pkg_info->{hooks} ) {
+			for my $hook ( @{$pkg_info->{hooks}} ) {
+				Xchat::unhook( $hook, $package );
+			}
+		}
+
+
+		if( exists $pkg_info->{gui_entry} ) {
+			plugingui_remove( $pkg_info->{gui_entry} );
+		}
+		
+		Symbol::delete_package( $package );
+		delete $scripts{$package};
+		return Xchat::EAT_ALL;
+	} else {
+		Xchat::print( qq{"$file" is not loaded.\n} );
+		return Xchat::EAT_NONE;
+	}
+}
+
+sub unload_all {
+	for my $package ( keys %scripts ) {
+		unload( $scripts{$package}->{filename} );
+	}
+	
+	return Xchat::EAT_ALL;
+}
+
+sub reload {
+	my $file = shift @_;
+	my $package = file2pkg( $file );
+	my $pkg_info = pkg_info( $package );
+	my $fullpath = $file;
+	
+	if( $pkg_info ) {
+		$fullpath = $pkg_info->{filename};
+		unload( $file );
+	}
+	
+	load( $fullpath );
+	return Xchat::EAT_ALL;
+}
+
+sub reload_all {
+	my @dirs = Xchat::get_info( "xchatdirfs" ) || Xchat::get_info( "xchatdir" );
+	push @dirs, File::Spec->catdir( $dirs[0], "plugins" );
+	for my $dir ( @dirs ) {
+		my $auto_load_glob = File::Spec->catfile( $dir, "*.pl" );
+		my @scripts = map { $_->{filename} }
+			sort { $a->{loaded_at} <=> $b->{loaded_at} } values %scripts;
+		push @scripts, File::Glob::bsd_glob( $auto_load_glob );
+
+		my %seen;
+		@scripts = grep { !$seen{ $_ }++ } @scripts;
+
+		unload_all();
+		for my $script ( @scripts ) {
+			if( !pkg_info( file2pkg( $script ) ) ) {
+				load( $script );
+			}
+		}
+	}
+}
+
+sub expand_homedir {
+	my $file = shift @_;
+
+	if ( $^O eq "MSWin32" ) {
+		$file =~ s/^~/$ENV{USERPROFILE}/;
+	} else {
+		$file =~ s{^~}{
+			(getpwuid($>))[7] ||  $ENV{HOME} || $ENV{LOGDIR}
+		}ex;
+	}
+	return $file;
+}
+
+sub file2pkg {
+	my $string = File::Basename::basename( shift @_ );
+	$string =~ s/\.pl$//i;
+	$string =~ s|([^A-Za-z0-9/])|'_'.unpack("H*",$1)|eg;
+	return "Xchat::Script::" . $string;
+}
+
+sub pkg_info {
+	my $package = shift @_;
+	return $scripts{$package};
+}
+
+sub find_external_pkg {
+	my $level = 1;
+
+	while( my @frame = caller( $level ) ) {
+		return @frame if $frame[0] !~ /^Xchat/;
+		$level++;
+	}
+
+}
+
+sub find_pkg {
+	my $level = 1;
+
+	while( my ($package, $file, $line) = caller( $level ) ) {
+		return $package if $package =~ /^Xchat::Script::/;
+		$level++;
+	}
+
+	my @frame = find_external_pkg();
+	my $location;
+
+	if( $frame[0] or $frame[1] ) {
+		$location = $frame[1] ? $frame[1] : "package $frame[0]";
+		$location .= " line $frame[2]";
+	} else {
+		$location = "unknown location";
+	}
+
+	die "Unable to determine which script this hook belongs to. at $location\n";
+
+}
+
+sub fix_callback {
+	my ($package, $callback) = @_;
+	
+	unless( ref $callback ) {
+		# change the package to the correct one in case it was hardcoded
+		$callback =~ s/^.*:://;
+		$callback = qq[${package}::$callback];
+
+		no strict 'subs';
+		$callback = \&{$callback};
+	}
+	
+	return $callback;
+}
+
+1
diff --git a/plugins/perl/lib/Xchat/List/Network.pm b/plugins/perl/lib/Xchat/List/Network.pm
new file mode 100644
index 00000000..da2f52dd
--- /dev/null
+++ b/plugins/perl/lib/Xchat/List/Network.pm
@@ -0,0 +1,32 @@
+package Xchat::List::Network;
+use strict;
+use warnings;
+use Storable qw(dclone);
+my $last_modified;
+my @servers;
+
+sub get {
+	my $server_file = Xchat::get_info( "xchatdirfs" ) . "/servlist_.conf";
+
+	# recreate the list only if the server list file has changed
+	if( -f $server_file && 
+			(!defined $last_modified || $last_modified != -M $server_file ) ) {
+		$last_modified = -M _;
+
+		if( open my $fh, "<", $server_file ) {
+			local $/ = "\n\n";
+			while( my $record = <$fh> ) {
+				chomp $record;
+				next if $record =~ /^v=/; # skip the version line
+				push @servers, Xchat::List::Network::Entry::parse( $record );
+			}
+		} else {
+			warn "Unable to open '$server_file': $!";
+		}
+	}
+
+	my $clone = dclone( \@servers );
+	return @$clone;
+}
+
+1
diff --git a/plugins/perl/lib/Xchat/List/Network/AutoJoin.pm b/plugins/perl/lib/Xchat/List/Network/AutoJoin.pm
new file mode 100644
index 00000000..16036a9d
--- /dev/null
+++ b/plugins/perl/lib/Xchat/List/Network/AutoJoin.pm
@@ -0,0 +1,82 @@
+package Xchat::List::Network::AutoJoin;
+use strict;
+use warnings;
+
+use overload
+#	'%{}' => \&as_hash,
+#	'@{}' => \&as_array,
+	'""'   => 'as_string',
+	'0+'   => 'as_bool';
+
+sub new {
+	my $class = shift;
+	my $line = shift;
+
+	my @autojoins;
+
+	if ( $line ) {
+		my ( $channels, $keys ) = split / /, $line, 2;
+		my @channels = split /,/, $channels;
+		my @keys     = split /,/, ($keys || '');
+
+		for my $channel ( @channels ) {
+			my $key = shift @keys;
+			$key = '' unless defined $key;
+
+			push @autojoins, {
+				channel => $channel,
+				key     => $key,
+				};
+		}
+	}
+	return bless \@autojoins, $class;
+}
+
+sub channels {
+	my $self = shift;
+
+	if( wantarray ) {
+		return map { $_->{channel} } @$self;
+	} else {
+		return scalar @$self;
+	}
+}
+
+sub keys {
+	my $self = shift;
+	return map { $_->{key} } @$self  ;
+
+}
+
+sub pairs {
+	my $self = shift;
+
+	my @channels = $self->channels;
+	my @keys = $self->keys;
+
+	my @pairs = map { $_ => shift @keys } @channels;
+}
+
+sub as_hash {
+	my $self = shift;
+	return +{ $self->pairs };
+}
+
+sub as_string {
+	my $self = shift;
+	return join " ",
+		join( ",", $self->channels ),
+		join( ",", $self->keys );
+}
+
+sub as_array {
+	my $self = shift;
+	return [ map { \%$_ } @$self ];
+}
+
+sub as_bool {
+	my $self = shift;
+	return $self->channels ? 1 : "";
+}
+
+1
diff --git a/plugins/perl/lib/Xchat/List/Network/Entry.pm b/plugins/perl/lib/Xchat/List/Network/Entry.pm
new file mode 100644
index 00000000..e40b48bd
--- /dev/null
+++ b/plugins/perl/lib/Xchat/List/Network/Entry.pm
@@ -0,0 +1,105 @@
+package Xchat::List::Network::Entry;
+use strict;
+use warnings;
+
+my %key_for = (
+	I => "irc_nick1",
+	i => "irc_nick2",
+	U => "irc_user_name",
+	R => "irc_real_name",
+	P => "server_password",
+	B => "nickserv_password",
+	N => "network",
+	D => "selected",
+	E => "encoding",
+);
+my $letter_key_re = join "|", keys %key_for;
+
+sub parse {
+	my $data  = shift;
+	my $entry = {
+		irc_nick1       => undef,
+		irc_nick2       => undef,
+		irc_user_name   => undef,
+		irc_real_name   => undef,
+		server_password => undef,
+
+		# the order of the channels need to be maintained
+		# list of { channel => .., key => ... }
+		autojoins         => Xchat::List::Network::AutoJoin->new( '' ),
+		connect_commands   => [],
+		flags             => {},
+		selected          => undef,
+		encoding          => undef,
+		servers           => [],
+		nickserv_password => undef,
+		network           => undef,
+	};
+
+	my @fields = split /\n/, $data;
+	chomp @fields;
+
+	for my $field ( @fields ) {
+	SWITCH: for ( $field ) {
+			/^($letter_key_re)=(.*)/ && do {
+				$entry->{ $key_for{ $1 } } = $2;
+				last SWITCH;
+			};
+
+			/^J.(.*)/ && do {
+				$entry->{ autojoins } =
+					Xchat::List::Network::AutoJoin->new( $1 );
+			};
+
+			/^F.(.*)/ && do {
+				$entry->{ flags } = parse_flags( $1 );
+			};
+
+			/^S.(.+)/ && do {
+				push @{$entry->{servers}}, parse_server( $1 );
+			};
+
+			/^C.(.+)/ && do {
+				push @{$entry->{connect_commands}}, $1;
+			};
+		}
+	}
+
+#	$entry->{ autojoins } = $entry->{ autojoin_channels };
+	return $entry;
+}
+
+sub parse_flags {
+	my $value = shift || 0;
+	my %flags;
+
+	$flags{ "cycle" }         = $value & 1  ? 1 : 0;
+	$flags{ "use_global" }    = $value & 2  ? 1 : 0;
+	$flags{ "use_ssl" }       = $value & 4  ? 1 : 0;
+	$flags{ "autoconnect" }   = $value & 8  ? 1 : 0;
+	$flags{ "use_proxy" }     = $value & 16 ? 1 : 0;
+	$flags{ "allow_invalid" } = $value & 32 ? 1 : 0;
+
+	return \%flags;
+}
+
+sub parse_server {
+	my $data = shift;
+	if( $data ) {
+		my ($host, $port) = split /\//, $data;
+		unless( $port ) {
+			my @parts = split /:/, $host;
+
+			# if more than 2 then we are probably dealing with a IPv6 address
+			# if less than 2 then no port was specified
+			if( @parts == 2 ) {
+				$port = $parts[1];
+			}
+		}
+
+		$port ||= 6667;
+		return { host => $host, port => $port };
+	}
+}
+
+1