package Cpanel::Easy::Utils::HttpdTest;

# cpanel10 - Cpanel/Easy/Utils/HttpdTest.pm  Copyright(c) 2005-2007 cPanel, Inc.
#                                                           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;
no warnings qw(redefine);

use Cpanel::Config ();

eval { require Cpanel::Ips };

our $cgi_guts = <<'END_CGI';
#!/usr/bin/perl

my $message = "Hello World " . time() . "\n"; 

{
    use bytes; 
    my $msg_len = length($message);
    print "Content-type: text/html\nContent-length: $msg_len\n\n";
}

print $message;

END_CGI

sub add_cgi_test {
    my ( $self, $force ) = @_;
    return if $self->{'_'}{'add_cgi_test'} && !$force;

    $self->{'_'}{'add_cgi_test'}++;
    my $ext = '.cgi';

    # No real reason to check multiple extensions in cgi-sys directory since it's a scriptalias
    #    for my $ext ( $self->get_addhandler_cgiscript_array_from_httpdconf() ) {
    push @{ $self->{'_'}{'post_httpd_restart_tests'} },
      {
        'name'    => "Basic CGI-SYS Request $ext",
        'command' => $self->get_cgi_test_command_hr(
            {
                'path'          => "/usr/local/cpanel/cgi-sys/cp_ea3_cgi_test$ext",
                'content'       => $Cpanel::Easy::Utils::HttpdTest::cgi_guts,
                'localhost_uri' => "/cgi-sys/cp_ea3_cgi_test$ext",
                'mode'          => '0755',
                'uid'           => 0,
                'gid'           => 10,
                'ok_regexp'     => qr{Hello World \d+},
                'fatal'         => 0,
            }
        ),
      };

    #    }
}

sub run_ssl_cgi_uri {
    my ( $easy, $ip, $port, $keep ) = @_;

    if ( ref $easy->{'_'}{'acl_restricts_user_nobody'}{'/usr/bin/perl'} eq 'HASH' ) {
        my $limits = join( ',', @{ $easy->{'_'}{'acl_restricts_user_nobody'}{'/usr/bin/perl'}{'regex_matches'} } );
        $easy->print_alert_color( 'red', q{ACL limits on '[_1]' detected ([_2])}, '/usr/bin/perl', $limits );
        return ( 1, 'skip' );
    }

    my $path = '/usr/local/cpanel/cgi-sys/cp_ea3_cgi_test.cgi';

    if ( -e $path ) {
        unlink $path if $easy->file_md5_hex($path) ne Digest::MD5::md5_hex($Cpanel::Easy::Utils::HttpdTest::cgi_guts);
    }

    if ( !-e $path ) {
        open my $fh, '>', $path or return ( '', "Could not make script: $!" );    # not the same kind of 'usual' ea3 return()
        print {$fh} $Cpanel::Easy::Utils::HttpdTest::cgi_guts;
        close $fh;
    }

    chmod 0755, $path;
    chown 0, 10, $path;

    my ( $content, $response, $headers_hr ) = $easy->get_localhost_uri( '/cgi-sys/cp_ea3_cgi_test.cgi', 1, $ip, $port );
    unlink $path if !$keep;

    return ( $content, $response, $headers_hr, $path );
}

sub get_cgi_test_command_hr {
    my ( $self, $args_hr ) = @_;

    return sub {
        my ( $easy, $is_fatal_boolean_sr ) = @_;
        my $path = $args_hr->{'path'};
        unlink $path;
        if ( ref $easy->{'_'}{'acl_restricts_user_nobody'}{'/usr/bin/perl'} eq 'HASH' ) {
            my $limits = join( ',', @{ $easy->{'_'}{'acl_restricts_user_nobody'}{'/usr/bin/perl'}{'regex_matches'} } );
            $easy->print_alert_color( 'red', q{ACL limits on '[_1]' detected ([_2])}, '/usr/bin/perl', $limits );
            return ( 1, 'skip' );
        }

        my ( $report_only, $skip_root ) = ( 1, 0 );    # maybe set in obj/pref instead ??
        ${$is_fatal_boolean_sr} = 1 if ( $args_hr->{'fatal'} );
        ${$is_fatal_boolean_sr} = 0 if $easy->{'_'}{'prefs'}{'do_not_revert_on_test_failure'};

        # don't even try to open() if no uid/gid data was passed
        my $uid;
        my $gid;
        $uid = $args_hr->{'uid'}                      if defined( $args_hr->{'uid'} );
        $gid = $args_hr->{'gid'}                      if defined( $args_hr->{'gid'} );
        $uid = ( getpwnam( $args_hr->{'user'} ) )[2]  if defined( $args_hr->{'user'} );
        $gid = ( getpwnam( $args_hr->{'group'} ) )[3] if defined( $args_hr->{'group'} );
        if ( $uid !~ m{ \A \d+ \z }xms || $gid !~ m{ \A \d+ \z }xms ) {
            return ( 0, q{Could not determine valid uid/gid for test script} );
        }

        # setup script
        if ( open my $test_fh, '>', $path ) {
            print {$test_fh} $args_hr->{'content'};
            close $test_fh;

            chown( $uid, $gid, $path ) or return ( 0, q{Could not chown '[_1]' to '[_2]': [_3]}, $path, "$uid:$gid", $! );
            chmod( oct( $args_hr->{'mode'} ), $path ) or return ( 0, q{Could not chmod '[_1]' to '[_2]': [_3]}, $path, $args_hr->{'mode'}, $! );
        }
        else {
            $self->ls_ld_each_part_of( $path, $report_only, $skip_root );
            return ( 0, q{Could not open() file '[_1]' for writing: [_2]}, $path, $! );
        }

        my $content = $easy->get_localhost_uri( $args_hr->{'localhost_uri'} );

        if ( !$content ) {
            $self->ls_ld_each_part_of( $path, $report_only, $skip_root );
            unlink $path;
            return ( 0, q{There was no content. This message is normal if your system has been configured to disallow CGI applications.} );
        }
        elsif ( $content =~ $args_hr->{'ok_regexp'} || $content =~ m{Hello World \d+} ) {
            unlink $path;
            return ( 1, 'ok' );
        }
        else {
            $self->ls_ld_each_part_of( $path, $report_only, $skip_root );
            unlink $path;
            return ( 0, qq{content received but out put was unexpected:\n$content} );
        }
    };
}

sub get_ip_and_port_of_ssl_vhost {
    my ( $self, $force ) = @_;

    if ( exists $self->{'get_ip_and_port_of_ssl_vhost_cache'} ) {
        if ( !$force ) {
            return @{ $self->{'get_ip_and_port_of_ssl_vhost_cache'} } if ref $self->{'get_ip_and_port_of_ssl_vhost_cache'} eq 'ARRAY';
        }
    }

    my $port         = $self->get_ssl_httpd_port();
    my $ip           = '';
    my $tentative_ip = '';
    my $ip_hr        = defined &Cpanel::Ips::fetchipslist ? Cpanel::Ips::fetchipslist() : '';

    SafeFile::safe_readwrite(
        $self->_get_main_httpd_conf(),
        sub {
            my ( $rw_fh, $safe_replace_content_coderef ) = @_;

          LINE:
            while ( my $line = <$rw_fh> ) {
                chomp $line;
                if ( $line =~ m{\A \s* [<] VirtualHost \s+ (\d+\.\d+\.\d+\.\d+) [:] \Q$port\E \s* [>] }xmsi ) {
                    $tentative_ip = $1;
                    if ( $ip_hr eq '' || exists $ip_hr->{$ip} ) {
                        $ip = $tentative_ip;
                        last LINE;
                    }
                }
            }

            return;
        },
    );

    $self->{'get_ip_and_port_of_ssl_vhost_cache'} = [ $ip, $port ];
    return ( $ip, $port );
}

sub get_addhandler_cgiscript_array_from_httpdconf {
    my ( $self, $force ) = @_;

    if ( exists $self->{'get_addhandler_cgiscript_array_from_httpdconf_cache'} ) {
        if ( !$force ) {
            return @{ $self->{'get_addhandler_cgiscript_array_from_httpdconf_cache'} } if ref $self->{'get_addhandler_cgiscript_array_from_httpdconf_cache'} eq 'ARRAY';
        }
    }

    my @exts;
    my %seen;
    SafeFile::safe_readwrite(
        $self->_get_main_httpd_conf(),
        sub {
            my ( $rw_fh, $safe_replace_content_coderef ) = @_;

          LINE:
            while ( my $line = <$rw_fh> ) {
                chomp $line;
                if ( $line =~ m{\A \s* AddHandler \s+ cgi-script \s+ (\S+ (?:\s+ \S+)*) \z }xmsi ) {
                    my $exts = $1;    # copy it to be safe
                    for my $ext ( split( /\s+/, $exts ) ) {
                        push @exts, $ext if !exists $seen{$ext};
                        $seen{$ext}++;
                    }
                }
            }

            return;
        },
    );

    $self->{'get_addhandler_cgiscript_array_from_httpdconf_cache'} = \@exts;
    return @exts;
}

1;
