#!/usr/local/cpanel/3rdparty/bin/perl
# gniza4cp WHM Plugin — Remote Destination CRUD
use strict;
use warnings;

use lib '/usr/local/cpanel/whostmgr/docroot/cgi/gniza4cp-whm/lib';

use Whostmgr::HTMLInterface ();
use Cpanel::Form            ();
use File::Copy              ();
use IPC::Open3              ();
use Symbol                  qw(gensym);
use Gniza4cpWHM::Config;
use Gniza4cpWHM::Validator;
use Gniza4cpWHM::UI;

my $form   = Cpanel::Form::parseform();
my $method = $ENV{'REQUEST_METHOD'} // 'GET';
my $action = $form->{'action'} // 'list';

# Route to handler
if    ($action eq 'test')      { handle_test_connection() }
elsif ($action eq 'disk_info') { handle_disk_info()       }
elsif ($action eq 'add')       { handle_add()    }
elsif ($action eq 'edit')      { handle_edit()   }
elsif ($action eq 'delete')    { handle_delete() }
else                           { handle_list()   }

exit;

# ── Test Connection (JSON) ────────────────────────────────────

sub handle_test_connection {
    print "Content-Type: application/json\r\n\r\n";

    unless ($method eq 'POST' && Gniza4cpWHM::UI::verify_csrf_token($form->{'gniza4cp_csrf'})) {
        print qq({"success":false,"message":"Invalid or expired token. Please reload and try again."});
        exit;
    }

    # Generate fresh token after consuming the old one (CSRF is single-use)
    my $new_csrf = Gniza4cpWHM::UI::generate_csrf_token();

    my $type = $form->{'remote_type'} || 'ssh';

    if ($type eq 'ssh') {
        my $host        = $form->{'host'} // '';
        my $port        = $form->{'port'} || '22';
        my $user        = $form->{'user'} || 'root';
        my $auth_method = $form->{'auth_method'} || 'key';
        my $key         = $form->{'key'}  // '';
        my $password    = $form->{'password'} // '';

        if ($host eq '') {
            print qq({"success":false,"message":"Host is required.","csrf":"$new_csrf"});
            exit;
        }

        if ($auth_method eq 'password') {
            if ($password eq '') {
                print qq({"success":false,"message":"Password is required.","csrf":"$new_csrf"});
                exit;
            }
        } else {
            if ($key eq '') {
                print qq({"success":false,"message":"SSH key path is required.","csrf":"$new_csrf"});
                exit;
            }
        }

        my ($ok, $err) = Gniza4cpWHM::UI::test_ssh_connection(
            host        => $host,
            port        => $port,
            user        => $user,
            auth_method => $auth_method,
            key         => $key,
            password    => $password,
        );
        if ($ok) {
            print qq({"success":true,"message":"SSH connection successful.","csrf":"$new_csrf"});
        } else {
            $err //= 'Unknown error';
            $err =~ s/\\/\\\\/g;
            $err =~ s/"/\\"/g;
            $err =~ s/\n/\\n/g;
            $err =~ s/\r/\\r/g;
            $err =~ s/\t/\\t/g;
            $err =~ s/[\x00-\x1f]//g;
            print qq({"success":false,"message":"SSH connection failed: $err","csrf":"$new_csrf"});
        }
    }
    elsif ($type eq 's3' || $type eq 'gdrive') {
        my %rclone_args = (type => $type);
        if ($type eq 's3') {
            $rclone_args{s3_access_key_id}     = $form->{'S3_ACCESS_KEY_ID'} // '';
            $rclone_args{s3_secret_access_key}  = $form->{'S3_SECRET_ACCESS_KEY'} // '';
            $rclone_args{s3_region}             = $form->{'S3_REGION'} || 'us-east-1';
            $rclone_args{s3_endpoint}           = $form->{'S3_ENDPOINT'} // '';
            $rclone_args{s3_bucket}             = $form->{'S3_BUCKET'} // '';

            if ($rclone_args{s3_access_key_id} eq '' || $rclone_args{s3_secret_access_key} eq '') {
                print qq({"success":false,"message":"S3 access key and secret are required.","csrf":"$new_csrf"});
                exit;
            }
            if ($rclone_args{s3_bucket} eq '') {
                print qq({"success":false,"message":"S3 bucket is required.","csrf":"$new_csrf"});
                exit;
            }
        } else {
            $rclone_args{gdrive_service_account_file} = $form->{'GDRIVE_SERVICE_ACCOUNT_FILE'} // '';
            $rclone_args{gdrive_root_folder_id}       = $form->{'GDRIVE_ROOT_FOLDER_ID'} // '';

            if ($rclone_args{gdrive_service_account_file} eq '') {
                print qq({"success":false,"message":"Service account file path is required.","csrf":"$new_csrf"});
                exit;
            }
        }

        my ($ok, $err) = Gniza4cpWHM::UI::test_rclone_connection(%rclone_args);
        if ($ok) {
            my $label = $type eq 's3' ? 'S3' : 'Google Drive';
            print qq({"success":true,"message":"$label connection successful.","csrf":"$new_csrf"});
        } else {
            $err //= 'Unknown error';
            $err =~ s/\\/\\\\/g;
            $err =~ s/"/\\"/g;
            $err =~ s/\n/\\n/g;
            $err =~ s/\r/\\r/g;
            $err =~ s/\t/\\t/g;
            $err =~ s/[\x00-\x1f]//g;
            print qq({"success":false,"message":"Connection failed: $err","csrf":"$new_csrf"});
        }
    }
    else {
        print qq({"success":false,"message":"Unknown remote type.","csrf":"$new_csrf"});
    }
    exit;
}

# ── List ─────────────────────────────────────────────────────

sub handle_list {
    print "Content-Type: text/html\r\n\r\n";
    Whostmgr::HTMLInterface::defheader('GNIZA Backup Manager — Remotes', '', '/cgi/gniza4cp-whm/remotes.cgi');

    print Gniza4cpWHM::UI::page_header('Remote Destinations');
    print Gniza4cpWHM::UI::render_nav('remotes.cgi');
    print Gniza4cpWHM::UI::render_flash();

    my @remotes = Gniza4cpWHM::UI::list_remotes();

    # Build schedule map: remote_name => [schedule_names]
    my %schedule_map;
    for my $sname (Gniza4cpWHM::UI::list_schedules()) {
        my $scfg = Gniza4cpWHM::Config::parse(Gniza4cpWHM::UI::schedule_conf_path($sname), 'schedule');
        my $remotes_str = $scfg->{REMOTES} // '';
        if ($remotes_str eq '') {
            # Empty REMOTES = targets ALL remotes
            for my $r (@remotes) { push @{$schedule_map{$r}}, $sname; }
        } else {
            for my $r (split /,/, $remotes_str) {
                $r =~ s/^\s+|\s+$//g;
                push @{$schedule_map{$r}}, $sname if $r ne '';
            }
        }
    }

    print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};

    if (@remotes) {
        print qq{<div class="overflow-x-auto rounded-box border border-base-content/5 bg-base-100"><table class="table">\n};
        print qq{<thead><tr><th>Name</th><th>Type</th><th>Destination</th><th>Disk Usage</th><th>Schedules</th><th>Retention</th><th>Actions</th></tr></thead>\n};
        print qq{<tbody>\n};
        for my $name (@remotes) {
            my $conf = Gniza4cpWHM::Config::parse(Gniza4cpWHM::UI::remote_conf_path($name), 'remote');
            my $esc_name  = Gniza4cpWHM::UI::esc($name);
            my $type      = $conf->{REMOTE_TYPE} // 'ssh';
            my $retention_raw = $conf->{RETENTION_COUNT} // '';
            my $retention = Gniza4cpWHM::UI::esc($retention_raw ne '' ? $retention_raw : '30');

            my ($type_label, $dest);
            if ($type eq 's3') {
                $type_label = 'S3';
                $dest = 's3://' . Gniza4cpWHM::UI::esc($conf->{S3_BUCKET} // '');
            } elsif ($type eq 'gdrive') {
                $type_label = 'GDrive';
                my $sa = $conf->{GDRIVE_SERVICE_ACCOUNT_FILE} // '';
                $sa =~ s{.*/}{};
                $dest = 'gdrive:' . Gniza4cpWHM::UI::esc($sa);
            } else {
                $type_label = 'SSH';
                my $host = Gniza4cpWHM::UI::esc($conf->{REMOTE_HOST} // '');
                my $port = Gniza4cpWHM::UI::esc($conf->{REMOTE_PORT} // '22');
                $dest = "$host:$port";
            }

            # Schedule count + tooltip
            my @sched_names = @{$schedule_map{$name} // []};
            my $sched_count = scalar @sched_names;
            my $sched_html;
            if ($sched_count > 0) {
                my $sched_tip = Gniza4cpWHM::UI::esc(join(', ', @sched_names));
                $sched_html = qq{<span class="badge badge-sm badge-info tooltip tooltip-top" data-tip="$sched_tip">$sched_count</span>};
            } else {
                $sched_html = qq{<span class="text-base-content/60">0</span>};
            }

            print qq{<tr class="hover">};
            print qq{<td><strong>$esc_name</strong></td>};
            print qq{<td><span class="badge badge-sm">$type_label</span></td>};
            print qq{<td>$dest</td>};
            print qq{<td data-remote-disk="$esc_name"><span class="loading loading-spinner loading-xs"></span></td>};
            print qq{<td>$sched_html</td>};
            print qq{<td>$retention</td>};
            print qq{<td>};
            print qq{<div class="flex items-center gap-2">};
            print qq{<button type="button" class="btn btn-primary btn-sm" onclick="location.href='remotes.cgi?action=edit&amp;name=$esc_name'">Edit</button>};
            print qq{<form method="POST" action="remotes.cgi" class="inline">};
            print qq{<input type="hidden" name="action" value="delete">};
            print qq{<input type="hidden" name="name" value="$esc_name">};
            print Gniza4cpWHM::UI::csrf_hidden_field();
            print qq{<button type="submit" class="btn btn-error btn-sm" onclick="return confirm('Delete remote $esc_name?')">Delete</button>};
            print qq{</form>};
            print qq{</div>};
            print qq{</td>};
            print qq{</tr>\n};
        }
        print qq{</tbody>\n</table></div>\n};

        # JavaScript: fetch disk info for each remote via safe DOM methods
        print <<'JS';
<script>
document.addEventListener('DOMContentLoaded', function() {
    var cells = document.querySelectorAll('td[data-remote-disk]');
    cells.forEach(function(td) {
        var name = td.getAttribute('data-remote-disk');
        fetch('remotes.cgi?action=disk_info&name=' + encodeURIComponent(name))
            .then(function(r) { return r.json(); })
            .then(function(data) {
                td.textContent = '';
                var span = document.createElement('span');
                span.className = 'text-xs';
                if (!data.ok) { span.className += ' text-base-content/60'; }
                span.textContent = data.disk || '\u2014';
                td.appendChild(span);
            })
            .catch(function() {
                td.textContent = '';
                var span = document.createElement('span');
                span.className = 'text-xs text-base-content/60';
                span.textContent = '\u2014';
                td.appendChild(span);
            });
    });
});
</script>
JS
    } else {
        print qq{<p>No remote destinations configured. Add a remote to enable multi-remote backups.</p>\n};
        print qq{<p class="text-xs text-base-content/60 mt-2">Remote configs are stored in <code>/etc/gniza4cp/remotes.d/</code>.</p>\n};
    }

    print qq{</div>\n</div>\n};

    print qq{<div class="flex items-center gap-2 mt-4">\n};
    print qq{  <button type="button" class="btn btn-primary btn-sm" onclick="location.href='remotes.cgi?action=add'">Add Remote</button>\n};
    print qq{</div>\n};

    print Gniza4cpWHM::UI::page_footer();
    Whostmgr::HTMLInterface::footer();
}

# ── Disk Info (JSON, GET) ────────────────────────────────────

sub handle_disk_info {
    print "Content-Type: application/json\r\n\r\n";

    my $name = $form->{'name'} // '';
    my $name_err = Gniza4cpWHM::Validator::validate_remote_name($name);
    if ($name_err) {
        print qq({"ok":false,"disk":"Invalid remote name"});
        exit;
    }

    my $conf_path = Gniza4cpWHM::UI::remote_conf_path($name);
    unless (-f $conf_path) {
        print qq({"ok":false,"disk":"Remote not found"});
        exit;
    }

    my $conf = Gniza4cpWHM::Config::parse($conf_path, 'remote');
    my $type = $conf->{REMOTE_TYPE} // 'ssh';
    my $base = $conf->{REMOTE_BASE} // '/backups';

    my $disk_str = eval { _get_disk_info($conf, $type, $base) };
    if ($@ || !defined $disk_str) {
        my $err = $@ || 'Unknown error';
        $err =~ s/[\r\n]/ /g;
        $err =~ s/"/\\"/g;
        print qq({"ok":false,"disk":"Connection failed"});
        exit;
    }

    $disk_str =~ s/\\/\\\\/g;
    $disk_str =~ s/"/\\"/g;
    $disk_str =~ s/\n/\\n/g;
    $disk_str =~ s/\r/\\r/g;
    $disk_str =~ s/[\x00-\x1f]//g;
    print qq({"ok":true,"disk":"$disk_str"});
    exit;
}

sub _get_disk_info {
    my ($conf, $type, $base) = @_;

    if ($type eq 'ssh') {
        return _get_disk_info_ssh($conf, $base);
    } else {
        return _get_disk_info_rclone($conf, $type, $base);
    }
}

sub _get_disk_info_ssh {
    my ($conf, $base) = @_;

    my $host        = $conf->{REMOTE_HOST} // '';
    my $port        = $conf->{REMOTE_PORT} || '22';
    my $user        = $conf->{REMOTE_USER} || 'root';
    my $auth_method = $conf->{REMOTE_AUTH_METHOD} || 'key';
    my $key         = $conf->{REMOTE_KEY} // '';
    my $password    = $conf->{REMOTE_PASSWORD} // '';

    my @ssh_args = (
        'ssh', '-n',
        '-p', $port,
        '-o', 'StrictHostKeyChecking=accept-new',
        '-o', 'ConnectTimeout=10',
        '-o', 'BatchMode=yes',
    );

    # Build df command — quote $base for remote shell
    (my $safe_base = $base) =~ s/'/'\\''/g;
    my $remote_cmd = "df -B1 '$safe_base' 2>/dev/null | tail -1";

    my @cmd;
    if ($auth_method eq 'password') {
        # Remove BatchMode for password auth
        @ssh_args = grep { $_ ne 'BatchMode=yes' } @ssh_args;
        push @ssh_args, "$user\@$host", $remote_cmd;
        $ENV{SSHPASS} = $password;
        @cmd = ('sshpass', '-e', @ssh_args);
    } else {
        push @ssh_args, '-i', $key;
        push @ssh_args, "$user\@$host", $remote_cmd;
        @cmd = @ssh_args;
    }

    my ($in, $out, $err_fh) = (undef, undef, gensym);
    my $pid = eval { IPC::Open3::open3($in, $out, $err_fh, @cmd) };
    delete $ENV{SSHPASS};
    return undef unless $pid;
    close $in if $in;

    # Timeout: kill subprocess after 20 seconds
    local $SIG{ALRM} = sub { kill 'TERM', $pid; };
    alarm(20);

    my $stdout = do { local $/; <$out> } // '';
    close $out;
    close $err_fh;
    waitpid($pid, 0);
    alarm(0);

    return undef if ($? >> 8) != 0;

    chomp $stdout;
    return undef if $stdout eq '';

    # df -B1 output: Filesystem 1B-blocks Used Available Use% Mounted
    my @fields = split /\s+/, $stdout;
    return undef if @fields < 4;

    my ($total, $used, $avail) = ($fields[1], $fields[2], $fields[3]);
    return undef unless $total =~ /^\d+$/ && $used =~ /^\d+$/ && $avail =~ /^\d+$/;

    return _human_size($used) . ' used / ' . _human_size($total) . ' total (' . _human_size($avail) . ' free)';
}

sub _get_disk_info_rclone {
    my ($conf, $type, $base) = @_;

    # Build temp rclone config
    my $tmpfile = "/tmp/gniza4cp-rclone-disk-$$.conf";
    my $conf_content = '';
    my $about_path   = '';

    if ($type eq 's3') {
        my $key_id   = $conf->{S3_ACCESS_KEY_ID} // '';
        my $secret   = $conf->{S3_SECRET_ACCESS_KEY} // '';
        my $region   = $conf->{S3_REGION} || 'us-east-1';
        my $endpoint = $conf->{S3_ENDPOINT} // '';
        my $bucket   = $conf->{S3_BUCKET} // '';

        $conf_content = "[remote]\ntype = s3\nprovider = AWS\naccess_key_id = $key_id\nsecret_access_key = $secret\nregion = $region\n";
        $conf_content .= "endpoint = $endpoint\n" if $endpoint ne '';
        $about_path = "remote:$bucket$base";
    }
    elsif ($type eq 'gdrive') {
        my $sa_file   = $conf->{GDRIVE_SERVICE_ACCOUNT_FILE} // '';
        my $folder_id = $conf->{GDRIVE_ROOT_FOLDER_ID} // '';

        $conf_content = "[remote]\ntype = drive\nscope = drive\nservice_account_file = $sa_file\n";
        $conf_content .= "root_folder_id = $folder_id\n" if $folder_id ne '';
        $about_path = "remote:$base";
    }
    else {
        return undef;
    }

    if (open my $fh, '>', $tmpfile) {
        print $fh $conf_content;
        close $fh;
        chmod 0600, $tmpfile;
    } else {
        return undef;
    }

    my @cmd = ('rclone', '--config', $tmpfile, '--timeout', '15s', 'about', '--json', $about_path);
    my ($in, $out, $err_fh) = (undef, undef, gensym);
    my $pid = eval { IPC::Open3::open3($in, $out, $err_fh, @cmd) };
    unless ($pid) {
        unlink $tmpfile;
        return undef;
    }
    close $in if $in;

    # Timeout: kill subprocess after 20 seconds
    local $SIG{ALRM} = sub { kill 'TERM', $pid; };
    alarm(20);

    my $stdout = do { local $/; <$out> } // '';
    close $out;
    close $err_fh;
    waitpid($pid, 0);
    alarm(0);
    my $exit_code = $? >> 8;
    unlink $tmpfile;

    return undef if $exit_code != 0;

    # Parse JSON fields: used, total, free
    my ($used)  = ($stdout =~ /"used"\s*:\s*(\d+)/);
    my ($total) = ($stdout =~ /"total"\s*:\s*(\d+)/);
    my ($free)  = ($stdout =~ /"free"\s*:\s*(\d+)/);

    if (defined $used && defined $total && $total > 0) {
        my $free_str = defined $free ? ' (' . _human_size($free) . ' free)' : '';
        return _human_size($used) . ' used / ' . _human_size($total) . ' total' . $free_str;
    } elsif (defined $used) {
        return _human_size($used) . ' used';
    }

    return undef;
}

sub _human_size {
    my ($bytes) = @_;
    return '0 B' unless defined $bytes && $bytes >= 0;

    my @units = ('B', 'KB', 'MB', 'GB', 'TB', 'PB');
    my $i = 0;
    my $size = $bytes + 0.0;
    while ($size >= 1024 && $i < $#units) {
        $size /= 1024;
        $i++;
    }
    if ($i == 0) {
        return sprintf('%d %s', $size, $units[$i]);
    }
    return sprintf('%.2f %s', $size, $units[$i]);
}

# ── Add ──────────────────────────────────────────────────────

sub handle_add {
    my @errors;

    if ($method eq 'POST') {
        unless (Gniza4cpWHM::UI::verify_csrf_token($form->{'gniza4cp_csrf'})) {
            push @errors, 'Invalid or expired form token. Please try again.';
        }

        my $name = $form->{'remote_name'} // '';
        my $name_err = Gniza4cpWHM::Validator::validate_remote_name($name);
        push @errors, $name_err if $name_err;

        if (!@errors && -f Gniza4cpWHM::UI::remote_conf_path($name)) {
            push @errors, "A remote named '$name' already exists.";
        }

        my %data;
        for my $key (@Gniza4cpWHM::Config::REMOTE_KEYS) {
            $data{$key} = $form->{$key} // '';
        }

        if (!@errors) {
            my $validation_errors = Gniza4cpWHM::Validator::validate_remote_config(\%data);
            push @errors, @$validation_errors;
        }

        if (!@errors) {
            my $type = $data{REMOTE_TYPE} || 'ssh';
            my ($conn_ok, $conn_err);

            if ($type eq 'ssh') {
                ($conn_ok, $conn_err) = Gniza4cpWHM::UI::test_ssh_connection(
                    host        => $data{REMOTE_HOST},
                    port        => $data{REMOTE_PORT} || '22',
                    user        => $data{REMOTE_USER} || 'root',
                    auth_method => $data{REMOTE_AUTH_METHOD} || 'key',
                    key         => $data{REMOTE_KEY},
                    password    => $data{REMOTE_PASSWORD},
                );
            } else {
                my %rclone_args = (type => $type);
                if ($type eq 's3') {
                    $rclone_args{s3_access_key_id}     = $data{S3_ACCESS_KEY_ID};
                    $rclone_args{s3_secret_access_key}  = $data{S3_SECRET_ACCESS_KEY};
                    $rclone_args{s3_region}             = $data{S3_REGION} || 'us-east-1';
                    $rclone_args{s3_endpoint}           = $data{S3_ENDPOINT};
                    $rclone_args{s3_bucket}             = $data{S3_BUCKET};
                } else {
                    $rclone_args{gdrive_service_account_file} = $data{GDRIVE_SERVICE_ACCOUNT_FILE};
                    $rclone_args{gdrive_root_folder_id}       = $data{GDRIVE_ROOT_FOLDER_ID};
                }
                ($conn_ok, $conn_err) = Gniza4cpWHM::UI::test_rclone_connection(%rclone_args);
            }
            push @errors, "Connection test failed: $conn_err" unless $conn_ok;
        }

        if (!@errors) {
            # Ensure main config exists (gniza4cp backup requires it)
            _ensure_main_config();

            # Copy example template then write values
            my $dest = Gniza4cpWHM::UI::remote_conf_path($name);
            my $example = Gniza4cpWHM::UI::remote_example_path();
            if (-f $example) {
                File::Copy::copy($example, $dest)
                    or do { push @errors, "Failed to create remote file: $!"; goto RENDER_ADD; };
            }
            my ($ok, $err) = Gniza4cpWHM::Config::save($dest, \%data, \@Gniza4cpWHM::Config::REMOTE_KEYS);
            if ($ok) {
                # Initialize remote directory structure
                my $type = $data{REMOTE_TYPE} || 'ssh';
                my %init_args = (
                    type        => $type,
                    remote_base => $data{REMOTE_BASE} || '/backups',
                );
                if ($type eq 'ssh') {
                    $init_args{host}        = $data{REMOTE_HOST};
                    $init_args{port}        = $data{REMOTE_PORT} || '22';
                    $init_args{user}        = $data{REMOTE_USER} || 'root';
                    $init_args{auth_method} = $data{REMOTE_AUTH_METHOD} || 'key';
                    $init_args{key}         = $data{REMOTE_KEY};
                    $init_args{password}    = $data{REMOTE_PASSWORD};
                } elsif ($type eq 's3') {
                    $init_args{s3_access_key_id}     = $data{S3_ACCESS_KEY_ID};
                    $init_args{s3_secret_access_key}  = $data{S3_SECRET_ACCESS_KEY};
                    $init_args{s3_region}             = $data{S3_REGION};
                    $init_args{s3_endpoint}           = $data{S3_ENDPOINT};
                    $init_args{s3_bucket}             = $data{S3_BUCKET};
                } else {
                    $init_args{gdrive_service_account_file} = $data{GDRIVE_SERVICE_ACCOUNT_FILE};
                    $init_args{gdrive_root_folder_id}       = $data{GDRIVE_ROOT_FOLDER_ID};
                }
                Gniza4cpWHM::UI::init_remote_dir(%init_args);

                if ($form->{'wizard'}) {
                    Gniza4cpWHM::UI::set_flash('success', "Remote '$name' created. Now set up a schedule.");
                    print "Status: 302 Found\r\n";
                    print "Location: schedules.cgi?action=add&wizard=1&remote_name=" . _uri_escape($name) . "\r\n\r\n";
                    exit;
                }
                Gniza4cpWHM::UI::set_flash('success', "Remote '$name' created successfully.");
                print "Status: 302 Found\r\n";
                print "Location: remotes.cgi\r\n\r\n";
                exit;
            } else {
                push @errors, "Failed to save remote config: $err";
            }
        }
    }

    RENDER_ADD:

    print "Content-Type: text/html\r\n\r\n";
    Whostmgr::HTMLInterface::defheader('GNIZA Backup Manager — Add Remote', '', '/cgi/gniza4cp-whm/remotes.cgi');

    print Gniza4cpWHM::UI::page_header('Add Remote Destination');
    print Gniza4cpWHM::UI::render_nav('remotes.cgi');

    if (@errors) {
        print Gniza4cpWHM::UI::render_errors(\@errors);
    }

    # Pre-populate from POST if validation failed, else defaults
    my $conf = {};
    if ($method eq 'POST') {
        for my $key (@Gniza4cpWHM::Config::REMOTE_KEYS) {
            $conf->{$key} = $form->{$key} // '';
        }
    } elsif ($form->{'key_path'}) {
        $conf->{REMOTE_KEY} = $form->{'key_path'};
    }
    my $name_val = Gniza4cpWHM::UI::esc($form->{'remote_name'} // '');

    render_remote_form($conf, $name_val, 0, $form->{'wizard'} ? 1 : 0);

    print Gniza4cpWHM::UI::page_footer();
    Whostmgr::HTMLInterface::footer();
}

# ── Edit ─────────────────────────────────────────────────────

sub handle_edit {
    my $name = $form->{'name'} // '';
    my @errors;

    # Validate name
    my $name_err = Gniza4cpWHM::Validator::validate_remote_name($name);
    if ($name_err) {
        Gniza4cpWHM::UI::set_flash('error', "Invalid remote name.");
        print "Status: 302 Found\r\n";
        print "Location: remotes.cgi\r\n\r\n";
        exit;
    }

    my $conf_path = Gniza4cpWHM::UI::remote_conf_path($name);
    unless (-f $conf_path) {
        Gniza4cpWHM::UI::set_flash('error', "Remote '$name' not found.");
        print "Status: 302 Found\r\n";
        print "Location: remotes.cgi\r\n\r\n";
        exit;
    }

    if ($method eq 'POST') {
        unless (Gniza4cpWHM::UI::verify_csrf_token($form->{'gniza4cp_csrf'})) {
            push @errors, 'Invalid or expired form token. Please try again.';
        }

        my %data;
        for my $key (@Gniza4cpWHM::Config::REMOTE_KEYS) {
            $data{$key} = $form->{$key} // '';
        }

        if (!@errors) {
            my $validation_errors = Gniza4cpWHM::Validator::validate_remote_config(\%data);
            push @errors, @$validation_errors;
        }

        if (!@errors) {
            my $type = $data{REMOTE_TYPE} || 'ssh';
            my ($conn_ok, $conn_err);

            if ($type eq 'ssh') {
                ($conn_ok, $conn_err) = Gniza4cpWHM::UI::test_ssh_connection(
                    host        => $data{REMOTE_HOST},
                    port        => $data{REMOTE_PORT} || '22',
                    user        => $data{REMOTE_USER} || 'root',
                    auth_method => $data{REMOTE_AUTH_METHOD} || 'key',
                    key         => $data{REMOTE_KEY},
                    password    => $data{REMOTE_PASSWORD},
                );
            } else {
                my %rclone_args = (type => $type);
                if ($type eq 's3') {
                    $rclone_args{s3_access_key_id}     = $data{S3_ACCESS_KEY_ID};
                    $rclone_args{s3_secret_access_key}  = $data{S3_SECRET_ACCESS_KEY};
                    $rclone_args{s3_region}             = $data{S3_REGION} || 'us-east-1';
                    $rclone_args{s3_endpoint}           = $data{S3_ENDPOINT};
                    $rclone_args{s3_bucket}             = $data{S3_BUCKET};
                } else {
                    $rclone_args{gdrive_service_account_file} = $data{GDRIVE_SERVICE_ACCOUNT_FILE};
                    $rclone_args{gdrive_root_folder_id}       = $data{GDRIVE_ROOT_FOLDER_ID};
                }
                ($conn_ok, $conn_err) = Gniza4cpWHM::UI::test_rclone_connection(%rclone_args);
            }
            push @errors, "Connection test failed: $conn_err" unless $conn_ok;
        }

        if (!@errors) {
            my ($ok, $err) = Gniza4cpWHM::Config::save($conf_path, \%data, \@Gniza4cpWHM::Config::REMOTE_KEYS);
            if ($ok) {
                Gniza4cpWHM::UI::set_flash('success', "Remote '$name' updated successfully.");
                print "Status: 302 Found\r\n";
                print "Location: remotes.cgi\r\n\r\n";
                exit;
            } else {
                push @errors, "Failed to save remote config: $err";
            }
        }
    }

    print "Content-Type: text/html\r\n\r\n";
    Whostmgr::HTMLInterface::defheader('GNIZA Backup Manager — Edit Remote', '', '/cgi/gniza4cp-whm/remotes.cgi');

    print Gniza4cpWHM::UI::page_header("Edit Remote: " . Gniza4cpWHM::UI::esc($name));
    print Gniza4cpWHM::UI::render_nav('remotes.cgi');

    if (@errors) {
        print Gniza4cpWHM::UI::render_errors(\@errors);
    }

    # Load config (or re-use POST data on error)
    my $conf;
    if (@errors && $method eq 'POST') {
        $conf = {};
        for my $key (@Gniza4cpWHM::Config::REMOTE_KEYS) {
            $conf->{$key} = $form->{$key} // '';
        }
    } else {
        $conf = Gniza4cpWHM::Config::parse($conf_path, 'remote');
    }

    render_remote_form($conf, Gniza4cpWHM::UI::esc($name), 1);

    print Gniza4cpWHM::UI::page_footer();
    Whostmgr::HTMLInterface::footer();
}

# ── Delete ───────────────────────────────────────────────────

sub handle_delete {
    if ($method ne 'POST') {
        print "Status: 302 Found\r\n";
        print "Location: remotes.cgi\r\n\r\n";
        exit;
    }

    unless (Gniza4cpWHM::UI::verify_csrf_token($form->{'gniza4cp_csrf'})) {
        Gniza4cpWHM::UI::set_flash('error', 'Invalid or expired form token.');
        print "Status: 302 Found\r\n";
        print "Location: remotes.cgi\r\n\r\n";
        exit;
    }

    my $name = $form->{'name'} // '';
    my $name_err = Gniza4cpWHM::Validator::validate_remote_name($name);
    if ($name_err) {
        Gniza4cpWHM::UI::set_flash('error', 'Invalid remote name.');
        print "Status: 302 Found\r\n";
        print "Location: remotes.cgi\r\n\r\n";
        exit;
    }

    my $conf_path = Gniza4cpWHM::UI::remote_conf_path($name);
    if (-f $conf_path) {
        unlink $conf_path;
        Gniza4cpWHM::UI::set_flash('success', "Remote '$name' deleted.");
    } else {
        Gniza4cpWHM::UI::set_flash('error', "Remote '$name' not found.");
    }

    print "Status: 302 Found\r\n";
    print "Location: remotes.cgi\r\n\r\n";
    exit;
}

# ── Shared Form Renderer ────────────────────────────────────

sub render_remote_form {
    my ($conf, $name_val, $is_edit, $wizard) = @_;

    my $action_val = $is_edit ? 'edit' : 'add';
    my $remote_type = $conf->{REMOTE_TYPE} // 'ssh';

    print qq{<form method="POST" action="remotes.cgi">\n};
    print qq{<input type="hidden" name="action" value="$action_val">\n};
    if ($wizard) {
        print qq{<input type="hidden" name="wizard" value="1">\n};
    }
    print Gniza4cpWHM::UI::csrf_hidden_field();

    if ($is_edit) {
        print qq{<input type="hidden" name="name" value="$name_val">\n};
    }

    # Remote name
    print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
    print qq{<h2 class="card-title text-sm">Remote Identity</h2>\n};
    print qq{<div class="flex items-center gap-3 mb-2.5">\n};
    print qq{  <label class="w-44 font-medium text-sm" for="remote_name">Remote Name</label>\n};
    if ($is_edit) {
        print qq{  <input type="text" class="input input-bordered input-sm w-full max-w-xs" value="$name_val" disabled>\n};
    } else {
        print qq{  <input type="text" class="input input-bordered input-sm w-full max-w-xs" id="remote_name" name="remote_name" value="$name_val" required>\n};
        print qq{  <span class="text-xs text-base-content/60 ml-2">Letters, digits, hyphens, underscores</span>\n};
    }
    print qq{</div>\n};

    # Remote type selector
    my $ssh_checked    = ($remote_type eq 'ssh')    ? ' checked' : '';
    my $s3_checked     = ($remote_type eq 's3')     ? ' checked' : '';
    my $gdrive_checked = ($remote_type eq 'gdrive') ? ' checked' : '';

    print qq{<div class="flex items-center gap-3 mb-2.5">\n};
    print qq{  <label class="w-44 font-medium text-sm">Remote Type</label>\n};
    print qq{  <div class="join inline-flex items-stretch">\n};
    print qq{    <input type="radio" name="REMOTE_TYPE" class="join-item btn btn-sm m-0" aria-label="SSH" value="ssh" onchange="gniza4cpTypeChanged()"$ssh_checked>\n};
    print qq{    <input type="radio" name="REMOTE_TYPE" class="join-item btn btn-sm m-0" aria-label="Amazon S3" value="s3" onchange="gniza4cpTypeChanged()"$s3_checked>\n};
    print qq{    <input type="radio" name="REMOTE_TYPE" class="join-item btn btn-sm m-0" aria-label="Google Drive" value="gdrive" onchange="gniza4cpTypeChanged()"$gdrive_checked>\n};
    print qq{  </div>\n};
    print qq{</div>\n};

    print qq{</div>\n</div>\n};

    # ── SSH fields ────────────────────────────────────────────
    my $ssh_hidden = ($remote_type ne 'ssh') ? ' hidden' : '';

    # SSH key guidance
    print qq{<div id="type-ssh-guidance"$ssh_hidden>\n};
    print Gniza4cpWHM::UI::render_ssh_guidance();
    print qq{</div>\n};

    my $auth_method = $conf->{REMOTE_AUTH_METHOD} // 'key';
    my $key_checked = ($auth_method ne 'password') ? ' checked' : '';
    my $pw_checked  = ($auth_method eq 'password') ? ' checked' : '';
    my $key_hidden  = ($auth_method eq 'password') ? ' hidden' : '';
    my $pw_hidden   = ($auth_method ne 'password') ? ' hidden' : '';

    print qq{<div id="type-ssh-fields"$ssh_hidden>\n};
    print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
    print qq{<h2 class="card-title text-sm">SSH Connection</h2>\n};
    _field($conf, 'REMOTE_HOST', 'Hostname / IP', 'Required');
    _field($conf, 'REMOTE_PORT', 'SSH Port', 'Default: 22');
    _field($conf, 'REMOTE_USER', 'SSH User', 'Default: root');

    # Auth method toggle
    print qq{<div class="flex items-center gap-3 mb-2.5">\n};
    print qq{  <label class="w-44 font-medium text-sm">Auth Method</label>\n};
    print qq{  <div class="join inline-flex items-stretch">\n};
    print qq{    <input type="radio" name="REMOTE_AUTH_METHOD" class="join-item btn btn-sm m-0" aria-label="SSH Key" value="key" onchange="gniza4cpAuthChanged()"$key_checked>\n};
    print qq{    <input type="radio" name="REMOTE_AUTH_METHOD" class="join-item btn btn-sm m-0" aria-label="Password" value="password" onchange="gniza4cpAuthChanged()"$pw_checked>\n};
    print qq{  </div>\n};
    print qq{</div>\n};

    # Key field
    print qq{<div id="auth-key-field"$key_hidden>\n};
    _field($conf, 'REMOTE_KEY', 'SSH Private Key', 'Absolute path', 'Path to the private key file used for passwordless SSH authentication');
    print qq{</div>\n};

    # Password field
    my $pw_val = Gniza4cpWHM::UI::esc($conf->{REMOTE_PASSWORD} // '');
    print qq{<div id="auth-password-field"$pw_hidden>\n};
    print qq{<div class="flex items-center gap-3 mb-2.5">\n};
    print qq{  <label class="w-44 font-medium text-sm" for="REMOTE_PASSWORD">SSH Password</label>\n};
    print qq{  <input type="password" class="input input-bordered input-sm w-full max-w-xs" id="REMOTE_PASSWORD" name="REMOTE_PASSWORD" value="$pw_val">\n};
    print qq{  <span class="text-xs text-base-content/60 ml-2">Requires sshpass on server</span>\n};
    print qq{</div>\n};
    print qq{</div>\n};

    print qq{</div>\n</div>\n};
    print qq{</div>\n};

    # ── S3 fields ─────────────────────────────────────────────
    my $s3_hidden = ($remote_type ne 's3') ? ' hidden' : '';

    print qq{<div id="type-s3-fields"$s3_hidden>\n};
    print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
    print qq{<h2 class="card-title text-sm">Amazon S3 / S3-Compatible</h2>\n};
    _field($conf, 'S3_ACCESS_KEY_ID', 'Access Key ID', 'Required');
    _password_field($conf, 'S3_SECRET_ACCESS_KEY', 'Secret Access Key', 'Required');
    _field($conf, 'S3_REGION', 'Region', 'Default: us-east-1');
    _field($conf, 'S3_ENDPOINT', 'Custom Endpoint', 'For MinIO, Wasabi, etc.', 'Only needed for S3-compatible services, leave empty for AWS');
    _field($conf, 'S3_BUCKET', 'Bucket Name', 'Required');
    print qq{<p class="text-xs text-base-content/60 mt-2">Requires <code>rclone</code> installed on this server.</p>\n};
    print qq{</div>\n</div>\n};
    print qq{</div>\n};

    # ── Google Drive fields ───────────────────────────────────
    my $gdrive_hidden = ($remote_type ne 'gdrive') ? ' hidden' : '';

    print qq{<div id="type-gdrive-fields"$gdrive_hidden>\n};
    print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
    print qq{<h2 class="card-title text-sm">Google Drive</h2>\n};
    _field($conf, 'GDRIVE_SERVICE_ACCOUNT_FILE', 'Service Account JSON', 'Absolute path, required', 'Google Cloud service account key file for API access');
    _field($conf, 'GDRIVE_ROOT_FOLDER_ID', 'Root Folder ID', 'Optional', 'Google Drive folder ID to use as the root for backups');
    print qq{<p class="text-xs text-base-content/60 mt-2">Requires <code>rclone</code> installed on this server.</p>\n};
    print qq{</div>\n</div>\n};
    print qq{</div>\n};

    # ── Common fields ─────────────────────────────────────────
    print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
    print qq{<h2 class="card-title text-sm">Storage Path</h2>\n};
    _field($conf, 'REMOTE_BASE', 'Remote Base Dir', 'Default: /backups', 'Root directory on the remote where all backup snapshots are stored');
    print qq{</div>\n</div>\n};

    # Transfer
    print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
    print qq{<h2 class="card-title text-sm">Transfer Settings</h2>\n};
    _field($conf, 'BWLIMIT', 'Bandwidth Limit', 'KB/s, 0 = unlimited', 'Throttle transfer speed to avoid saturating the network');
    print qq{<div id="rsync-opts-field"$ssh_hidden>\n};
    _field($conf, 'RSYNC_EXTRA_OPTS', 'Extra rsync Options', 'SSH only', 'Additional flags appended to rsync commands, e.g. --compress');
    print qq{</div>\n};
    print qq{</div>\n</div>\n};

    # Retention
    print qq{<div class="card bg-white shadow-sm border border-base-300 mb-6">\n<div class="card-body">\n};
    print qq{<h2 class="card-title text-sm">Retention</h2>\n};
    _field($conf, 'RETENTION_COUNT', 'Snapshots to Keep', 'Default: 30', 'Number of backup snapshots to retain per account before pruning old ones');
    print qq{</div>\n</div>\n};

    # Submit
    print qq{<div class="flex items-center gap-2 mt-4">\n};
    my $btn_label = $is_edit ? 'Save Changes' : 'Create Remote';
    print qq{  <button type="submit" class="btn btn-primary btn-sm">$btn_label</button>\n};
    print qq{  <button type="button" class="btn btn-secondary btn-sm" id="test-conn-btn" onclick="gniza4cpTestConnection()">Test Connection</button>\n};
    print qq{  <button type="button" class="btn btn-info btn-sm" onclick="location.href='remotes.cgi'">Cancel</button>\n};
    print qq{</div>\n};

    print qq{<div id="gniza4cp-alert-area" class="mt-4"></div>\n};

    print qq{</form>\n};

    my $js_csrf = Gniza4cpWHM::UI::esc(Gniza4cpWHM::UI::generate_csrf_token());
    print qq{<script>var gniza4cpCsrf = '$js_csrf';\n};
    print <<'JS';
function gniza4cpGetType() {
    var radios = document.querySelectorAll('input[name="REMOTE_TYPE"]');
    for (var i = 0; i < radios.length; i++) {
        if (radios[i].checked) return radios[i].value;
    }
    return 'ssh';
}

function gniza4cpGetAuthMethod() {
    var radios = document.querySelectorAll('input[name="REMOTE_AUTH_METHOD"]');
    for (var i = 0; i < radios.length; i++) {
        if (radios[i].checked) return radios[i].value;
    }
    return 'key';
}

function gniza4cpTypeChanged() {
    var type = gniza4cpGetType();
    var sshFields    = document.getElementById('type-ssh-fields');
    var sshGuidance  = document.getElementById('type-ssh-guidance');
    var s3Fields     = document.getElementById('type-s3-fields');
    var gdriveFields = document.getElementById('type-gdrive-fields');
    var rsyncOpts    = document.getElementById('rsync-opts-field');

    sshFields.hidden    = (type !== 'ssh');
    sshGuidance.hidden  = (type !== 'ssh');
    s3Fields.hidden     = (type !== 's3');
    gdriveFields.hidden = (type !== 'gdrive');
    rsyncOpts.hidden    = (type !== 'ssh');
}

function gniza4cpAuthChanged() {
    var method = gniza4cpGetAuthMethod();
    var keyField = document.getElementById('auth-key-field');
    var pwField  = document.getElementById('auth-password-field');
    if (method === 'password') {
        keyField.hidden = true;
        pwField.hidden = false;
    } else {
        keyField.hidden = false;
        pwField.hidden = true;
    }
}

function gniza4cpTestConnection() {
    var type = gniza4cpGetType();
    var btn  = document.getElementById('test-conn-btn');
    var fd   = new FormData();
    fd.append('action', 'test');
    fd.append('gniza4cp_csrf', gniza4cpCsrf);
    fd.append('remote_type', type);

    if (type === 'ssh') {
        var host = document.getElementById('REMOTE_HOST').value;
        var port = document.getElementById('REMOTE_PORT').value;
        var user = document.getElementById('REMOTE_USER').value;
        var authMethod = gniza4cpGetAuthMethod();
        var key  = document.getElementById('REMOTE_KEY').value;
        var pw   = document.getElementById('REMOTE_PASSWORD').value;

        if (!host) { gniza4cpToast('error', 'Host is required.'); return; }
        if (authMethod === 'password' && !pw) { gniza4cpToast('error', 'Password is required.'); return; }
        if (authMethod === 'key' && !key) { gniza4cpToast('error', 'SSH key path is required.'); return; }

        fd.append('host', host);
        fd.append('port', port);
        fd.append('user', user);
        fd.append('auth_method', authMethod);
        fd.append('key', key);
        fd.append('password', pw);
    }
    else if (type === 's3') {
        var keyId  = document.getElementById('S3_ACCESS_KEY_ID').value;
        var secret = document.getElementById('S3_SECRET_ACCESS_KEY').value;
        var bucket = document.getElementById('S3_BUCKET').value;
        if (!keyId || !secret) { gniza4cpToast('error', 'S3 access key and secret are required.'); return; }
        if (!bucket) { gniza4cpToast('error', 'S3 bucket is required.'); return; }
        fd.append('S3_ACCESS_KEY_ID', keyId);
        fd.append('S3_SECRET_ACCESS_KEY', secret);
        fd.append('S3_REGION', document.getElementById('S3_REGION').value);
        fd.append('S3_ENDPOINT', document.getElementById('S3_ENDPOINT').value);
        fd.append('S3_BUCKET', bucket);
    }
    else if (type === 'gdrive') {
        var saFile = document.getElementById('GDRIVE_SERVICE_ACCOUNT_FILE').value;
        if (!saFile) { gniza4cpToast('error', 'Service account file path is required.'); return; }
        fd.append('GDRIVE_SERVICE_ACCOUNT_FILE', saFile);
        fd.append('GDRIVE_ROOT_FOLDER_ID', document.getElementById('GDRIVE_ROOT_FOLDER_ID').value);
    }

    btn.disabled = true;
    btn.innerHTML = '<span class="loading loading-spinner loading-xs"></span> Testing\u2026';

    fetch('remotes.cgi', { method: 'POST', body: fd })
        .then(function(r) { return r.json(); })
        .then(function(data) {
            gniza4cpToast(data.success ? 'success' : 'error', data.message);
            if (data.csrf) gniza4cpCsrf = data.csrf;
        })
        .catch(function(err) {
            gniza4cpToast('error', 'Request failed: ' + err.toString());
        })
        .finally(function() {
            btn.disabled = false;
            btn.innerHTML = 'Test Connection';
        });
}

function gniza4cpToast(type, msg) {
    var area = document.getElementById('gniza4cp-alert-area');
    if (!area) return;
    area.innerHTML = '';
    var el = document.createElement('div');
    el.className = 'alert alert-' + type;
    el.className += ' px-5 py-3 rounded-lg text-sm';
    el.textContent = msg;
    area.appendChild(el);
    setTimeout(function() { el.style.opacity = '0'; }, type === 'error' ? 6000 : 3000);
    setTimeout(function() { area.innerHTML = ''; }, type === 'error' ? 6500 : 3500);
}
</script>
JS
}

sub _field {
    my ($conf, $key, $label, $hint, $tip) = @_;
    my $val = Gniza4cpWHM::UI::esc($conf->{$key} // '');
    my $hint_html = $hint ? qq{ <span class="text-xs text-base-content/60 ml-2">} . Gniza4cpWHM::UI::esc($hint) . qq{</span>} : '';
    my $tip_html = $tip ? qq{ <span class="tooltip tooltip-top" data-tip="} . Gniza4cpWHM::UI::esc($tip) . qq{">&#9432;</span>} : '';
    print qq{<div class="flex items-center gap-3 mb-2.5">\n};
    print qq{  <label class="w-44 font-medium text-sm whitespace-nowrap" for="$key">$label$tip_html</label>\n};
    print qq{  <input type="text" class="input input-bordered input-sm w-full max-w-xs" id="$key" name="$key" value="$val">\n};
    print qq{  $hint_html\n} if $hint;
    print qq{</div>\n};
}

sub _password_field {
    my ($conf, $key, $label, $hint) = @_;
    my $val = Gniza4cpWHM::UI::esc($conf->{$key} // '');
    my $hint_html = $hint ? qq{ <span class="text-xs text-base-content/60 ml-2">} . Gniza4cpWHM::UI::esc($hint) . qq{</span>} : '';
    print qq{<div class="flex items-center gap-3 mb-2.5">\n};
    print qq{  <label class="w-44 font-medium text-sm" for="$key">$label</label>\n};
    print qq{  <input type="password" class="input input-bordered input-sm w-full max-w-xs" id="$key" name="$key" value="$val">\n};
    print qq{  $hint_html\n} if $hint;
    print qq{</div>\n};
}

sub _uri_escape {
    my ($str) = @_;
    $str =~ s/([^A-Za-z0-9._~-])/sprintf("%%%02X", ord($1))/ge;
    return $str;
}

sub _ensure_main_config {
    my $config_file = '/etc/gniza4cp/gniza4cp.conf';
    return if -f $config_file;

    # Create dirs
    for my $dir ('/etc/gniza4cp', '/etc/gniza4cp/remotes.d', '/etc/gniza4cp/schedules.d', '/var/log/gniza4cp') {
        mkdir $dir unless -d $dir;
    }

    # Copy example or write defaults
    my $example = '/usr/local/gniza4cp/etc/gniza4cp.conf.example';
    if (-f $example) {
        File::Copy::copy($example, $config_file);
    } else {
        if (open my $fh, '>', $config_file) {
            print $fh qq{# gniza4cp configuration — auto-created by WHM plugin\n};
            print $fh qq{TEMP_DIR="/usr/local/gniza4cp/workdir"\n};
            print $fh qq{INCLUDE_ACCOUNTS=""\n};
            print $fh qq{EXCLUDE_ACCOUNTS="nobody"\n};
            print $fh qq{LOG_DIR="/var/log/gniza4cp"\n};
            print $fh qq{LOG_LEVEL="info"\n};
            print $fh qq{LOG_RETAIN=90\n};
            print $fh qq{NOTIFY_EMAIL=""\n};
            print $fh qq{NOTIFY_ON="failure"\n};
            print $fh qq{LOCK_FILE="/var/run/gniza4cp.lock"\n};
            print $fh qq{SSH_TIMEOUT=30\n};
            print $fh qq{SSH_RETRIES=3\n};
            print $fh qq{RSYNC_EXTRA_OPTS=""\n};
            close $fh;
        }
    }
}
