HEX
Server: Apache/2.4.65 (Debian)
System: Linux 88f31f35b0b8 6.1.0-38-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.147-1 (2025-08-02) x86_64
User: www-data (33)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/uptime-monitor-plus/includes/class-ajax.php
<?php
defined('ABSPATH') || exit;

/**
 * AJAX handlers for UptimeMonitor+.
 */
class UptimeMonitorPlus_Ajax {

    /**
     * Initialize AJAX hooks.
     */
    public static function init() {
        add_action( 'wp_ajax_uptimemonitorplus_login', array( __CLASS__, 'handle_login' ) );
        add_action( 'wp_ajax_uptimemonitorplus_disconnect', array( __CLASS__, 'handle_disconnect' ) );
        add_action( 'wp_ajax_uptimemonitorplus_get_domain', array( __CLASS__, 'handle_get_domain' ) );
        add_action( 'wp_ajax_uptimemonitorplus_get_ssl', array( __CLASS__, 'handle_get_ssl' ) );
        add_action( 'wp_ajax_uptimemonitorplus_get_sparklines', array( __CLASS__, 'handle_get_sparklines' ) );
        add_action( 'wp_ajax_uptimemonitorplus_get_checks', array( __CLASS__, 'handle_get_checks' ) );
        add_action( 'wp_ajax_uptimemonitorplus_get_analytics', array( __CLASS__, 'handle_get_analytics' ) );
        add_action( 'wp_ajax_uptimemonitorplus_get_errors', array( __CLASS__, 'handle_get_errors' ) );
        add_action( 'wp_ajax_uptimemonitorplus_get_incidents', array( __CLASS__, 'handle_get_incidents' ) );
        add_action( 'wp_ajax_uptimemonitorplus_get_calendar', array( __CLASS__, 'handle_get_calendar' ) );
        add_action( 'wp_ajax_uptimemonitorplus_update_notifications', array( __CLASS__, 'handle_update_notifications' ) );
        add_action( 'wp_ajax_uptimemonitorplus_dashboard_widget', array( __CLASS__, 'handle_dashboard_widget' ) );
    }

    /**
     * Verify nonce and capability.
     *
     * @return bool
     */
    private static function verify_request() {
        if ( ! check_ajax_referer( 'uptimemonitorplus_nonce', 'nonce', false ) ) {
            wp_send_json_error( array( 'message' => 'Invalid nonce' ), 403 );
            return false;
        }
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => 'Insufficient permissions' ), 403 );
            return false;
        }
        return true;
    }

    /**
     * Handle login AJAX request.
     */
    public static function handle_login() {
        if ( ! self::verify_request() ) {
            return;
        }

        $email    = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
        $password = isset( $_POST['password'] ) ? sanitize_text_field( wp_unslash( $_POST['password'] ) ) : '';

        if ( empty( $email ) || empty( $password ) ) {
            wp_send_json_error( array( 'message' => 'Email and password are required' ), 400 );
            return;
        }

        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->login( $email, $password );

        if ( is_wp_error( $result ) ) {
            $error_data = $result->get_error_data();
            wp_send_json_error( array(
                'message' => $result->get_error_message(),
                'code'    => $result->get_error_code(),
            ), isset( $error_data['status'] ) ? $error_data['status'] : 400 );
            return;
        }

        if ( empty( $result['accessToken'] ) ) {
            wp_send_json_error( array( 'message' => 'No access token received' ), 400 );
            return;
        }

        // Store JWT temporarily in a transient (expires in 10 minutes)
        set_transient( 'uptimemonitorplus_jwt_token', $result['accessToken'], 600 );

        // Auto-register domain
        $register_result = self::auto_register_domain( $result['accessToken'] );

        if ( is_wp_error( $register_result ) ) {
            $error_data = $register_result->get_error_data();
            wp_send_json_error( array(
                'message' => $register_result->get_error_message(),
                'code'    => $register_result->get_error_code(),
            ), isset( $error_data['status'] ) ? $error_data['status'] : 400 );
            return;
        }

        // Clear JWT transient after successful registration
        delete_transient( 'uptimemonitorplus_jwt_token' );

        wp_send_json_success( $register_result );
    }

    /**
     * Auto-register domain and generate API token.
     *
     * @param string $jwt_token JWT access token.
     * @return array|WP_Error
     */
    private static function auto_register_domain( $jwt_token ) {
        $client   = new UptimeMonitorPlus_API_Client();
        $site_url = site_url();

        // Get user's domains
        $domains = $client->get_domains( $jwt_token );
        if ( is_wp_error( $domains ) ) {
            return $domains;
        }

        $domain_list = isset( $domains['domains'] ) ? $domains['domains'] : $domains;
        $domain_id   = null;

        // Normalize site URL for comparison
        $normalized_site = strtolower( rtrim( preg_replace( '#^https?://#', '', $site_url ), '/' ) );

        // Search for existing domain
        if ( is_array( $domain_list ) ) {
            foreach ( $domain_list as $domain ) {
                $domain_url = isset( $domain['url'] ) ? $domain['url'] : '';
                $normalized_domain = strtolower( rtrim( preg_replace( '#^https?://#', '', $domain_url ), '/' ) );
                if ( $normalized_domain === $normalized_site ) {
                    $domain_id = $domain['id'];
                    break;
                }
            }
        }

        // Create domain if not found
        if ( ! $domain_id ) {
            $create_result = $client->create_domain( $jwt_token, $site_url );
            if ( is_wp_error( $create_result ) ) {
                return $create_result;
            }
            $domain_id = isset( $create_result['id'] ) ? $create_result['id'] : null;
            if ( ! $domain_id ) {
                return new WP_Error( 'DOMAIN_CREATE_FAILED', 'Failed to create domain' );
            }
        }

        // Generate API token
        $token_result = $client->generate_api_token( $jwt_token, $domain_id );
        if ( is_wp_error( $token_result ) ) {
            return $token_result;
        }

        if ( empty( $token_result['token'] ) ) {
            return new WP_Error( 'TOKEN_GENERATE_FAILED', 'Failed to generate API token' );
        }

        // Save options
        update_option( 'uptimemonitorplus_api_token', sanitize_text_field( $token_result['token'] ) );
        update_option( 'uptimemonitorplus_domain_id', sanitize_text_field( $domain_id ) );

        return array(
            'domain_id' => $domain_id,
            'connected' => true,
        );
    }

    /**
     * Handle disconnect AJAX request.
     */
    public static function handle_disconnect() {
        if ( ! self::verify_request() ) {
            return;
        }

        $domain_id = get_option( 'uptimemonitorplus_domain_id', '' );

        if ( ! empty( $domain_id ) ) {
            // Try to revoke the token via API (best-effort)
            $jwt_token = get_transient( 'uptimemonitorplus_jwt_token' );
            if ( $jwt_token ) {
                $client = new UptimeMonitorPlus_API_Client();
                $client->revoke_api_token( $jwt_token, $domain_id );
            }
        }

        // Clear stored options
        update_option( 'uptimemonitorplus_api_token', '' );
        update_option( 'uptimemonitorplus_domain_id', '' );
        update_option( 'uptimemonitorplus_extra_email', '' );

        // Clear all transients
        global $wpdb;
        $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
                '_transient_uptimemonitorplus_%',
                '_transient_timeout_uptimemonitorplus_%'
            )
        );

        wp_send_json_success( array( 'disconnected' => true ) );
    }

    /**
     * Handle get domain data AJAX request.
     */
    public static function handle_get_domain() {
        if ( ! self::verify_request() ) {
            return;
        }

        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->get_domain();

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle get SSL data AJAX request.
     */
    public static function handle_get_ssl() {
        if ( ! self::verify_request() ) {
            return;
        }

        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->get_ssl();

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle get sparklines AJAX request.
     */
    public static function handle_get_sparklines() {
        if ( ! self::verify_request() ) {
            return;
        }

        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->get_sparklines();

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle get checks AJAX request.
     */
    public static function handle_get_checks() {
        if ( ! self::verify_request() ) {
            return;
        }

        $params = array();
        if ( isset( $_POST['cursor'] ) ) {
            $params['cursor'] = sanitize_text_field( wp_unslash( $_POST['cursor'] ) );
        } elseif ( isset( $_GET['cursor'] ) ) {
            $params['cursor'] = sanitize_text_field( wp_unslash( $_GET['cursor'] ) );
        }
        if ( isset( $_POST['limit'] ) ) {
            $params['limit'] = absint( $_POST['limit'] );
        } elseif ( isset( $_GET['limit'] ) ) {
            $params['limit'] = absint( $_GET['limit'] );
        }
        if ( isset( $_POST['date'] ) ) {
            $params['date'] = sanitize_text_field( wp_unslash( $_POST['date'] ) );
        } elseif ( isset( $_GET['date'] ) ) {
            $params['date'] = sanitize_text_field( wp_unslash( $_GET['date'] ) );
        }
        if ( isset( $_POST['tz'] ) ) {
            $params['tz'] = sanitize_text_field( wp_unslash( $_POST['tz'] ) );
        } elseif ( isset( $_GET['tz'] ) ) {
            $params['tz'] = sanitize_text_field( wp_unslash( $_GET['tz'] ) );
        }

        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->get_checks( $params );

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle get analytics AJAX request.
     */
    public static function handle_get_analytics() {
        if ( ! self::verify_request() ) {
            return;
        }

        $range  = isset( $_POST['range'] ) ? sanitize_text_field( wp_unslash( $_POST['range'] ) ) : ( isset( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '24h' );
        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->get_analytics( $range );

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle get errors AJAX request.
     */
    public static function handle_get_errors() {
        if ( ! self::verify_request() ) {
            return;
        }

        $range  = isset( $_POST['range'] ) ? sanitize_text_field( wp_unslash( $_POST['range'] ) ) : ( isset( $_GET['range'] ) ? sanitize_text_field( wp_unslash( $_GET['range'] ) ) : '24h' );
        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->get_errors( $range );

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle get incidents AJAX request.
     */
    public static function handle_get_incidents() {
        if ( ! self::verify_request() ) {
            return;
        }

        $page      = isset( $_POST['page'] ) ? absint( $_POST['page'] ) : ( isset( $_GET['page'] ) ? absint( $_GET['page'] ) : 1 );
        $page_size = isset( $_POST['pageSize'] ) ? absint( $_POST['pageSize'] ) : ( isset( $_GET['pageSize'] ) ? absint( $_GET['pageSize'] ) : 20 );
        $client    = new UptimeMonitorPlus_API_Client();
        $result    = $client->get_incidents( $page, $page_size );

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle get calendar AJAX request.
     */
    public static function handle_get_calendar() {
        if ( ! self::verify_request() ) {
            return;
        }

        $months = isset( $_POST['months'] ) ? absint( $_POST['months'] ) : ( isset( $_GET['months'] ) ? absint( $_GET['months'] ) : 3 );
        $offset = isset( $_POST['offset'] ) ? intval( $_POST['offset'] ) : ( isset( $_GET['offset'] ) ? intval( $_GET['offset'] ) : 0 );
        $tz     = isset( $_POST['tz'] ) ? sanitize_text_field( wp_unslash( $_POST['tz'] ) ) : ( isset( $_GET['tz'] ) ? sanitize_text_field( wp_unslash( $_GET['tz'] ) ) : 'UTC' );
        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->get_calendar( $months, $offset, $tz );

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle update notifications AJAX request.
     */
    public static function handle_update_notifications() {
        if ( ! self::verify_request() ) {
            return;
        }

        $data = array();

        if ( isset( $_POST['emailNotifications'] ) ) {
            $data['emailNotifications'] = rest_sanitize_boolean( wp_unslash( $_POST['emailNotifications'] ) );
        }
        if ( isset( $_POST['sslAlertSuppressedDays'] ) ) {
            $raw = wp_unslash( $_POST['sslAlertSuppressedDays'] );
            if ( is_array( $raw ) ) {
                $data['sslAlertSuppressedDays'] = array_values( array_map( 'absint', $raw ) );
            } else {
                $data['sslAlertSuppressedDays'] = array();
            }
        }
        if ( isset( $_POST['extraEmail'] ) ) {
            $email = sanitize_email( wp_unslash( $_POST['extraEmail'] ) );
            $data['extraEmail'] = ! empty( $email ) ? $email : null;
        }

        $client = new UptimeMonitorPlus_API_Client();
        $result = $client->update_notifications( $data );

        if ( is_wp_error( $result ) ) {
            self::handle_api_error( $result );
            return;
        }

        wp_send_json_success( $result );
    }

    /**
     * Handle dashboard widget AJAX request.
     */
    public static function handle_dashboard_widget() {
        if ( ! self::verify_request() ) {
            return;
        }

        $cached = get_transient( 'uptimemonitorplus_widget_data' );
        if ( false !== $cached ) {
            wp_send_json_success( $cached );
            return;
        }

        $client      = new UptimeMonitorPlus_API_Client();
        $domain_data = $client->get_domain();

        if ( is_wp_error( $domain_data ) ) {
            self::handle_api_error( $domain_data );
            return;
        }

        $widget_data = array( 'domain' => $domain_data );

        // Try to get SSL data
        $ssl_data = $client->get_ssl();
        if ( ! is_wp_error( $ssl_data ) ) {
            $widget_data['ssl'] = $ssl_data;
        }

        // Try to get sparkline data
        $sparkline_data = $client->get_sparklines();
        if ( ! is_wp_error( $sparkline_data ) ) {
            $widget_data['sparkline'] = $sparkline_data;
        }

        // Cache for 5 minutes
        set_transient( 'uptimemonitorplus_widget_data', $widget_data, 300 );

        wp_send_json_success( $widget_data );
    }

    /**
     * Handle API errors, detecting 401 for token invalidation.
     *
     * @param WP_Error $error The WP_Error object.
     */
    private static function handle_api_error( $error ) {
        $error_data = $error->get_error_data();
        $status     = isset( $error_data['status'] ) ? $error_data['status'] : 400;

        if ( 401 === $status ) {
            // Token is invalid or expired - clear stored token
            update_option( 'uptimemonitorplus_api_token', '' );
            update_option( 'uptimemonitorplus_domain_id', '' );
        }

        wp_send_json_error( array(
            'message' => $error->get_error_message(),
            'code'    => $error->get_error_code(),
        ), $status );
    }
}