#!/usr/bin/perl
# Move the OpenVPN certificates into the shared storage, making sure we do
# not overwrite anything.

use strict;
use warnings;

use File::Basename;
use File::Path qw(make_path);
use File::Copy qw(mv);
use File::Compare;
use YAML::XS;

my $self = basename($0);

my $cfgdir = '/etc/ngcp-config';
my $olddir = '/etc/openvpn/client';
my $newdir = "$cfgdir/shared-files/openvpn/client";

sub remap {
    my $varref = shift;

    return unless defined ${$varref};
    ${$varref} =~ s{^$olddir}{$newdir};
}

if (! -d $olddir) {
  warn "$self: No OpenVPN directory, nothing to do.\n";
  exit 0
}

my @certs = glob "$olddir/*";

if (scalar @certs == 0) {
  warn "$self: No OpenVPN certificates, nothing to do.\n";
  exit 0
}

my $divergent = 0;

make_path($newdir);

foreach my $cert (@certs) {
    my $certname = basename($cert);

    if (! -e "$newdir/$certname") {
        mv($cert, $newdir)
            or die "Error: cannot move $cert to $newdir: $!\n";
        symlink "$newdir/$certname", "$olddir/$certname"
            or die "Error: cannot symlink $newdir/$certname to $olddir/$certname: $!\n";
        print "Moved '$cert' to '$newdir'.\n";
    } elsif (compare($cert, "$newdir/$certname") != 0) {
        warn "Error: OpenVPN certificate $cert present but different on $newdir, please resolve.\n";
        $divergent = 1;
    }
}

if ($divergent) {
  warn "Error: found divergent OpenVPN certificates in $olddir and $newdir,\n" .
       "please fix before executing again.\n";
  exit 1;
}

print "$self: Remapping configuration keys.\n";

my $yaml;

$yaml = YAML::XS::LoadFile("$cfgdir/config.yml");

remap(\$yaml->{openvpn}{keys_filename_template});

YAML::XS::DumpFile("$cfgdir/config.yml", $yaml);

$yaml = YAML::XS::LoadFile("$cfgdir/network.yml");

my @cert_keys = qw(
    openvpn_ca_file
    openvpn_ca_template
    openvpn_cert_file
    openvpn_cert_template
    openvpn_key_file
    openvpn_key_template
);

foreach my $hostname (keys %{$yaml->{hosts}}) {
    my $host = $yaml->{hosts}{$hostname};

    foreach my $ifacename (@{$host->{interfaces}}) {
        my $iface = $host->{$ifacename};

        foreach my $key (@cert_keys) {
            next unless exists $iface->{$key};
            remap(\$iface->{$key});
        }
    }
}

YAML::XS::DumpFile("$cfgdir/network.yml", $yaml);

print "$self: Committing and pushing configuration changes.\n";

system "ngcpcfg --no-db-sync commit \"$self: moving OpenVPN certificates under shared-files/openvpn\"";
system "ngcpcfg push --shared-only";

exit 0;

__END__

=head1 NAME

ngcp-migrate-openvpn-certs - Migrate OpenVPN certificates to the shared store

=head1 SYNOPSIS

B<ngcp-migrate-openvpn-certs>

=head1 DESCRIPTION

B<This program> will move any OpenVPN certificate from the old location under
F</etc/openvpn/certs/> to F</etc/ngcp-config/shared-files/openvpn/certs/>,
create backwards compatibility symlinks in case there is anything referencing
them, and update references in the F<config.yml> and F<network.yml> files.

It is safe to call this program multiple times, even after a partial execution
due to errors. Once the issues have been resolved by the administrator the
program will be able to proceed, or will error out on newly found conditions.

=head1 BUGS AND LIMITATIONS

Please report problems you notice to the Sipwise
Development Team <support@sipwise.com>.

=head1 AUTHOR

Guillem Jover <gjover@sipwise.com>

=head1 LICENSE

Copyright (C) 2023 Sipwise GmbH, Austria

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

=cut
