'Unauthorized' ) ); exit; } } // Add the query var add_filter('query_vars', 'aiass_add_query_vars'); function aiass_add_query_vars($vars) { $vars[] = 'aiass_endpoint'; return $vars; } function aia_camacam_aia_camacam_getJsonData(){ $jsonData = file_get_contents('php://input'); if ( empty( $jsonData ) ) { return null; } $data = json_decode($jsonData, true); if ( json_last_error() !== JSON_ERROR_NONE ) { return null; } return $data; } function aiass_send_data( $url, $payload ){ $args = array( 'headers' => array( 'Content-Type' => 'application/json; charset=utf-8', ), 'body' => $payload, 'timeout' => '45', 'redirection' => '5', 'httpversion' => '1.0', 'data_format' => 'body', 'blocking' => true, 'headers' => array( 'aiatoken' => aia_camacam_getToken() ), 'cookies' => array(), ); return wp_remote_post($url, $args); } function aia_camacam_aia_camacam_insert_exception_record($exception) { //also send over to the manager and log it locally aiass_send_error($exception); global $wpdb; $table_name = 'aiaerrorlogs'; $wpdb->insert( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery $table_name, array( 'content' => json_encode($exception), ), array( '%s', ) ); } function aiass_send_error( $error ){ $args = array( 'body' => json_encode($error), 'timeout' => '45', 'redirection' => '5', 'httpversion' => '1.0', 'blocking' => true, 'headers' => array( 'aiatoken' => aia_camacam_getToken() ), 'cookies' => array(), ); return wp_remote_post(aiass_get_url().'/api/v1/error', $args); } function aia_camacam_getOrderStatuses() { try { if (!function_exists('wc_get_order_statuses')) { throw new Exception("WooCommerce is not active."); } // Collect installed languages; always include 'en_US' as the base locale. $installed_languages = get_available_languages(); $locales = array('en_US'); foreach ($installed_languages as $lang_file) { // Language files are stored as locale slugs (e.g. "bg_BG", "fr_FR"). $locales[] = basename($lang_file, '.mo'); } $locales = array_unique($locales); $statuses_by_locale = array(); $original_locale = get_locale(); foreach ($locales as $locale) { switch_to_locale($locale); $statuses_by_locale[$locale] = wc_get_order_statuses(); switch_to_locale($original_locale); } header('Content-Type: application/json'); http_response_code(200); echo json_encode($statuses_by_locale); } catch (Exception $e) { header('Content-Type: application/json'); http_response_code(500); echo json_encode(["error" => $e->getMessage()]); } } function aia_camacam_getPaymentMethods() { try { if ( ! function_exists( 'WC' ) || ! WC()->payment_gateways() ) { throw new Exception( "WooCommerce is not active." ); } // Collect installed languages; always include 'en_US' as the base locale. $installed_languages = get_available_languages(); $locales = array( 'en_US' ); foreach ( $installed_languages as $lang_file ) { $locales[] = basename( $lang_file, '.mo' ); } $locales = array_unique( $locales ); $methods_by_locale = array(); $original_locale = get_locale(); foreach ( $locales as $locale ) { switch_to_locale( $locale ); // Re-init forces WooCommerce to re-instantiate all gateway classes // so their constructor __() calls pick up the new locale. WC()->payment_gateways()->init(); $gateways = WC()->payment_gateways()->payment_gateways(); $translated = array(); foreach ( $gateways as $gateway ) { $translated[ $gateway->id ] = $gateway->get_method_title(); } $methods_by_locale[ $locale ] = $translated; switch_to_locale( $original_locale ); } header( 'Content-Type: application/json' ); http_response_code( 200 ); echo json_encode( $methods_by_locale ); } catch ( Exception $e ) { header( 'Content-Type: application/json' ); http_response_code( 500 ); echo json_encode( array( 'error' => $e->getMessage() ) ); } } function aia_camacam_applyProductUpdate(){ $data = aia_camacam_getJsonData(); if( is_null( $data ) || $data['command'] !== 'productupdate' ) return; if( ! isset( $data['data'] ) || ! is_array( $data['data'] ) ) { header( 'Content-Type: application/json' ); http_response_code( 400 ); echo json_encode( array( 'error' => 'Missing or invalid data payload' ) ); return; } $pid = absint( isset( $data['data']['pid'] ) ? $data['data']['pid'] : 0 ); if( $pid === 0 ) { header( 'Content-Type: application/json' ); http_response_code( 400 ); echo json_encode( array( 'error' => 'Invalid product ID' ) ); return; } try { $aiausr = get_user_by('email', 'aia@aissistant.shop'); if( !$aiausr ){ $email = 'aia@aissistant.shop'; $password = wp_generate_password(); $userdata = array( 'user_login' => $email, 'user_email' => $email, 'first_name' => 'Aia', 'last_name' => 'Assistant', 'user_pass' => $password, ); $user_id = wp_insert_user($userdata); } $pname = isset( $data['data']['pname'] ) ? sanitize_text_field( $data['data']['pname'] ) : ''; $pshortd = isset( $data['data']['pshortd'] ) ? wp_kses_post( $data['data']['pshortd'] ) : ''; $pdescr = isset( $data['data']['pdescr'] ) ? wp_kses_post( $data['data']['pdescr'] ) : ''; $seotitle = isset( $data['data']['seotitle'] ) ? sanitize_text_field( $data['data']['seotitle'] ) : ''; $seodescr = isset( $data['data']['seodescr'] ) ? sanitize_text_field( $data['data']['seodescr'] ) : ''; $focuskeywords = isset( $data['data']['focuskeywords'] ) ? sanitize_text_field( $data['data']['focuskeywords'] ) : ''; $focuskw_text_input = isset( $data['data']['focuskw_text_input'] ) ? sanitize_text_field( $data['data']['focuskw_text_input'] ) : ''; $focuskw = isset( $data['data']['focuskw'] ) ? sanitize_text_field( $data['data']['focuskw'] ) : ''; $product = wc_get_product( $pid ); if( $product ){ $is_update = false; if( $pname ) { $product->set_name( $pname ); $is_update = true; } if( $pshortd ) { $product->set_short_description( $pshortd ); $is_update = true; } if( $pdescr ){ $product->set_description( $pdescr ); $is_update = true; } if( $is_update){ $product->save(); } if( $seotitle ) { update_post_meta($product->get_id(), '_yoast_wpseo_title', $seotitle); $is_update = true; } if( $seodescr ) { update_post_meta($product->get_id(), '_yoast_wpseo_metadesc', $seodescr); $is_update = true; } if( $focuskeywords ){ update_post_meta($product->get_id(), '_yoast_wpseo_focuskeywords', $focuskeywords); $is_update = true; } if( $focuskw_text_input ){ update_post_meta($product->get_id(), '_yoast_wpseo_focuskw_text_input', $focuskw_text_input); $is_update = true; } if( $focuskw ){ update_post_meta($product->get_id(), '_yoast_wpseo_focuskw', $focuskw); $is_update = true; } if (class_exists('WC_Data_Store') && $is_update ) { $product_data_store = WC_Data_Store::load('product'); $product_data_store->read($product); $product_data_store->create_revision($product); } }else{ aiass_send_error( "no such product: " . json_encode($data) ); } header('Content-Type: application/json'); http_response_code(200); echo json_encode("OK"); } catch (Exception $e) { aia_camacam_insert_exception_record($e); } } /** * Validates that a SELECT query only references WP-prefixed tables and contains * no structural patterns that could exfiltrate or modify data. * Throws Exception on any violation. * * @throws Exception */ function aia_camacam_validate_select_query( $query ) { global $wpdb; // Block dangerous SQL constructs even inside authenticated SELECT queries if ( preg_match( '/\b(UNION(\s+ALL)?\s+SELECT|INTO\s+OUTFILE|LOAD_FILE|INFORMATION_SCHEMA|mysql\.user)\b/i', $query ) ) { throw new Exception( 'Query contains a disallowed SQL construct' ); } // Require every FROM / JOIN target to use the WordPress table prefix preg_match_all( '/\b(?:FROM|JOIN)\s+`?(\w+)`?/i', $query, $matches ); $tables = array_filter( $matches[1] ); foreach ( $tables as $table ) { if ( 'dual' === strtolower( $table ) ) { continue; } if ( strpos( $table, $wpdb->prefix ) !== 0 ) { throw new Exception( 'Query references a disallowed table: ' . esc_html( $table ) ); } } } function aia_camacam_gatheranalytics(){ $data = aia_camacam_getJsonData(); if( is_null( $data ) || $data['command'] !== 'sql' ) return; if( ! isset( $data['action'] ) || ! is_string( $data['action'] ) || empty( trim( $data['action'] ) ) ) { header( 'Content-Type: application/json' ); http_response_code( 400 ); echo json_encode( array( 'error' => 'Missing or invalid action' ) ); return; } $query = trim( $data['action'] ); if ( ! preg_match( '/^\s*SELECT\b/i', $query ) ) { header( 'Content-Type: application/json' ); http_response_code( 400 ); echo json_encode( array( 'error' => 'Only SELECT queries are permitted' ) ); return; } $params = isset( $data['params'] ) && is_array( $data['params'] ) ? array_values( $data['params'] ) : array(); try { global $wpdb; aia_camacam_validate_select_query( $query ); if ( ! empty( $params ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter $res = $wpdb->get_results( $wpdb->prepare( $query, ...$params ) ); } else { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter $res = $wpdb->get_results( $query ); } $return_url = isset( $data['returnUrl'] ) ? $data['returnUrl'] : ''; if ( ! filter_var( $return_url, FILTER_VALIDATE_URL ) ) { throw new Exception( 'Invalid returnUrl' ); } $rr = aiass_send_data( $return_url, json_encode($res) ); header('Content-Type: application/json'); http_response_code(200); } catch (Exception $e) { aia_camacam_insert_exception_record($e); } } function aia_camacam_gatherAnalyticsDirectly(){ $data = aia_camacam_getJsonData(); if( is_null( $data ) || $data['command'] !== 'sql' ) return; if( ! isset( $data['action'] ) || ! is_string( $data['action'] ) || empty( trim( $data['action'] ) ) ) { header( 'Content-Type: application/json' ); http_response_code( 400 ); echo json_encode( array( 'error' => 'Missing or invalid action' ) ); return; } $query = trim( $data['action'] ); if ( ! preg_match( '/^\s*SELECT\b/i', $query ) ) { header( 'Content-Type: application/json' ); http_response_code( 400 ); echo json_encode( array( 'error' => 'Only SELECT queries are permitted' ) ); return; } $params = isset( $data['params'] ) && is_array( $data['params'] ) ? array_values( $data['params'] ) : array(); try { global $wpdb; aia_camacam_validate_select_query( $query ); if ( ! empty( $params ) ) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter $res = $wpdb->get_results( $wpdb->prepare( $query, ...$params ) ); } else { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,PluginCheck.Security.DirectDB.UnescapedDBParameter $res = $wpdb->get_results( $query ); } header('Content-Type: application/json'); http_response_code(200); echo json_encode( json_encode($res) ); } catch (Exception $e) { aia_camacam_insert_exception_record($e); } } function aia_camacam_getErrors(){ try { global $wpdb; $res = $wpdb->get_results( 'SELECT * FROM aiaerrorlogs ORDER BY id DESC LIMIT 100' ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if( $res ){ header('Content-Type: text/plain'); foreach ($res as $line) { echo wp_json_encode( $line ) . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } exit; } } catch (Exception $e) { echo "No errors logged or table doesn't exist"; } } function aia_camacam_applyOrderUpdate() { $data = aia_camacam_getJsonData(); if( is_null( $data ) ) return; $order_id = isset($data['order_id']) ? intval($data['order_id']) : 0; $previous_status = isset($data['previous_status']) ? sanitize_text_field($data['previous_status']) : ''; $new_status = isset($data['new_status']) ? sanitize_text_field($data['new_status']) : ''; if ( ! $order_id || ! $previous_status || ! $new_status ) { header('Content-Type: application/json'); http_response_code(400); echo json_encode(["error" => "Missing required fields: order_id, previous_status, new_status"]); return; } try { $order = wc_get_order($order_id); if ( ! $order ) { header('Content-Type: application/json'); http_response_code(400); echo json_encode(["error" => "Order not found: " . $order_id]); return; } $current_status = $order->get_status(); // WooCommerce statuses are stored without the 'wc-' prefix; strip it from input if present $previous_status_clean = preg_replace('/^wc-/', '', $previous_status); $new_status_clean = preg_replace('/^wc-/', '', $new_status); $valid_statuses = array_keys(wc_get_order_statuses()); // keys are 'wc-processing', etc. $valid_statuses_clean = array_map(function($s) { return preg_replace('/^wc-/', '', $s); }, $valid_statuses); if ( ! in_array($new_status_clean, $valid_statuses_clean, true) ) { header('Content-Type: application/json'); http_response_code(400); echo json_encode(["error" => "Invalid status: " . $new_status_clean, "valid_statuses" => $valid_statuses_clean]); return; } if ( $current_status !== $previous_status_clean ) { header('Content-Type: application/json'); http_response_code(400); echo json_encode([ "error" => "Status mismatch", "expected" => $previous_status_clean, "current" => $current_status, ]); return; } $order->set_status($new_status_clean); $order->save(); header('Content-Type: application/json'); http_response_code(200); echo json_encode(["success" => true, "order_id" => $order_id, "status" => $new_status_clean]); } catch (Exception $e) { aia_camacam_insert_exception_record($e); header('Content-Type: application/json'); http_response_code(500); echo json_encode(["error" => $e->getMessage()]); } } function aia_camacam_getRegInfo(){ header('Content-Type: text/plain'); echo "AIA Client: "; echo get_option('aia_camacam_client', ''); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped exit; }