PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.4.7
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.4.7
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 / query / dropped-file.php
ai-engine / classes / query Last commit date
assist-feedback.php 10 months ago assistant.php 2 months ago base.php 2 months ago dropped-file.php 3 months ago edit-image.php 3 months ago embed.php 3 months ago feedback.php 10 months ago function.php 8 months ago image.php 2 months ago parameter.php 1 year ago text.php 6 months ago transcribe.php 8 months ago
dropped-file.php
255 lines
1 <?php
2
3 class Meow_MWAI_Query_DroppedFile {
4 private $data;
5 private $rawData;
6 private $type; // Defines what the data is about ('refId', 'url', or 'data')
7 private $purpose; // 'analysis' or 'generated'
8 private $mimeType; // 'image/jpeg' or any other mime type
9 private $fileId; // The ID of the file in the database
10 public $originalPath; // The original file path (for files loaded from disk)
11
12 /**
13 * Fetch content from a URL, handling internal vs external URLs differently.
14 * Internal URLs (same site) use wp_remote_get to avoid SSRF blocking issues.
15 * External URLs use wp_safe_remote_get for SSRF protection.
16 */
17 private static function fetch_url_content( $url ) {
18 $parts = wp_parse_url( $url );
19 if ( !isset( $parts['scheme'] ) || !in_array( $parts['scheme'], [ 'http', 'https' ], true ) ) {
20 throw new Exception( 'Invalid URL scheme; only HTTP/HTTPS allowed.' );
21 }
22
23 // Check if internal URL by comparing hostnames (handles http/https mismatch)
24 $site_host = wp_parse_url( get_site_url(), PHP_URL_HOST );
25 $url_host = wp_parse_url( $url, PHP_URL_HOST );
26 $is_internal = ( $site_host === $url_host );
27
28 if ( $is_internal ) {
29 $response = wp_remote_get( $url, [ 'timeout' => 60, 'sslverify' => false ] );
30 }
31 else {
32 // SSRF protection for external URLs
33 $response = wp_safe_remote_get( $url, [ 'timeout' => 60, 'redirection' => 0 ] );
34 }
35
36 if ( is_wp_error( $response ) ) {
37 throw new Exception( 'AI Engine: Failed to download file: ' . $response->get_error_message() );
38 }
39
40 $data = wp_remote_retrieve_body( $response );
41 if ( empty( $data ) ) {
42 throw new Exception( 'AI Engine: Failed to download file contents from URL.' );
43 }
44
45 return $data;
46 }
47
48 public static function from_url( $url, $purpose, $mimeType = null, $fileId = null ) {
49 if ( empty( $mimeType ) ) {
50 $mimeType = Meow_MWAI_Core::get_mime_type( $url );
51 }
52 return new Meow_MWAI_Query_DroppedFile( $url, 'url', $purpose, $mimeType, $fileId );
53 }
54
55 public static function from_data( $data, $purpose, $mimeType = null ) {
56 return new Meow_MWAI_Query_DroppedFile( $data, 'data', $purpose, $mimeType );
57 }
58
59 public static function from_path( $path, $purpose, $mimeType = null ) {
60 // Sanitize path to prevent PHAR deserialization attacks
61 $path = Meow_MWAI_Core::sanitize_file_path( $path );
62 $data = file_get_contents( $path );
63 if ( empty( $mimeType ) ) {
64 $mimeType = Meow_MWAI_Core::get_mime_type( $path );
65 }
66 $droppedFile = new Meow_MWAI_Query_DroppedFile( $data, 'data', $purpose, $mimeType );
67 // Store the original path for filename extraction
68 $droppedFile->originalPath = $path;
69 return $droppedFile;
70 }
71
72 public static function from_refId( $refId, $purpose, $mimeType = null ) {
73 return new Meow_MWAI_Query_DroppedFile( $refId, 'refId', $purpose, $mimeType );
74 }
75
76 /**
77 * Create DroppedFile from provider file_id reference (OpenAI, Anthropic, etc.)
78 *
79 * For PDFs uploaded to provider Files APIs, we only store the file_id
80 * Examples: OpenAI 'file-xxx', Anthropic 'file_xxx'
81 * These are reference-only - the file data lives on the provider's servers
82 * Do NOT try to load file data from these - use get_refId() to get the file_id
83 */
84 public static function from_provider_file_id( $fileId, $purpose, $mimeType = null ) {
85 return new Meow_MWAI_Query_DroppedFile( $fileId, 'provider_file_id', $purpose, $mimeType );
86 }
87
88 public function __construct( $data, $type, $purpose, $mimeType = null, $fileId = null ) {
89 if ( !empty( $type ) && $type !== 'refId' && $type !== 'url' && $type !== 'data' && $type !== 'provider_file_id' ) {
90 throw new Exception( 'AI Engine: The file type can only be refId, url, data, or provider_file_id.' );
91 }
92 if ( !empty( $purpose ) && $purpose !== 'analysis' && $purpose !== 'generated' ) {
93 throw new Exception( 'AI Engine: The file purpose can only be analysis or generated.' );
94 }
95 $this->data = $data;
96 $this->type = $type;
97 $this->purpose = $purpose;
98 $this->mimeType = $mimeType;
99 $this->fileId = $fileId;
100 }
101
102 public function get_url() {
103 if ( $this->type === 'url' ) {
104 return $this->data;
105 }
106 throw new Exception( 'AI Engine: The file is not an URL.' );
107 }
108
109 private function get_raw_data() {
110 if ( !empty( $this->rawData ) ) {
111 return $this->rawData;
112 }
113 if ( $this->type === 'provider_file_id' ) {
114 // Provider file IDs are reference-only (file data lives on provider's servers)
115 // Common mistake: trying to load file data for PDFs in conversation history
116 // Fix: Check file mime type before calling get_data()/get_base64()/get_inline_base64_url()
117 // For PDFs: use get_refId() to get the file_id string instead
118 throw new Exception( 'AI Engine: Cannot get raw data for provider file ID (file_id: ' . $this->data . '). Use get_refId() instead.' );
119 }
120 if ( $this->type === 'refId' ) {
121 global $mwai_core;
122
123 // Prefer loading from disk to avoid HTTP rewrites or CDN issues
124 $path = $mwai_core->files->get_path( $this->data );
125 if ( !empty( $path ) ) {
126 $path = Meow_MWAI_Core::sanitize_file_path( $path );
127 if ( file_exists( $path ) && is_readable( $path ) ) {
128 $data = file_get_contents( $path );
129 if ( $data === false ) {
130 throw new Exception( 'AI Engine: Failed to read file contents for refId: ' . $this->data );
131 }
132 $this->rawData = $data;
133 return $this->rawData;
134 }
135 }
136
137 // Fallback to the public URL if the local path is unavailable
138 $url = $mwai_core->files->get_url( $this->data );
139 if ( empty( $url ) ) {
140 throw new Exception( 'AI Engine: Could not find file URL for refId: ' . $this->data );
141 }
142 $this->rawData = self::fetch_url_content( $url );
143 return $this->rawData;
144 }
145 else if ( $this->type === 'url' ) {
146 // For internal URLs, try to read from disk first (more efficient)
147 $site_host = wp_parse_url( get_site_url(), PHP_URL_HOST );
148 $url_host = wp_parse_url( $this->data, PHP_URL_HOST );
149 if ( $site_host === $url_host ) {
150 $upload_dir = wp_upload_dir();
151 // Normalize protocols for comparison (http vs https)
152 $normalized_url = preg_replace( '/^https?:/', '', $this->data );
153 $normalized_upload_url = preg_replace( '/^https?:/', '', $upload_dir['baseurl'] );
154 if ( strpos( $normalized_url, $normalized_upload_url ) === 0 ) {
155 $local_path = str_replace( $normalized_upload_url, $upload_dir['basedir'], $normalized_url );
156 $local_path = Meow_MWAI_Core::sanitize_file_path( $local_path );
157 if ( file_exists( $local_path ) && is_readable( $local_path ) ) {
158 $this->rawData = file_get_contents( $local_path );
159 if ( $this->rawData !== false ) {
160 return $this->rawData;
161 }
162 }
163 }
164 }
165
166 // Fetch via HTTP (handles internal vs external URLs with SSRF protection)
167 $this->rawData = self::fetch_url_content( $this->data );
168 return $this->rawData;
169 }
170 else if ( $this->type === 'data' ) {
171 return $this->data;
172 }
173 throw new Exception( 'AI Engine: The file is not data or an URL.' );
174 }
175
176 public function get_data() {
177 if ( $this->type === 'provider_file_id' ) {
178 // Provider file IDs are just references, no data loading needed
179 throw new Exception( 'AI Engine: Cannot get data for provider file ID. Use get_refId() instead.' );
180 }
181 if ( $this->type === 'refId' || $this->type === 'url' ) {
182 return $this->get_raw_data();
183 }
184 else if ( $this->type === 'data' ) {
185 return $this->data;
186 }
187 throw new Exception( 'AI Engine: The file is not data or an URL.' );
188 }
189
190 public function get_base64() {
191 $data = $this->get_raw_data();
192 return base64_encode( $data );
193 }
194
195 // Will return something like "data:image/jpeg;base64,{data}"
196 public function get_inline_base64_url() {
197 $b64 = $this->get_base64();
198 return "data:{$this->mimeType};base64,{$b64}";
199 }
200
201 public function get_type() {
202 return $this->type;
203 }
204
205 public function get_purpose() {
206 return $this->purpose;
207 }
208
209 public function get_mimeType() {
210 return $this->mimeType;
211 }
212
213 public function is_image() {
214 return strpos( $this->mimeType, 'image' ) !== false;
215 }
216
217 public function get_fileId() {
218 return $this->fileId;
219 }
220
221 public function get_refId() {
222 if ( $this->type === 'refId' || $this->type === 'provider_file_id' ) {
223 return $this->data;
224 }
225 return null;
226 }
227
228 // Return a filename for this file. If the file is an URL, use the basename of
229 // its path. If the file is raw data, generate a generic name based on the mime type.
230 public function get_filename() {
231 // If we have an original path (from from_path), use its basename
232 if ( !empty( $this->originalPath ) ) {
233 return basename( $this->originalPath );
234 }
235 if ( $this->type === 'refId' ) {
236 global $mwai_core;
237 $path = $mwai_core->files->get_path( $this->data );
238 return basename( $path );
239 }
240 if ( $this->type === 'url' ) {
241 $path = parse_url( $this->data, PHP_URL_PATH );
242 return basename( $path );
243 }
244 if ( $this->type === 'data' ) {
245 if ( !empty( $this->mimeType ) ) {
246 $parts = explode( '/', $this->mimeType );
247 $ext = end( $parts );
248 return 'file.' . $ext;
249 }
250 return 'file.bin';
251 }
252 return 'file';
253 }
254 }
255