package tftpd;

use warnings;
use strict;

use Net::TFTPd 0.03 qw(%OPCODES);
use IO::Socket::INET;
use Data::Dump qw/dump/;
use CouchDB;

use server;

our $debug = server::debug;

our $dir  = "$server::base_dir/tftp";

sub path {
	my $glob = shift;
	my $path = (glob("$dir/$glob"))[0];
	die "can't find anything for $dir/$glob" unless $path;
	warn 'path ', $path if $debug;
	$path =~ s{^$dir}{};
	return $path;
}

use progress_bar;

sub transfer_status {
	my $request = shift;
	my $r = $request->{'_REQUEST_'} || die "no _REQUEST_ in ",dump( $request );

	if( $r->{'OPCODE'} eq $OPCODES{'RRQ'} ) {
		progress_bar::tick( $r->{FileName}, $r->{BlkSize} * $r->{LASTACK}, $r->{BlkSize} * $r->{LASTBLK} );
	} elsif ( $r->{'OPCODE'} eq $OPCODES{'WRQ'} ) {
		die "WRQ disabled";
	} else {
		warn "IGNORED: ", dump( $request );
	}
}

use config;

sub tftp_request {
	my $request = shift;

	warn 'request: ', dump( $request ) if $debug;

	my $ip = $request->{_REQUEST_}->{PeerAddr};
	config::for_ip( $ip );

	if ( $request->{RootDir} ne $dir ) {
		$request->{RootDir} = $dir;
		warn "new root: $dir";
	}

	my $file = $request->{'_REQUEST_'}{'FileName'};
	my $opcode = $OPCODES{$request->{'_REQUEST_'}{'OPCODE'}};

	my $audit = {
		ip => $ip,
		opcode => $opcode,
		path => $file,
		state => 'start',
	};
	CouchDB::audit( $opcode, $audit );

	progress_bar::start;

	# process the request
	if( $request->processRQ() ) {
		my $size = -s "$dir/$file";
		$audit->{state} = 'finish';
		$audit->{size} = $size;
		CouchDB::audit( $opcode, $audit );
	} else {
		$audit->{state} = 'error';
		$audit->{error} = Net::TFTPd->error;
		CouchDB::audit( $opcode, $audit );
		$request->processRQ();
	}

}

use server;

sub start {

	warn 'start';

	# XXX we need to setup listener ourselfs because we need Reuse
	my %params = (
		Proto => 'udp',
#		LocalAddr => $server::ip,
#		LocalAddr => '0.0.0.0',
		LocalPort => 69,
		Reuse => 1,
	);

	my $udpserver = IO::Socket::INET->new(%params);
	die "can't start server ",dump( \%params ), " $!" unless $udpserver;

	my $listener = bless {
		RootDir => $dir,

		ACKtimeout  => 4,
		ACKretries  => 4,
		Readable    => 1,
		Writable    => 0,
		Timeout => 3600,

		CallBack => \&transfer_status,
#		BlkSize => 8192,
#		BlkSize => 512,
		BlkSize => 1456,	# IBM GE seems to be picky
		Debug => 99,
		%params, # merge user parameters
		_UDPSERVER_ => $udpserver,
	}, 'Net::TFTPd';

	warn 'listener: ',dump( $listener ) if $debug;

	CouchDB::audit( 'start', {
		addr => $listener->{LocalAddr},
		port => $listener->{LocalPort},
		timeout => $listener->{Timeout},
		params => { %params },
	});

	while(1) {

		# wait for any request (RRQ or WRQ)
		if(my $request = $listener->waitRQ()) {
			server->refresh;
			tftp_request $request;
		} elsif ( my $error = Net::TFTPd->error ) {
			warn $error;
		}

	}

}

warn "loaded";

1;
