HEX
Server: LiteSpeed
System: Linux emerald.serverdnsnetwork.com 5.14.0-611.5.1.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Tue Nov 11 08:09:09 EST 2025 x86_64
User: mahdetej (1135)
PHP: 8.1.34
Disabled: show_source, system, shell_exec, passthru, exec, popen, proc_open
Upload Files
File: /home/mahdetej/www/wp-content/plugins/Melli-shild/admin/class-melli-shield-settings.php
<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class Melli_Shield_Settings {

    public function register_settings() {
        register_setting( Melli_Shield_Config::SETTINGS_GROUP, Melli_Shield_Config::OPTION_SETTINGS, array(
            'type'              => 'array',
            'sanitize_callback' => array( $this, 'sanitize_settings' ),
        ) );

        register_setting( Melli_Shield_Config::SETTINGS_GROUP, Melli_Shield_Config::OPTION_ALLOWED_SERVICES, array(
            'type'              => 'string',
            'sanitize_callback' => array( $this, 'sanitize_services' ),
            'default'           => '',
        ) );

        // Mode
        add_settings_section( 'ds_mode', __( 'Shield Mode', 'melli-shield' ), function() {
            echo '<p>' . esc_html__( 'Select the operating mode.', 'melli-shield' ) . '</p>';
        }, Melli_Shield_Config::PAGE_SETTINGS );

        add_settings_field( 'ds_mode_field', __( 'Current Mode', 'melli-shield' ), array( $this, 'render_mode' ), Melli_Shield_Config::PAGE_SETTINGS, 'ds_mode' );

        // Blockers
        add_settings_section( 'ds_blockers', __( 'Blocker Options', 'melli-shield' ), function() {
            echo '<p>' . esc_html__( 'Enable or disable individual blockers.', 'melli-shield' ) . '</p>';
        }, Melli_Shield_Config::PAGE_SETTINGS );

        $blockers = array(
            'block_fonts'     => array( __( 'Block External Fonts', 'melli-shield' ), __( 'Google Fonts, Typekit, Font Awesome', 'melli-shield' ) ),
            'block_cdn'       => array( __( 'Block External CDNs', 'melli-shield' ), __( 'cdnjs, jsDelivr, unpkg', 'melli-shield' ) ),
            'block_analytics' => array( __( 'Block Analytics & Tracking', 'melli-shield' ), __( 'Google Analytics, GTM, Hotjar, Clarity', 'melli-shield' ) ),
            'block_updates'   => array( __( 'Block WordPress Updates', 'melli-shield' ), __( 'Core, plugin, theme update checks', 'melli-shield' ) ),
            'block_gravatar'  => array( __( 'Block Gravatar', 'melli-shield' ), __( 'Replace with local SVG avatar', 'melli-shield' ) ),
            'block_embeds'    => array( __( 'Block External Embeds', 'melli-shield' ), __( 'YouTube, Vimeo, Twitter oEmbed', 'melli-shield' ) ),
            'block_recaptcha' => array( __( 'Block reCAPTCHA', 'melli-shield' ), __( 'reCAPTCHA, hCaptcha, Turnstile', 'melli-shield' ) ),
            'block_heartbeat' => array( __( 'Limit Heartbeat API', 'melli-shield' ), __( 'Reduce to 120s, disable in Offline', 'melli-shield' ) ),
            'block_email'     => array( __( 'Block Emails (Offline)', 'melli-shield' ), __( 'Intercept and log instead of sending', 'melli-shield' ) ),
            'allow_messenger' => array( __( 'Allow Messenger APIs', 'melli-shield' ), __( 'Telegram, Slack, Discord, Push', 'melli-shield' ) ),
        );

        foreach ( $blockers as $key => $info ) {
            add_settings_field(
                'ds_' . $key,
                $info[0],
                array( $this, 'render_checkbox' ),
                Melli_Shield_Config::PAGE_SETTINGS,
                'ds_blockers',
                array( 'key' => $key, 'desc' => $info[1] )
            );
        }

        // Services
        add_settings_section( 'ds_services', __( 'Allowed Service URLs', 'melli-shield' ), function() {
            echo '<p>' . esc_html__( 'Domains for SMS, Telegram, payment gateways. Allowed in ALL modes including Offline.', 'melli-shield' ) . '</p>';
        }, Melli_Shield_Config::PAGE_SETTINGS );

        add_settings_field( 'ds_services_field', __( 'Service Domains', 'melli-shield' ), array( $this, 'render_services' ), Melli_Shield_Config::PAGE_SETTINGS, 'ds_services' );

        // Logging
        add_settings_section( 'ds_log', __( 'Logging', 'melli-shield' ), function() {
            echo '<p>' . esc_html__( 'Configure request logging.', 'melli-shield' ) . '</p>';
        }, Melli_Shield_Config::PAGE_SETTINGS );

        add_settings_field( 'ds_log_enabled', __( 'Enable Logging', 'melli-shield' ), array( $this, 'render_checkbox' ), Melli_Shield_Config::PAGE_SETTINGS, 'ds_log', array( 'key' => 'log_enabled', 'desc' => __( 'Log blocked and allowed requests.', 'melli-shield' ) ) );
        add_settings_field( 'ds_log_retention', __( 'Retention (days)', 'melli-shield' ), array( $this, 'render_number' ), Melli_Shield_Config::PAGE_SETTINGS, 'ds_log', array( 'key' => 'log_retention', 'min' => 1, 'max' => 365 ) );
    }

    // ========================================
    // Field Renderers
    // ========================================

    public function render_mode() {
        $mode  = Melli_Shield_Utils::get_mode();
        $modes = array(
            'normal'   => array( '🟢', __( 'Normal — All requests allowed', 'melli-shield' ), '#00a32a' ),
            'national' => array( '🟡', __( 'National — Only Iranian domains allowed', 'melli-shield' ), '#dba617' ),
            'offline'  => array( '🔴', __( 'Offline — All external blocked', 'melli-shield' ), '#d63638' ),
        );
        $opt_name = Melli_Shield_Config::OPTION_SETTINGS;
        foreach ( $modes as $key => $m ) {
            $style = ( $mode === $key ) ? 'font-weight:bold;color:' . $m[2] . ';' : '';
            printf(
                '<label style="display:block;margin-bottom:10px;padding:8px 12px;border:1px solid #ddd;border-radius:4px;%1$s"><input type="radio" name="%2$s[mode]" value="%3$s" %4$s /> %5$s %6$s</label>',
                esc_attr( $style ),
                esc_attr( $opt_name ),
                esc_attr( $key ),
                checked( $mode, $key, false ),
                esc_html( $m[0] ),
                esc_html( $m[1] )
            );
        }
    }

    public function render_checkbox( $args ) {
        $val  = Melli_Shield_Utils::get_setting( $args['key'], 0 );
        $desc = isset( $args['desc'] ) ? $args['desc'] : '';
        printf(
            '<label><input type="checkbox" name="%1$s[%2$s]" value="1" %3$s /> %4$s</label>',
            esc_attr( Melli_Shield_Config::OPTION_SETTINGS ),
            esc_attr( $args['key'] ),
            checked( 1, $val, false ),
            esc_html( $desc )
        );
    }

    public function render_number( $args ) {
        $val = Melli_Shield_Utils::get_setting( $args['key'], 30 );
        $min = isset( $args['min'] ) ? $args['min'] : 1;
        $max = isset( $args['max'] ) ? $args['max'] : 365;
        printf(
            '<input type="number" name="%1$s[%2$s]" value="%3$d" min="%4$d" max="%5$d" class="small-text msh-input" />',
            esc_attr( Melli_Shield_Config::OPTION_SETTINGS ),
            esc_attr( $args['key'] ),
            (int) $val,
            (int) $min,
            (int) $max
        );
    }

    // ========================================
    // Service Domains + Quick Add
    // ========================================

    public function render_services() {
        $services = get_option( Melli_Shield_Config::OPTION_ALLOWED_SERVICES, '' );
        $opt      = Melli_Shield_Config::OPTION_ALLOWED_SERVICES;
        ?>
        <textarea name="<?php echo esc_attr( $opt ); ?>" rows="8" class="msh-input msh-services-textarea"
                  placeholder="api.kavenegar.com&#10;api.telegram.org&#10;api.zarinpal.com"
        ><?php echo esc_textarea( $services ); ?></textarea>
        <p class="msh-card__hint"><?php esc_html_e( 'هر خط یک دامنه. با افزودن example.com، زیردامنه‌ها هم مجاز می‌شوند.', 'melli-shield' ); ?></p>

        <div class="msh-quick-section">
            <p class="msh-quick-section__title"><?php esc_html_e( 'افزودن سریع', 'melli-shield' ); ?></p>

            <p class="msh-quick-section__title"><?php esc_html_e( 'سرویس‌های ایرانی', 'melli-shield' ); ?></p>
            <div class="msh-quick-chips">
                <?php
                $iranian = array(
                    __( 'ارائه‌دهندگان SMS', 'melli-shield' ) => "api.kavenegar.com\nrest.payamak-panel.com\napi.sms.ir\napi.ghasedak.me\napi.melipayamak.com\napi2.ippanel.com\napi.limosms.com\napi.farazsms.com\nsms.magfa.com\napi.payamresan.com",
                    __( 'زرین‌پال', 'melli-shield' )          => "api.zarinpal.com\nwww.zarinpal.com\nsandbox.zarinpal.com",
                    __( 'آیدی‌پی', 'melli-shield' )           => "api.idpay.ir",
                    __( 'نکست‌پی', 'melli-shield' )           => "api.nextpay.org\nnextpay.org",
                    __( 'پی‌.آی‌آر', 'melli-shield' )         => "pay.ir\napi.pay.ir",
                    __( 'زیبال', 'melli-shield' )            => "gateway.zibal.ir\napi.zibal.ir",
                    __( 'پی‌پینگ', 'melli-shield' )           => "api.payping.ir",
                    __( 'وندار', 'melli-shield' )            => "api.vandar.io\nipg.vandar.io",
                    __( 'بانک‌های شاپرک', 'melli-shield' )   => "sep.shaparak.ir\npec.shaparak.ir\nbpm.shaparak.ir\nsadad.shaparak.ir\nmabna.shaparak.ir\nasan.shaparak.ir\nikc.shaparak.ir\nmellat.shaparak.ir\npna.shaparak.ir",
                    __( 'جیبیت', 'melli-shield' )             => "api.jibit.ir\nnapi.jibit.ir",
                    __( 'پی‌ان‌آ', 'melli-shield' )           => "pna.shaparak.ir\nrefund.pna.co.ir",
                    __( 'سیزپی', 'melli-shield' )            => "rt.sizpay.ir",
                    __( 'آقای پرداخت', 'melli-shield' )       => "panel.aqayepardakht.ir",
                    __( 'بیت‌پی', 'melli-shield' )            => "bitpay.ir\napi.bitpay.ir",
                    __( 'نقشه ایرانی', 'melli-shield' )      => "api.neshan.org\napi.cedarmaps.com\nmap.ir\napi.balad.ir",
                    __( 'CDN ایرانی', 'melli-shield' )       => "cdn.arvancloud.ir\nstatic.arvancloud.ir\narvancloud.com",
                    __( 'یکتانت', 'melli-shield' )           => "cdn.yektanet.com\nyektanet.com",
                );
                $this->render_quick_buttons( $iranian );
                ?>
            </div>

            <p class="msh-quick-section__title"><?php esc_html_e( 'پیام‌رسان', 'melli-shield' ); ?></p>
            <div class="msh-quick-chips">
                <?php
                $messenger = array(
                    __( 'Telegram', 'melli-shield' )      => 'api.telegram.org',
                    __( 'Slack', 'melli-shield' )          => "hooks.slack.com\napi.slack.com",
                    __( 'Discord', 'melli-shield' )        => "discord.com\ndiscordapp.com",
                    __( 'WhatsApp', 'melli-shield' )       => "api.whatsapp.com\ngraph.facebook.com",
                    __( 'Push Services', 'melli-shield' )  => "fcm.googleapis.com\nonesignal.com\napi.pusher.com\napi.pushover.net",
                );
                $this->render_quick_buttons( $messenger );
                ?>
            </div>

            <p class="msh-quick-section__title"><?php esc_html_e( 'درگاه بین‌المللی', 'melli-shield' ); ?></p>
            <div class="msh-quick-chips">
                <?php
                $intl_payment = array(
                    __( 'PayPal', 'melli-shield' )        => "api.paypal.com\nwww.paypal.com\napi-m.paypal.com\napi-3t.paypal.com\nipnpb.paypal.com",
                    __( 'Stripe', 'melli-shield' )        => "api.stripe.com\njs.stripe.com\nhooks.stripe.com\ncheckout.stripe.com\nfiles.stripe.com",
                    __( 'Square', 'melli-shield' )        => "connect.squareup.com\napi.squareup.com\nweb.squarecdn.com",
                    __( 'Mollie', 'melli-shield' )        => "api.mollie.com\nwww.mollie.com",
                    __( 'Razorpay', 'melli-shield' )      => "api.razorpay.com\ncheckout.razorpay.com",
                    __( 'Paddle', 'melli-shield' )        => "vendors.paddle.com\ncheckout.paddle.com\nsandbox-vendors.paddle.com",
                    __( 'Braintree', 'melli-shield' )     => "api.braintreegateway.com\npayments.braintree-api.com\nclient-analytics.braintreegateway.com",
                    __( 'Authorize.net', 'melli-shield' ) => "api.authorize.net\napi2.authorize.net\naccept.authorize.net",
                    __( '2Checkout', 'melli-shield' )     => "api.2checkout.com\nwww.2checkout.com\nsecure.2checkout.com",
                    __( 'Klarna', 'melli-shield' )        => "api.klarna.com\napi-na.klarna.com\napi-oc.klarna.com",
                    __( 'Adyen', 'melli-shield' )         => "checkout-live.adyen.com\npal-live.adyen.com",
                    __( 'WooCommerce', 'melli-shield' )   => "woocommerce.com\napi.woocommerce.com",
                );
                $this->render_quick_buttons( $intl_payment );
                ?>
            </div>

            <p class="msh-quick-section__title"><?php esc_html_e( 'ایمیل', 'melli-shield' ); ?></p>
            <div class="msh-quick-chips">
                <?php
                $email = array(
                    __( 'Mailchimp', 'melli-shield' )   => "api.mailchimp.com\nus1.api.mailchimp.com",
                    __( 'SendGrid', 'melli-shield' )    => "api.sendgrid.com",
                    __( 'Mailgun', 'melli-shield' )     => "api.mailgun.net",
                    __( 'Amazon SES', 'melli-shield' )  => "email.us-east-1.amazonaws.com\nemail.eu-west-1.amazonaws.com",
                    __( 'Brevo', 'melli-shield' )       => "api.sendinblue.com\napi.brevo.com",
                    __( 'Postmark', 'melli-shield' )    => "api.postmarkapp.com",
                    __( 'SparkPost', 'melli-shield' )   => "api.sparkpost.com",
                );
                $this->render_quick_buttons( $email );
                ?>
            </div>

            <p class="msh-quick-section__title"><?php esc_html_e( 'سایر APIها', 'melli-shield' ); ?></p>
            <div class="msh-quick-chips">
                <?php
                $other = array(
                    __( 'Google APIs', 'melli-shield' )  => "www.googleapis.com\nmaps.googleapis.com\ntranslate.googleapis.com\nfcm.googleapis.com",
                    __( 'Cloudflare', 'melli-shield' )   => "api.cloudflare.com\nchallenges.cloudflare.com",
                    __( 'reCAPTCHA', 'melli-shield' )    => "www.google.com\nwww.gstatic.com\nwww.recaptcha.net",
                    __( 'hCaptcha', 'melli-shield' )     => "hcaptcha.com\njs.hcaptcha.com\napi.hcaptcha.com",
                    __( 'Zapier', 'melli-shield' )       => "hooks.zapier.com",
                    __( 'GitHub API', 'melli-shield' )   => "api.github.com\nraw.githubusercontent.com",
                    __( 'OpenAI', 'melli-shield' )       => "api.openai.com",
                );
                $this->render_quick_buttons( $other );
                ?>
            </div>
        </div>

        <div class="msh-info-box">
            <strong><?php esc_html_e( 'تفاوت سرویس مجاز و لیست سفید:', 'melli-shield' ); ?></strong>
            <?php esc_html_e( 'هر دو از مسدودسازی معاف‌اند. سرویس‌ها برای API و درگاه مناسب‌اند؛ لیست سفید برای دامنه‌های عمومی در صفحه جداگانه.', 'melli-shield' ); ?>
        </div>
        <?php
    }

    /**
     * Render Quick Add buttons from array.
     *
     * @param array $groups Label => domains pairs.
     */
    private function render_quick_buttons( $groups ) {
        foreach ( $groups as $label => $domains ) {
            $tooltip = str_replace( "\n", ', ', $domains );
            printf(
                '<button type="button" class="msh-btn msh-btn--chip msh-quick-add" data-domains="%s" title="%s">+ %s</button>',
                esc_attr( $domains ),
                esc_attr( $tooltip ),
                esc_html( $label )
            );
        }
    }

    // ========================================
    // Sanitize
    // ========================================

    public function sanitize_settings( $input ) {
        $s = array();

        // Checkboxes
        $checks = array(
            'block_fonts', 'block_cdn', 'block_analytics', 'block_updates',
            'block_gravatar', 'block_embeds', 'block_recaptcha', 'block_heartbeat',
            'block_email', 'log_enabled', 'allow_messenger',
        );
        foreach ( $checks as $k ) {
            $s[ $k ] = isset( $input[ $k ] ) ? 1 : 0;
        }

        // Number
        $s['log_retention'] = isset( $input['log_retention'] )
            ? max( 1, min( 365, absint( $input['log_retention'] ) ) )
            : 30;

        return $s;
    }

    public function sanitize_services( $input ) {
        if ( empty( $input ) ) {
            return '';
        }

        $clean = array();
        foreach ( explode( "\n", $input ) as $line ) {
            $d = strtolower( trim( $line ) );
            $d = preg_replace( '#^https?://#', '', $d );
            $d = preg_replace( '#^www\.#', '', $d );
            $d = preg_replace( '#/.*$#', '', $d );
            $d = preg_replace( '#:\d+$#', '', $d );
            $d = sanitize_text_field( $d );
            if ( ! empty( $d ) && strpos( $d, '.' ) !== false ) {
                $clean[] = $d;
            }
        }

        $clean = array_unique( $clean );
        sort( $clean );
        return implode( "\n", $clean );
    }
}