package Cpanel::Easy::Apache::2_4;

# cpanel - Cpanel/Easy/Apache/2_4.pm              Copyright(c) 2016 cPanel, Inc.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

# make sure any changes here are reflected in Cpanel::Easy::Apache::get_apache_defaults_text() if necessary

use Cpanel::FileUtils        ();
use Cpanel::Version::Compare ();
use Cpanel::Version::Tiny    ();

our $version = '2.4.37';

our $easyconfig = {
    'name'               => 'Apache 2.4',
    'version'            => $version,
    'note'               => 'Newest version from the Apache project with performance improvements over Apache 2.2.',
    'hastargz'           => 1,
    'src_cd2'            => 'httpd-2.4',                                                                                      # _apache_versions()
    'verify_on'          => q{Please read the cPanel/WHM "Critical Changes in Apache 2.4" documentation before upgrading.},
    'verify_on_url'      => 'https://go.cpanel.net/a24fyi',
    'dryrun_fails_fatal' => 1,
    'depend_fails_fatal' => 1,
    'perl_modules'       => {
        'Digest::SHA1'     => 1,
        'Crypt::PasswdMD5' => 1
    },
    'modself' => sub {
        my ( $easy, $self_hr, $profile_hr ) = @_;

        # Case 60403: Apache 2.4 should only be available for cPanel/WHM 11.34 and above
        # NOTE: 11.33 is used because that's the internal development version number.
        my $ver_lock = Cpanel::Version::Compare::compare( $Cpanel::Version::Tiny::VERSION_BUILD, '>=', '11.35' );
        $self_hr->{'skip'} = 1 unless $ver_lock;

        if ( $easy->{'getos_releaseversion'} >= 7 && -d '/etc/systemd/system/' ) {
            $self_hr->{step}{5.1} = {
                'name'    => 'Setting up httpd.service',
                'command' => sub {
                    my ($self) = @_;

                    if ( !-e '/etc/systemd/system/httpd.service' || !`/bin/grep ^ExecStop= /etc/systemd/system/httpd.service` ) {
                        open( my $fh, '>', '/etc/systemd/system/httpd.service' ) || return ( 0, q{Could not open '[_1]' for writing: [_2]}, '/etc/systemd/system/httpd.service', $! );
                        print {$fh} <<"END_CONT";
[Unit]
Description=Apache webserver managed by cPanel EasyApache
ConditionPathExists=!/etc/httpddisable
ConditionPathExists=!/etc/apachedisable
ConditionPathExists=!/etc/httpdisable

[Service]
Type=forking
ExecStart=/usr/local/cpanel/scripts/restartsrv_httpd --no-verbose
ExecStop=/usr/local/cpanel/scripts/restartsrv_httpd stop --no-verbose

[Install]
WantedBy=multi-user.target
END_CONT
                        close($fh);

                    }

                    return ( 1, 'Ok' );
                },
            };
        }
    },
    'configure' => {
        '--prefix'              => ['/usr/local/apache'],
        '--with-included-apr'   => [],
        '--with-suexec-logfile' => ['/usr/local/apache/logs/suexec_log'],
        '--with-suexec-caller'  => ['nobody'],
        '--with-suexec-uidmin'  => ['100'],
        '--with-suexec-gidmin'  => ['100'],
        '--with-suexec-docroot' => ['/'],
        '--with-suexec-userdir' => ['public_html'],
        '--with-mpm'            => ['prefork'],
        '--enable-modules'      => ['none'],                                # needed to prevent Apache 2.4 from building all modules, even if unselected
        '--disable-v4-mapped'   => [],
        '--with-crypto'         => [],                                      # this is passed to apr-util
    },
    'dryrun' => {
        '0.1' => {
            'name'    => 'Applying patches if any',
            'command' => sub {
                my ($self) = @_;

                my @patches = qw(
                  2.4.2_cpanel_ssl_engine_rand.patch
                  2.2_cpanel_whmserverstatus.patch
                  2.2.17_cpanel_safe_conf_defaults.patch
                  2.2.17_cpanel_suexec_script_share.patch
                  2.2.17_cpanel_mailman_suexec.patch
                  2.2_cpanel_fileprotect_suexec_httpusergroupallow.patch
                  2.2_cpanel_apachectl_startssl_support.patch
                  2.4.2_cpanel_apachectl_ulimit.patch
                  2.4.2_cpanel_apache_dump_directives.patch
                  2.2_cpanel_default_user_dir.patch
                  2.2.12_cpanel_server_listen.patch
                  2.4_external_pcre.patch
                  0001-Patch-suexec-to-allow-userdir-with-FastCGI.patch
                );

                if ( $self->has_cloudlinux_support() ) {
                    push @patches, 'apr-2.4-httpd.3.patch';
                }

                # Case 63889: Resolves an issue on 64-bit CentOS 5.x devices where
                # the proper PKG_CONFIG_PATH isn't being set when both openssl 32-bit
                # and 64-bit devel packages are installed.  This patch applies
                # blanketly for all CentOS 5.x devices.
                if ( $self->is_redhat() && $self->{'getos_releaseversion'} =~ /^5/ && $self->{'cpu_bits'} eq '64' ) {
                    push @patches, '2.4.4_centos5x_pkgconfig_lib64.patch';
                }

                my @rc = ( 1, 'Ok' );
                foreach my $patch (@patches) {
                    if ( -e '../cppatch/' . $patch ) {
                        @rc = $self->apply_patch( '../cppatch/' . $patch );
                        return @rc if !$rc[0];
                    }
                }
                return @rc;
            },
        },
        '1' => {
            'name'    => 'Setting permissions on cgi-sys',
            'command' => sub {
                my ($self) = @_;
                chmod 0755, '/usr/local/cpanel/cgi-sys';
                return ( 1, 'Ok' );
            },
        },
        '2' => {
            'name'    => 'Check SSL',
            'command' => sub {
                my ($self) = @_;
                my $openssl_path = $self->get_openssl_prefix( { 'skip_mach' => 1 } );

                if ($openssl_path) {
                    $self->add_to_configure( { '--with-ssl' => $openssl_path } );
                }
                return ( 1, 'Ok' );
            },
        },
    },
    'step' => {
        '0' => {
            'name'    => 'Setting up cp identifier',
            'command' => sub {
                my ($self) = @_;
                my %err;
                my $version  = $self->{'version'};     # took out because messed up vim parsing
                my $revision = $self->{'revision'};    # took out because of messed up vim parsing

                my $rc = Cpanel::FileUtils::regex_rep_file(
                    'server/main.c',
                    { qr{^(\s*)(printf[(]["]Server\s+built[:].*)$} => q{$1$2\n$1} . q{printf("Cpanel::Easy::Apache v%s rev%s\\\n", } . qq{"$version", "$revision");}, }, \%err,
                );

                return ( 0, 'Could not patch for cp identifier' ) if !$rc;
                return ( 1, 'ok' );
            },
        },
        '0.01' => {
            'name'    => 'Check PCRE',
            'command' => sub {
                my $self = shift;
                my ( $path, @text ) = $self->get_path_installed('Cpanel::Easy::OptLib::pcre');
                if ($path) {
                    $self->{'__'}{'pcre_path'} = $path;
                    $self->add_to_configure( { '--with-pcre' => [$path] } );

                    # we need to additionally affect LD_LIBRARY_PATH, this change affects %ENV outside of this local scope
                    $ENV{'LD_LIBRARY_PATH'} = ( exists $ENV{'LD_LIBRARY_PATH'} and $ENV{'LD_LIBRARY_PATH'} ) ? qq($path/lib:$ENV{'LD_LIBRARY_PATH'}) : qq($path/lib);    # wrapped qq's in () for clarity
                }
                return ( $path, @text );
            },
        },

        # This is a backwards-compatibility addition for access control directives.
        '0.04' => {
            'name'    => 'Apache 2.2 backwards-compatibility',
            'command' => sub {
                my $self = shift;

                # When Apache went from 2.2 to 2.4, they changed the default compilation
                # to use dynamic modules (DSO). So, if you said '--enable-cgi', Apache
                # 2.2 would compile it into httpd.  In contrast, Apache 2.4 makes a DSO out
                # of it.  This poses a problem because many applications within cPanel/WHM
                # and EasyApache depend on the 'httpd -l' functionality to determine Apache
                # functionality.  So, this is needed for backwards-compatibility.

                my @static = qw( authn_core authz_core authn_file authz_host
                  authz_groupfile authz_user auth_basic include filter
                  log_config logio setenvif ssl mime status autoindex
                  suexec cgi negotiation dir actions userdir alias
                  rewrite access-compat unixd socache_dbm slotmem_shm
                  socache_shmcb
                );

                # Turning off mod_info requires cP 11.52.1.0 or later;
                # versions before get mod_info, so as not to cause potential
                # breakage.
                if ( Cpanel::Version::Compare::compare( $Cpanel::Version::Tiny::VERSION_BUILD, '<', '11.52.1.0' ) ) {
                    push @static, 'info';
                }

                for my $mod (@static) {
                    $self->add_to_configure( { "--enable-$mod" => ['static'] } );
                }

                return ( 1, 'Ok' );
            },
        },
        '0.1' => {
            'name'    => 'Configure',
            'command' => sub {
                my ($self) = @_;

                if ( -d '/usr/kerberos/include' && $ENV{'CFLAGS'} !~ m{/usr/kerberos/include} ) {
                    $ENV{'CFLAGS'} .= ' -I/usr/kerberos/include';
                }

                $self->add_to_configure( $self->get_raw_opts_if_any('Apache2_4') );

                $self->print_configure();
                local $ENV{'CFLAGS'}   = $ENV{'CFLAGS'};
                local $ENV{'LDFLAGS'}  = $ENV{'LDFLAGS'};
                local $ENV{'CPPFLAGS'} = $ENV{'CPPFLAGS'};

                # 64 bit specific
                $ENV{'LDFLAGS'} = $ENV{'LDFLAGS'} . ' -L/usr/lib64'    if ( $self->{'cpu_bits'} eq '64' );
                $ENV{'CFLAGS'}  = $ENV{'CFLAGS'} . ' -m64 -fPIC -DPIC' if ( $self->{'cpu_bits'} eq '64' );

                # Disable SNI support
                # $ENV{'CFLAGS'} = $ENV{'CFLAGS'} . ' -DOPENSSL_NO_TLSEXT=1';

                # Custom openssl.  Only the opt LDFLAGS are required for most versions of 2.2.
                # kerberos added for bug in 2.2.10   https://issues.apache.org/bugzilla/show_bug.cgi?id=46168
                if ( $self->get_openssl_prefix( { 'skip_mach' => 1 } ) eq '/opt/openssl' ) {
                    $ENV{'LDFLAGS'} = '-L/opt/openssl/lib -Wl,-R/opt/openssl/lib ' . $ENV{'LDFLAGS'};
                }
                elsif ( -d '/usr/kerberos/lib64' && $ENV{'LDFLAGS'} !~ m{/usr/kerberos/lib64} ) {
                    $ENV{'LDFLAGS'} .= ' -L/usr/kerberos/lib64';
                }
                elsif ( -d '/usr/kerberos/lib' && $ENV{'LDFLAGS'} !~ m{/usr/kerberos/lib} ) {
                    $ENV{'LDFLAGS'} .= ' -L/usr/kerberos/lib';
                }

                return $self->run_system_cmd_returnable( [ './configure', $self->get_configure_as_array() ] );
            },
        },
        '1' => {
            'name'    => 'Making Apache 2.4',
            'command' => sub {
                my ($self) = @_;
                $self->hook_script('/usr/local/cpanel/scripts/before_apache_make');
                my @cmd = ( 'make', @{ $self->get_make_options() } );
                return $self->run_system_cmd_returnable( [@cmd] );
            },
        },
        '2' => {
            'name'    => 'Installing Apache 2.4',
            'command' => sub {
                my ($self) = @_;
                return $self->apache_make_install();
            }
        },
        '4' => {
            'name'    => 'APXSing mod_bwlimited',
            'command' => sub {
                my ($self) = @_;

                return $self->run_system_cmd_returnable( [ qw(/usr/local/apache/bin/apxs -i -a -c), '../mod_bwlimited.c' ] );
            },
        },
        '4.5' => {
            'name'    => 'APXSing mod_disable_suexec',
            'command' => sub {
                my ($self) = @_;

                return $self->run_system_cmd_returnable( [ qw(/usr/local/apache/bin/apxs -i -c), '../mod_disable_suexec.c' ] );
            },
        },
        '4.7' => {
            'name'    => 'APXSing mod_hostinglimits',
            'command' => sub {
                my $self = shift;
                return $self->cloudlinux_update();
            },
        },
        '4.8' => {
            'name'    => 'Activate modules in alternate conf file',
            'command' => sub {
                my ($self) = @_;
                my $alt_conf = $self->{'_'}{'httpd.conf-make_install_created'};
                if ( -e $alt_conf ) {
                    open my $alt_conf_fh, '+<', $alt_conf || return ( 0, q{Could not open '[_1]' for read/write: [_2]}, $alt_conf, $! );
                    my @HTTP_CONF = <$alt_conf_fh>;
                    unshift @HTTP_CONF, "LoadModule bwlimited_module modules/mod_bwlimited.so\n";
                    seek( $alt_conf_fh, 0, 0 );
                    print $alt_conf_fh @HTTP_CONF;
                    truncate( $alt_conf_fh, tell($alt_conf_fh) );
                    close $alt_conf_fh;
                    return ( 1, 'Ok' );
                }
                return ( 0, q{Could not locate [_1]}, $alt_conf );
            },
        },
        '4.9' => {
            'name'    => 'Check proxydomains settings',
            'command' => sub {
                my $self = shift;
                return $self->proxydomains_configuration();
            },
        },
        '5' => {
            'name'    => 'Removing invalid User and Group directive',
            'command' => sub {
                my ($self) = @_;
                my @conf_files = ( $self->_get_main_httpd_conf() );
                if ( $self->{'trying_previous_httpd_conf'} && -e $self->{'_'}{'httpd.conf-make_install_created'} ) {
                    push @conf_files, $self->{'_'}{'httpd.conf-make_install_created'};
                }

                my @rc = ();
                foreach my $conf_file (@conf_files) {
                    @rc = $self->strip_apache_directive( $conf_file, 'User ', 'Group ' );
                    return @rc unless $rc[0];
                }
                return @rc;
            },
        },

        # 'modify_later_easy-config_hr' phpasuser starts at 6.1
        # 'modify_later_easy-config_hr' fastcgi starts at 8.1

    },
};

1;
