#!/usr/bin/env perl

use warnings;
use strict;
use autodie qw(:all);
use Carp;

use FindBin qw($Bin);
use lib "$Bin/../lib";

my $path = $Bin;
$path =~ s/^(.+)\/.+/$1/;	# Remove the final directory, probably "/bin"

# Where the page keeps the file caches it roots at $ENV{'root_dir'} - the country
# it remembers for a visitor among them.  By default that is this distribution,
# which is where the page would keep them when it is run by hand (the page's own
# comments show it being run with root_dir=$(pwd)/..).  A caller that has set
# root_dir has chosen somewhere else for them and is left alone: the test suite
# points it into the directory it is about to delete, so that a run leaves
# nothing behind in the checkout and one test's cached country cannot answer for
# the next.
$ENV{'root_dir'} = $path unless(defined($ENV{'root_dir'}));

eval {
	my $testnumber = $ARGV[0];
	if($testnumber =~ /^(\d+)$/) {
		doit($1);
	}
};

Carp::croak $@ if $@;

sub doit
{
	my $test = shift or Carp::croak;

	# Tests 3 and 4 simulate a browser in the US, so that the page infers the
	# country from the client's IP address.  The IP is one that the page's
	# CGI::ACL is built to trust (see cgi-bin/page.fcgi), so that those two
	# tests are the geocoder answering and not a lesson in access control: an
	# address the ACL cannot place in a country is refused (test 10), and so is
	# one whose country it blocks (test 11).
	my $us_ip = '108.44.193.70';

	# The address test 10 asks from: a private address, which CGI::Lingua
	# places in no country - it skips private and loopback addresses - so
	# CGI::ACL refuses the client.  It is not chosen to look like an attacker:
	# the address alone is what the refusal is about.
	my $unplaceable_ip = '10.0.0.1';

	# The address tests 7 and 8 ask from as well: 7 is refused although it comes
	# from the same address, so what refuses it is the user agent and nothing
	# else about the client.
	#
	# Geo::Coder::Free::Allow keeps a list of user agents it never serves, and
	# this is one of them (the list is %blacklist_agents in
	# lib/Geo/Coder/Free/Allow.pm, which names this agent 'zgrab').  The allowed
	# case uses an ordinary browser's agent - not an empty one, so that the test
	# covers the path where an agent is present and is not on the list.
	my $blacklisted_agent = 'Mozilla/5.0 zgrab/0.x';
	my $browser_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';

	if($test == 1) {
		# print "Test 1 - q\n";
		# system("$^X $Bin/../cgi-bin/page.fcgi page=query q='1600+Pennsylvania+Avenue+NW+Washington+DC+USA'");
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US'");
	} elsif($test == 2) {
		# print "Test 2 - scantext\n";
		system("$^X $Bin/../cgi-bin/page.fcgi page=query scantext='Washington+DC+USA'");
	} elsif($test == 3) {
		$ENV{'REMOTE_ADDR'} = $us_ip;	# An IP address in the US, trusted by the page's ACL
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US'");
	} elsif($test == 4) {
		$ENV{'REMOTE_ADDR'} = $us_ip;	# An IP address in the US, so no need for country in the address
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD'");
	} elsif($test == 5) {
		# The landing page.  It renders a template that this distribution
		# ships, so what it prints is the page, not just the log.
		system("$^X $Bin/../cgi-bin/page.fcgi page=index");
	} elsif($test == 6) {
		# The server's own status page: the site title it was configured
		# with, the domain it was asked about, and the visitor types in the
		# log (LOGDIR points at the one t/bin.t writes).
		system("$^X $Bin/../cgi-bin/page.fcgi page=meta_data");
	} elsif($test == 7) {
		# A client the site will not serve.  Everything about this request
		# is test 3's - the same address, asking the same question - except
		# the user agent, so the page has nothing else to refuse it for.
		$ENV{'REMOTE_ADDR'} = $us_ip;
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		$ENV{'HTTP_USER_AGENT'} = $blacklisted_agent;
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US'");
	} elsif($test == 8) {
		# The same client, allowed through: the answer is served.
		$ENV{'REMOTE_ADDR'} = $us_ip;
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		$ENV{'HTTP_USER_AGENT'} = $browser_agent;
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US'");
	} elsif($test == 9) {
		# Test 7's refusal, asked with HEAD: the page must send the headers and
		# no body (RFC 7231 §4.3.2, which the block page cites).
		$ENV{'REMOTE_ADDR'} = $us_ip;
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		$ENV{'HTTP_USER_AGENT'} = $blacklisted_agent;
		$ENV{'REQUEST_METHOD'} = 'HEAD';
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US'");
	} elsif($test == 10) {
		# The page's own ACL refusing a client.  Everything about this request
		# is test 3's - an ordinary browser, asking the question the stub can
		# answer - except the address, which no country can be looked up for,
		# so CGI::ACL refuses it before any other layer is consulted.
		$ENV{'REMOTE_ADDR'} = $unplaceable_ip;
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		$ENV{'HTTP_USER_AGENT'} = $browser_agent;
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US'");
	} elsif($test == 11) {
		# A client from a country the site blocks.  The address is the one the
		# site trusts, so the ACL serves it and the refusal is the country
		# list's.  The country is passed the way a web server or proxy passes
		# it, which is how CGI::Lingua reads the client's country.
		$ENV{'REMOTE_ADDR'} = $us_ip;
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		$ENV{'HTTP_USER_AGENT'} = $browser_agent;
		$ENV{'HTTP_CF_IPCOUNTRY'} = 'RU';
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US'");
	} elsif($test == 12) {
		# A client the page has blacklisted.  It asks a question - the one the
		# stub answers - whose wording reads like a SQL injection attempt, so
		# the page adds the address to its blacklist and refuses it.  The
		# address and the user agent are test 3's and 8's, which the site
		# serves, so the query string is the only thing it can be refused for.
		$ENV{'REMOTE_ADDR'} = $us_ip;
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		$ENV{'HTTP_USER_AGENT'} = $browser_agent;
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US+OR+NOT+1=1'");
	} elsif($test == 13) {
		# A query page submission carrying a CSRF token the site did not
		# issue.  Everything else is test 3's, so the token is what the page
		# refuses.  The token is well-formed enough to be a token and signed
		# with nothing, which is what a forgery that guessed the shape looks
		# like.
		$ENV{'REMOTE_ADDR'} = $us_ip;
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		$ENV{'HTTP_USER_AGENT'} = $browser_agent;
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US' csrf_token='0123456789abcdef'");
	} elsif($test == 14) {
		# The same submission as test 13, with a token this configuration
		# signed: the page serves the answer.  The token is supplied by the
		# caller, which knows the stub's secret, because a token is signed
		# with a configuration this program does not read.
		$ENV{'REMOTE_ADDR'} = $us_ip;
		$ENV{'SERVER_PROTOCOL'} = 'HTTP/1.1';
		$ENV{'HTTP_USER_AGENT'} = $browser_agent;
		system("$^X $Bin/../cgi-bin/page.fcgi page=query q='NASA+TIROS+RD+GREENBELT+MD+US' csrf_token='$ENV{'CSRF_TOKEN'}'");
	}
}
