<?php /*

 Composr
 Copyright (c) Christopher Graham, 2004-2024

 See docs/LICENSE.md for full licensing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright  Christopher Graham
 * @package    securitylogging
 */

/**
 * Hook class.
 */
class Hook_cns_warnings_ban_ip
{
    /**
     * Get details for this hook.
     *
     * @return ?array The details (null: hook disabled)
     */
    public function get_details() : ?array
    {
        if (!addon_installed('cns_warnings') || !addon_installed('securitylogging')) {
            return null;
        }

        return [
            'order' => 1,
        ];
    }

    /**
     * Generate punitive action text from a punitive action database row.
     *
     * @param  array $row The database row
     * @return string The punitive action text
     */
    public function generate_text(array $row) : string
    {
        if (!addon_installed('cns_warnings') || !addon_installed('securitylogging')) {
            return '';
        }

        require_lang('cns_warnings');

        switch ($row['p_action']) {
            case '_PUNITIVE_IP_BANNED':
                return do_lang('_PUNITIVE_IP_BANNED', $row['p_ip_address']);

            default:
                return '';
        }
    }

    /**
     * Render form fields for the warnings screen.
     *
     * @param  Tempcode $add_text Tempcode to be included on the intro paragraph of the warnings screen (passed by reference)
     * @param  Tempcode $fields The fields to be rendered (passed by reference)
     * @param  Tempcode $hidden The hidden fields to be included (passed by reference)
     * @param  boolean $new Whether it is a new warning/punishment record
     * @param  LONG_TEXT $explanation The explanation for the warning/punishment record
     * @param  BINARY $is_warning Whether to make this a formal warning
     * @param  MEMBER $member_id The member the warning is for
     * @param  BINARY $spam_mode Whether this is a spam warning
     * @param  ?AUTO_LINK $post_id The ID of the forum post of which we clicked warn (null: we are not warning on a forum post)
     * @param  ?SHORT_TEXT $ip_address The IP address of the poster (null: we are not warning on a forum post)
     */
    public function get_form_fields(object &$add_text, object &$fields, object &$hidden, bool $new, string $explanation, int $is_warning, int $member_id, int $spam_mode, ?int $post_id, ?string $ip_address)
    {
        if (!addon_installed('cns_warnings') || !addon_installed('securitylogging')) {
            return;
        }

        if (!$new) {
            return;
        }

        if (has_actual_page_access(get_member(), 'admin_ip_ban')) {
            $already_banned_ip = ip_banned($GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_ip_address'));
            $fields->attach(form_input_tick(do_lang_tempcode('WHETHER_BANNED_IP'), do_lang_tempcode('DESCRIPTION_WHETHER_BANNED_IP'), 'banned_ip', (($spam_mode == 1) || ($already_banned_ip === true)), null, '1', false, ($already_banned_ip === true)));
        }
    }

    /**
     * Actualise punitive actions.
     * Note that this assumes action was applied through the warnings form, and that post parameters still exist.
     *
     * @param  array $punitive_messages Punitive action text to potentially be included in the PT automatically (passed by reference)
     * @param  AUTO_LINK $warning_id The ID of the warning that was created for this punitive action
     * @param  MEMBER $member_id The member this warning is being applied to
     * @param  SHORT_TEXT $username The username of the member this warning is being applied to
     * @param  SHORT_TEXT $explanation The defined explanation for this warning
     * @param  LONG_TEXT $message The message to be sent as a PT (passed by reference; you should generally use $punitive_text instead if you want to add PT text)
     */
    public function actualise_punitive_action(array &$punitive_messages, int $warning_id, int $member_id, string $username, string $explanation, string &$message)
    {
        if (!addon_installed('cns_warnings') || !addon_installed('securitylogging')) {
            return;
        }

        // Add the member's IP address to our IP bans if requested to IP ban and we have permission
        $_banned_ip = post_param_integer('banned_ip', 0);
        $banned_ip = '';
        if (has_actual_page_access(get_member(), 'admin_ip_ban')) {
            if ($_banned_ip == 1) {
                $banned_ip = $GLOBALS['FORUM_DRIVER']->get_member_row_field($member_id, 'm_ip_address');

                require_code('failure');
                add_ip_ban($banned_ip, $explanation, null, true, true); // Forced because a staff member triggered it
                log_it('IP_BANNED', $banned_ip);

                $GLOBALS['FORUM_DB']->query_insert('f_warnings_punitive', [
                    'p_warning_id' => $warning_id,
                    'p_member_id' => $member_id,
                    'p_ip_address' => $banned_ip,
                    'p_email_address' => '',
                    'p_hook' => 'ban_ip',
                    'p_action' => '_PUNITIVE_IP_BANNED',
                    'p_param_a' => '',
                    'p_param_b' => '',
                    'p_reversed' => 0,
                ]);

                $punitive_messages[] = do_lang('PUNITIVE_BAN_IP', null, null, null, null, false);
            }
        }
    }

    /**
     * Actualiser to undo a certain type of punitive action.
     *
     * @param  array $punitive_action The database row for the punitive action being undone
     * @param  array $warning The database row for the warning associated with the punitive action being undone
     */
    public function undo_punitive_action(array $punitive_action, array $warning)
    {
        $error = new Tempcode();
        if (!addon_installed__messaged('cns_warnings', $error)) {
            warn_exit($error);
        }
        if (!addon_installed__messaged('securitylogging', $error)) {
            warn_exit($error);
        }

        require_code('failure');

        $banned_ip = $punitive_action['p_ip_address'];
        $member_id = intval($punitive_action['p_member_id']);

        remove_ip_ban($banned_ip);

        log_it('IP_UNBANNED', $banned_ip, strval($member_id));
    }

    /**
     * Return information for the standing profile tab.
     *
     * @param  MEMBER $member_id_of The member whose profile we are viewing
     * @param  MEMBER $member_id_viewing The member who is viewing the profile
     * @param  array $warning_ids Array of formal warning IDs against this member for checking against queried punitive actions
     * @return array Array of maps with information about this punitive action
     */
    public function get_stepper(int $member_id_of, int $member_id_viewing, array $warning_ids) : array
    {
        if (!addon_installed('cns_warnings') || !addon_installed('securitylogging')) {
            return [];
        }

        $info = [];
        foreach ($warning_ids as $warning_id) {
            $row = $GLOBALS['SITE_DB']->query_select_value_if_there('f_warnings_punitive', 'id', ['p_warning_id' => $warning_id, 'p_action' => '_PUNITIVE_IP_BANNED', 'p_reversed' => 0]);
            if ($row !== null) {
                $info[] = [
                    'icon' => 'menu/adminzone/security/ip_ban',
                    'text' => do_lang_tempcode('STANDING_DANGER_IP_TEXT'),
                ];
                break;
            }
        }

        // Check if maybe the member was IP-banned outside of the warnings system through securitylogging
        if (count($info) == 0) {
            require_code('lookup');

            $username = null;
            $member_id = null;
            $ip = null;
            $email_address = null;
            $known_ip_addresses = lookup_user($member_id_of, $username, $member_id, $ip, $email_address);

            foreach (array_merge($known_ip_addresses, [$ip]) as $ip_check) {
                $ban_until = $GLOBALS['SITE_DB']->query_select('banned_ip', ['i_ban_until'], ['i_ban_positive' => 1, 'ip' => $ip]);
                if (array_key_exists(0, $ban_until)) {
                    $ip_banned = (($ban_until[0]['i_ban_until'] === null) || ($ban_until[0]['i_ban_until'] > time()));
                    if ($ip_banned) {
                        $info[] = [
                            'icon' => 'menu/adminzone/security/ip_ban',
                            'text' => do_lang_tempcode('STANDING_DANGER_IP_TEXT_2'),
                        ];
                        break;
                    }
                }
            }
        }

        return [
            [
                'order' => 100,
                'label' => do_lang('STANDING_DANGER'),
                'explanation' => do_lang('DESCRIPTION_STANDING_DANGER'),
                'icon' => 'buttons/no',
                'active' => !empty($info),
                'active_color' => 'danger',
                'info' => $info,
            ],
        ];
    }
}
