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/&/&/go;
$str =~ s/</</go;
$str =~ s/>/>/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;