# Bizanga protocol support module
# Copyright (C) 2009, AllWorldIT
# Copyright (C) 2008, LinuxRulz
# 
# 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 2 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


package cbp::protocols::Bizanga;


use strict;
use warnings;


use POSIX;
use URI::Escape;

use cbp::version;
use cbp::logging;
use cbp::dblayer;
use cbp::protocols;


# User plugin info
our $pluginInfo = {
	name 			=> "Bizanga Protocol Support Module",
	init		 	=> \&init,
	priority	 	=> 50,
	protocol_init	=> \&protocol_init,
	protocol_check	=> \&protocol_check,
	protocol_parse	=> \&protocol_parse,
	protocol_response	=> \&protocol_response,
	protocol_getresponse	=> \&protocol_getresponse,
	protocol_validate	=> \&protocol_validate,
};

# Module configuration
my %config;


# Response data
my ($response,$response_data);


# Create a child specific context
sub init {
	my $server = shift;
	my $inifile = $server->{'inifile'};

	# Defaults
	$config{'enable'} = 1;

	# Check if enabled
	if ($config{'enable'} =~ /^\s*(y|yes|1|on)\s*$/i) {
		$server->log(LOG_NOTICE,"  => Protocol(Bizanga): enabled");
		$config{'enable'} = 1;
	}
}


# Initialize per request data...
sub protocol_init {
	$response = undef;
	$response_data = undef;
}


# Check the buffer to see if this protocol is what we want
sub protocol_check {
	my ($server,$buffer) = @_;
	my $log = defined($server->{'config'}{'logging'}{'protocols'});
	

	# If we not enabled, don't do anything
	return undef if (!$config{'enable'});

	# Check for HTTP header
	if ($buffer =~ /^GET [^\s]+ HTTP\/(\d+)\.(\d+)\015?\012/) {
		my ($a,$b) = ($1,$2);

		$server->log(LOG_DEBUG,"[PROTOCOLS/Bizanga] Possible Bizanga (HTTP/$a.$b) protocol") if ($log);

		if ($buffer =~ /\015?\012\015?\012/) {
			$server->log(LOG_INFO,"[Protocols/Bizanga] Identified Bizanga (HTTP/$a.$b) protocol") if ($log);
			return 1;
		}
	}

	return 0;
}


# Process buffer into sessionData
sub protocol_parse {
	my ($server,$buffer) = @_;
	my $log = defined($server->{'config'}{'logging'}{'bizanga'});

	my %res;

	# remove /?
	$buffer =~ s/^\w+ \/\?//;

	# Loop with each line
	foreach my $item (split /[& ]/, $buffer) {
		# If we don't get a pair, b0rk
		last unless $item =~ s/^([^=]+)=(.*)$//;

		# Clean up strings, and shove into hash
		my ($param,$value) = (uri_unescape($1),uri_unescape($2));
		$res{$param} = $value;
		$server->log(LOG_DEBUG,"[BIZANGA] Request parameter '$param' with value '$value'") if ($log);
	}

	# We need some extra info to make everything else happy...
	$res{'protocol_state'} = "RCPT" if (!defined($res{'protocol_state'}));

	$res{'_protocol_transport'} = "HTTP";

	return \%res;
}


# Process response
sub protocol_response 
{
	my ($server,$resp,$data) = @_;
	my $log = defined($server->{'config'}{'logging'}{'protocols'});


	# Check protocol responses...
	if ($resp == PROTO_PASS) {
		$response = "200";
		$response_data = $data;
		$server->log(LOG_DEBUG,"[PROTOCOL/Bizanga] Received PROTO_PASS with response '$response':'$response_data'") if ($log);
		return CBP_CONTINUE;

	} elsif ($resp == PROTO_OK) {
		$response = "200";
		$response_data = $data;
		$server->log(LOG_DEBUG,"[PROTOCOL/Bizanga] Received PROTO_OK with response '$response':'$response_data'") if ($log);
		return CBP_STOP;

	} elsif ($resp == PROTO_REJECT) {
		if ($data =~ /^(5[0-9]{2}) (.*)/) {
			$response = "403";
			$response_data = $2;
		} else {
			$response = "403";
			$response_data = $data;
		}
		$server->log(LOG_DEBUG,"[PROTOCOL/Bizanga] Received PROTO_REJECT with response '$response':'$response_data'") if ($log);
		return CBP_STOP;

	} elsif ($resp == PROTO_DEFER) {
		if ($data =~ /^(4[0-9]{2}) (.*)/) {
			$response = "401";
			$response_data = $2;
		} else {
			$response = "401";
			$response_data = $data;
		}
		$server->log(LOG_DEBUG,"[PROTOCOL/Bizanga] Received PROTO_DEFER with response '$response':'$response_data'") if ($log);
		return CBP_STOP;

	} elsif ($resp == PROTO_HOLD) {
		$server->log(LOG_ERR,"[PROTOCOL/Bizanga] Unsupported return PROTO_HOLD");
		return CBP_STOP;

	} elsif ($resp == PROTO_REDIRECT) {
		$server->log(LOG_ERR,"[PROTOCOL/Bizanga] Unsupported return PROTO_REDIRECT");
		return CBP_STOP;

	} elsif ($resp == PROTO_DISCARD) {
		$server->log(LOG_ERR,"[PROTOCOL/Bizanga] Unsupported return PROTO_DISCARD");
		return CBP_STOP;

	} elsif ($resp == PROTO_FILTER) {
		$server->log(LOG_ERR,"[PROTOCOL/Bizanga] Unsupported return PROTO_FILTER");
		return CBP_STOP;

	} elsif ($resp == PROTO_PREPEND) {
		$server->log(LOG_ERR,"[PROTOCOL/Bizanga] Unsupported return PROTO_PREPEND");
		return CBP_CONTINUE;

	} elsif ($resp == PROTO_ERROR) {
		$response = "503";
		$response_data = defined($data) ? $data : "Unknown error";
		$server->log(LOG_DEBUG,"[PROTOCOL/Bizanga] Received PROTO_ERROR with response '$response':'$response_data'") if ($log);
		return CBP_STOP;

	} elsif ($resp == PROTO_DB_ERROR) {
		$response = "504";
		$response_data = defined($data) ? $data : "Database error";
		$server->log(LOG_DEBUG,"[PROTOCOL/Bizanga] Received PROTO_DB_ERROR with response '$response':'$response_data'") if ($log);
		return CBP_STOP;
	
	} elsif ($resp == PROTO_DATA_ERROR) {
		$response = "502";
		$response_data = defined($data) ? $data : "Database record error";
		$server->log(LOG_DEBUG,"[PROTOCOL/Bizanga] Received PROTO_DATA_ERROR with response '$response':'$response_data'") if ($log);
		return CBP_STOP;
	
	# Fallthrough
	} else {
		$server->log(LOG_ERR,"[PROTOCOL/Bizanga] Cannot understand response code '$resp'");
		return CBP_ERROR;
	}
}


# Get protocol response
sub protocol_getresponse 
{
	my $resp;


	# If its undefined, set to DUNNO
	if (!defined($response)) {
		$response = "200";
		$response_data = "Pass";
	}

	# Check if we have any additional data
	$response_data = "" if (!defined($response_data));	

	# Get timestamp
	my $timestamp = strftime("%a, %d %b %Y %H:%M:%S %Z",localtime());

	# Construct response
	$resp = "HTTP/1.0 $response $response_data
Date: $timestamp
Content-Length: 0
Content-Type: text/plain
Server: Policyd/".VERSION." (Cluebringer)
Connection: close
";

	return "$resp\n"
}


# Validate protocol data
sub protocol_validate {
	my ($server,$request) = @_;
	my $log = defined($server->{'config'}{'logging'}{'protocols'});
	

	# Check params
	if (!defined($request->{'client_address'}) || !($request->{'client_address'} =~ /^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$/) ) {
		my $client_address = defined($request->{'client_address'}) ? "'".$request->{'client_address'}."'" : "undef";
		$server->log(LOG_DEBUG,"[PROTOCOLS/Bizanga] Error, parameter 'client_address' cannot be $client_address") if ($log);
		return "Required parameter 'client_address' was not found or invalid format";
	}

	if (!defined($request->{'sender'}) || !($request->{'sender'} =~ /^(?:\S+@\S+|)$/) ) {
		my $sender = defined($request->{'sender'}) ? "'".$request->{'sender'}."'" : "undef";
		$server->log(LOG_DEBUG,"[PROTOCOLS/Bizanga] Error, parameter 'sender' cannot be $sender") if ($log);
		return "Required parameter 'sender' was not found or invalid format";
	}

	if (!defined($request->{'recipient'}) || !($request->{'recipient'} =~ /^\S+@\S+$/) ) {
		my $recipient = defined($request->{'recipient'}) ? "'".$request->{'recipient'}."'" : "undef";
		$server->log(LOG_DEBUG,"[PROTOCOLS/Bizanga] Error, parameter 'recipient' cannot be $recipient") if ($log);
		return "Required parameter 'recipient' was not found or invalid format";
	}
}




1;
# vim: ts=4
