PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.6.3
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.6.3
3.7.0 3.6.9 3.6.8 3.6.7 3.6.6 3.6.4 3.6.5 3.6.3 3.6.2 3.6.1 3.6.0 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.9 3.4.8 3.4.7 0.2.1 1.6.91 0.2.2 1.6.92 0.2.3 1.6.93 0.2.4 1.6.94 0.2.5 1.6.95 0.2.6 1.6.96 0.2.7 1.6.97 0.2.8 1.6.98 0.2.9 1.6.99 0.3.0 1.7.0 0.3.1 1.7.1 0.3.2 1.7.2 0.3.3 1.7.3 0.3.4 1.7.4 0.3.5 1.7.5 0.3.6 1.7.6 0.4.0 1.7.7 0.4.1 1.7.8 0.4.2 1.7.9 0.4.3 1.8.0 0.4.4 1.8.1 0.4.5 1.8.2 0.4.6 1.8.3 0.4.7 1.8.4 0.4.8 1.8.5 0.4.9 1.8.6 0.5.0 1.8.7 0.5.1 1.8.8 0.5.2 1.8.9 0.5.3 1.9.0 0.5.4 1.9.1 0.5.5 1.9.2 0.5.6 1.9.3 0.5.7 1.9.4 0.5.8 1.9.5 0.5.9 1.9.6 0.6.0 1.9.7 0.6.1 1.9.8 0.6.2 1.9.81 0.6.3 1.9.82 0.6.4 1.9.83 0.6.5 1.9.84 0.6.6 1.9.85 0.6.7 1.9.86 0.6.8 1.9.87 0.6.9 1.9.88 0.7.0 1.9.89 0.7.1 1.9.90 0.7.2 1.9.91 0.7.3 1.9.92 0.7.4 1.9.93 0.7.5 1.9.94 0.7.6 1.9.95 0.7.7 1.9.96 0.7.8 1.9.97 0.7.9 1.9.98 0.8.0 1.9.99 0.8.1 2.0.0 0.8.2 2.0.1 0.8.3 2.0.2 0.8.4 2.0.3 0.8.5 2.0.4 0.8.6 2.0.5 0.8.7 2.0.6 0.8.8 2.0.7 0.8.9 2.0.8 0.9.0 2.0.9 0.9.2 2.1.0 0.9.3 2.1.1 0.9.4 2.1.2 0.9.5 2.1.3 0.9.6 2.1.4 0.9.7 2.1.5 0.9.8 2.1.6 0.9.81 2.1.7 0.9.82 2.1.8 0.9.83 2.1.9 0.9.84 2.2.0 0.9.85 2.2.1 0.9.86 2.2.2 0.9.87 2.2.3 0.9.88 2.2.4 0.9.89 2.2.5 0.9.9 2.2.51 0.9.91 2.2.52 0.9.92 2.2.53 0.9.93 2.2.54 0.9.94 2.2.56 0.9.95 2.2.57 0.9.96 2.2.6 0.9.97 2.2.60 0.9.98 2.2.61 0.9.99 2.2.62 1.0.0 2.2.63 1.0.01 2.2.70 1.0.1 2.2.80 1.0.2 2.2.81 1.0.3 2.2.90 1.0.4 2.2.91 1.0.5 2.2.92 1.0.6 2.2.93 1.0.7 2.2.94 1.0.8 2.2.95 1.0.9 2.3.0 1.1.0 2.3.1 1.1.1 2.3.2 1.1.2 2.3.3 1.1.3 2.3.4 1.1.4 2.3.5 1.1.5 2.3.6 1.1.6 2.3.7 1.1.7 2.3.8 1.1.8 2.3.9 1.1.9 2.4.0 1.2.0 2.4.1 1.2.1 2.4.2 1.2.2 2.4.3 1.2.21 2.4.4 1.2.3 2.4.5 1.2.30 2.4.6 1.3.0 2.4.7 1.3.1 2.4.8 1.3.2 2.4.9 1.3.3 2.5.0 1.3.31 2.5.1 1.3.32 2.5.2 1.3.33 2.5.3 1.3.34 2.5.4 1.3.35 2.5.5 1.3.36 2.5.6 1.3.37 2.5.7 1.3.38 2.5.8 1.3.39 2.5.9 1.3.40 2.6.0 1.3.41 2.6.1 1.3.42 2.6.2 1.3.43 2.6.3 1.3.44 2.6.5 1.3.45 2.6.6 1.3.46 2.6.7 1.3.47 2.6.8 1.3.48 2.6.9 1.3.49 2.7.0 1.3.50 2.7.1 1.3.51 2.7.2 1.3.52 2.7.3 1.3.53 2.7.4 1.3.54 2.7.5 1.3.56 2.7.6 1.3.57 2.7.7 1.3.58 2.7.8 1.3.59 2.7.9 1.3.60 2.8.0 1.3.61 2.8.1 1.3.62 2.8.2 1.3.63 2.8.3 1.3.64 2.8.4 1.3.65 2.8.5 1.3.66 2.8.6 1.3.67 2.8.7 1.3.68 2.8.8 1.3.69 2.8.9 1.3.70 2.9.0 1.3.71 2.9.1 1.3.72 2.9.2 1.3.73 2.9.3 1.3.74 2.9.4 1.3.75 2.9.5 1.3.76 2.9.6 1.3.77 2.9.7 1.3.78 2.9.8 1.3.79 2.9.9 1.3.80 3.0.0 1.3.81 3.0.1 1.3.82 3.0.2 1.3.83 3.0.3 1.3.84 3.0.4 1.3.85 3.0.5 1.3.86 3.0.6 1.3.87 3.0.7 1.3.88 3.0.8 1.3.89 3.0.9 1.3.90 3.1.0 1.3.91 3.1.1 1.3.92 3.1.2 1.3.93 3.1.3 1.3.94 3.1.4 1.3.95 3.1.5 1.3.96 3.1.6 1.3.97 3.1.7 1.3.98 3.1.8 1.3.99 3.1.9 1.4.0 3.2.0 1.4.1 3.2.1 1.4.2 3.2.2 1.4.3 3.2.3 1.4.4 3.2.4 1.4.5 3.2.5 1.4.6 3.2.6 1.4.7 3.2.7 1.4.8 3.2.8 1.4.9 3.2.9 1.5.0 3.3.0 1.5.1 3.3.1 1.5.2 3.3.2 1.5.3 3.3.3 1.5.4 3.3.4 1.5.5 3.3.5 1.5.6 3.3.6 1.5.7 3.3.7 1.5.8 3.3.8 1.5.9 3.3.9 1.6.0 3.4.0 1.6.1 3.4.1 1.6.2 3.4.2 1.6.3 3.4.3 1.6.5 3.4.4 1.6.51 3.4.5 1.6.52 3.4.6 1.6.53 1.6.54 1.6.55 1.6.56 1.6.57 1.6.58 1.6.59 1.6.60 1.6.61 1.6.62 1.6.63 1.6.64 1.6.65 1.6.66 1.6.67 1.6.68 trunk 1.6.69 0.0.1 1.6.70 0.0.2 1.6.71 0.0.3 1.6.72 0.0.4 1.6.73 0.0.5 1.6.74 0.0.6 1.6.75 0.0.7 1.6.76 0.0.8 1.6.77 0.0.9 1.6.78 0.1.0 1.6.79 0.1.1 1.6.81 0.1.2 1.6.82 0.1.3 1.6.83 0.1.4 1.6.84 0.1.5 1.6.85 0.1.6 1.6.86 0.1.7 1.6.87 0.1.8 1.6.88 0.1.9 1.6.89 0.2.0 1.6.90
ai-engine / classes / services / session.php
ai-engine / classes / services Last commit date
image.php 1 month ago message-builder.php 1 month ago model-environment.php 8 months ago response-id-manager.php 1 month ago session.php 1 week ago usage-stats.php 2 months ago
session.php
203 lines
1 <?php
2
3 class Meow_MWAI_Services_Session {
4 private $core;
5 private $nonce = null;
6 // Memoized guest session id for this request. Without it, a guest whose first
7 // request has no cookie yet gets a fresh uniqid() on every call (setcookie
8 // does not populate $_COOKIE mid-request), so two calls in the same request
9 // disagree and guest-scoped checks (e.g. file ownership) mismatch.
10 private $sessionId = null;
11
12 public function __construct( $core ) {
13 $this->core = $core;
14 }
15
16 public function can_start_session() {
17 // Check if session already started
18 if ( session_status() !== PHP_SESSION_NONE ) {
19 return false;
20 }
21
22 // Check if we're in a context where sessions shouldn't be started
23 if ( wp_doing_cron() || defined( 'DOING_AUTOSAVE' ) ) {
24 return false;
25 }
26
27 // For AI Engine REST endpoints only - check if it's actually our endpoint
28 if ( $this->core->is_rest ) {
29 $request_uri = $_SERVER['REQUEST_URI'] ?? '';
30 // Only start sessions for actual AI Engine endpoints
31 if ( strpos( $request_uri, '/mwai/' ) === false && strpos( $request_uri, 'rest_route=/mwai/' ) === false ) {
32 return false;
33 }
34 }
35
36 // Allow developers to override
37 return apply_filters( 'mwai_allow_session', true );
38 }
39
40 public function get_nonce( $force = false ) {
41 // NONCE GENERATION LOGIC:
42 // - For logged-out users (unless forced): Return null - they must use /start_session endpoint
43 // - For logged-in users: Create user-specific nonce tied to their WP session
44 // - With $force=true: Always create nonce (used by /start_session endpoint)
45 //
46 // This ensures logged-in users get a nonce matching their auth context on page load,
47 // preventing rest_cookie_invalid_nonce errors when cookies are present.
48 if ( !$force && !is_user_logged_in() ) {
49 return null;
50 }
51 if ( isset( $this->nonce ) ) {
52 return $this->nonce;
53 }
54 $this->nonce = wp_create_nonce( 'wp_rest' );
55 return $this->nonce;
56 }
57
58 // ChatID
59 public function fix_chat_id( $query, $params ) {
60 if ( isset( $query->chatId ) && $query->chatId !== 'N/A' ) {
61 return $query->chatId;
62 }
63 $chatId = isset( $params['chatId'] ) ? $params['chatId'] : $query->session;
64 if ( $chatId === 'N/A' ) {
65 $chatId = $this->core->get_random_id( 8 );
66 }
67 $query->set_chat_id( $chatId );
68 return $chatId;
69 }
70
71 public function get_session_id() {
72 // Check if we have the session cookie
73 if ( isset( $_COOKIE['mwai_session_id'] ) ) {
74 return $_COOKIE['mwai_session_id'];
75 }
76
77 // Already generated one earlier in this request? Reuse it so repeated calls
78 // stay consistent (setcookie above does not populate $_COOKIE mid-request).
79 if ( $this->sessionId !== null ) {
80 return $this->sessionId;
81 }
82
83 // If no cookie exists and we can set one, create it now (lazy initialization)
84 if ( !headers_sent() && !wp_doing_cron() ) {
85 $this->sessionId = uniqid();
86 @setcookie( 'mwai_session_id', $this->sessionId, [
87 'expires' => 0,
88 'path' => '/',
89 'secure' => is_ssl(),
90 'httponly' => true,
91 ] );
92 return $this->sessionId;
93 }
94
95 // For cron jobs or when headers are sent, return a temporary session ID
96 return wp_doing_cron() ? 'wp-cron' : 'N/A';
97 }
98
99 public function get_ip_address( $force = false ) {
100 // Get the actual IP address
101 $ip_keys = [ 'HTTP_CF_CONNECTING_IP', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR',
102 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_X_REAL_IP', 'HTTP_FORWARDED_FOR',
103 'HTTP_FORWARDED', 'REMOTE_ADDR' ];
104 $actual_ip = null;
105 foreach ( $ip_keys as $key ) {
106 if ( array_key_exists( $key, $_SERVER ) === true ) {
107 $ips = explode( ',', $_SERVER[$key] );
108 foreach ( $ips as $ip ) {
109 $ip = trim( $ip );
110 if ( $this->validate_ip( $ip ) ) {
111 $actual_ip = $ip;
112 break 2;
113 }
114 }
115 }
116 }
117 if ( !$actual_ip ) {
118 $actual_ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1';
119 }
120
121 // If privacy_first is enabled and not forced, return hashed IP
122 if ( !$force && $this->core->get_option( 'privacy_first' ) ) {
123 // Use a salt that's unique per site but consistent
124 $salt = wp_salt( 'auth' );
125 // Create a hash that's consistent for the same IP but anonymized
126 return 'hashed_' . substr( hash( 'sha256', $actual_ip . $salt ), 0, 16 );
127 }
128
129 return $actual_ip;
130 }
131
132 public function get_user_data() {
133 $user = wp_get_current_user();
134 if ( empty( $user ) || empty( $user->ID ) ) {
135 return null;
136 }
137
138 // Return both the new format (for frontend) and placeholder format (for do_placeholders)
139 $userData = [
140 'ID' => $user->ID,
141 'name' => $user->display_name,
142 'email' => $user->user_email,
143 'avatar' => get_avatar_url( $user->ID ),
144 'type' => 'logged-in',
145 // Add placeholder keys for do_placeholders function
146 'FIRST_NAME' => get_user_meta( $user->ID, 'first_name', true ),
147 'LAST_NAME' => get_user_meta( $user->ID, 'last_name', true ),
148 'USER_LOGIN' => isset( $user->data ) && isset( $user->data->user_login ) ?
149 $user->data->user_login : null,
150 'DISPLAY_NAME' => isset( $user->data ) && isset( $user->data->display_name ) ?
151 $user->data->display_name : null,
152 'AVATAR_URL' => get_avatar_url( $user->ID ),
153 ];
154
155 return $userData;
156 }
157
158 public function get_user_id() {
159 // This function has to be re-thinked for all other API endpoints
160 $userId = null;
161 // If there is a current session, we probably know the current user
162 if ( is_user_logged_in() ) {
163 $userId = get_current_user_id();
164 }
165 // For guest users, return null instead of generating a string ID
166 // This allows the database to store NULL for guests, which displays as "Guest" in the UI
167 return $userId;
168 }
169
170 /**
171 * Get session-based user ID for guest users
172 * This creates a unique identifier based on session ID for tracking guest uploads
173 *
174 * @return string|null Session-based user ID or null if no session
175 */
176 public function get_session_user_id() {
177 $sessionId = $this->get_session_id();
178 if ( !$sessionId || $sessionId === 'N/A' ) {
179 return null;
180 }
181 // Create a consistent user ID based on session
182 // Prefix with 'session_' to distinguish from real user IDs
183 return 'session_' . $sessionId;
184 }
185
186 public function get_admin_user() {
187 $users = get_users( [ 'role' => 'administrator' ] );
188 if ( !empty( $users ) ) {
189 return $users[0];
190 }
191 return null;
192 }
193
194 // Private helper methods
195 private function validate_ip( $ip ) {
196 if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) === false ) {
197 return false;
198 }
199 return true;
200 }
201
202 }
203