#!/usr/bin/perl -w -T
# EtherWake server
# Features:
#  * user/password login method
#  * multiple interface management

use base qw(Net::Server::Fork);
use strict;

$ENV{PATH}='/bin:/usr/bin:/usr/local/bin';

__PACKAGE__->run((conf_file=>'/etc/ewserver.conf',
		ew_cmd=>'/usr/sbin/etherwake',
		ew_table_user=>'users',
		ew_table_intr=>'intauth',
		port=>'*:1980',
		cidr_allow=>'127.0.0.0/8',
		cidr_allow=>'10.0.0.0/16'));
exit;

use DBI;

sub options {
	my $self=shift;
	my $prop=$self->{server};
	my $ref=shift;
	
	$self->SUPER::options($ref);

	foreach ( qw(ew_auth_dsn ew_auth_user ew_auth_pass ew_cmd ew_table_user ew_table_intr) ) {
		$prop->{$_} = undef unless exists $prop->{$_};
		$ref->{$_} = \$prop->{$_};
	}
}

sub configure_hook {
	my $self=shift;
	$self->{server}->{setsid} = 1;
	$self->{server}->{log_file} = 'Sys::Syslog';
	$self->{server}->{syslog_ident} = 'ewserver';
}

sub post_accept_hook {
	my $self=shift;
	if (defined($self->{server}->{peeraddr})) {
		$self->log(2,"connection from: ".$self->{server}->{peeraddr});
	}
}

sub process_request {
	my $self=shift;
	my $user;
	my $pw;
	my $dbh;
	my $loginok=0;
	my $intf;
	while (my $line = <STDIN>) {
		chop($line);
		if ( $line =~ m/^USER (\S*)\s*$/ ) {
			$user=$1;
			print "200 Ok\n";
			$loginok=0;
		} elsif ( $line =~ m/^PASS (\S*)\s*$/ ) {
			$pw=$1;
			if(!defined($user)) {
				print "300 Undefined User\n";
				next;
			}
			if(!defined($dbh)) {
				if (not($dbh=DBI->connect($self->{server}->{ew_auth_dsn},
						$self->{server}->{ew_auth_user},
						$self->{server}->{ew_auth_pass},
						{ RaiseError=> 0, AutoCommit=> 0 }))) {
					$self->log(0,"db connection error:$DBI::errstr");
					print "500 db connection error\n";
					next;
				}
				if(!defined($dbh)) {
					$self->log(0,"db connection error:$DBI::errstr");
					print "500 db connection error\n";
					next;
				}
				$self->log(3,"db connected");
			}
			my $ref;
			$ref=$dbh->prepare(qq{SELECT pw FROM $self->{server}->{ew_table_user} WHERE login=?});
			if (! defined($ref) ) {
				$self->log(1,"query error:$DBI::errstr");
				print "510 Db Query error\n";
				next;
			}
			if (! $ref->execute($user)) {
				$self->log(1,"execute error:$DBI::errstr");
				print "510 Db Query error\n";
				next;
			}
			my $rpw;
			if ($ref->rows() == 0) {
				print "301 User unknown\n";
				next;
			}
			($rpw)=$ref->fetchrow_array();
			if ($rpw eq $pw) {
				print "220 Authenticated\n";
				$self->log(2,$user." logged in");
				$loginok=1;
			} else {
				print "300 Bad login\n";
				$self->log(2,$user." login failed");
				$loginok=0;
			}
		} elsif ( $line =~ m/^INTF (\S*)\s*$/ ) {
			$intf=$1;
			if ($loginok == 0) {
				print "300 Not logged in\n";
				next;
			}
			my $ref=$dbh->prepare(qq{
			SELECT login FROM $self->{server}->{ew_table_intr} WHERE ? ~ intreg AND login = ?
			});
			if (! defined($ref) ) {
					$self->log(1,"Db query failed:$DBI::errstr");
					print "510 Db query failed";
					$intf=undef;
			}
			if (! $ref->execute($intf,$user)) {
					$self->log(1,"Db query failed:$DBI::errstr");
					print "510 Db query failed";
					$intf=undef;
			}
			if ($ref->rows() == 0) {
				print "302 Not authorized to basztassad this interface\n";
				$intf=undef;
				next;
			} 
			print "200 Ok\n";
		} elsif ( $line =~ m/^WAKE (\S*)\s*$/ ) {
			my $mac=$1;
			if ($loginok == 0) {
				print "300 Not logged in\n";
				next;
			}
			if (! defined($intf)) {
				print "303 Undefined interface\n";
				next;
			}
			if ($mac=~m/^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$/ ) {
			} else {
				print "330 Not a valid mac address.\n";
				next;
			}
			my $retval=system(($self->{server}->{ew_cmd},"-b","-i",$intf,$mac));
			if($retval) {
				print "400 Command retval:".$retval."\n";
			} else {
				$self->log(2,"magic packet sent to ".$mac." via ".$intf." by user ".$user);
				print "200 Sending magic packet ok.\n";
			}
		} elsif ( $line =~ m/^QUIT\s*$/ ) {
			print "200 The End.\n";
			last;
		} else {
			print "400 Bad commandlofasz:".$line."\n";
		}
	}
	$dbh->disconnect()
}

sub post_process_request_hook {
	my $self=shift;
	if (defined($self->{server}->{peeraddr})) {
		$self->log(2,"leaved from: ".$self->{server}->{peeraddr});
	}
}

1;
