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

package Cpanel::Easy::Apache::UI::Utils;

=pod

=head1 Description

Utilities specific to the User Interface code that do not get rolled up into the
EasyApache "base::ball" object.

=head1 API

The following sections contain a description of each API in this module.

=cut

sub get_opt_params_lists {
    my ( $self, $easy ) = @_;

    my %proc = (
        'Cpanel::Easy::PHP5' => 1,
    );

    my @apache = grep m{^Cpanel::Easy::Apache::}, @{ $easy->{'state'}{'order'} };
    @proc{@apache} = ();
    my $ap_vers = join( '|', @{ $easy->{'state_config'}{'main_vers'} } );
    @apache = grep !m{::$ap_vers$}, @apache;

    my @php5 = grep m{^Cpanel::Easy::PHP5::}, @{ $easy->{'state'}{'order'} };
    @proc{@php5} = ();
    @php5 = grep !m{::\d+_\d+$}, @php5;

    my @non_php_includes = grep { !exists $proc{$_} } @{ $easy->{'state'}{'order'} };

    # force tomcat to be in order due to namespace changes
    {
        my @tc = sort grep( /tomcat|modjk/i, @non_php_includes );
        @non_php_includes = ( grep( !/tomcat|modjk/i, @non_php_includes ), @tc );
    }

    # TODO, remove "VERSION-TODO" if any, pnly PHP (hanlded above) will need this in the forseeable future so no rush

    my @prefs = @{ $easy->{'prefs_details_disp_order'} };

    return ( \@apache, \@php5, \@non_php_includes, \@prefs );
}

sub get_apache_hashref_order_and_current {
    my ( $self, $easy ) = @_;

    my %apv;
    my @ord;
    for my $ver ( sort { $a cmp $b } keys %{ $easy->{'state'}{'optmods'}{'Apache'}{'_main'} } ) {
        next if $easy->{'state'}{'optmods'}{'Apache'}{'_main'}{$ver}{'skip'};    # Case 60403
        push @ord, $ver;
        $apv{$ver} = $easy->{'state'}{'optmods'}{'Apache'}{'_main'}{$ver};
    }

    require Cpanel::HttpUtils;
    my $cur = Cpanel::HttpUtils::get_current_apache_version_key();

    return ( \%apv, \@ord, $cur );
}

sub get_current_php_versions {
    my ( $self, $easy ) = @_;
    local %ENV = ();

    return @{ $easy->{'_'}{'installed_php_versions'} } if ( defined $easy->{'_'}{'installed_php_versions'} );

    require Cpanel::SafeRun;

    my $phpout = -x '/usr/bin/php' ? Cpanel::SafeRun::saferunallerrors( '/usr/bin/php', '-v' ) : '';
    if ( $phpout =~ m/^PHP\s+(\d+\.\d+\.\d+)/m ) {
        my $mainversion = $1;
        $easy->{'_'}{'installed_php_versions'} = [$mainversion];
        return ($mainversion);
    }
    else {
        $easy->{'_'}{'installed_php_versions'} = [];
        return;
    }
}

sub merge_profile {
    my ( $self, $easy, $profile, $profile_hr ) = @_;

    $profile = $easy->determine_profile($profile);

    if ( $profile ne $easy->{'profile_main'} ) {
        my $dflt = $easy->deserialize( $easy->{'profile_main'} );
        my $newp = $easy->deserialize($profile);

        my $vns = 'Cpanel::Easy::PHP5';
        $newp->{$vns}       = $newp->{$vns}       ? 1 : 0;
        $dflt->{$vns}       = $dflt->{$vns}       ? 1 : 0;
        $profile_hr->{$vns} = $profile_hr->{$vns} ? 1 : 0;

        # properly handle selected profiles w/ no PHP
        my $no_php_profile = 0;
        if ( !$newp->{'Cpanel::Easy::PHP5'} ) {
            $no_php_profile = 1;
        }

        my $uber = $easy->merge_easyconfig_hrs_into_one( $dflt, $newp );

        my $ap  = delete $profile_hr->{'Apache'}{'version'};
        my $php = {
            'Cpanel::Easy::PHP5' => $no_php_profile ? 0 : $dflt->{'Cpanel::Easy::PHP5'},
        };

        require Cpanel::Easy::PHP5;
        for my $pp5 ( Cpanel::Easy::PHP5->versions() ) {
            $php->{ 'Cpanel::Easy::PHP5::' . $pp5 } = $profile_hr->{ 'Cpanel::Easy::PHP5::' . $pp5 } ? 1 : 0;
        }

        %{$profile_hr} = %{ $easy->merge_easyconfig_hrs_into_one( $profile_hr, $uber ) };

        $profile_hr->{'Apache'}{'version'} = $ap if !$profile_hr->{'Apache'}{'version'};
        for my $pp ( keys %{$php} ) {
            $profile_hr->{$pp} = $php->{$pp} if !exists $profile_hr->{$pp};
        }

        # remove dual pp vers
        my $have5 = 0;
        for my $pp5b ( Cpanel::Easy::PHP5->versions() ) {
            $profile_hr->{ 'Cpanel::Easy::PHP5::' . $pp5b } = 0 if $have5;
            $php->{ 'Cpanel::Easy::PHP5::' . $pp5b } = $profile_hr->{ 'Cpanel::Easy::PHP5::' . $pp5b } ? 1 : 0;
            $have5++ if $php->{ 'Cpanel::Easy::PHP5::' . $pp5b };
        }
    }
}

sub debug_dump_params {
    my ( $self, $easy ) = @_;

    print "<h3>DEBUG: passed parameters:</h3><ul>\n";
  NAME:
    for my $name ( $easy->get_param() ) {
        print "  <li>$name<ul>\n";
      VALUE:
        for my $value ( $easy->get_param($name) ) {
            print "<li>$value</li>\n";
        }
        print "  </ul></li>\n";
    }
    print "</ul>\n";
}

# extracted from get_profiles_hashref_and_order_arrayref, returns array of profile hashrefs
sub get_profiles_hashref {
    my ( $self, $easy ) = @_;

    my $main_prof = $easy->deserialize( $easy->{'profile_main'} );
    $main_prof->{'_meta'}{'name'} = '** DEFAULT **';
    $main_prof->{'_meta'}{'note'} = 'This option loads the last saved configuration. If the configuration is missing, then default values will be provided.';
    my %profiles = ( $easy->{'profile_main'} => $main_prof );

    if ( opendir my $prof_dh, $easy->{'profile_custom_dir'} ) {
        my @profs = sort { $a cmp $b } grep !/^\.+$/, readdir($prof_dh);
        close $prof_dh;
        for my $prof (@profs) {
            next if $prof !~ m{\.yaml$};
            $prof =~ s{\.yaml$}{};
            my $prof_hr = $easy->deserialize( $easy->{'profile_custom_dir'} . '/' . $prof . '.yaml' );
            $profiles{$prof} = $prof_hr;
        }
    }
    else {
        warn 'Could not open profile directory';
    }

    return \%profiles;
}

# extracted from get_profiles_hashref_and_order_arrayref
sub get_profiles_order_arrayref {
    my ( $self, $easy ) = @_;

    my %profiles = %{ $self->get_profiles_hashref($easy) };

    my @prof_order = ( $easy->{'profile_main'} );
    my @cp_ord;
    my @oth_ord;

    if ( opendir my $prof_dh, $easy->{'profile_custom_dir'} ) {
        my @profs = sort { $a cmp $b } grep !/^\.+$/, readdir($prof_dh);
        close $prof_dh;
        for my $prof (@profs) {
            next if $prof !~ m{\.yaml$};
            $prof =~ s{\.yaml$}{};

            if ( $prof =~ m{^cpanel_} ) {
                push @cp_ord, $prof;
            }
            else {
                push @oth_ord, $prof;
            }
        }
    }
    else {
        warn 'Could not open profile directory';
    }

    push @prof_order, ( @cp_ord, @oth_ord );
    return \@prof_order;
}

# refactored into separate subroutines, kept incase it's still used; functionally equivalent
sub get_profiles_hashref_and_order_arrayref {
    my ( $self, $easy ) = @_;

    my %profiles = %{ $self->get_profiles_hashref($easy) };

    my @prof_order = @{ $self->get_profiles_order_arrayref($easy) };

    return ( \%profiles, \@prof_order );
}

=pod

=head2 Cpanel::Easy::Apache::UI::Utils::get_standard_profile_filter()

Returns a compiled regular expression that can be used to compare against
the file names of profiles, and determines if the profile is an EasyApache
generated profile, and if so, should it be displayed on the profile
selection screen.

  Input:
    Hash -- Contains all key/value pair arguments
        'easy' -- "base::ball" god object
        'cloudlinux' -- boolean value if CloudLinux is installed or not.

  Output:
    Scalar -- compiled regular expression per qr// operator

  Note:
    We assume that EasyApache provided profiles begin with: cpanel_

=cut

sub get_standard_profile_filter {
    my $self = shift;
    my %args = @_;
    my $easy = $args{'easy'};
    my $filter;

    my $type = $args{'cloudlinux'} ? "cloud" : "centos";

    if ( $easy->cmp_cpanel_tier( $easy->get_cpanel_version(), '<=', '11.38' ) ) {

        # None of the old profiles file names begin with "cpanel_cloud" or "cpanel_centos"
        $filter = qr/^cpanel_[^c]/i;
    }
    else {
        # FB 104989 -- don't display standard ITK profile when it's unsupported
        my $itk = $easy->get_easyconfig_hr_from_ns_variations('Cpanel::Easy::Apache::MPMItk');

        if ( defined $itk->{'skip'} && $itk->{'skip'} ) {
            $filter = qr/^cpanel_${type}_(?!\w+?itk)/i;
        }
        else {
            $filter = qr/^cpanel_$type/i;
        }
    }

    return $filter;
}

=pod

=head2 Cpanel::Easy::Apache::UI::Utils::differentiate_profiles()

Gathers a list of available EasyApache profiles, and classifies them as
"default", "standard", and "custom".  The "default" profile is the one that's
used when you run EasyApache without a profile parameter.  The "standard"
profiles are those that are provided by, and maintained by EasyApache.
Finally, the "custom" profiles are those that fall in neither of these
categories.

  Input:
    Object Ref -- "base::ball" god object
    Boolean -- 1 if CloudLinux is installed, 0 if not

  Output:
    ARRAY ref -- list of "default" profiles
    ARRAY ref -- list of "standard" profiles
    ARRAY ref -- list of "custom" profiles
=cut

sub differentiate_profiles {
    my $self                  = shift;
    my $easy                  = shift;
    my $cloud_linux_installed = shift;
    my ( @default_profile, @standard_profiles, @custom_profiles );

    my $prof_order_ar  = $self->get_profiles_order_arrayref($easy);
    my $profiles_hr    = $self->get_profiles_hashref($easy);
    my $chosen         = exists $profiles_hr->{ $easy->get_param('profile') } ? $easy->get_param('profile') : $easy->{'profile_main'};
    my $profile_filter = $self->get_standard_profile_filter( 'easy' => $easy, 'cloudlinux' => $cloud_linux_installed );
    my $rhelsevenplus  = ( $easy->is_redhat() && $easy->{'getos_releaseversion'} >= 7 );

    my $index = 0;
    foreach my $prof ( @{$prof_order_ar} ) {
        my %profile_data;

        my ( $is_selected, $is_custom ) = ( $prof eq $chosen ? 1 : 0, 0 );

        my $name          = $profiles_hr->{$prof}{'_meta'}{'name'};
        my $note          = $profiles_hr->{$prof}{'_meta'}{'note'};
        my $short_summary = $easy->get_major_features( hashref => $profiles_hr->{$prof} );

        my $profile_data = {
            'index'        => $index,
            'profileKey'   => $prof,
            'profileName'  => $name,
            'profileDesc'  => $note,
            'isSelected'   => $is_selected,
            'shortSummary' => $short_summary,
        };

        if ( $prof eq $easy->{'profile_main'} ) {
            push @default_profile, $profile_data;
        }
        elsif ( $prof =~ $profile_filter ) {
            push @standard_profiles, $profile_data;
        }
        elsif ( $prof =~ m{^cpanel_default$} ) {
            unshift @standard_profiles, $profile_data;
        }
        elsif ( $prof =~ m{^cpanel_default22$} ) {
            unshift @standard_profiles, $profile_data unless $rhelsevenplus;
        }
        elsif ( $prof !~ m{^cpanel_} ) {    # Filter out old profiles.
            push @custom_profiles, $profile_data;
        }
        ++$index;
    }

    return ( \@default_profile, \@standard_profiles, \@custom_profiles );
}

# takes 3 arrays from differentiate_profiles, and returns ordered array of just profile names
# used by UI/CLI/Config.pm
sub profile_names_arrayref {
    my $self                  = shift;
    my $easy                  = shift;
    my $cloud_linux_installed = shift;

    # Same source as UI/HTML/Config.pm
    my ( $default, $standards, $customs ) = $self->differentiate_profiles( $easy, $cloud_linux_installed );

    my @profile_list = ();
    foreach my $prof ( @{$default}, @{$standards}, @{$customs} ) {
        push @profile_list, $prof->{profileKey};
    }

    return \@profile_list;
}

# TODO: Remove this once all HTML is converted to template
sub get_html_hidden_state_minus {
    my $self = shift;
    my $easy = shift;

    my %skip;
    @skip{@_} = @_;
    my $link = '';
    my $form = '';

  NAME:
    for my $name ( $easy->get_param() ) {
        next NAME if exists $skip{$name};

      REGEX:
        for my $qr ( values %skip ) {
            next REGEX if ref $qr ne 'Regexp';
            next NAME  if $name =~ m{$qr};
        }

      VALUE:
        for my $value ( $easy->get_param($name) ) {
            my $safe_name  = Cpanel::Encoder::html_encode_str($name);
            my $safe_value = Cpanel::Encoder::html_encode_str($value);
            $form .= qq{<input type="hidden" name="$safe_name" value="$safe_value" />\n};

            $link .= Cpanel::Encoder::uri_encode_str($name) . '=' . Cpanel::Encoder::uri_encode_str($value) . '&';
        }
    }

    $link =~ s{\&$}{};

    return ( $form, $link );
}

# TODO: Remove this once all HTML is converted to template
sub get_form_start {
    my ( $self, $easy, $conf_hr ) = @_;

    my $action = $conf_hr->{'new_action'} ? qq|<input type="hidden" name="action" value="$conf_hr->{'new_action'}" />| : '';
    my ( $form, $link ) = $self->get_html_hidden_state_minus( $easy, @{ $conf_hr->{'hidden_minus'} } );

    return <<"END_HTML";
  <form name="ea3_form" method="post" action="$conf_hr->{'form_action'}">
    $action
    $form
END_HTML

}

# TODO: Remove this once all HTML is converted to template
sub get_form_end {
    my ( $self, $easy, $conf_hr ) = @_;
    $conf_hr->{'submit'} = 'Next Step' if !$conf_hr->{'submit'};
    my $javascript = " $conf_hr->{'javascript'}" || ' ';
    my ( $pa, $pb ) = $conf_hr->{'no_p'} ? ( '', '' ) : ( '<p>', '</p>' );

    return qq|$pa<input type="submit" class="btn-primary primary default input-button" value="$conf_hr->{'submit'}"$javascript />$pb\n  </form>\n|;
}

################### New TEMPLATE UTILITY methods ###################

# This function gets the required form data for pages in EA
# wizard.
sub get_ea_config_data {
    my $self = shift;
    my $easy = shift;

    my %skip;
    @skip{@_} = @_;
    my %config_data;

  NAME:
    for my $name ( $easy->get_param() ) {
        next NAME if exists $skip{$name};

      REGEX:
        for my $qr ( values %skip ) {
            next REGEX if ref $qr ne 'Regexp';
            next NAME  if $name =~ m{$qr};
        }

      VALUE:
        for my $value ( $easy->get_param($name) ) {

            $config_data{$name} = $value;
        }
    }

    return \%config_data;
}

1;

__END__
