[go: up one dir, main page]

Menu

Diff of /lib/PACUtils.pm [000000] .. [r1]  Maximize  Restore

Switch to side-by-side view

--- a
+++ b/lib/PACUtils.pm
@@ -0,0 +1,1171 @@
+package PACUtils;
+
+##################################################################
+# This file is part of PAC( Perl Auto Connector)
+#
+# Copyright (C) 2010  David Torrejon Vaquerizas
+# 
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+###################################################################
+
+$|++;
+
+###################################################################
+# Import Modules
+
+# Standard
+use strict;
+use warnings;
+
+use FindBin qw ( $RealBin $Bin $Script );
+use POSIX qw ( strftime );
+use Crypt::CBC;
+use Socket;
+use Sys::Hostname;
+use Net::ARP;
+use Net::Ping;
+#use Data::Dumper;
+
+# GTK2
+use Gtk2 '-init';
+use Gtk2::Ex::Simple::List;
+
+# Module's functions/variables to export
+use vars qw( $VERSION @ISA @EXPORT @EXPORT_OK );
+require Exporter;
+@ISA        = qw( Exporter );
+@EXPORT     = qw(
+	_
+	__
+	_screenshot
+	_scale
+	_preview
+	_getMethods
+	_menuAvailableConnections
+	_wEnterValue
+	_wPopUpMenu
+	_wConfirm
+	_wMessage
+	_wYesNoCancel
+	_wPrePostEntry
+	_wExecEntry
+	_cfgSanityCheck
+	_cipherCFG
+	_decipherCFG
+	_wakeOnLan
+	_deleteOldestSessionLog
+	_buildPIDFile
+	_checkPIDFile
+	_deletePIDFile
+); # Functions/varibles to export
+
+@EXPORT_OK  = qw();
+
+# END: Import Modules
+###################################################################
+
+###################################################################
+# Define GLOBAL CLASS variables
+
+our $APPNAME	= 'PAC';
+our $APPVERSION	= '2.5.2';
+my $RES_DIR		= $RealBin . '/res';
+my $APPICON		= $RES_DIR . '/pac64x64.png';
+my $APPTERMICON	= $RES_DIR . '/pac_terminal64x64.png';
+my $CFG_DIR		= $ENV{'HOME'} . '/.pac';
+my $CIPHER		= Crypt::CBC -> new( -key => 'PAC Manager (David Torrejon Vaquerizas, david.tv@gmail.com)', -cipher => 'Blowfish', -salt => 1 ) or die "ERROR: $!";
+my $PIDFILE		= '/tmp/pac.pid';
+
+# END: Define GLOBAL CLASS variables
+###################################################################
+
+######################################################
+# START: Private functions definitions
+
+sub _ { return shift -> {_GLADE} -> get_widget( shift ); };
+
+sub __
+{
+	my $str = shift;
+	$str =~ s/&/&amp;/go;
+	$str =~ s/</&lt;/go;
+	$str =~ s/>/&gt;/go;
+	return $str;
+};
+
+sub _screenshot
+{
+	my $widget	= shift;
+	my $jpg		= shift;
+	
+	my $gdkpixbuf = Gtk2::Gdk::Pixbuf -> new( 'rgb', 0, 8, $widget -> allocation -> width, $widget -> allocation -> height );
+	
+	$gdkpixbuf -> get_from_drawable( $widget -> window, undef, $widget -> allocation -> x, $widget -> allocation -> y, 0, 0, $widget -> allocation -> width, $widget -> allocation -> height );
+	$gdkpixbuf -> save( $jpg, 'jpeg' );
+	
+	return 1;
+}
+
+sub _scale
+{
+	my $file	= shift;
+	my $w		= shift;
+	my $h		= shift;
+	my $ratio	= shift // '';
+	
+	my $gdkpixbuf = Gtk2::Gdk::Pixbuf -> new_from_file( $file );
+	
+	if ( $ratio )
+	{
+		if ( ( $gdkpixbuf -> get_width > $w ) || ( $gdkpixbuf -> get_height > $h ) )
+		{
+			if ( $gdkpixbuf -> get_width > $gdkpixbuf -> get_height )
+			{
+				$h = int( ( $w * $gdkpixbuf -> get_height ) / $gdkpixbuf -> get_width );
+			}
+			elsif ( $gdkpixbuf -> get_height >= $gdkpixbuf -> get_width)
+			{
+				$w = int( ( $h * $gdkpixbuf -> get_width ) / $gdkpixbuf -> get_height );
+			}
+		}
+	}
+	
+	return $gdkpixbuf -> scale_simple( $w, $h, 'GDK_INTERP_HYPER' );
+}
+
+sub _preview
+{
+	my $dialog = shift;
+	
+	$dialog -> set_preview_widget_active( 0 );
+	
+	my $file = $dialog -> get_preview_filename;
+	( defined $file && -f $file ) or return 1;
+	
+	my $preview = Gtk2::Image -> new;
+	$dialog -> set_preview_widget( $preview );
+	
+	my $preview_pixbuf = Gtk2::Gdk::Pixbuf -> new_from_file_at_size( $file, 256, 256 );
+	$preview -> set_from_pixbuf( $preview_pixbuf );
+	
+	$dialog -> set_preview_widget_active( $preview_pixbuf );
+	
+	return 1;
+}
+
+sub _getMethods
+{
+	my $self = shift;
+	
+	my %methods = (
+		'rdesktop' =>
+		{
+			'updateGUI'	=> sub
+			{
+				my $port = shift // 3389;
+				_( $self, 'framePort' )				-> set_sensitive( 1 );
+				_( $self, 'entryPort' )				-> set_text( $port );
+				_( $self, 'labelIP' )				-> set_text( 'IP / Hostname:' );
+				_( $self, 'entryIP' )				-> set_property( 'tooltip-markup', 'IP or Hostname of the machine to connect to' );
+				_( $self, 'frameUser' )				-> set_sensitive( 1 );
+				_( $self, 'frameExpect' )			-> set_sensitive( 0 );
+				_( $self, 'frameRemoteMacros' )		-> set_sensitive( 0 );
+				_( $self, 'frameLocalMacros' )		-> set_sensitive( 0 );
+				_( $self, 'frameVariables' )		-> set_sensitive( 0 );
+				_( $self, 'frameTerminalOptions' )	-> set_sensitive( 0 );
+				_( $self, 'labelExpect' )			-> set_sensitive( 0 );
+				_( $self, 'labelRemoteMacros' )		-> set_sensitive( 0 );
+				_( $self, 'labelLocalMacros' )		-> set_sensitive( 0 );
+				_( $self, 'labelVariables' )		-> set_sensitive( 0 );
+				_( $self, 'labelTerminalOptions' )	-> set_sensitive( 0 );
+				_( $self, 'labelCmdLineOptions' )	-> set_markup( " '<b>rdesktop</b>' command line options: " );
+			},
+			'icon' => Gtk2::Gdk::Pixbuf -> new_from_file_at_scale( $RES_DIR . '/pac_method_rdesktop.jpg', 16, 16, 0 )
+		},
+		'vncviewer' =>
+		{
+			'updateGUI'	=> sub
+			{
+				my $port = shift // 5900;
+				_( $self, 'framePort' )				-> set_sensitive( 1 );
+				_( $self, 'entryPort' )				-> set_text( $port );
+				_( $self, 'labelIP' )				-> set_text( 'IP / Hostname:' );
+				_( $self, 'entryIP' )				-> set_property( 'tooltip-markup', 'IP or Hostname of the machine to connect to' );
+				_( $self, 'entryUser' )				-> set_text( '' );
+				_( $self, 'frameUser' )				-> set_sensitive( 0 );
+				_( $self, 'frameExpect' )			-> set_sensitive( 0 );
+				_( $self, 'frameRemoteMacros' )		-> set_sensitive( 0 );
+				_( $self, 'frameLocalMacros' )		-> set_sensitive( 0 );
+				_( $self, 'frameVariables' )		-> set_sensitive( 0 );
+				_( $self, 'frameTerminalOptions' )	-> set_sensitive( 0 );
+				_( $self, 'labelExpect' )			-> set_sensitive( 0 );
+				_( $self, 'labelRemoteMacros' )		-> set_sensitive( 0 );
+				_( $self, 'labelLocalMacros' )		-> set_sensitive( 0 );
+				_( $self, 'labelVariables' )		-> set_sensitive( 0 );
+				_( $self, 'labelTerminalOptions' )	-> set_sensitive( 0 );
+				_( $self, 'labelCmdLineOptions' )	-> set_markup( " '<b>vncviewer</b>' command line options: " );
+			},
+			'icon' => Gtk2::Gdk::Pixbuf -> new_from_file_at_scale( $RES_DIR . '/pac_method_vncviewer.jpg', 16, 16, 0 )
+		},
+		'cu' =>
+		{
+			'updateGUI'	=> sub
+			{
+				_( $self, 'framePort' )				-> set_sensitive( 0 );
+				_( $self, 'entryPort' )				-> set_text( '' );
+				_( $self, 'labelIP' )				-> set_text( 'System / Phone:' );
+				_( $self, 'entryIP' )				-> set_property( 'tooltip-markup', 'Enter string of kind: system | phone | "dir"' );
+				_( $self, 'labelExpect' )			-> set_sensitive( 1 );
+				_( $self, 'labelRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelVariables' )		-> set_sensitive( 1 );
+				_( $self, 'labelTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'frameUser' )				-> set_sensitive( 1 );
+				_( $self, 'frameExpect' )			-> set_sensitive( 1 );
+				_( $self, 'frameRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameVariables' )		-> set_sensitive( 1 );
+				_( $self, 'frameTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'labelCmdLineOptions' )	-> set_markup( " '<b>cu</b>' command line options: " );
+			},
+			'icon' => Gtk2::Gdk::Pixbuf -> new_from_file_at_scale( $RES_DIR . '/pac_method_cu.jpg', 16, 16, 0 ),
+			'escape' => [ '~.' ]
+		},
+		'remote-tty' => 
+		{
+			'updateGUI'	=> sub
+			{
+				_( $self, 'framePort' )				-> set_sensitive( 0 );
+				_( $self, 'entryPort' )				-> set_text( '' );
+				_( $self, 'labelIP' )				-> set_text( 'TTY Socket:' );
+				_( $self, 'entryIP' )				-> set_property( 'tooltip-markup', 'Enter a TTY / Serial socket (eg: /dev/tty*)' );
+				_( $self, 'labelExpect' )			-> set_sensitive( 1 );
+				_( $self, 'labelRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelVariables' )		-> set_sensitive( 1 );
+				_( $self, 'labelTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'frameUser' )				-> set_sensitive( 1 );
+				_( $self, 'frameExpect' )			-> set_sensitive( 1 );
+				_( $self, 'frameRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameVariables' )		-> set_sensitive( 1 );
+				_( $self, 'frameTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'labelCmdLineOptions' )	-> set_markup( " '<b>remote-tty</b>' command line options: " );
+			},
+			'icon' => Gtk2::Gdk::Pixbuf -> new_from_file_at_scale( $RES_DIR . '/pac_method_remote-tty.jpg', 16, 16, 0 )
+		},
+		'ssh' =>
+		{
+			'updateGUI'	=> sub
+			{
+				my $port = shift // 22;
+				_( $self, 'framePort' )				-> set_sensitive( 1 );
+				_( $self, 'entryPort' )				-> set_text( $port );
+				_( $self, 'labelIP' )				-> set_text( 'IP / Hostname:' );
+				_( $self, 'entryIP' )				-> set_property( 'tooltip-markup', 'IP or Hostname of the machine to connect to' );
+				_( $self, 'labelExpect' )			-> set_sensitive( 1 );
+				_( $self, 'labelRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelVariables' )		-> set_sensitive( 1 );
+				_( $self, 'labelTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'frameUser' )				-> set_sensitive( 1 );
+				_( $self, 'frameExpect' )			-> set_sensitive( 1 );
+				_( $self, 'frameRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameVariables' )		-> set_sensitive( 1 );
+				_( $self, 'frameTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'labelCmdLineOptions' )	-> set_markup( " '<b>ssh</b>' command line options: " );
+			},
+			'icon' => Gtk2::Gdk::Pixbuf -> new_from_file_at_scale( $RES_DIR . '/pac_method_ssh.jpg', 16, 16, 0 ),
+			'escape' => [ '~.' ]
+		},
+		'telnet' =>
+		{
+			'updateGUI'	=> sub
+			{
+				my $port = shift // 23;
+				_( $self, 'framePort' )				-> set_sensitive( 1 );
+				_( $self, 'entryPort' )				-> set_text( $port );
+				_( $self, 'labelIP' )				-> set_text( 'IP / Hostname:' );
+				_( $self, 'entryIP' )				-> set_property( 'tooltip-markup', 'IP or Hostname of the machine to connect to' );
+				_( $self, 'labelExpect' )			-> set_sensitive( 1 );
+				_( $self, 'labelRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelVariables' )		-> set_sensitive( 1 );
+				_( $self, 'labelTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'frameUser' )				-> set_sensitive( 1 );
+				_( $self, 'frameExpect' )			-> set_sensitive( 1 );
+				_( $self, 'frameRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameVariables' )		-> set_sensitive( 1 );
+				_( $self, 'frameTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'labelCmdLineOptions' )	-> set_markup( " '<b>telnet</b>' command line options: " );
+			},
+			'icon' => Gtk2::Gdk::Pixbuf -> new_from_file_at_scale( $RES_DIR . '/pac_method_telnet.jpg', 16, 16, 0 ),
+			'escape' => [ "\c]", "quit\n" ]
+		},
+		'sftp' =>
+		{
+			'updateGUI'	=> sub
+			{
+				my $port = shift // 22;
+				_( $self, 'framePort' )				-> set_sensitive( 1 );
+				_( $self, 'entryPort' )				-> set_text( $port );
+				_( $self, 'labelIP' )				-> set_text( 'IP / Hostname:' );
+				_( $self, 'entryIP' )				-> set_property( 'tooltip-markup', 'IP or Hostname of the machine to connect to' );
+				_( $self, 'labelExpect' )			-> set_sensitive( 1 );
+				_( $self, 'labelRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelVariables' )		-> set_sensitive( 1 );
+				_( $self, 'labelTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'frameUser' )				-> set_sensitive( 1 );
+				_( $self, 'frameExpect' )			-> set_sensitive( 1 );
+				_( $self, 'frameRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameVariables' )		-> set_sensitive( 1 );
+				_( $self, 'frameTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'labelCmdLineOptions' )	-> set_markup( " '<b>sftp</b>' command line options: " );
+			},
+			'icon' => Gtk2::Gdk::Pixbuf -> new_from_file_at_scale( $RES_DIR . '/pac_method_sftp.jpg', 16, 16, 0 )
+		},
+		'ftp' =>
+		{
+			'updateGUI'	=> sub
+			{
+				my $port = shift // 21;
+				_( $self, 'framePort' )				-> set_sensitive( 1 );
+				_( $self, 'entryPort' )				-> set_text( $port );
+				_( $self, 'labelIP' )				-> set_text( 'IP / Hostname:' );
+				_( $self, 'entryIP' )				-> set_property( 'tooltip-markup', 'IP or Hostname of the machine to connect to' );
+				_( $self, 'labelExpect' )			-> set_sensitive( 1 );
+				_( $self, 'labelRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'labelVariables' )		-> set_sensitive( 1 );
+				_( $self, 'labelTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'frameUser' )				-> set_sensitive( 1 );
+				_( $self, 'frameExpect' )			-> set_sensitive( 1 );
+				_( $self, 'frameRemoteMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameLocalMacros' )		-> set_sensitive( 1 );
+				_( $self, 'frameVariables' )		-> set_sensitive( 1 );
+				_( $self, 'frameTerminalOptions' )	-> set_sensitive( 1 );
+				_( $self, 'labelCmdLineOptions' )	-> set_markup( " '<b>ftp</b>' command line options: " );
+			},
+			'icon' => Gtk2::Gdk::Pixbuf -> new_from_file_at_scale( $RES_DIR . '/pac_method_ftp.jpg', 16, 16, 0 )
+		},
+	);
+	
+	return %methods;
+}
+
+sub _menuAvailableConnections
+{
+	my @tray_menu_items;
+	
+	push( @tray_menu_items, { label => 'Local Shell', stockicon => 'gtk-home', code => sub { $PACMain::FUNCS{_MAIN}{_GUI}{shellBtn} -> clicked; } } );
+	push( @tray_menu_items, { separator => 1 } );
+	
+	foreach my $environment ( sort { uc($a) cmp uc($b) } keys %{ $PACMain::FUNCS{_MAIN}{_CFG}{'environments'} } )
+	{
+		# Create a submenu with connections for the environment
+		my @sub_menu;
+		my $is_shell = 0;
+		foreach my $connection ( sort { uc($a) cmp uc($b) } keys %{ $PACMain::FUNCS{_MAIN}{_CFG}{'environments'}{$environment} } )
+		{
+			my $connected = 0;
+			foreach my $uuid ( keys %PACMain::RUNNING )
+			{
+				next unless ( $PACMain::RUNNING{$uuid}{environment} // 0 && $PACMain::RUNNING{$uuid}{connection} // 0 );
+				next unless ( ( $PACMain::RUNNING{$uuid}{environment} eq $environment ) && ( $PACMain::RUNNING{$uuid}{connection} eq $connection )  && $PACMain::RUNNING{$uuid}{terminal}{CONNECTED} // 0 );
+				$is_shell = $PACMain::RUNNING{$uuid}{is_shell} // 0;
+				++$connected;
+			}
+			push( @sub_menu,
+			{
+				label		=> ( $connected ? "($connected) " : '' ) . $connection,
+				stockicon	=> $connected ? 'gtk-connect' : 'gtk-disconnect',
+				tooltip		=> $PACMain::FUNCS{_MAIN}{_CFG}{'environments'}{$environment}{$connection}{'description'},
+				code		=> sub { $PACMain::FUNCS{_MAIN} -> _executeTerminal( $environment, $connection ); }
+			} );
+		}
+		# Save that submenu into this environment menu
+		push( @tray_menu_items,
+		{
+			 label		=> $environment,
+			 submenu	=> \@sub_menu
+		} ) unless $is_shell;
+	}
+	
+	return \@tray_menu_items;
+}
+
+sub _wEnterValue
+{
+	my $self = shift;
+	my $lblup = shift;
+	my $lbldown = shift;
+	my $default = shift // '';
+	
+	my $env		= $self -> {_ENVIRONMENT};
+	my $conn	= $self -> {_CONNECTION};
+	
+	my %w;
+	
+	# Create the dialog window,
+	$w{window}{data} = Gtk2::Dialog -> new_with_buttons(
+		"$APPNAME (v$APPVERSION) : Enter data",
+		undef,
+		'modal',
+		'gtk-ok'		=> 'ok',
+		'gtk-cancel'	=> 'cancel'
+	);
+	# and setup some dialog properties.
+	$w{window}{data} -> set_default_response( 'ok' );
+	$w{window}{data} -> set_position( 'center' );
+	$w{window}{data} -> set_icon_from_file( $APPICON );
+	$w{window}{data} -> set_size_request( -1, -1 );
+	$w{window}{data} -> set_resizable( 0 );
+	$w{window}{data} -> set_border_width( 5 );
+		
+		# Create an HBox to contain a picture and a label
+		$w{window}{gui}{hbox} = Gtk2::HBox -> new( 0, 0 );
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{hbox}, 1, 1, 5 );
+		$w{window}{gui}{hbox} -> set_border_width( 5 );
+			
+			# Create image
+			$w{window}{gui}{img} = Gtk2::Image -> new_from_stock( 'gtk-edit', 'dialog' );
+			$w{window}{gui}{hbox} -> pack_start( $w{window}{gui}{img}, 0, 1, 5 );
+			
+			# Create 1st label
+			$w{window}{gui}{lblup} = Gtk2::Label -> new();
+			$w{window}{gui}{hbox} -> pack_start( $w{window}{gui}{lblup}, 1, 1, 5 );
+			$w{window}{gui}{lblup} -> set_markup( $lblup );
+		
+		# Create 2nd label
+		$w{window}{gui}{lbldwn} = Gtk2::Label -> new();
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{lbldwn}, 1, 1, 5 );
+		$w{window}{gui}{lbldwn} -> set_text( $lbldown );
+		
+		# Create the entry widget
+		$w{window}{gui}{entry} = Gtk2::Entry -> new();
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{entry}, 0, 1, 5 );
+		$w{window}{gui}{entry} -> set_text( $default );
+		$w{window}{gui}{entry} -> set_activates_default( 1 );
+	
+	# Show the window (in a modal fashion)
+	$w{window}{data} -> show_all();
+	my $ok = $w{window}{data} -> run();
+	
+	my $val = ( $ok eq 'ok' ) ? $w{window}{gui}{entry} -> get_chars( 0, -1 ) : undef;
+	
+	$w{window}{data} -> destroy();
+	
+	return $val;
+}
+
+# Next function borrowed (and modified) from Quentin Sculo <squentin@free.fr>, from his amazing 'gmusicbrowser'
+sub _wPopUpMenu
+{
+	my $mref	= shift;
+	our $event	= shift;
+	my $below	= shift // '0';
+
+	my $count = 0;
+
+	my $menu = Gtk2::Menu -> new;
+	
+	for my $m ( @$mref )
+	{
+		my $label = $m -> {label};
+		my $sensitive = $m -> {sensitive} // 1;
+		
+		my $item;
+		
+		# Build menu item
+		if ( ! defined $label )
+		{
+			next unless $m -> {separator};
+			$item = Gtk2::SeparatorMenuItem -> new;
+		}
+		elsif ( $m -> {stockicon} )
+		{
+			$item = Gtk2::ImageMenuItem -> new( $label );
+			$item -> set_image( Gtk2::Image -> new_from_stock( $m -> {stockicon}, 'menu' ) );
+		}
+		elsif ( $m -> {fileicon} )
+		{
+			$item = Gtk2::ImageMenuItem -> new( $label );
+			$item -> set_image( Gtk2::Image -> new_from_file( $m -> {fileicon} ) );
+		}
+		elsif ( ( $m -> {check} || $m -> {radio} ) && ! $m -> {submenu} )
+		{
+			$item = Gtk2::CheckMenuItem -> new( $label );
+			$item -> set_active( $m -> {active} // 0 );
+			$item -> set_draw_as_radio( 1 ) if $m -> {radio};
+		}
+		else
+		{
+			$item = Gtk2::MenuItem -> new( $label );
+		}
+		
+		# Set some properties
+		$item -> set_sensitive( $sensitive );
+		$m -> {separator} or $item -> set( 'use_underline' => 0 );
+		defined $m -> {tooltip} and $item -> set_property( 'tooltip-text', $m -> {tooltip} );
+		
+		# Check if this entry is a submenu definition
+		if ( my $submenu = $m -> {submenu} )
+		{
+			ref $submenu eq 'ARRAY' and $submenu = &_wPopUpMenu( $submenu, $event );
+			next unless $submenu;
+			$item -> set_submenu( $submenu );
+		}
+		else
+		{
+			$item -> signal_connect( activate => $m -> {code} );
+		}
+		
+		$count++;
+		$menu -> append( $item );
+	}
+	defined wantarray and return $menu;
+	return unless $count;
+	
+	$menu -> show_all;
+	if ( defined $event )	{ $menu -> popup( undef, undef, ( $below ? \&_pos : undef ), undef, $event -> button, $event -> time ); }
+	else					{ $menu -> popup( undef, undef, undef, undef, 0, 0 ); }
+	
+	sub _pos
+	{
+	    my $h			= $_[0] -> size_request -> height;
+	    my $ymax		= $event -> get_screen -> get_height;
+	    my ( $x, $y )	= $event -> window ->get_origin;
+	    my $dy			= ( $event -> window -> get_size )[1];
+	    
+		if ( $dy + $y + $h > $ymax )
+		{
+	        $y -= $h;
+	        $y = 0 if $y < 0;
+	    }
+	    else
+		{
+			$y += $dy;
+		}
+	    
+		return $x, $y;
+	}
+ }
+
+sub _wMessage
+{
+	my $window	= shift;
+	my $msg		= shift;
+	
+	my $windowConfirm = Gtk2::MessageDialog -> new_with_markup( 
+		$window,
+		'GTK_DIALOG_DESTROY_WITH_PARENT',
+		'GTK_MESSAGE_INFO',
+		'none',
+		$msg
+	);   
+	$windowConfirm -> add_buttons( 'gtk-ok' => 'ok' );
+	$windowConfirm -> set_icon_from_file( $APPICON );
+	$windowConfirm -> set_title( "$APPNAME (v$APPVERSION) : Message" );
+	
+	$windowConfirm -> show_all;
+	my $close = $windowConfirm -> run;
+	$windowConfirm -> destroy;
+	
+	return 1;
+}
+
+sub _wConfirm
+{
+	my $window	= shift;
+	my $msg		= shift;
+	
+	my $windowConfirm = Gtk2::MessageDialog -> new_with_markup( 
+		$window,
+		'GTK_DIALOG_DESTROY_WITH_PARENT',
+		'GTK_MESSAGE_QUESTION',
+		'none',
+		$msg
+	);   
+	$windowConfirm -> add_buttons( 'gtk-ok' => 'yes', 'gtk-cancel'=> 'no');
+	$windowConfirm -> set_icon_from_file( $APPICON );
+	$windowConfirm -> set_title( "Confirm action : $APPNAME (v$APPVERSION)" );
+	
+	$windowConfirm -> show_all();
+	my $close = $windowConfirm -> run;
+	$windowConfirm -> destroy();
+	
+	return ( $close eq 'yes' );
+}
+
+sub _wYesNoCancel
+{
+	my $window	= shift;
+	my $msg		= shift;
+	
+	my $windowConfirm = Gtk2::MessageDialog -> new_with_markup( 
+		$window,
+		'GTK_DIALOG_DESTROY_WITH_PARENT',
+		'GTK_MESSAGE_QUESTION',
+		'none',
+		$msg
+	);   
+	$windowConfirm -> add_buttons( 'gtk-yes' => 'yes', 'gtk-no'=> 'no', 'gtk-cancel'=> 'cancel');
+	$windowConfirm -> set_icon_from_file( $APPICON );
+	$windowConfirm -> set_title( "Confirm action : $APPNAME (v$APPVERSION)" );
+	
+	$windowConfirm -> show_all();
+	my $close = $windowConfirm -> run;
+	$windowConfirm -> destroy();
+	
+	return ( ( $close eq 'delete-event' ) || ( $close eq 'cancel' ) ) ? 'cancel' : $close;
+}
+
+sub _cfgSanityCheck
+{
+	my $cfg = shift;
+	
+	$$cfg{'defaults'}{'version'}						//= $APPVERSION;
+	$$cfg{'defaults'}{'auto accept key'}				//= 1;
+	$$cfg{'defaults'}{'record command history'}			//= 1;
+	$$cfg{'defaults'}{'show screenshots'}				//= 1;
+	$$cfg{'defaults'}{'back color'}						//= '#000000000000';
+	$$cfg{'defaults'}{'close terminal on disconnect'}	//= '';
+	$$cfg{'defaults'}{'close to tray'}					//= 1;
+	$$cfg{'defaults'}{'command prompt'}					//= '[#%\$>]|\:\/\s*$';
+	$$cfg{'defaults'}{'cursor shape'}					//= 'block';
+	$$cfg{'defaults'}{'debug'}							//= 0;
+	$$cfg{'defaults'}{'tabs in main window'}			//= 0;
+	$$cfg{'defaults'}{'hide on connect'}				//= 0;
+	$$cfg{'defaults'}{'ping port before connect'}		//= 0;
+	$$cfg{'defaults'}{'ping port timeout'}				//= 1;
+	$$cfg{'defaults'}{'open connections in tabs'}		//= 1;
+	$$cfg{'defaults'}{'proxy ip'}						//= '';
+	$$cfg{'defaults'}{'proxy pass'}						//= '';
+	$$cfg{'defaults'}{'proxy port'}						//= '';
+	$$cfg{'defaults'}{'proxy user'}						//= '';
+	$$cfg{'defaults'}{'shell binary'}					//= '/bin/bash';
+	$$cfg{'defaults'}{'shell options'}					//= '-login';
+	$$cfg{'defaults'}{'shell directory'}				//= $ENV{'HOME'};
+	$$cfg{'defaults'}{'tabs position'}					//= 'top';
+	$$cfg{'defaults'}{'save on exit'}					//= 0;
+	$$cfg{'defaults'}{'start iconified'}				//= 0;
+	$$cfg{'defaults'}{'start maximized'}				//= 1;
+	$$cfg{'defaults'}{'start main maximized'}			//= 0;
+	$$cfg{'defaults'}{'terminal transparency'}			//= 0;
+	$$cfg{'defaults'}{'terminal font'}					//= 'Monospace 9';
+	$$cfg{'defaults'}{'terminal scrollback lines'}		//= 5000;
+	$$cfg{'defaults'}{'text color'}						//= '#cc62cc62cc62';
+	$$cfg{'defaults'}{'connected color'}				//= '#0CBA00'; #mid-green
+	$$cfg{'defaults'}{'disconnected color'}				//= '#FF0000'; #red
+	$$cfg{'defaults'}{'new data color'}					//= '#0088FF'; #blue
+	$$cfg{'defaults'}{'timeout command'}				//= 60;
+	$$cfg{'defaults'}{'timeout connect'}				//= 40;
+	$$cfg{'defaults'}{'use bw icon'}					//= 0;
+	$$cfg{'defaults'}{'use proxy'}						//= 0;
+	$$cfg{'defaults'}{'use system proxy'}				//= 1;
+	$$cfg{'defaults'}{'only one instance'}				//= 1;
+	$$cfg{'defaults'}{'save session logs'}				//= 0;
+	$$cfg{'defaults'}{'session logs folder'}			//= $CFG_DIR . '/session_logs';
+	$$cfg{'defaults'}{'session logs amount'}			//= 10;
+
+	$$cfg{'defaults'}{'global variables'}				//= {};
+	
+	$$cfg{'tmp'}{'changed'}								= 0;
+	
+	if ( ! defined $$cfg{'environments'} )
+	{
+		$$cfg{'environments'} = {};
+		return 1;
+	}
+	
+	foreach my $env ( keys %{ $$cfg{'environments'} } )
+	{
+		foreach my $conn ( keys %{ $$cfg{'environments'}{$env} } )
+		{
+			my $screenshot_file = '';
+			if ( ! defined $$cfg{'environments'}{$env}{$conn}{'screenshot'} )
+			{
+				$screenshot_file = $CFG_DIR . '/screenshots/pac_screenshot_' . rand( length( $env . '-' . $conn ) ). '.jpg';
+				while( -f $screenshot_file ) { $screenshot_file = $CFG_DIR . '/screenshots/pac_screenshot_' . rand( length( $env . '-' . $conn ) ). '.jpg'; }
+			}
+			
+			$$cfg{'environments'}{$env}{$conn}{'description'}		//= "Connection with '$env' -> '$conn'";
+			$$cfg{'environments'}{$env}{$conn}{'screenshot'}		//= $screenshot_file;
+			$$cfg{'environments'}{$env}{$conn}{'title'}				//= "$env - $conn";
+			$$cfg{'environments'}{$env}{$conn}{'ip'}				//= '';
+			$$cfg{'environments'}{$env}{$conn}{'port'}				//= 22;
+			$$cfg{'environments'}{$env}{$conn}{'user'}				//= '';
+			$$cfg{'environments'}{$env}{$conn}{'pass'}				//= '';
+			$$cfg{'environments'}{$env}{$conn}{'method'}			//= 'ssh';
+			$$cfg{'environments'}{$env}{$conn}{'options'}			//= '';
+			$$cfg{'environments'}{$env}{$conn}{'manual'}			//= 0;
+			$$cfg{'environments'}{$env}{$conn}{'use proxy'}			//= 0;
+			
+			$$cfg{'environments'}{$env}{$conn}{'variables'}			//= [];
+			
+			if ( ! defined $$cfg{'environments'}{$env}{$conn}{'local before'} )
+			{
+				$$cfg{'environments'}{$env}{$conn}{'local before'} = [];
+			}
+			else
+			{
+				my $i = 0;
+				foreach my $hash ( @{ $$cfg{'environments'}{$env}{$conn}{'local before'} } )
+				{
+					if ( ! ref( $hash ) )
+					{
+						delete $$cfg{'environments'}{$env}{$conn}{'local before'}[$i];
+						$$cfg{'environments'}{$env}{$conn}{'local before'}[$i]{'default'} //= 1;
+						$$cfg{'environments'}{$env}{$conn}{'local before'}[$i]{'command'} = $hash // '';
+					}
+					else
+					{
+						$$hash{'default'} //= 1;
+						$$hash{'command'} //= '';
+					}
+					++$i;
+				}
+			}
+			
+			if ( ! defined $$cfg{'environments'}{$env}{$conn}{'expect'} )
+			{
+				$$cfg{'environments'}{$env}{$conn}{'expect'} = [];
+			}
+			else
+			{
+				my $i = 0;
+				foreach my $hash ( @{ $$cfg{'environments'}{$env}{$conn}{'expect'} } )
+				{
+					if ( ! ref( $hash ) )
+					{
+						delete $$cfg{'environments'}{$env}{$conn}{'expect'}[$i];
+						$$cfg{'environments'}{$env}{$conn}{'expect'}[$i]{'active'} = 1;
+						$$cfg{'environments'}{$env}{$conn}{'expect'}[$i]{'expect'} = '';
+						$$cfg{'environments'}{$env}{$conn}{'expect'}[$i]{'send'} = '';
+						$$cfg{'environments'}{$env}{$conn}{'expect'}[$i]{'hidden'} = 0;
+						$$cfg{'environments'}{$env}{$conn}{'expect'}[$i]{'return'} = 1;
+					}
+					else
+					{
+						$$hash{'active'} //= 1;
+						$$hash{'expect'} //= '';
+						$$hash{'hidden'} //= 0;
+						$$hash{'send'} //= '';
+						$$hash{'return'} //= 1;
+					}
+					++$i;
+				}
+			}
+			
+			if ( ! defined $$cfg{'environments'}{$env}{$conn}{'local connected'} )
+			{
+				$$cfg{'environments'}{$env}{$conn}{'local connected'} = [];
+			}
+			else
+			{
+				my $i = 0;
+				foreach my $hash ( @{ $$cfg{'environments'}{$env}{$conn}{'local connected'} } )
+				{
+					if ( ! ref( $hash ) )
+					{
+						delete $$cfg{'environments'}{$env}{$conn}{'local connected'}[$i];
+						$$cfg{'environments'}{$env}{$conn}{'local connected'}[$i]{'confirm'} = 0;
+						$$cfg{'environments'}{$env}{$conn}{'local connected'}[$i]{'txt'} = $hash // '';
+					}
+					else
+					{
+						$$hash{'confirm'} //= 0;
+						$$hash{'txt'} //= '';
+					}
+					++$i;
+				}
+			}
+			
+			if ( ! defined $$cfg{'environments'}{$env}{$conn}{'macros'} )
+			{
+				$$cfg{'environments'}{$env}{$conn}{'macros'} = [];
+			}
+			else
+			{
+				my $i = 0;
+				foreach my $hash ( @{ $$cfg{'environments'}{$env}{$conn}{'macros'} } )
+				{
+					if ( ! ref( $hash ) )
+					{
+						delete $$cfg{'environments'}{$env}{$conn}{'macros'}[$i];
+						$$cfg{'environments'}{$env}{$conn}{'macros'}[$i]{'confirm'} = 0;
+						$$cfg{'environments'}{$env}{$conn}{'macros'}[$i]{'txt'} = $hash // '';
+					}
+					else
+					{
+						$$hash{'confirm'} //= 0;
+						$$hash{'txt'} //= '';
+					}
+					++$i;
+				}
+			}
+			
+			if ( ! defined $$cfg{'environments'}{$env}{$conn}{'local after'} )
+			{
+				$$cfg{'environments'}{$env}{$conn}{'local after'} = [];
+			}
+			else
+			{
+				my $i = 0;
+				foreach my $hash ( @{ $$cfg{'environments'}{$env}{$conn}{'local after'} } )
+				{
+					if ( ! ref( $hash ) )
+					{
+						delete $$cfg{'environments'}{$env}{$conn}{'local after'}[$i];
+						$$cfg{'environments'}{$env}{$conn}{'local after'}[$i]{'default'} = 1;
+						$$cfg{'environments'}{$env}{$conn}{'local after'}[$i]{'command'} = $hash // '';
+					}
+					else
+					{
+						$$hash{'default'} //= 1;
+						$$hash{'command'} //= '';
+					}
+					++$i;
+				}
+			}
+			if ( ! defined $$cfg{'environments'}{$env}{$conn}{'terminal options'} )
+			{
+				$$cfg{'environments'}{$env}{$conn}{'terminal options'}{'use personal settings'} = 0;
+			}
+			else
+			{
+				$$cfg{'environments'}{$env}{$conn}{'terminal options'}{'use personal settings'} //= 0;
+			}
+		}
+	}
+	
+	return 1;
+}
+
+sub _cipherCFG
+{
+	my $cfg = shift;
+	
+	foreach my $var ( keys %{ $$cfg{'defaults'}{'global variables'} } )
+	{
+		( $$cfg{'defaults'}{'global variables'}{$var}{'hidden'} eq '1' ) and $$cfg{'defaults'}{'global variables'}{$var}{'value'} = $CIPHER -> encrypt_hex( $$cfg{'defaults'}{'global variables'}{$var}{'value'} );
+	}
+	
+	foreach my $env ( keys %{ $$cfg{'environments'} } )
+	{
+		foreach my $conn ( keys %{ $$cfg{'environments'}{$env} } )
+		{
+			$$cfg{'environments'}{$env}{$conn}{'pass'} = $CIPHER -> encrypt_hex( $$cfg{'environments'}{$env}{$conn}{'pass'} );
+			
+			foreach my $hash ( @{ $$cfg{'environments'}{$env}{$conn}{'expect'} } )
+			{
+				( $$hash{'hidden'} eq '1' ) and $$hash{'send'} = $CIPHER -> encrypt_hex( $$hash{'send'} );
+			}
+		}
+	}
+	
+	return 1;
+}
+
+sub _decipherCFG
+{
+	my $cfg = shift;
+	
+	foreach my $var ( keys %{ $$cfg{'defaults'}{'global variables'} } )
+	{
+		( $$cfg{'defaults'}{'global variables'}{$var}{'hidden'} eq '1' ) and $$cfg{'defaults'}{'global variables'}{$var}{'value'} = $CIPHER -> decrypt_hex( $$cfg{'defaults'}{'global variables'}{$var}{'value'} );
+	}
+	
+	foreach my $env ( keys %{ $$cfg{'environments'} } )
+	{
+		foreach my $conn ( keys %{ $$cfg{'environments'}{$env} } )
+		{
+			$$cfg{'environments'}{$env}{$conn}{'pass'} = $CIPHER -> decrypt_hex( $$cfg{'environments'}{$env}{$conn}{'pass'} );
+			
+			foreach my $hash ( @{ $$cfg{'environments'}{$env}{$conn}{'expect'} } )
+			{
+				( $$hash{'hidden'} eq '1' ) and $$hash{'send'} = $CIPHER -> decrypt_hex( $$hash{'send'} );
+			}
+		}
+	}
+	
+	return 1;
+}
+
+sub _wakeOnLan
+{
+	my $ip			= shift // '';
+	my $port		= shift // 9;
+	my $mac			= shift // '';
+	my $ping_port	= shift // 7;
+	
+	#if ( ( $ip !~ /^([1-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.([0-9]|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$/go ) && ( $ip !~ /^$/go ) )
+	if ( ! ( ( $ip =~ /\b(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\b/ ) && $1 < 255 && $2 < 255 && $3 < 255 && $4 < 255 ) )
+	{
+		my $packed_ip = gethostbyname( $ip );
+		defined $packed_ip and $ip = inet_ntoa( $packed_ip );
+	}
+	
+	my %w;
+	
+	# Create the dialog window,
+	$w{window}{data} = Gtk2::Dialog -> new_with_buttons(
+		"$APPNAME (v$APPVERSION) : Wake On LAN",
+		undef,
+		'modal',
+		'gtk-ok'		=> 'ok',
+		'gtk-cancel'	=> 'cancel'
+	);
+	# and setup some dialog properties.
+	$w{window}{data} -> set_default_response( 'ok' );
+	$w{window}{data} -> set_position( 'center' );
+	$w{window}{data} -> set_icon_from_file( $APPICON );
+	$w{window}{data} -> set_size_request( -1, -1 );
+	$w{window}{data} -> set_resizable( 0 );
+	$w{window}{data} -> set_border_width( 5 );
+		
+		# Create an HBox to contain a picture and a label
+		$w{window}{gui}{hbox} = Gtk2::HBox -> new( 0, 0 );
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{hbox}, 1, 1, 5 );
+		$w{window}{gui}{hbox} -> set_border_width( 5 );
+			
+			# Create image
+			$w{window}{gui}{img} = Gtk2::Image -> new_from_icon_name( 'computer', 'dialog' );
+			$w{window}{gui}{hbox} -> pack_start( $w{window}{gui}{img}, 0, 1, 5 );
+			
+			# Create 1st label
+			$w{window}{gui}{lblup} = Gtk2::Label -> new();
+			$w{window}{gui}{hbox} -> pack_start( $w{window}{gui}{lblup}, 1, 1, 5 );
+			$w{window}{gui}{lblup} -> set_markup( "<b>Enter the following data\nand press 'Ok' to send Magic Packet:</b>" );
+		
+		$w{window}{gui}{table} = Gtk2::Table -> new( 3, 3, 0 );
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{table}, 1, 1, 5 );
+			
+			# Create MAC label
+			$w{window}{gui}{lblmac} = Gtk2::Label -> new();
+			$w{window}{gui}{table} -> attach_defaults( $w{window}{gui}{lblmac}, 0, 1, 0, 1 );
+			$w{window}{gui}{lblmac} -> set_text( 'MAC Address: ' );
+			
+			# Create MAC entry widget
+			$w{window}{gui}{entrymac} = Gtk2::Entry -> new();
+			$w{window}{gui}{table} -> attach_defaults( $w{window}{gui}{entrymac}, 1, 2, 0, 1 );
+			$w{window}{gui}{entrymac} -> set_text( $mac );
+			$w{window}{gui}{entrymac} -> set_activates_default( 1 );
+			$w{window}{gui}{entrymac} -> grab_focus;
+			
+			# Create MAC icon widget
+			$w{window}{gui}{iconmac} = Gtk2::Image -> new_from_stock( 'gtk-no', 'menu' );
+			$w{window}{gui}{table} -> attach_defaults( $w{window}{gui}{iconmac}, 2, 3, 0, 1 );
+			
+			# Create HOST label
+			$w{window}{gui}{lblip} = Gtk2::Label -> new();
+			$w{window}{gui}{table} -> attach_defaults( $w{window}{gui}{lblip}, 0, 1, 1, 2 );
+			$w{window}{gui}{lblip} -> set_text( 'Host: ' );
+			
+			# Create HOST entry widget
+			$w{window}{gui}{entryip} = Gtk2::Entry -> new();
+			$w{window}{gui}{table} -> attach_defaults( $w{window}{gui}{entryip}, 1, 2, 1, 2 );
+			$w{window}{gui}{entryip} -> set_text( $ip );
+			$w{window}{gui}{entryip} -> set_sensitive( 0 );
+			$w{window}{gui}{entryip} -> set_activates_default( 0 );
+			
+			# Create IP icon widget
+			$w{window}{gui}{iconip} = Gtk2::Image -> new_from_stock( 'gtk-yes', 'menu' );
+			$w{window}{gui}{table} -> attach_defaults( $w{window}{gui}{iconip}, 2, 3, 1, 2 );
+			
+			# Create PORT label
+			$w{window}{gui}{lblport} = Gtk2::Label -> new();
+			$w{window}{gui}{table} -> attach_defaults( $w{window}{gui}{lblport}, 0, 1, 2, 3 );
+			$w{window}{gui}{lblport} -> set_text( 'Port Number: ' );
+			
+			# Create PORT entry widget
+			$w{window}{gui}{entryport} = Gtk2::SpinButton -> new_with_range( 1, 65535, 1 );
+			$w{window}{gui}{table} -> attach_defaults( $w{window}{gui}{entryport}, 1, 2, 2, 3 );
+			$w{window}{gui}{entryport} -> set_value( $port );
+			$w{window}{gui}{entryport} -> set_activates_default( 1 );
+		
+		$w{window}{gui}{separator} = Gtk2::HSeparator -> new();
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{separator}, 0, 1, 0 );
+		
+		$w{window}{gui}{cbbroadcast} = Gtk2::CheckButton -> new_with_label( 'Send to broadcast' );
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{cbbroadcast}, 0, 1, 0 );
+		$w{window}{gui}{cbbroadcast} -> set_active( 1 );
+		$w{window}{gui}{cbbroadcast} -> set_sensitive( $ip );
+		
+		$w{window}{gui}{separator} = Gtk2::HSeparator -> new();
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{separator}, 0, 1, 0 );
+		
+		$w{window}{gui}{lblstatus} = Gtk2::Label -> new();
+		$w{window}{data} -> vbox -> pack_start( $w{window}{gui}{lblstatus}, 0, 1, 5 );
+		$w{window}{gui}{lblstatus} -> set_text( "Retrieving MAC for '$ip' ..." );
+	
+	# Show the window (in a modal fashion)
+	$w{window}{data} -> show_all();
+	
+	# Setup some callbacks...
+	$w{window}{gui}{entrymac} -> signal_connect( 'event' => sub
+	{
+		$w{window}{data} -> get_action_area -> foreach( sub
+		{
+			return 1 unless $_[0] -> get_label eq 'gtk-ok';
+			$w{window}{gui}{iconmac} -> set_from_stock( $w{window}{gui}{entrymac} -> get_chars( 0, -1 ) =~ /^[\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}$/go ? 'gtk-yes' : 'gtk-no', 'menu' );
+			$_[0] -> set_sensitive( $w{window}{gui}{entrymac} -> get_chars( 0, -1 ) =~ /^[\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}$/go ? 1 : 0 );
+		} );
+		return 0;
+	} );
+	
+	##########################################################
+	# Try some net movement to resolve remote host's MAC address
+	if ( $ip )
+	{
+		$w{window}{gui}{table} -> set_sensitive( 0 );
+		Gtk2 -> main_iteration while Gtk2 -> events_pending;
+		my $PING = Net::Ping -> new( 'tcp' );
+		$PING -> tcp_service_check( 1 );
+		$PING -> port_number( $ping_port );
+		my $up = $PING -> ping( $ip, '1' );
+		$mac = Net::ARP::arp_lookup( '', $ip );
+		if ( $mac eq 'unknown' )
+		{
+			$up = $PING -> ping( $ip, '1' );
+			$mac = Net::ARP::arp_lookup( '', $ip );
+		}
+		$w{window}{gui}{iconip} -> set_from_stock( $up ? 'gtk-connect' : 'gtk-disconnect', 'menu' );
+		$w{window}{gui}{entrymac} -> set_text( $mac );
+		$w{window}{gui}{entrymac} -> select_region( 0, length( $mac ) );
+		$w{window}{gui}{lblstatus} -> set_text( "'$ip' TCP port $ping_port seems to be " . ( $up ? 'REACHABLE' : 'UNREACHABLE' ) );
+		$w{window}{gui}{table} -> set_sensitive( 1 );
+		$w{window}{gui}{entrymac} -> grab_focus;
+		$w{window}{data} -> get_action_area -> foreach( sub
+		{
+			return 1 unless $_[0] -> get_label eq 'gtk-ok';
+			$_[0] -> set_sensitive( $w{window}{gui}{entrymac} -> get_chars( 0, -1 ) =~ /^[\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}[:-][\da-fA-F]{2}$/go ? 1 : 0 );
+		} );
+		Gtk2 -> main_iteration while Gtk2 -> events_pending;
+	}
+	else
+	{
+		$w{window}{gui}{entrymac} -> set_text( $mac );
+		$w{window}{gui}{entrymac} -> select_region( 0, length( $mac ) );
+		$w{window}{gui}{lblstatus} -> set_text( 'No IP to test reachability' );
+	}
+
+	##########################################################
+	
+	my $ok = $w{window}{data} -> run();
+	$mac = $w{window}{gui}{entrymac} -> get_chars( 0, -1 );
+	$w{window}{data} -> destroy();
+	
+	return 1 if $ok ne 'ok';
+	
+	my $broadcast = $w{window}{gui}{cbbroadcast} -> get_active;
+	
+	# Prepare UDP socket
+	socket( S, PF_INET, SOCK_DGRAM, getprotobyname( 'udp' ) ) || die "ERROR: Can't create socket ($!)";
+	$broadcast and setsockopt(S, SOL_SOCKET, SO_BROADCAST, 1) || die "ERROR: Can't change socket properties ($!)";
+	
+	# Prepare destination addresss
+	$ip and my $ipaddr = inet_aton( $ip ) || die "ERROR: Unknown host: $ip";
+	my $paddr = $broadcast ? sockaddr_in( 0x2fff, INADDR_BROADCAST ) :  sockaddr_in( $port, $ipaddr )|| die "ERROR: Sockaddr_in failed ($!)";
+	
+	# Prepare the magic packet ( 6 times the hex 'ff' and 16 times the 'clean' MAC addresss)
+	my $new_mac = $mac;
+	$new_mac =~ s/[:-]//g;
+	my $MAGIC = ( "\xff" x 6 ) . ( pack( 'H12', $new_mac ) x 16 );
+	my $SIZE = $broadcast ? 0 : length( $MAGIC );
+
+	if ( ! send( S, $MAGIC, $SIZE, $paddr ) )
+	{
+		_wMessage( undef, "ERROR: Sending magic packet to $ip (MAC: $mac) failed:\n$!" );
+		return 0;
+	}
+	else
+	{
+		send( S, $MAGIC, $SIZE, $paddr );
+		send( S, $MAGIC, $SIZE, $paddr );
+		# Try sending a couple of packets to standard WoL ports of provided host ip...
+		send( S, $MAGIC, $SIZE, sockaddr_in( 7, $ipaddr ) );
+		send( S, $MAGIC, $SIZE, sockaddr_in( 9, $ipaddr ) );
+		
+		_wMessage( undef, "Wake On Lan 'Magic Packet'\nCORRECTLY sent to " . ( $broadcast ? 'BROADCAST' : "IP: $ip" ) . "\n(MAC: $mac)" );
+	}
+	
+	return 1;
+}
+
+sub _deleteOldestSessionLog
+{
+	my $cfg = shift;
+	my $env = shift;
+	my $conn = shift;
+	
+	my $folder = $$cfg{'defaults'}{'session logs folder'};
+	my $max = $$cfg{'defaults'}{'session logs amount'};
+	
+	opendir( my $F, $folder ) or die "ERROR: Could not open folder '$folder' for reading: $!";
+	
+	my @total;
+	foreach my $file ( readdir $F )
+	{
+		next unless $file =~ /^PAC_\[(.+)_(.+)\]_\[(\d{8})_(\d{6})\]\.txt$/g;
+		my ( $fenv, $fconn, $fdate, $ftime ) = ( $1, $2, $3, $4 );
+		next unless ( $fenv eq $env && $fconn eq $conn );
+		push( @total, "$folder/$file" );
+	}
+	
+	close $F;
+	
+	return 1 unless scalar( @total ) ge $max;
+	
+	my $i = 0;
+	foreach my $file ( sort { $a cmp $b } @total )
+	{
+		unlink $file or die "ERROR: Could not delete oldest log file '$file': $!";
+		last if ( ( scalar( @total ) - $max ) <= $i++ );
+	}
+	
+	return 1;
+}
+
+sub _buildPIDFile
+{
+	if ( ! open( F, ">$PIDFILE" ) )
+	{
+		print STDERR "ERROR: Could not open pid file '$PIDFILE' for writting: $!";
+		return 0;
+	}
+	
+	print F $$;
+	
+	close F;
+	
+	return 1;
+}
+
+sub _checkPIDFile
+{
+	open( F, "$PIDFILE" ) or return 0;
+	my $pid = <F>;
+	close F;
+	
+	return $pid;
+}
+
+sub _deletePIDFile { return unlink $PIDFILE; }
+
+1;