#!/usr/bin/perl

# this is the email thingy coded for the cute tenchi boy
# so he doesn't have to put a lame mailto: link on his page
# and get spammed all the time

# coded by, and copyright 1999 by Nick Johnson
# all rights reserved

# form fields should be:
# FROM
# SUBJECT
# MESSAGE
# ACTION 

use CGI;
use Socket;

###########################
# user configurable options:
#
# your outgoing mail server (usually mail.yourdomain)
$mailer = "mail.morons.org";  
#
# where to find the nslookup utility, typically /usr/sbin/nslookup
$nslookup = "/usr/sbin/nslookup";
#
# where to find the mailform HTML file
$form = "/web/spatula.net/feedback/mailform.html";
#
# who to send the messages to (your address)
$mailto = "feedback\@spatula.net";
#
# your mail domain, eg "foo.com" or "gronk.net"
$mydomain = "spatula.net";
##########################
# end of options


CGI::ReadParse(*data);

sub header {
	print "Content-type: text/html\n\n";
	print "<HTML>\n";
}

sub footer {
	print "</HTML>\n";
	
}

sub error {
	my ($message) = @_;
	header();
	open(IN, $form);
	while(<IN>) {
		if (/^<!-- don't/) {
			print $message;
		} else {
			next if /^<!--/;
			s/_(\w+)_/$data{$1}/g;
			print;
		}
	}
	footer();
	exit;
}

sub mailconnect {
	my($host) = @_;
	my($remote, $port, $iaddr, $paddr, $proto, $line);
	
	$remote = $host || $mailer || 'localhost';
	$port = 25;
	$iaddr = inet_aton($remote) || return(0);
	$paddr = sockaddr_in($port, $iaddr);

	$proto = getprotobyname('tcp') || return(0);
	socket(MAILER, PF_INET, SOCK_STREAM, $proto) || return(0);
	connect(MAILER, $paddr) || return(0);
	select MAILER; $|=1; select STDOUT;
}

sub sendexpect {
	my ($send, $expect, $timeout) = @_;
        $send =~ s/\r\n/\n/gs;
        $send =~ s/\n/\r\n/gs;
	print MAILER $send;
	$rin = '';
	vec($rin, fileno(MAILER),1) = 1;
	$ret = select($rin, undef, undef, $timeout);
	if ($ret < 1) {
		return(0);
	}
	sysread(MAILER, $data, 8192);
	if ($data =~ /$expect/) {
		return($data);
	}
	$rin = '';
	vec($rin, fileno(MAILER),1) = 1;
	if (select($rin, undef, undef, 0)) {
		1 while (sysread(MAILER, $data, 8192)>0);
	}
	return(0);
}

sub validate {
my ($host,$from) = @_;
return (-1) if !mailconnect($host);
$fou = '';
$one = sendexpect("",220,30);
$two = sendexpect("HELO $mydomain\r\n",250,10) if $one;
$thr = sendexpect("MAIL FROM: <emailform_validator\@$mydomain>\r\n", 250,10) if $two;
$fou = sendexpect("RCPT TO: <$from>\r\n", 250, 10) if $thr;
sendexpect("QUIT\r\n",221,10);
close MAILER;
if (!($one && $two && $thr)) {
	return(-1);
}
return($fou == 0? 0 : 1);
}

sub getmx {
my ($mydomain,$count) = @_;
# get a list of mail exchangers, organized by priority
# recursively cope with subdomains

my($good,$lineno,$tryagain);
return(0) if $count > 2;
$count++;
open(DNS,"$nslookup -q=mx $mydomain 2>&1|");
$good = 0;
$lineno=0;
%mailers = ();
$tryagain = "";
while(<DNS>) {
	$lineno++;
	if ($lineno == 4 && /^([a-z0-9][a-z0-9\-]+\.[a-z]+)$/) {
		$tryagain = $1;
		last;
	}
	if (/preference = (\d+), mail exchanger = (.+)/) {
		$good=1;
		push(@{ $mailers{$1}}, $2);
	}
}
close DNS;
if ($tryagain) {
	return(getmx($tryagain,$count));
}
return($good);
}


sub wrap {
	my ($text,$wraplen) = @_;
	my ($pos, $len);
	$text =~ s/[\n\r]+/ /gs;
	$pos = 0;
	$len = length($text);
	while($pos <= $len) {
		if ($pos + $wraplen > $len) {
			$text .= "\n";
			last;
		}
		$loc = rindex($text, " ", $pos + $wraplen);
		if ($loc < $pos) {
			$loc = $pos + $wraplen;
		}
		$pos = $loc + 1;
		substr($text,$loc,1) = "\n";
	}
	return($text);
}

if (!$data{ACTION}) {
	header();
	open(IN, $form); 
	while(<IN>) {
		s/_\w+_//g;
		s/^<!--.*-->//;
		print;
	}
	footer();
	exit;
}

if (!$data{FROM} || !$data{MESSAGE} || !$data{SUBJECT}) {
	error("You did not fill out the form completely.  Please try harder.<BR>");
}

# try to validate the fields as much as possible

$from = $data{FROM};
$subject = $data{SUBJECT};
$message = wrap($data{MESSAGE},75);

if ($from =~ /^([\w\.\-]+)@(([0-9a-z][\w\-]*\.)+([a-z]+))$/i) {
	$username = $1;
	$domain = $2;
} else {
	error("The FROM address you specified is not even remotely valid.<BR>");
}

$domain =~ s/[^a-z0-9\-\.]//g;  # make doubly sure no metacharacters sneak in
$username =~ s/[^w\.\-]//g;
$from =~ s/[^\@\w\-\.]//g;

$mydomregex = $mydomain;
$mydomregex =~ s/(\W)/\\$1/g;
if ($domain =~ /$mydomregex/i) {
	$message = '';
	$message .= 'Linux pansy bedwetter! ' if $ENV{HTTP_USER_AGENT} =~ /linux/i;
	$message .= 'Oh hello; Like I believe you have an account on my box!';
	error($message);
}

if (!getmx($domain) && !gethostbyname($domain)) {
	error("Your email address is not valid.<BR>");
}

$hishost = '';
# prefer higher priority mailers, and keep trying until one works
foreach (sort {$a<=>$b} keys %mailers) {
	foreach $hostname (@{ $mailers{$_}}) {
		$ret = validate($hostname, $from);
		next if $ret == -1;
		if ($ret==1) {
			$hishost = $hostname;
		}
		last;
	}
	last if $hishost;
}

# if no mail exchangers work, try the hostname directly
if (!$hishost) {
	if (validate($domain, $from)==1) {
		$hishost = $domain;
	}
}

if (!$hishost) {
	error("Unable to verify your email address.<BR>");
}

if (length($from) > 80) {
	error("Do you really expect me to believe that's a valid email address?<BR>");
}

if (length($message) > 8192) {
	error("Blah blah blah, I'm not gonna read something that long.<BR>");
}

if (length($subject) > length($message)) {
	error("Your subject is longer than your whole message.  That doesn't make much sense, now does it?<BR>");
}

# ok, everything looks good, let's send an email

$poster = $ENV{REMOTE_HOST} || $ENV{REMOTE_ADDR};

# here's where we actually send the message
error("Mailer connect error") if !mailconnect($mailer);
error("Mailer connect error") if !sendexpect("","220",60);
error("Mailer HELO  error") if !sendexpect("HELO $mydomain\r\n",'250',2);
error("Mailer MAIL error") if !sendexpect("MAIL from: <$mailto>\r\n",'250',2);
error("Mailer RCPT error") if !sendexpect("RCPT To: <$mailto>\r\n",'250',2);
error("Mailer DATA error $data") if !sendexpect("DATA\r\n","354",2);
error("Mailer MSG error $data") if !sendexpect("From: Web Page Comment <$mailto>
Reply-to: <$from>
To: You <$mailto>
Subject: $subject

Web form was filled out by $poster.

From   : $from (allegedly)
Subject: $subject

Message:
$message

.\n",'250',2);
error("Mailer QUIT error") if !sendexpect("QUIT\r\n",'221',2);
close MAILER;

if ($data{RETURN}) {
	print "Location: $data{RETURN}\n\n";
} else {
	header();
	print "<HEAD><TITLE>Thanks</TITLE></HEAD>\n";
	print "<BODY><H1>Thank You</H1>\n";
	print "<P>Your message has been sent\n";
	print "</BODY>\n";
	footer();
}
