From 827898cc219c20d38ed29aab78edabcc3feaa5a6 Mon Sep 17 00:00:00 2001
From: darode <drd.sqki@gmail.com>
Date: Mon, 11 Apr 2011 16:35:45 +0000
Subject: [PATCH] 2011-04-11 Dario Rodriguez <dario.rodriguez@artica.es>

	* pandora_agent_installer: Added code to install tentacle_server with
	pandora agent.
	* AIX/pandora_agent.conf: Added two new parameters proxy_mode and
	proxy_user.
	* tentacle_server: Added tentacle server with proxy support.
	* pandora_agent_daemon: Add feature to stop tentacle_server when
	pandora agent stops.
	* Linux/pandora_agent.conf: Added two new parameters proxy_mode and
	proxy_user.
	* HP-UX/pandora_agent.conf: Added two new parameters proxy_mode and
	proxy_user.
	* pandora_agent: Added support to tentacle_server and proxy_mode.
	* SunOS/pandora_agent.conf: Added two new parameters proxy_mode and
	proxy_user.



git-svn-id: https://svn.code.sf.net/p/pandora/code/trunk@4185 c3f86ba8-e40f-0410-aaad-9ba5e7f4b01f
---
 pandora_agents/unix/AIX/pandora_agent.conf   |    6 +
 pandora_agents/unix/ChangeLog                |   17 +
 pandora_agents/unix/HP-UX/pandora_agent.conf |    6 +
 pandora_agents/unix/Linux/pandora_agent.conf |    6 +
 pandora_agents/unix/SunOS/pandora_agent.conf |    6 +
 pandora_agents/unix/pandora_agent            |   29 +-
 pandora_agents/unix/pandora_agent_daemon     |   21 +
 pandora_agents/unix/pandora_agent_installer  |   16 +-
 pandora_agents/unix/tentacle_server          | 1473 ++++++++++++++++++
 9 files changed, 1578 insertions(+), 2 deletions(-)
 create mode 100755 pandora_agents/unix/tentacle_server

diff --git a/pandora_agents/unix/AIX/pandora_agent.conf b/pandora_agents/unix/AIX/pandora_agent.conf
index 7145f5dc0f..e9d73924c4 100755
--- a/pandora_agents/unix/AIX/pandora_agent.conf
+++ b/pandora_agents/unix/AIX/pandora_agent.conf
@@ -82,6 +82,12 @@ transfer_mode tentacle
 # If set to 1 allows the agent to be configured via the web console (Only Enterprise version) 
 # remote_config 1
 
+# If set to 1 start babel agent in proxy mode 
+# proxy_mode 1
+
+# User which runs tentacle server in proxy mode. Required with if proxy mode is enable (by default babel)
+# proxy_user pandora
+
 # User the agent will run as
 #pandora_user root
 
diff --git a/pandora_agents/unix/ChangeLog b/pandora_agents/unix/ChangeLog
index d01d17c1b5..2b7d355bd1 100644
--- a/pandora_agents/unix/ChangeLog
+++ b/pandora_agents/unix/ChangeLog
@@ -1,3 +1,20 @@
+2011-04-11 Dario Rodriguez <dario.rodriguez@artica.es>
+
+	* pandora_agent_installer: Added code to install tentacle_server with
+	pandora agent.
+	* AIX/pandora_agent.conf: Added two new parameters proxy_mode and 
+	proxy_user.
+	* tentacle_server: Added tentacle server with proxy support.
+	* pandora_agent_daemon: Add feature to stop tentacle_server when
+	pandora agent stops.
+	* Linux/pandora_agent.conf: Added two new parameters proxy_mode and 
+	proxy_user.
+	* HP-UX/pandora_agent.conf: Added two new parameters proxy_mode and 
+	proxy_user.
+	* pandora_agent: Added support to tentacle_server and proxy_mode.
+	* SunOS/pandora_agent.conf: Added two new parameters proxy_mode and 
+	proxy_user.
+
 2011-04-04 Dario Rodriguez <dario.rodriguez@artica.es>
 
 	* pandora_agent_installer: Fixed an error with chown in AIX OS.
diff --git a/pandora_agents/unix/HP-UX/pandora_agent.conf b/pandora_agents/unix/HP-UX/pandora_agent.conf
index 7c768b816c..765abc0358 100755
--- a/pandora_agents/unix/HP-UX/pandora_agent.conf
+++ b/pandora_agents/unix/HP-UX/pandora_agent.conf
@@ -82,6 +82,12 @@ transfer_mode tentacle
 # If set to 1 allows the agent to be configured via the web console (Only Enterprise version) 
 # remote_config 1
 
+# If set to 1 start babel agent in proxy mode 
+# proxy_mode 1
+
+# User which runs tentacle server in proxy mode. Required with if proxy mode is enable (by default babel)
+# proxy_user pandora
+
 # User the agent will run as
 #pandora_user root
 
diff --git a/pandora_agents/unix/Linux/pandora_agent.conf b/pandora_agents/unix/Linux/pandora_agent.conf
index 43cddeda88..8f05e7e72c 100755
--- a/pandora_agents/unix/Linux/pandora_agent.conf
+++ b/pandora_agents/unix/Linux/pandora_agent.conf
@@ -97,6 +97,12 @@ transfer_mode tentacle
 # If set to 1 allows the agent to be configured via the web console (Only Enterprise version) 
 #remote_config 1
 
+# If set to 1 start babel agent in proxy mode 
+# proxy_mode 1
+
+# User which runs tentacle server in proxy mode. Required with if proxy mode is enable (by default babel)
+# proxy_user pandora
+
 # Number of threads to execute modules in parallel
 #agent_threads 1
 
diff --git a/pandora_agents/unix/SunOS/pandora_agent.conf b/pandora_agents/unix/SunOS/pandora_agent.conf
index 676bcd055a..e49508583d 100755
--- a/pandora_agents/unix/SunOS/pandora_agent.conf
+++ b/pandora_agents/unix/SunOS/pandora_agent.conf
@@ -82,6 +82,12 @@ transfer_mode tentacle
 # If set to 1 allows the agent to be configured via the web console (Only Enterprise version) 
 # remote_config 1
 
+# If set to 1 start babel agent in proxy mode 
+# proxy_mode 1
+
+# User which runs tentacle server in proxy mode. Required with if proxy mode is enable (by default babel)
+# proxy_user pandora
+
 # User the agent will run as
 #pandora_user root
 
diff --git a/pandora_agents/unix/pandora_agent b/pandora_agents/unix/pandora_agent
index 6d14f58896..6baf24a5e5 100755
--- a/pandora_agents/unix/pandora_agent
+++ b/pandora_agents/unix/pandora_agent
@@ -148,7 +148,9 @@ my %Conf = (
 	'agent_threads' => 1,
 	'udp_server_port' => 41122,
 	'udp_server_auth_address' => '0.0.0.0',
-	'udp_server' => 0
+	'udp_server' => 0,
+	'proxy_mode' => 0,
+	'proxy_user' => 'pandora'
 );
 
 # Modules
@@ -608,6 +610,28 @@ sub check_remote_config () {
 	`renice "$Conf{'pandora_nice'}" "$PID"`;
 }
 
+################################################################################
+# SUB launch_tentacle_proxy
+# Launchs tentacle server in proxy mode.
+################################################################################
+sub launch_tentacle_proxy () {
+		
+	# Check if proxy mode is enable.
+	if ($Conf{'proxy_mode'}) {		
+			
+		my $auxPid = fork();	
+		
+		if (! $auxPid) {
+			
+			my $new_process = "sudo -u ".$Conf{'proxy_user'}." tentacle_server -b ".$Conf{'server_ip'}." -g ".$Conf{'server_port'};
+
+			log_message ('setup', 'Proxy mode enabled');	
+					
+			exec ($new_process);		
+		}
+	}
+}
+
 ################################################################################
 # Delete old collections and download new collections.
 ################################################################################
@@ -1379,6 +1403,9 @@ sleep ($Conf{'delayed_startup'});
 my $PID = $$;
 `renice "$Conf{'pandora_nice'}" "$PID"`;
 
+#Launch tentacle server in proxy mode if needed
+launch_tentacle_proxy();
+
 # Add the plugins directory to the PATH
 $ENV{'PATH'} .= ":$ConfDir/plugins";
 
diff --git a/pandora_agents/unix/pandora_agent_daemon b/pandora_agents/unix/pandora_agent_daemon
index 35f799691a..ee44b9d66d 100755
--- a/pandora_agents/unix/pandora_agent_daemon
+++ b/pandora_agents/unix/pandora_agent_daemon
@@ -23,6 +23,7 @@
 PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin
 PANDORA_PATH=/etc/pandora
 DAEMON=/usr/bin/pandora_agent
+DAEMON_TENTACLE=/usr/bin/tentacle_server
 LOGFILE=/var/log/pandora/pandora_agent.log
 PANDORA_USER=root
 
@@ -59,6 +60,19 @@ pidof_pandora () {
 	echo $PANDORA_PID
 }
 
+pidof_tentacle () {
+	COLUMNS=400
+	OS_NAME=`uname -s`
+	if [ $OS_NAME = "HP-UX" ]
+	then
+		TENTACLE_PID=`ps -ex | grep "$DAEMON_TENTACLE" | grep -v grep | head -1 | awk '{ print $1 }'`
+	else
+		TENTACLE_PID=`ps -Af | grep "$DAEMON_TENTACLE" | grep -v grep | head -1 | awk '{ print $2 }'`
+	fi
+	
+	echo $TENTACLE_PID
+}
+
 if [ ! -f $DAEMON ]
 then
 	echo "Pandora FMS Agent not found at $DAEMON, please check setup"
@@ -90,6 +104,13 @@ case "$1" in
 			echo "Stopping Pandora Agent."
 			su $PANDORA_USER -c "kill -9 $PANDORA_PID >/dev/null 2>&1"
 		fi
+		
+		TENTACLE_PID=`pidof_tentacle`
+		if [ ! -z "$TENTACLE_PID" ]
+		then
+			echo "Stopping Tentacle Server."
+			kill $TENTACLE_PID > /dev/null 2>&1
+		fi		
 	;;
 
 	status)
diff --git a/pandora_agents/unix/pandora_agent_installer b/pandora_agents/unix/pandora_agent_installer
index 6429793ac1..2048ea16a6 100755
--- a/pandora_agents/unix/pandora_agent_installer
+++ b/pandora_agents/unix/pandora_agent_installer
@@ -23,6 +23,7 @@ PANDORA_CFG=/etc/pandora
 PANDORA_LOG_DIR=/var/log/pandora
 PANDORA_LOG=pandora_agent.log
 TENTACLE=/usr/bin/tentacle_client
+TENTACLE_SERVER=/usr/bin/tentacle_server
 PANDORA_MAN=/usr/share/man
 
 MODE=$1
@@ -88,6 +89,7 @@ uninstall () {
 		PANDORA_EXEC_BIN=/usr/local/bin/pandora_agent_exec
 		PANDORA_CFG=/usr/local/etc/pandora
 		TENTACLE=/usr/local/bin/tentacle_client
+		TENTACLE_SERVER=/usr/local/bin/tentacle_server
 		PANDORA_MAN=/usr/local/man
 	fi
 
@@ -102,6 +104,7 @@ uninstall () {
 		rm -Rf $PANDORA_BASE$PANDORA_TEMP/data_out 2> /dev/null
 	else
 		rm -Rf $PANDORA_BASE$PANDORA_TEMP 2> /dev/null
+		rm -Rf $PANDORA_BASE$TENTACLE_SERVER 2> /dev/null
 	fi 
 	rm -Rf $PANDORA_BASE$PANDORA_CFG/pandora_agent.conf 2> /dev/null
 	
@@ -161,6 +164,7 @@ install () {
 		PANDORA_EXEC_BIN=/usr/local/bin/pandora_agent_exec
 		PANDORA_CFG=/usr/local/etc/pandora
 		TENTACLE=/usr/local/bin/tentacle_client
+		TENTACLE_SERVER=/usr/local/bin/tentacle_server
 		PANDORA_MAN=/usr/local/man
 	fi
 
@@ -317,7 +321,17 @@ install () {
 	cp -r collections $PANDORA_BASE$PANDORA_HOME
 	chmod -R 700 $PANDORA_BASE$PANDORA_HOME/collections
 	ln -s $PANDORA_BASE$PANDORA_HOME/collections $PANDORA_BASE$PANDORA_CFG
-
+	
+	echo "Copying tentacle server to $PANDORA_BASE$TENTACLE_SERVER"
+	cp tentacle_server $PANDORA_BASE$TENTACLE_SERVER
+	chmod 755 $PANDORA_BASE$TENTACLE_SERVER	
+	if [ "$OS_NAME" = "FreeBSD" ]
+	then
+		chown $PANDORA_USER:wheel $PANDORA_BASE$TENTACLE_SERVER	
+	else
+		chown $PANDORA_USER:0 $PANDORA_BASE$TENTACLE_SERVER
+	fi	
+	
 	echo "Copying tentacle client to $PANDORA_BASE$TENTACLE"
 	cp tentacle_client $PANDORA_BASE$TENTACLE
 	chmod 755 $PANDORA_BASE$TENTACLE
diff --git a/pandora_agents/unix/tentacle_server b/pandora_agents/unix/tentacle_server
new file mode 100755
index 0000000000..ec20f8b0ce
--- /dev/null
+++ b/pandora_agents/unix/tentacle_server
@@ -0,0 +1,1473 @@
+#!/usr/bin/perl
+##########################################################################
+# Tentacle Server
+# See http://www.openideas.info/wiki for protocol description.
+# Tentacle have IANA assigned port tpc/41121 as official port.
+##########################################################################
+# Copyright (c) 2007-2008  Ramon Novoa  <rnovoa@artica.es>
+# Copyright (c) 2005-2010 Artica Soluciones Tecnologicas S.L
+#
+# tentacle_server.pl	Tentacle Server. See http://www.openideas.info/wiki for
+#                       protocol description.
+#
+# 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; version 2 of the License.
+# 
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+##########################################################################
+
+package tentacle::server;
+=head1 NAME
+
+tentacle_server - Tentacle Server
+
+=head1 VERSION
+
+Version 0.3.0
+
+=head1 USAGE
+
+tentacle_server B<< -s F<storage_directory> >> [I<options>]
+
+=head1 DESCRIPTION
+
+B<tentacle_server(1)> is a server for B<tentacle>, a B<client/server> file transfer protocol that aims to be:
+
+=over
+
+=item    * Secure by design.
+
+=item    * Easy to use.
+
+=item    * Versatile and cross-platform. 
+
+=back 
+
+Tentacle was created to replace more complex tools like SCP and FTP for simple file transfer/retrieval, and switch from authentication mechanisms like .netrc, interactive logins and SSH keys to X.509 certificates. Simple password authentication over a SSL secured connection is supported too.
+
+The client and server (B<TCP port 41121>) are designed to be run from the command line or called from a shell script, and B<no configuration files are needed>. 
+
+=cut
+
+
+use strict;
+use warnings;
+use Getopt::Std;
+use IO::Select;
+use IO::Socket::INET;
+use Thread::Semaphore;
+use POSIX ":sys_wait_h";
+
+# Program version
+our $VERSION = '0.3.0';
+
+# Address to listen on
+my $t_address = '0.0.0.0';
+
+# Block size for socket read/write operations in bytes
+my $t_block_size = 1024;
+
+# Client socket
+my $t_client_socket;
+
+# Run as daemon, 1 true, 0 false
+my $t_daemon = 0;
+
+# Storage directory
+my $t_directory = '';
+
+# Filters
+my @t_filters;
+
+# String containing quoted invalid file name characters
+my $t_invalid_chars = '\?\[\]\/\\\=\+\<\>\:\;\'\,\*\~';
+
+# Log messages, 1 enabled, 0 disabled
+my $t_log = 0;
+
+# Maximum number of simultaneous connections
+my $t_max_conn = 10;
+
+# Maximum file size allowed by the server in bytes
+my $t_max_size = 2000000;
+
+# File overwrite, 1 enabled, 0 disabled
+my $t_overwrite = 0;
+
+# Port to listen on
+my $t_port = 41121;
+
+# Server password
+my $t_pwd = '';
+
+# Do not output error messages, 1 enabled, 0 disabled
+my $t_quiet = 0;
+
+# Number of retries for socket read/write operations
+my $t_retries = 3;
+
+# Select handler
+my $t_select;
+
+# Semaphore
+my $t_sem;
+
+# Server socket
+my $t_server_socket;
+
+# Use SSL, 1 true, 0 false
+my $t_ssl = 0;
+
+# SSL ca certificate file
+my $t_ssl_ca = '';
+
+# SSL certificate file
+my $t_ssl_cert = '';
+
+# SSL private key file
+my $t_ssl_key = '';
+
+# SSL private key password
+my $t_ssl_pwd = '';
+
+# Timeout for socket read/write operations in seconds
+my $t_timeout = 1;
+
+#Bridge IP to actuate as a proxy
+my $t_proxy_ip = undef;
+
+# Proxy port by default 41121
+my $t_proxy_port = 41121;
+
+# Proxy socket
+my $t_proxy_socket;
+
+# Proxy selected handler
+my $t_proxy_select;
+
+################################################################################
+## SUB print_help
+## Print help screen.
+################################################################################
+sub print_help {
+
+	print ("Usage: $0 -s <storage directory> [options]\n\n");
+	print ("Tentacle server v$VERSION. See http://www.openideas.info/wiki for protocol description.\n\n");
+	print ("Options:\n");
+	print ("\t-a ip_address\tAddress to listen on (default $t_address).\n");
+	print ("\t-c number\tMaximum number of simultaneous connections (default $t_max_conn).\n");
+	print ("\t-d\t\tRun as daemon.\n");
+	print ("\t-e cert\t\tOpenSSL certificate file. Enables SSL.\n");
+	print ("\t-f ca_cert\tVerify that the peer certificate is signed by a ca.\n");
+	print ("\t-h\t\tShow help.\n");
+	print ("\t-i\t\tFilters.\n");
+	print ("\t-k key\t\tOpenSSL private key file.\n");
+	print ("\t-m size\t\tMaximum file size in bytes (default ${t_max_size}b).\n");
+	print ("\t-o\t\tEnable file overwrite.\n");
+	print ("\t-p port\t\tPort to listen on (default $t_port).\n");
+	print ("\t-q\t\tQuiet. Do now print error messages.\n");
+	print ("\t-r number\tNumber of retries for network opertions (default $t_retries).\n");
+	print ("\t-t time\t\tTime-out for network operations in seconds (default ${t_timeout}s).\n");
+	print ("\t-v\t\tBe verbose.\n");
+	print ("\t-w\t\tPrompt for OpenSSL private key password.\n");
+	print ("\t-x pwd\t\tServer password.\n\n");
+	print ("\t-b proxy_ip_address\t\tProxied server address.\n\n");	
+	print ("\t-g proxy_port\t\tPort of proxied server.\n\n");
+}
+
+################################################################################
+## SUB daemonize
+## Turn the current process into a daemon.
+################################################################################
+sub daemonize {
+	my $pid;
+
+	require POSIX;
+
+	chdir ('/') || error ("Cannot chdir to /: $!.");
+	umask 0;
+
+	open (STDIN, '/dev/null') || error ("Cannot read /dev/null: $!.");
+
+	# Do not be verbose when running as a daemon
+	open (STDOUT, '>/dev/null') || error ("Cannot write to /dev/null: $!.");
+	open (STDERR, '>/dev/null') || error ("Cannot write to /dev/null: $!.");
+
+	# Fork
+	$pid = fork ();
+	if (! defined ($pid)) {
+		error ("Cannot fork: $!.");
+	}
+
+	# Parent
+	if ($pid != 0) {
+		exit;
+	}
+
+	# Child
+	POSIX::setsid () || error ("Cannot start a new session: $!.");
+}
+
+################################################################################
+## SUB start_win_service
+## Turn the current process into a Windows service.
+################################################################################
+#sub start_win_service {
+#	require Win32::Daemon;
+#
+#	# Tell the OS to start the service
+#	Win32::Daemon::StartService ();
+#
+#	# Wait until the service manager is ready
+#	while (SERVICE_START_PENDING != Win32::Daemon::State()) {
+#		sleep (1);
+#	}
+#
+#	# Tell the service manager we are running
+#	Win32::Daemon::State (SERVICE_RUNNING);
+#
+#	# Call Win32::Daemon::StopService() when done
+#}
+
+################################################################################
+## SUB parse_options
+## Parse command line options and initialize global variables.
+################################################################################
+sub parse_options {
+	my %opts;
+	my $tmp;
+
+	# Get options
+	if (getopts ('a:c:de:f:hi:k:m:op:qr:s:t:vwx:b:g:', \%opts) == 0 || defined ($opts{'h'})) {
+		print_help ();
+		exit 1;
+	}
+
+	# Address
+	if (defined ($opts{'a'})) {
+		$t_address = $opts{'a'};
+		if ($t_address !~ /^[a-zA-Z\.]+$/ && ($t_address  !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
+			|| $1 < 0 || $1 > 255 || $2 < 0 || $2 > 255
+			|| $3 < 0 || $3 > 255 || $4 < 0 || $4 > 255)) {
+			error ("Address $t_address is not valid.");
+		}
+	}
+	
+	# Maximum simultaneous connections
+	if (defined ($opts{'c'})) {
+		$t_max_conn = $opts{'c'};
+		if ($t_max_conn !~ /^\d+$/ || $t_max_conn < 1) {
+			error ("Invalid number of maximum simultaneous connections.");
+		}
+	}
+
+	# Run as daemon
+	if (defined ($opts{'d'})) {
+		if ($^ eq 'MSWin32') {
+			error ("-d flag not available for this OS.");
+		}
+
+		$t_daemon = 1;
+	}
+
+	# Enable SSL
+	if (defined ($opts{'e'})) {
+
+		require IO::Socket::SSL;
+
+		$t_ssl_cert = $opts{'e'};
+		if (! -f $t_ssl_cert) {
+			error ("File $t_ssl_cert does not exist.");
+		}
+
+		$t_ssl = 1;
+	}
+
+	# Verify peer certificate
+	if (defined ($opts{'f'})) {
+		$t_ssl_ca = $opts{'f'};
+		if (! -f $t_ssl_ca) {
+			error ("File $t_ssl_ca does not exist.");
+		}
+	}
+
+	# Filters (regexp:dir;regexp:dir...)
+	if (defined ($opts{'i'})) {
+		my @filters = split (';', $opts{'i'});
+		foreach my $filter (@filters) {
+			my ($regexp, $dir) = split (':', $filter);
+			next unless defined ($regexp) && defined ($dir);
+
+			# Remove any trailing /
+			my $char = chop ($dir);
+			$dir .= $char if ($char) ne '/';
+
+			push(@t_filters, [$regexp, $dir]);
+		}
+	}
+
+	# SSL private key file
+	if (defined ($opts{'k'})) {
+		$t_ssl_key = $opts{'k'};
+		if (! -f $t_ssl_key) {
+			error ("File $t_ssl_key does not exist.");
+		}
+	}
+
+	# Maximum file size
+	if (defined ($opts{'m'})) {
+		$t_max_size = $opts{'m'};
+		if ($t_max_size !~ /^\d+$/ || $t_max_size < 1) {
+			error ("Invalid maximum file size.");
+		}
+	}
+
+	# File overwrite
+	if (defined ($opts{'o'})) {
+		$t_overwrite = 1;
+	}
+
+	# Port
+	if (defined ($opts{'p'})) {
+		$t_port = $opts{'p'};
+		if ($t_port !~ /^\d+$/ || $t_port < 1 || $t_port > 65535) {
+			error ("Port $t_port is not valid.");
+		}
+	}
+
+	# Quiet mode
+	if (defined ($opts{'q'})) {
+		$t_quiet = 1;
+	}
+
+	# Retries
+	if (defined ($opts{'r'})) {
+		$t_retries = $opts{'r'};
+		if ($t_retries !~ /^\d+$/ || $t_retries < 1) {
+			error ("Invalid number of retries for network operations.");
+		}
+	}
+
+	# Storage directory
+	if (defined ($opts{'s'})) {
+
+		$t_directory = $opts{'s'};
+		
+		# Check that directory exists
+		if (! -d $t_directory) {
+			error ("Directory $t_directory does not exist.");
+		}
+
+		# Check directory permissions
+		if (! -w $t_directory) {
+			error ("Cannot write to directory $t_directory.");
+		}
+
+		# Remove the trailing / if present
+		$tmp = chop ($t_directory);
+		if ($tmp ne '/') {
+			$t_directory .= $tmp;
+		}
+	}
+	else {
+		if (! defined($opts{'b'})) {
+			print_help ();
+			exit 1;
+		}
+	}
+
+	# Timeout
+	if (defined ($opts{'t'})) {
+		$t_timeout = $opts{'t'};
+		if ($t_timeout !~ /^\d+$/ || $t_timeout < 1) {
+			error ("Invalid timeout for network operations.");
+		}
+	}
+
+	# Be verbose
+	if (defined ($opts{'v'})) {
+		$t_log = 1;
+	}
+
+	# SSL private key password
+	if (defined ($opts{'w'})) {
+		$t_ssl_pwd = ask_passwd ("Enter private key file password: ", "Enter private key file password again for confirmation: ");
+	}
+
+	# Server password
+	if (defined ($opts{'x'})) {
+		$t_pwd = $opts{'x'};
+	}
+	
+	#Proxy IP address
+	if (defined ($opts{'b'})) {
+		$t_proxy_ip = $opts{'b'};
+		if ($t_proxy_ip !~ /^[a-zA-Z\.]+$/ && ($t_proxy_ip  !~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
+			|| $1 < 0 || $1 > 255 || $2 < 0 || $2 > 255
+			|| $3 < 0 || $3 > 255 || $4 < 0 || $4 > 255)) {
+			error ("Proxy address $t_proxy_ip is not valid.");
+		}		
+	}
+	
+	# Proxy Port
+	if (defined ($opts{'g'})) {
+		$t_proxy_port = $opts{'g'};
+		if ($t_proxy_port !~ /^\d+$/ || $t_proxy_port < 1 || $t_proxy_port > 65535) {
+			error ("Proxy port $t_port is not valid.");
+		}
+	}	
+}
+
+################################################################################
+## SUB sigchld_handler
+## Handle child process termination.
+################################################################################
+sub sigchld_handler {
+
+	while (waitpid(-1, &WNOHANG) > 0) {
+		$t_sem->up ();
+	}
+
+	$SIG{CHLD} = \&sigchld_handler;
+}
+
+################################################################################
+## SUB start_proxy
+## Open the server socket.
+################################################################################
+sub open_proxy {
+
+	# Connect to server
+	$t_proxy_socket = IO::Socket::INET->new (
+	    PeerAddr => $t_proxy_ip,
+		PeerPort => $t_proxy_port,
+	);
+
+	if (! defined ($t_proxy_socket)) {
+		error ("Cannot connect to $t_proxy_ip on port $t_proxy_port: $!.");
+	}
+	
+	# Create proxy selector
+	$t_proxy_select = IO::Select->new ();	
+	$t_proxy_select->add ($t_proxy_socket);	
+	
+}
+
+################################################################################
+## SUB start_server
+## Open the server socket.
+################################################################################
+sub start_server {
+
+	$t_server_socket = IO::Socket::INET->new (
+		Listen    => $t_max_conn,
+		LocalAddr => $t_address,
+		LocalPort => $t_port,
+		Proto     => 'tcp',
+		ReuseAddr     => 1,
+	);
+
+        if (! defined ($t_server_socket)) {
+		error ("Cannot open socket for address $t_address on port $t_port: $!.");
+	}
+
+	print_log ("Server listening on $t_address port $t_port (press <ctr-c> to stop)");
+	
+	# Say message if tentacle proxy is enable
+	if (defined ($t_proxy_ip)) {
+		print_log ("Proxy Mode enable, data will be sent to $t_proxy_ip port $t_proxy_port");	
+	}
+
+}
+
+################################################################################
+## SUB send_data_proxy
+## Send data to proxy socket.
+################################################################################
+sub send_data_proxy {
+	my $data = $_[0];
+	my $retries = 0;
+	my $size;
+	my $total = 0;
+	my $written;
+
+	$size = length ($data);
+
+	while (1) {
+
+		# Try to write data to the socket
+		if ($t_proxy_select->can_write ($t_timeout)) {
+
+			$written = syswrite ($t_proxy_socket, $data, $size - $total, $total);
+
+			# Write error
+			if (! defined ($written)) {
+				error ("Connection error from " . $t_proxy_socket->sockhost () . ": $!.");
+			}
+			
+			# EOF
+			if ($written == 0) {
+				error ("Connection from " . $t_proxy_socket->sockhost () . " unexpectedly closed.");
+			}
+	
+		}
+
+		$total += $written;
+
+		# Check if all data was written
+		if ($total == $size) {
+			return;
+		}
+
+		# Retry
+		$retries++;
+
+		# But check for error conditions first
+		if ($retries > $t_retries) {
+			error ("Connection from " . $t_proxy_socket->sockhost () . " timed out.");
+		}
+	}
+}
+
+################################################################################
+## SUB close_proxy
+## Close the proxy socket.
+################################################################################
+sub close_proxy {
+	$t_proxy_socket->close ();
+	print_log ("Proxy socket closed");
+	
+}
+
+################################################################################
+## SUB stop_server
+## Close the server socket.
+################################################################################
+sub stop_server {
+	
+	$t_server_socket->close ();
+	print_log ("Server going down");
+	
+	exit 0;
+}
+
+################################################################################
+## SUB start_ssl
+## Convert the client socket to an IO::Socket::SSL socket.
+################################################################################
+sub start_ssl {
+	my $err;
+
+	if ($t_ssl_ca eq '') {
+		IO::Socket::SSL->start_SSL (
+			$t_client_socket,
+			SSL_cert_file => $t_ssl_cert,
+			SSL_key_file => $t_ssl_key,
+			SSL_passwd_cb => sub {return $t_ssl_pwd},
+			SSL_server => 1,
+			# Verify peer
+			SSL_verify_mode => 0x01,
+		);
+	}
+	else {
+		IO::Socket::SSL->start_SSL (
+			$t_client_socket,
+			SSL_ca_file => $t_ssl_ca,
+			SSL_cert_file => $t_ssl_cert,
+			SSL_key_file => $t_ssl_key,
+			SSL_passwd_cb => sub {return $t_ssl_pwd},
+			SSL_server => 1,
+			# Fail verification if no peer certificate exists
+			SSL_verify_mode => 0x03,
+		);
+	}
+
+	$err = IO::Socket::SSL::errstr ();
+	if ($err ne '') {
+		error ($err);
+	}
+
+	print_log ("SSL started for " . $t_client_socket->sockhost ());
+}
+
+################################################################################
+## SUB accept_connection
+## Accept an incoming connection and fork.
+################################################################################
+sub accept_connection {
+	my $pid;
+
+	while (1) {
+
+		# Accept connection
+		$t_client_socket = $t_server_socket->accept ();
+
+		if (! defined ($t_client_socket)) {
+
+			# EINTR
+			if ($! ne '') {
+				next;
+			}
+
+			error ("accept: $!.");
+		}
+
+		last;
+	}
+
+	print_log ("Client connected from " . $t_client_socket->sockhost ());
+
+	# Fork and serve the client
+	$pid = fork ();
+	if (! defined ($pid)) {
+		error ("Cannot fork: $!.");
+	}
+	
+	# Child
+	if ($pid == 0) {
+		
+		# We do not need the server socket
+		$t_server_socket->close ();
+		
+		# Add client socket to select queue
+		$t_select = IO::Select->new ();
+		$t_select->add ($t_client_socket);
+		
+		# Start SSL
+		if ($t_ssl == 1) {
+			start_ssl ();
+		}
+
+		# Authenticate client
+		if ($t_pwd ne '') {
+			auth_pwd ();
+		}
+
+		# Check if proxy mode is enable
+		if (defined ($t_proxy_ip)) {
+
+			serve_proxy_connection ();	
+			
+		} else {
+			
+			serve_connection ();
+		}
+		
+		$t_client_socket->close ();
+
+		# Must exit now
+		exit;
+	}
+
+	# Parent
+	$t_client_socket->close ();
+}
+
+################################################################################
+## SUB serve_proxy_connection
+## Actuate as a proxy between its client and other tentacle server.
+################################################################################
+sub serve_proxy_connection {
+	my $read;
+	my $data=1;
+	
+	# Start a connection with the other Tentacle Server
+	open_proxy();		
+	
+	my $command;
+
+	# Read commands
+	while ($command = recv_command ($t_block_size)) {
+		# Client wants to send a file
+		if ($command =~ /^SEND <(.*)> SIZE (\d+)$/) {
+			recv_file_proxy($command, $2);
+		}
+		# Client wants to receive a file
+		elsif ($command =~ /^RECV <(.*)>$/) {
+			send_file_proxy ($command);
+		}		
+		# Quit
+		elsif ($command =~ /^QUIT$/) {
+			print_log ("Connection closed from " . $t_client_socket->sockhost ());
+			
+			# End proxy connection
+			send_data_proxy("QUIT\n");				
+			last;
+		}
+		# Unknown command
+		else {
+			print_log ("Unknown command '$command' from " . $t_client_socket->sockhost ());
+			last;
+		}		
+	}	
+
+	# End a connection with the other Tentacle Server
+	close_proxy();
+}
+
+################################################################################
+## SUB serve_connection
+## Read and process commands from the client.
+################################################################################
+sub serve_connection {
+	my $command;
+
+	# Read commands
+	while ($command = recv_command ($t_block_size)) {
+		
+		# Client wants to send a file
+		if ($command =~ /^SEND <(.*)> SIZE (\d+)$/) {
+			print_log ("Request to send file '$1' size ${2}b from " . $t_client_socket->sockhost ());
+			recv_file ($1, $2);
+		}
+		# Client wants to receive a file
+		elsif ($command =~ /^RECV <(.*)>$/) {
+			print_log ("Request to receive file '$1' from " . $t_client_socket->sockhost ());
+			send_file ($1);
+		}
+		# Quit
+		elsif ($command =~ /^QUIT$/) {
+			print_log ("Connection closed from " . $t_client_socket->sockhost ());
+			last;
+		}
+		# Unknown command
+		else {
+			print_log ("Unknown command '$command' from " . $t_client_socket->sockhost ());
+			last;
+		}
+	}
+}
+
+################################################################################
+## SUB auth_pwd
+## Authenticate client with server password.
+################################################################################
+sub auth_pwd {
+	my $client_digest;
+	my $command;
+	my $pwd_digest;
+
+	require Digest::MD5;
+	
+	# Wait for password
+	$command = recv_command ($t_block_size);
+	if ($command !~ /^PASS (.*)$/) {
+		error ("Client " . $t_client_socket->sockhost () . " did not authenticate.");
+	}
+	
+	$client_digest = $1;
+	$pwd_digest = Digest::MD5::md5 ($t_pwd);
+	$pwd_digest = Digest::MD5::md5_hex ($pwd_digest);
+
+	if ($client_digest ne $pwd_digest) {
+		error ("Invalid password from " . $t_client_socket->sockhost () . ".");
+	}
+
+	print_log ("Client " . $t_client_socket->sockhost () . " authenticated");
+	send_data ("PASS OK\n");
+}
+
+################################################################################
+## SUB recv_file_proxy
+## Redirect file from agent to proxy
+################################################################################
+sub recv_file_proxy ($$) {
+	my ($command, $size) = @_;
+	
+	# Send command to proxy
+	print_log ("[PROXY] Host: ".$t_client_socket->sockhost()." send ".$command." to ".$t_proxy_socket->sockhost ());	
+	send_data_proxy($command."\n");
+
+	# Proxied server response 
+	my $rc = dump_data($t_proxy_socket, $t_client_socket);
+
+	# Check if there was an error	
+	if ($rc == -1) {
+		return;
+	}
+	
+	# Client send data to server
+	$rc = dump_data($t_client_socket, $t_proxy_socket, $size);		
+	
+	# Check if there was an error
+	if ($rc == -1) {
+		return;
+	}	
+	
+	# Server says if data was recieved or not
+	$rc = dump_data($t_proxy_socket, $t_client_socket);
+	
+	# Check if there was an error
+	if ($rc == -1) {	
+		return;
+	}		
+	
+}
+
+################################################################################
+## SUB recv_file
+## Receive a file of size $_[1] and save it in $t_directory as $_[0].
+################################################################################
+sub recv_file {
+	my $base_name = $_[0];
+	my $data = '';
+	my $file;
+	my $size = $_[1];
+
+	# Check file name
+	if ($base_name =~ /[$t_invalid_chars]/) {
+		print_log ("File '$base_name' size ${size}b from " . $t_client_socket->sockhost () . " has an invalid file name");
+		send_data ("SEND ERR\n");
+		return;
+	}
+
+	# Check file size, empty files are not allowed
+	if ($size < 1 || $size > $t_max_size) {
+		print_log ("File '$base_name' size ${size}b from " . $t_client_socket->sockhost () . " is too big");
+		send_data ("SEND ERR\n");
+		return;
+	}
+	
+	# Apply filters
+	$file = "$t_directory/" . apply_filters ($base_name) . $base_name;
+
+	# Check if file exists
+	if (-f $file && $t_overwrite == 0) {
+		print_log ("File '$base_name' size ${size}b from " . $t_client_socket->sockhost () . " already exists");
+		send_data ("SEND ERR\n");
+		return;
+	}
+
+	send_data ("SEND OK\n");
+
+	# Receive file
+	$data = recv_data_block ($size);
+
+	# Write it to disk
+	open (FILE, "> $file") || error ("Cannot open file '$file' for writing.");
+	binmode (FILE);
+	print (FILE $data);
+	close (FILE);
+
+	send_data ("SEND OK\n");
+	print_log ("Received file '$base_name' size ${size}b from " . $t_client_socket->sockhost ());
+}
+
+################################################################################
+## SUB send_file_proxy
+## Redirect file from agent to proxy
+################################################################################
+sub send_file_proxy ($) {
+	my ($command) = @_;
+	my $size;
+	# Send command to proxy
+	print_log ("[PROXY] ".$t_client_socket->sockhost()." send ".$command." to ".$t_proxy_socket->sockhost ());	
+	send_data_proxy($command."\n");
+
+	$command = recv_command_proxy($t_block_size);
+	
+	if ($command =~ /^RECV SIZE (\d+)$/) {
+		$size = $1;
+	}
+	
+	print_log ("[PROXY] ".$t_proxy_socket->sockhost()." send ".$command." to ".$t_client_socket->sockhost ());		
+	
+	send_data($command."\n");
+	
+	# Client send OK to server
+	my $rc = dump_data($t_client_socket, $t_proxy_socket);
+	
+	# Check if there was an error	
+	if ($rc == -1) {
+		return;
+	}
+	
+	# Proxied server send the file to client
+	$rc = dump_data($t_proxy_socket, $t_client_socket, $size);		
+	
+	# Check if there was an error
+	if ($rc == -1) {
+		return;
+	}	
+	
+}
+
+################################################################################
+## SUB send_file
+## Send a file to the client
+################################################################################
+sub send_file {
+	my $base_name = $_[0];
+	my $data = '';
+	my $file;
+	my $response;
+	my $size;
+
+	# Check file name
+	if ($base_name =~ /[$t_invalid_chars]/) {
+		print_log ("Requested file '$base_name' from " . $t_client_socket->sockhost () . " has an invalid file name");
+		send_data ("RECV ERR\n");
+		return;
+	}
+	
+	# Apply filters
+	$file = "$t_directory/" . apply_filters ($base_name) . $base_name;
+
+	# Check if file exists
+	if (! -f $file) {
+		print_log ("Requested file '$file' from " . $t_client_socket->sockhost () . " does not exist");
+		send_data ("RECV ERR\n");
+		return;
+	}
+
+	$size = -s $file;
+	send_data ("RECV SIZE $size\n");
+	
+	# Wait for client response
+	$response = recv_command ($t_block_size);
+	if ($response ne "RECV OK") {
+		print_log ("Requested file '$file' from " . $t_client_socket->sockhost () . " not sent");
+		return;
+	}
+
+	# Send the file
+	open (FILE, $file) || error ("Cannot open file '$file' for reading.");
+	binmode (FILE);
+
+	while ($data = <FILE>) {
+		send_data ($data);
+	}
+
+	close (FILE);
+
+	print_log ("Requested file '$file' from " . $t_client_socket->sockhost () . " sent");
+}
+
+################################################################################
+# Common functions
+################################################################################
+
+################################################################################
+## SUB print_log
+## Print log messages.
+################################################################################
+sub print_log {
+
+	if ($t_log == 1) {
+		print (STDOUT "[log] $_[0]\n");
+	}
+}
+
+################################################################################
+## SUB error
+## Print an error and exit the program.
+################################################################################
+sub error {
+
+	if ($t_quiet == 0) {
+		print (STDERR "[err] $_[0]\n");
+	}
+
+	exit 1;
+}
+
+################################################################################
+## SUB recv_data_proxy
+## Recv data from proxy socket.
+################################################################################
+sub recv_data_proxy {
+	my $data;
+	my $read;
+	my $retries = 0;
+	my $size = $_[0];
+
+	while (1) {
+
+		# Try to read data from the socket
+		if ($t_proxy_select->can_read ($t_timeout)) {
+			
+			# Read at most $size bytes
+			$read = sysread ($t_proxy_socket, $data, $size);
+
+			# Read error
+			if (! defined ($read)) {
+				error ("Read error from " . $t_proxy_socket->sockhost () . ": $!.");
+			}
+	
+			# EOF
+			if ($read == 0) {
+				error ("Connection from " . $t_proxy_socket->sockhost () . " unexpectedly closed.");
+			}
+	
+			return ($read, $data);
+		}
+
+		# Retry
+		$retries++;
+
+		# But check for error conditions first
+		if ($retries > $t_retries) {
+			error ("Connection from " . $t_proxy_socket->sockhost () . " timed out.");
+		}
+	}
+}
+################################################################################
+## SUB recv_data
+## Read data from the client socket. Returns the number of bytes read and the
+## string of bytes as a two element array.
+################################################################################
+sub recv_data {
+	my $data;
+	my $read;
+	my $retries = 0;
+	my $size = $_[0];
+
+	while (1) {
+
+		# Try to read data from the socket
+		if ($t_select->can_read ($t_timeout)) {
+			
+			# Read at most $size bytes
+			$read = sysread ($t_client_socket, $data, $size);
+
+			# Read error
+			if (! defined ($read)) {
+				error ("Read error from " . $t_client_socket->sockhost () . ": $!.");
+			}
+	
+			# EOF
+			if ($read == 0) {
+				error ("Connection from " . $t_client_socket->sockhost () . " unexpectedly closed.");
+			}
+	
+			return ($read, $data);
+		}
+
+		# Retry
+		$retries++;
+
+		# But check for error conditions first
+		if ($retries > $t_retries) {
+			error ("Connection from " . $t_client_socket->sockhost () . " timed out.");
+		}
+	}
+}
+
+################################################################################
+## SUB send_data
+## Write data to the client socket.
+################################################################################
+sub send_data {
+	my $data = $_[0];
+	my $retries = 0;
+	my $size;
+	my $total = 0;
+	my $written;
+
+	$size = length ($data);
+
+	while (1) {
+
+		# Try to write data to the socket
+		if ($t_select->can_write ($t_timeout)) {
+
+			$written = syswrite ($t_client_socket, $data, $size - $total, $total);
+
+			# Write error
+			if (! defined ($written)) {
+				error ("Connection error from " . $t_client_socket->sockhost () . ": $!.");
+			}
+			
+			# EOF
+			if ($written == 0) {
+				error ("Connection from " . $t_client_socket->sockhost () . " unexpectedly closed.");
+			}
+	
+		}
+
+		$total += $written;
+
+		# Check if all data was written
+		if ($total == $size) {
+			return;
+		}
+
+		# Retry
+		$retries++;
+
+		# But check for error conditions first
+		if ($retries > $t_retries) {
+			error ("Connection from " . $t_client_socket->sockhost () . " timed out.");
+		}
+	}
+}
+
+################################################################################
+## SUB dump_data
+## Dump data from a socket to another one. Following Tentacle Protocol.
+################################################################################
+sub dump_data($$;$) {
+	my ($from, $to, $size) = @_;
+	my $read;
+	my $data;
+	my $buffer = "";
+	my $written;
+	my $total = $size;
+	my $t_select_read = undef;
+	my $t_select_write = undef;
+	
+	# Assign the correct selector for each socket
+	if ($from == $t_proxy_socket) {
+		# We must read from t_proxy_socket
+		$t_select_read = $t_proxy_select;
+		$t_select_write = $t_select;
+	} else {
+		# We must read from t_client_socket
+		$t_select_read = $t_select;
+		$t_select_write = $t_proxy_select;
+	}
+	
+	while (1) {
+		
+		# Ensure we can read from socket
+		if ($t_select_read->can_read()) {
+			# Ensure we can write from socket
+			if ($t_select_write->can_write()) {
+
+				$read = "";
+				$read = sysread ($from, $data, $t_block_size);
+
+				# Read error
+				if (! defined ($read)) {
+					error ("Read error from " . $from->sockhost () . ": $!.");
+					return -1;
+				}
+
+				# EOF
+				if ($read == 0) {
+					error ("Connection from " . $from->sockhost () . " unexpectedly closed.");
+					return -1;
+				}		
+				
+				$written = syswrite ($to, $data, $read);
+				
+				if ($written != $read) {
+					error ("Connection from " . $to->sockhost () . " unexpectedly closed.");
+					return -1;
+				}
+				
+				$buffer .= $data;
+				
+				if (! defined ($size)) {
+					# If no size defined check for \n
+					# because a command was sent.
+
+					if ($buffer =~ /\n$/) {
+						
+						# Delete CHAR \n
+						chop($buffer);
+						
+						print_log ("[PROXY] ".$from->sockhost()." send ".$buffer." to ".$to->sockhost ());
+						
+						# Returns error if proxy returns error to end connection
+						if ($buffer =~ /SEND ERR/) {
+							return -1;
+						} else {
+							return 0;
+						}
+					}
+				} else {
+					# If size is defined check if all bytes were sent
+					$size = $size - $read;
+					
+					if ($size == 0) {
+						print_log ("[PROXY] ".$from->sockhost()." send ".$total."b to ".$to->sockhost());
+						
+						return 0;
+					}
+				}
+			}
+		}
+	}
+}
+
+################################################################################
+## SUB recv_command_proxy
+## Read a command from the proxied server, ended by a new line character.
+################################################################################
+sub recv_command_proxy {
+	my $buffer;
+	my $char;
+	my $command = '';
+	my $read;
+	my $total = 0;
+
+	while (1) {
+		
+		($read, $buffer) = recv_data_proxy ($t_block_size);
+		$command .= $buffer;
+		$total += $read;
+
+		# Check if the command is complete
+		$char = chop ($command);
+		if ($char eq "\n") {
+			return $command;
+		}
+	
+		$command .= $char;
+
+		# Avoid overflow
+		if ($total > $t_block_size) {
+			error ("Received too much data from " . $t_proxy_socket->sockhost () . ".");
+		}
+	}
+}
+
+################################################################################
+## SUB recv_command
+## Read a command from the client, ended by a new line character.
+################################################################################
+sub recv_command {
+	my $buffer;
+	my $char;
+	my $command = '';
+	my $read;
+	my $total = 0;
+
+	while (1) {
+		
+		($read, $buffer) = recv_data ($t_block_size);
+		$command .= $buffer;
+		$total += $read;
+
+		# Check if the command is complete
+		$char = chop ($command);
+		if ($char eq "\n") {
+			return $command;
+		}
+	
+		$command .= $char;
+
+		# Avoid overflow
+		if ($total > $t_block_size) {
+			error ("Received too much data from " . $t_client_socket->sockhost () . ".");
+		}
+	}
+}
+
+################################################################################
+## SUB recv_data_block
+## Read $_[0] bytes of data from the client.
+################################################################################
+sub recv_data_block {
+	my $buffer = '';
+	my $data = '';
+	my $read;
+	my $size = $_[0];
+	my $total = 0;
+
+	while (1) {
+
+		($read, $buffer) = recv_data ($size - $total);
+		$data .= $buffer;
+		$total += $read;
+
+		# Check if all data has been read
+		if ($total == $size) {
+			return $data;
+		}
+	}
+}
+
+################################################################################
+## SUB ask_passwd
+## Asks the user for a password.
+################################################################################
+sub ask_passwd {
+	my $msg1 = $_[0];
+	my $msg2 = $_[1];
+	my $pwd1;
+	my $pwd2;
+
+	require Term::ReadKey;
+
+	# Disable keyboard echo
+	Term::ReadKey::ReadMode('noecho');
+	
+	# Promt for password
+	print ($msg1);
+	$pwd1 = Term::ReadKey::ReadLine(0);
+	print ("\n$msg2");
+	$pwd2 = Term::ReadKey::ReadLine(0);
+	print ("\n");
+
+	# Restore original settings
+	Term::ReadKey::ReadMode('restore');
+
+	if ($pwd1 ne $pwd2) {
+		print ("Error: passwords do not match.\n");
+		exit 1;
+	}
+
+	# Remove the trailing new line character
+	chop $pwd1;
+
+	return $pwd1;
+}
+
+################################################################################
+## SUB apply_filters
+## Applies filters to the given file.
+################################################################################
+sub apply_filters ($) {
+	my ($file_name) = @_;
+
+	foreach my $filter (@t_filters) {
+		my ($regexp, $dir) = @{$filter};
+		if ($file_name =~ /$regexp/) {
+			print_log ("File '$file_name' matches filter '$regexp' (changing to directory '$dir')");
+			return $dir . '/';
+		}
+	}
+
+	return '';
+}
+
+################################################################################
+# Main
+################################################################################
+
+# Never run as root
+if ($> == 0 && $^O ne 'MSWin32') {
+	print ("Error: for safety reasons $0 cannot be run with root privileges.\n");
+	exit 1;
+}
+
+# Parse command line options
+parse_options ();
+
+# Check command line arguments
+if ($#ARGV != -1) {
+	print_help ();
+	exit 1;
+}
+
+# Run as daemon?
+if ($t_daemon == 1 && $^O ne 'MSWin32') {
+	daemonize ();
+}
+
+# Handle ctr-c
+if ($^O eq 'MSWin32') {
+	$SIG{INT2} = \&stop_server;
+}
+else {
+	$SIG{INT} = \&stop_server;
+}
+
+# Handle SIGCHLD
+$SIG{CHLD} = \&sigchld_handler;
+
+start_server ();
+
+# Initialize semaphore
+$t_sem = Thread::Semaphore->new ($t_max_conn);
+
+# Accept connections
+while (1) {
+	$t_sem->down ();
+	accept_connection ();
+}
+
+__END__
+
+=head1 REQUIRED ARGUMENTES
+
+=over 
+
+=item B<< -s F<storage_directory> >>	Root directory to store the files received by the server
+
+=back 
+
+=head1 OPTIONS
+
+=over
+
+=item 	I<-a ip_address>	Address to B<listen> on (default I<0.0.0.0>).
+
+=item	I<-c number>		B<Maximum> number of simultaneous B<connections> (default I<10>).
+
+=item	I<-d>			Run as B<daemon>.
+
+=item	I<-e cert>		B<OpenSSL certificate> file. Enables SSL.
+
+=item	I<-f ca_cert>	Verify that the peer certificate is signed by a B<CA>.
+
+=item	I<-h>			Show B<help>.
+
+=item	I<-i>			B<Filters>.
+
+=item	I<-k key>		B<OpenSSL private key> file.
+
+=item	I<-m size>		B<Maximum file size> in bytes (default I<2000000b>).
+
+=item	I<-o>			Enable file B<overwrite>.
+
+=item	I<-p port>		B<Port to listen> on (default I<41121>).
+
+=item	I<-q>			B<Quiet>. Do now print error messages.
+
+=item	I<-r number>		B<Number of retries> for network opertions (default I<3>).
+
+=item	I<-t time>		B<Time-out> for network operations in B<seconds> (default I<1s>).
+
+=item	I<-v>			Be B<verbose>.
+
+=item	I<-w>			Prompt for B<OpenSSL private key password>.
+
+=item	I<-x> pwd		B<Server password>.
+
+=back
+
+=head1 EXIT STATUS
+
+=over 
+
+=item 0 on Success
+
+=item 1 on Error
+
+=back 
+
+=head1 CONFIGURATION
+
+Tentacle doesn't use any configurationf files, all the configuration is done by the options passed when it's started.
+
+=head1 DEPENDENCIES
+
+L<Getopt::Std>, L<IO::Select>, L<IO::Socket::INET>, L<Thread::Semaphore>, L<POSIX> 
+
+
+=head1 LICENSE
+
+This is released under the GNU Lesser General Public License.
+
+=head1 SEE ALSO
+
+L<Getopt::Std>, L<IO::Select>, L<IO::Socket::INET>, L<Thread::Semaphore>, L<POSIX> 
+
+Protocol description and more info at: L<< http://openideas.info/wiki/index.php?title=Tentacle >>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2005-2010 Artica Soluciones Tecnologicas S.L
+
+=cut
+