#!/usr/bin/perl
# save_chain.cgi
# Save a new or existing chain

require './nftables-lib.pl';    ## no critic
use strict;
use warnings;
our (%in, %text);
ReadParse();
error_setup($text{'chain_err'});
assert_acl('chains');

my @tables = get_nftables_save();
my $table = $tables[$in{'table'}];
$table || error($text{'chain_notable'});
assert_table_acl($table);

my $is_new = $in{'new'} ? 1 : 0;
my $is_rename = $in{'rename'} ? 1 : 0;
my $name = $in{'chain_name'};
$name =~ s/^\s+// if (defined($name));
$name =~ s/\s+$// if (defined($name));
$name =~ /^\w[\w-]*$/ || error($text{'chain_ename'});

my $old = $is_rename ? $in{'chain_old'} : $name;
$old =~ s/^\s+// if (defined($old));
$old =~ s/\s+$// if (defined($old));

if ($is_new) {
	$table->{'chains'}->{$name} && error($text{'chain_edup'});
	}
elsif ($is_rename) {
	$table->{'chains'}->{$old} || error($text{'chain_nochain'});
	if ($name ne $old && $table->{'chains'}->{$name}) {
		error($text{'chain_edup'});
		}
	}
else {
	$table->{'chains'}->{$name} || error($text{'chain_nochain'});
	}

if ($is_rename) {
	if ($name eq $old) {
		redirect("index.cgi?table=$in{'table'}");
		return;
		}
	if ($name ne $old) {
		$table->{'chains'}->{$name} = $table->{'chains'}->{$old};
		delete($table->{'chains'}->{$old});

		foreach my $r (@{$table->{'rules'}}) {
			$r->{'chain'} = $name
			    if ($r->{'chain'} && $r->{'chain'} eq $old);
			my $changed = 0;
			if ($r->{'jump'} && $r->{'jump'} eq $old) {
				$r->{'jump'} = $name;
				$changed = 1;
				}
			if ($r->{'goto'} && $r->{'goto'} eq $old) {
				$r->{'goto'} = $name;
				$changed = 1;
				}
			$r->{'text'} = format_rule_text($r) if ($changed);
			}
		}

	my $err = save_table_configuration($table, @tables);
	error(text('rename_chain_failed', $err)) if ($err);
	webmin_log(
		"rename", "chain", $old,
		{
			'new' => $name,
			'table' => $table->{'name'},
			'family' => $table->{'family'}
		}
	);
	redirect("index.cgi?table=$in{'table'}");
	return;
	}

my $type = $in{'chain_type'};
my $hook = $in{'chain_hook'};
my $priority = $in{'chain_priority'};
my $policy = $in{'chain_policy'};

for my $v (\$type, \$hook, \$priority, \$policy) {
	$$v =~ s/^\s+// if (defined($$v));
	$$v =~ s/\s+$// if (defined($$v));
	}
$type = undef if (!defined($type) || $type eq '');
$hook = undef if (!defined($hook) || $hook eq '');
$priority = undef if (!defined($priority) || $priority eq '');
$policy = undef if (!defined($policy) || $policy eq '');

validate_chain_base($type, $hook, $priority, $policy) ||
    error($text{'chain_ebase'});

my $chain = $table->{'chains'}->{$name} || {};
$chain->{'type'} = $type;
$chain->{'hook'} = $hook;
$chain->{'priority'} = $priority;
$chain->{'policy'} = $policy;
$table->{'chains'}->{$name} = $chain;

my $err = save_table_configuration($table, @tables);
error(text('chain_failed', $err)) if ($err);

webmin_log($is_new ? "create" : "modify",
	"chain", $name, {'table' => $table->{'name'}, 'family' => $table->{'family'}});
redirect("index.cgi?table=$in{'table'}");
