#!/usr/bin/perl
##########################################################################
# Pandora FMS Mail Transfer
# This is a tool for transfering Pandora FMS data files by mail (SMTP/POP)
##########################################################################
# Copyright (c) 2011-2021 Artica Soluciones Tecnologicas S.L
#
# 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
# 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.
##########################################################################

use strict;
use warnings;
use Net::SMTP;
use Mail::POP3Client;
use MIME::Parser;
$| = 1;

# GLOBAL VARIABLES 

my $boundary='frontier';

####### FUNCTIONS #######

########################################################################
## SUB check_args
## Checks the command line arguments given at the function call.
########################################################################
sub check_args(){
	my $num_args = $#ARGV + 1;
	my $action = $ARGV[0];
	my $conf_file = $ARGV[1];
	my $filename = $ARGV[2];
	my $error = "Usage: mail_transfer.pl {send|receive conf_file [FILE]}\n";
	my $error_conf_file = "conf_file does not exist or is not readable\n";
	my $error_filename = "File to send does not exist or is not readable\n";
	
	if (($num_args < 2) || (($action ne "send") && ($action ne "receive"))) {
		die $error;
	} elsif ((!(-e $conf_file)) || (!(-r $conf_file))) {
		die $error_conf_file;
	} elsif (($action eq "send") && ((!(-e $filename)) || (!(-r $filename)))) {
		die $error_filename;
	}
}

########################################################################
## SUB parse_conf
## Reads the entire conf file and stores all the information given
########################################################################
sub parse_conf ($$) {

	my $conf_file = $_[0];
	my $conf_hash = $_[1];
	
	open (CONF, $conf_file);
	my $line;
	
	while (<CONF>)
	{
		$line = $_;
		# Get the smtp user
		if ($line =~ /^smtp_user\s([a-zA-Z0-9\.\_\-\@]+)/) {
			$conf_hash -> {smtp_user} = $1;
		}
		# Get the smtp pass
		elsif ($line =~ /^smtp_pass\s(.+)/) {
			$conf_hash -> {smtp_pass} = $1;
		}
		# Get the smtp hostname
		elsif ($line =~ /^smtp_hostname\s([a-zA-Z0-9\.\_\-\@]+)/) {
			$conf_hash -> {smtp_hostname} = $1;
		}
		# Get the pop3 user
		elsif ($line =~ /^pop3_user\s([a-zA-Z0-9\.\_\-\@]+)/) {
			$conf_hash -> {pop3_user} = $1;
		}
		# Get the pop3 pass
		elsif ($line =~ /^pop3_pass\s(.+)/) {
			$conf_hash -> {pop3_pass} = $1;
		}
		# Get the pop3 hostname
		elsif ($line =~ /^pop3_hostname\s([a-zA-Z0-9\.\_\-\@]+)/) {
			$conf_hash -> {pop3_hostname} = $1;
		}
		# Get the pop3 ssl flag to know if it's enabled or not
		elsif ($line =~ /^pop3_ssl\s(0|1)/) {
			$conf_hash -> {pop3_ssl} = $1;
		}
		# Get the pop3 ssl port
		elsif ($line =~ /^pop3_ssl_port\s([0-9]{1,5})/) {
			$conf_hash -> {pop3_ssl_port} = $1;
		}
		# Get the path where to save the attached file
		elsif ($line =~ /^pathtosave\s(.+)/) {
			$conf_hash -> {pathtosave} = $1;
		}
		# Get the receiver's email where to send the attached file
		elsif ($line =~ /^receiver_email\s([a-zA-Z0-9\.\_\-\@]+)/) {
			$conf_hash -> {receiver_email} = $1;
		}
	}
	close CONF;
}

########################################################################
## SUB send_mail
## Sends an attachement file via email using smtp
########################################################################
sub send_mail($) {
	
	my $conf_hash = $_[0];
	my $smtp;
	my $attachment = $conf_hash -> {filename};

	# Get the filename in case the full path was given
	# Split the full path with '/', the last item will be the filename
	my @file_path = split ('/', $attachment);

	# Get the array's last position with '-1' index
	my $attach_file = $file_path[-1];

	my $host = $conf_hash -> {smtp_hostname};
	my $from = $conf_hash -> {smtp_user};
	my $password = $conf_hash -> {smtp_pass};
	my $to = $conf_hash -> {receiver_email};
		
	open(DATA, $attachment) || die("mail_transfer.pl: ERROR: Could not open the file $attach_file"); 
		my @xml = <DATA>;
	close(DATA);

	$smtp = Net::SMTP->new($host,
                           Hello => $host,
                           Timeout => 30,
                           Debug   => 0,
                          ) || die("mail_trasfer.pl: ERROR: Could not connect to $host");

	$smtp->auth($from, $password);
	$smtp->mail($from);
	$smtp->to($to);
	$smtp->data();
	$smtp->datasend("To: $to\n");
	$smtp->datasend("From: $from\n");
	$smtp->datasend("Subject: Pandora mail transfer\n");
	$smtp->datasend("MIME-Version: 1.0\n");
	$smtp->datasend("Content-Type: application/text; name=" . $attach_file . "\n");
	$smtp->datasend("Content-Disposition: attachment; filename=" . $attach_file . "\n");
	$smtp->datasend("Content-type: multipart/mixed boundary=" . $boundary . "\n");
	$smtp->datasend("\n");
	$smtp->datasend("@xml\n");
	$smtp->dataend() || print "mail_transfer.pl: ERROR: Data end failed: $!";
	$smtp->quit;
}

########################################################################
## SUB receive_mail
## Fetch the last email with 'Pandora mail transfer' as subject and
## download the attached file into the specified folder
########################################################################
sub receive_mail ($) {
	
	my $conf_hash = $_[0];
	my $user = $conf_hash -> {pop3_user};
	my $password = $conf_hash -> {pop3_pass};
	my $host = $conf_hash -> {pop3_hostname};
	my $ssl = $conf_hash -> {pop3_ssl};
	my $ssl_port = $conf_hash -> {pop3_ssl_port};
	my $pathtosave = $conf_hash -> {pathtosave};
    my $pop3;

    if ($ssl == 1){
	    $pop3 = new Mail::POP3Client(
			    USER		=>	$user,
			    PASSWORD	=>	$password,
			    HOST		=>	$host,
			    USESSL		=>	1,
			    PORT		=>	$ssl_port,
			    DEBUG		=>	0
	    ) or die "mail_transfer.pl: Connection failed\n";
    } else {
       $pop3 = new Mail::POP3Client(
			    USER		=>	$user,
			    PASSWORD	=>	$password,
			    HOST		=>	$host,
			    USESSL		=>	0,
			    PORT		=>	110,
			    DEBUG		=>	0
        ) or die "mail_transfer.pl: Connection failed\n";
    }

	my $tot_msg = $pop3->Count();

	if ($tot_msg == 0){
		print "No more emails avalaible\n";
	    return (0); # End program
	}
	elsif ($tot_msg eq '0E0'){
		print "No new emails available\n";
        return (0);
	}
	else{
		printf "There are $tot_msg messages \n\n";
	}

	# the list of valid file extensions. we do extensions, not
	# mime-types, because they're easier to understand from
	# an end-user perspective (no research is required).

	my $valid_exts = "txt xml data";
	my %msg_ids; # used to keep track of seen emails.

	# create a subdirectory if does not exist
	#print "Using directory '$pathtosave' for newly downloaded files.\n";
	if (!(-d $pathtosave)) {
		mkdir($pathtosave, 0777) or die "mail_transfer.pl: Error creating output directory\n";
	}
		
	# get the message to feed to MIME::Parser.
	my $msg = $pop3->HeadAndBody($tot_msg);
	my $header = $pop3->Head($tot_msg);
	
	if (($header !~ /Subject:\sPandora\smail\stransfer/) || ($header !~ /boundary=$boundary/)) {
		print "Deleting message not valid\n";

        # delete current email
        $pop3->Delete($tot_msg);

    	# clean up and close the connection.
    	$pop3->Close;
    
        return -1; 

	}

	# create a MIME::Parser object to
	# extract any attachments found within.
	my $parser = new MIME::Parser;

	$parser->output_dir($pathtosave);
	my $entity = $parser->parse_data($msg);

	# extract our mime parts and go through each one.
	my @parts = $entity->parts;
		
	foreach my $part (@parts) {

		# determine the path to the file in question.
		my $path = ($part->bodyhandle) ? $part->bodyhandle->path : undef;

		# move on if it's not defined,
		# else figure out the extension.
		next unless $path; 
		$path =~ /\w+\.([^.]+)$/;
		my $ext = $1; 
		next unless $ext;

		# we continue only if our extension is correct.
		my $continue; $continue++ if $valid_exts =~ /$ext/i;

    	# delete the blasted thing.
    	unless ($valid_exts =~ /$ext/) {
       		print "  Removing unwanted filetype ($ext): $path\n";
       		unlink $path or print " > Error removing file at $path: $!.";
       		next; # move on to the next attachment or message.
    	}

    	# a valid file type. yummy!
    	print "  Keeping valid file: $path.\n";
    }

    # delete current email
    $pop3->Delete($tot_msg);

	# clean up and close the connection.
	$pop3->Close;
}


####### MAIN #######

# Check the given command line arguments
check_args();

# Once checked store them
my $action = $ARGV[0];
my $conf_file = $ARGV[1];
my $filename = $ARGV[2];

# If the action is 'send', store the 'file_to_send'
my %conf_hash;
if ($action eq "send") {
	$conf_hash {filename} = $filename;
}

# Parse the config file 
parse_conf($conf_file, \%conf_hash);

# Call 'send_mail' function in its case
if ($action eq "send") {
	send_mail(\%conf_hash);
}

# Or call the 'receive_mail' function.
my $returncode = 1;

if ($action eq "receive") {
    while ($returncode != 0) {
        $returncode = receive_mail(\%conf_hash);
    }
}