package Cpanel::CCS::DomainTLS;

# cpanel - Cpanel/CCS/DomainTLS.pm                 Copyright 2020 cPanel, L.L.C.
#                                                           All rights reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

use strict;
use warnings;

use Cpanel::Autodie                         ();
use Cpanel::Domain::TLS                     ();
use Cpanel::FileUtils::Copy                 ();
use Cpanel::AcctUtils::DomainOwner::Tiny    ();
use Cpanel::Chdir                           ();
use Cpanel::Mkdir                           ();
use Cpanel::SSL::Objects::Certificate::File ();
use Cpanel::SafetyBits::Chown               ();
use Cpanel::Proxy::Tiny                     ();

use Cpanel::CCS::Constants ();

our $install_dir = Cpanel::CCS::Constants::CCS_INSTALL_DIR();

=head1 NAME

Cpanel::CCS::DomainTLS

=head1 SYNOPSIS

Cpanel::CCS::DomainTLS::real_copy_certs_for_domains(@domains);

=head1 DESCRIPTION

Module for adding or removing certs to CCS' DomainTLS store.

=head1 SUBROUTINES

=head2 real_copy_certs_for_domains

Copies installed certs for domains from cPanel and installs them for the
relevant domains in CCS.

Input is ARRAY of domain names.
Returns undef

=cut

sub real_copy_certs_for_domains {
    my @domains = @_;
    Cpanel::Mkdir::ensure_directory_existence_and_mode( "$install_dir/conf/domain-TLS", 0700 );
    foreach my $domain (@domains) {

        # Get the domain owner
        my $owner = Cpanel::AcctUtils::DomainOwner::Tiny::getdomainowner($domain);

        # Get the cert path
        my $combined = Cpanel::Domain::TLS->get_tls_path($domain);
        my $to       = "$install_dir/conf/domain-TLS/$domain.pem";
        my ( $success, $msg ) = Cpanel::FileUtils::Copy::safecopy( $combined, $to );
        if ( !$success && $msg ) {
            warn $msg;
            next;
        }
        if ( !Cpanel::SafetyBits::Chown::safe_chown( 'cpanel-ccs', 'cpanel-ccs', "$install_dir/conf/domain-TLS/" ) ) {
            warn "Cannot change ownership of file '$install_dir/conf/domain-TLS/' to 'cpanel-ccs'!";
            next;
        }
        if ( !Cpanel::SafetyBits::Chown::safe_chown( 'cpanel-ccs', 'cpanel-ccs', $to ) ) {
            warn "Cannot change ownership of file '$to' to 'cpanel-ccs'!";
            next;
        }

        # Now let's make symlinks for all covered domains.
        my $cert_obj           = Cpanel::SSL::Objects::Certificate::File->new( path => $to );
        my $additional_doms_ar = $cert_obj->domains();
        my $proxies = Cpanel::Proxy::Tiny::get_known_proxy_subdomains(); 
        foreach my $extra_dom (@$additional_doms_ar) {
            next if $extra_dom eq $domain;

            # Ensure the user owns the extra domain covered on the cert OR it
            # is a proxy subdomain before we install.
            if( !grep { "$_.$domain" eq $extra_dom } keys %$proxies ) {
                my $extradomowner = Cpanel::AcctUtils::DomainOwner::Tiny::getdomainowner($extra_dom);
                next if $extradomowner ne $owner;
            }
            my $chdir = Cpanel::Chdir->new("$install_dir/conf/domain-TLS/");

            # Clobber any previous link or file if it exists, presumably we're updating it here.
            do {
                unlink("$install_dir/conf/domain-TLS/$extra_dom.pem") or die "Couldn't delete '$install_dir/conf/domain-TLS/$extra_dom.pem': $!";
            } if -e "$install_dir/conf/domain-TLS/$extra_dom.pem";
            Cpanel::Autodie::symlink_if_no_conflict( "$domain.pem", "$extra_dom.pem" );
        }
    }

    # Scheduling the task a bit into the future will allow task de-duplication when running AutoSSL
    # i.e., we won't have to restart cpanel_ccs for each domain that is updated
    require Cpanel::ServerTasks;
    Cpanel::ServerTasks::schedule_task( ['CpServicesTasks'], 60, "restartsrv cpanel_ccs" );

    return;
}

=head2 real_remove_certs_for_domains

Removes certs from CCS' DomainTLS store for the indicated @domains.

Input is ARRAY of domain names.
Returns undef

=cut

sub real_remove_certs_for_domains {
    my @domains = @_;
    foreach my $domain (@domains) {

        # Get the domain owner
        my $owner = Cpanel::AcctUtils::DomainOwner::Tiny::getdomainowner($domain);

        my $file     = "$install_dir/conf/domain-TLS/$domain.pem";
        my $cert_obj = Cpanel::SSL::Objects::Certificate::File->new( path => $file );

        unlink($file) or do {

            # Don't warn on ENOENT
            warn "Deletion of $file failed: $!" if $! != 2;
        };

        # Also murder the cache file
        unlink("$file.cache") or do {
            warn "Deletion of $file failed: $!" if $! != 2;
        };

        # Now let's kill all links to the covered domains.
        my $additional_doms_ar = $cert_obj->domains();
        my $proxies = Cpanel::Proxy::Tiny::get_known_proxy_subdomains(); 
        foreach my $extra_dom (@$additional_doms_ar) {
            next if $extra_dom eq $domain;

            # Ensure the user owns the extra domain covered on the cert.
            # In cases where the domain was deleted, this will return
            # 'default' for both domains.
            if( !grep { "$_.$domain" eq $extra_dom } keys %$proxies ) {
                my $extradomowner = Cpanel::AcctUtils::DomainOwner::Tiny::getdomainowner($domain);
                next if $extradomowner ne $owner;
            }
            unlink("$install_dir/conf/domain-TLS/$extra_dom.pem") or do {
                warn "Deletion of $file failed: $!" if $! != 2;
            };
            unlink("$install_dir/conf/domain-TLS/$extra_dom.pem.cache") or do {
                warn "Deletion of $file failed: $!" if $! != 2;
            };
        }

    }
    return;
}

1;
