#!/usr/bin/perl
# vim: sw=4

use v5.14;
use utf8;
use warnings;
use autodie qw(:all);

use Path::Tiny;
use Net::NNTP;
use POSIX qw(strftime);

##############################################################################
sub create_instance {
my $t = path('/tmp/news/');
my $e = $t->child('etc/');

$ENV{INNCONF} = "$e/inn.conf";

$t->remove_tree;
$t->child($_)->mkdir foreach qw(
	etc/
	spool/ spool/incoming/ spool/overview/
	lib/ lib/http/ lib/log/ run/
);

path($ENV{INNCONF})->spew(<< "END");
mta:				/bin/true
pathhost:			inn2.packages.debian.org
domain:				autopkgtest.debian.org
server:				localhost
patharticles:			$t/spool/articles
pathbin:			/usr/lib/news/bin
pathdb:				$t/lib
pathetc:			$e
pathnews:			$t/lib
pathrun:			$t/run
pathspool:			$t/spool
enableoverview:			true
ovmethod:			tradindexed
hismethod:			hisv6
nnrpdloadlimit:		0
END

$e->child('storage.conf')->spew(<< 'END');
method tradspool {
    newsgroups: *
    class: 0
}
END

$e->child('incoming.conf')->spew(<< 'END');
peer ME { hostname: "127.0.0.1, ::1" }
END

$e->child('newsfeeds')->spew(<< 'END');
ME:!*::
END

$e->child('readers.conf')->spew(<< 'END');
auth "localhost" {
    hosts: "localhost, 127.0.0.1, ::1, stdin"
    default: "<localhost>"
}

access "localhost" {
    users: "<localhost>"
    newsgroups: "*"
    access: RPA
    addcanlockuser: none
}
END

$e->child('inn-secrets.conf')->touch;

$t->child('spool/tradspool.map')->touch;

$t->child('lib/active')->spew(<< 'END');
control 0000000000 0000000001 n
control.cancel 0000000000 0000000001 n
junk 0000000000 0000000001 y
END

$t->child('lib/history')->touch;

##############################################################################
system(qw(chown news: -R), $t);
say "\nRun makehistory...";
system('/usr/lib/news/bin/makehistory');
system(qw(chown news: -R), $t);

##############################################################################
say "\nStart innd...";

my $child = fork();
if (not $child) {
	system(qw(
		systemd-run --uid=9 --gid=9 --pipe --wait --collect -u testinn
		),
		'systemd-socket-activate',
		-l => 1119,
		-E => "INNCONF=$ENV{INNCONF}",
		'/usr/lib/news/bin/innd', '-f',
	);
	exit;
}

}

##############################################################################
if (not -d '/run/systemd/system/') {
	warn "IGNORED: systemd is not active!\n";
	exit 77;
}

my $rc = eval { system(qw(systemd-run --version)) };

if (not defined $rc or $rc) {
	warn "IGNORED: systemd-run is not available!\n";
	exit 77;
}

create_instance();

$SIG{__DIE__} = sub {
	print "\n\n";
	eval { system(qw(systemctl status --no-pager --lines=5 --full testinn.service)); };
	print "\n\n";
};

my $nntp;
my $attempts = 5;

while ($attempts-- > 0) {
	say "\nConnect to innd...";
	$nntp = Net::NNTP->new(
		Host =>		'localhost',
		Port =>		1119,
		Timeout =>	5,
		Reader =>	0,
	);
	last if $nntp;
	sleep 2;
}

die 'FAILED: could not connect to innd' if not $nntp;

my $help = $nntp->help;
die 'FAILED: could not get help' if not $help or not @$help;

say "\nRun ctlinnd mode...";
system(qw(ctlinnd mode));

post_ihave($nntp);
post_nntp($nntp);
post_rnews();
post_inews();
test_reader($nntp);

$nntp->quit;

say "\nShut down innd...";
system(qw(ctlinnd shutdown), 'end of testing');

say "\nStop the testinn.service unit...";
eval { system(qw(systemctl stop testinn.service)); };

say "\nExiting $0.";
exit;

##############################################################################
sub date822 {
	return strftime("%a, %d %b %Y %H:%M:%S %z", localtime(time()));
}

##############################################################################
sub post_ihave {
	my ($nntp) = @_;
	say "\nPost an article with NNTP IHAVE...";

	my $id = "<test.ihave.$$\@inn2.packages.debian.org>";
	my $date = date822();
	$nntp->ihave($id, << "END") or die 'FAILURE: cannot post: ' . $nntp->message;
Path: not-for-mail
From: Marco d'Itri <md\@linux.it>
Newsgroups: junk
Subject: NNTP ihave test
Date: $date
Message-ID: $id

test nntp ihave
END
	return;
}

##############################################################################
sub post_nntp {
	my ($nntp) = @_;

	say "\nPost an article with NNTP POST...";
	$nntp->reader or die 'FAILURE: cannot switch to reader mode: ' . $nntp->message;
	$nntp->postok or die 'FAILURE: not OK to post: ' . $nntp->message;
	$nntp->post([
		"From: Marco d'Itri <md\@linux.it>\n",
		"Newsgroups: junk\n",
		"Subject: NNTP post test\n",
		"\n",
		"test nntp post\n",
	]) or die 'FAILURE: cannot post: ' . $nntp->message;
	return;
}

##############################################################################
sub post_rnews {
	say "\nPost an article with rnews...";

	my $id = "<test.rnews.$$\@inn2.packages.debian.org>";
	my $date = date822();
	my $article = << "END";
Path: not-for-mail
From: Marco d'Itri <md\@linux.it>
Newsgroups: junk
Subject: rnews test
Date: $date
Message-ID: $id

test rnews
END
	open(my $fh, '|-', '/usr/bin/rnews -N -v');
	print $fh $article;
	close($fh);
}

##############################################################################
sub post_inews {
	say "\nPost an article with inews...";

	my $id = "<test.inews.$$\@inn2.packages.debian.org>";
	my $article = << "END";
From: Marco d'Itri <md\@linux.it>
Newsgroups: junk
Subject: inews test
Message-ID: $id

test inews
END
	open(my $fh, '|-', '/usr/bin/inews -p 1119 -h');
	print $fh $article;
	close($fh);
}

##############################################################################
sub test_reader {
	my ($nntp) = @_;

	$nntp->reader;

	$nntp->group('junk');
	my ($id, $article, $article_id);

	$id = $nntp->next;
	die 'FAILURE: no ID after next' if not $id;
	$article = $nntp->article($id);
	die "FAILURE: no article for $id" if not $article;
	foreach (@$article) {
		next if not /^Message-ID: (.+)/;
		$article_id = $1;
		last;
	}
	die "FAILURE: $id != $article_id" if not $id eq $article_id;

	$id = 4;
	$article = $nntp->article($id);
	die "FAILURE: no article for $id" if not $article;
	die "FAILURE: wrong article for $id"
		if not grep { /Marco d'Itri/ } @$article;
	return;
}

