PluginProbe ʕ •ᴥ•ʔ
AI Engine – The Chatbot, AI Framework & MCP for WordPress / 3.1.2
AI Engine – The Chatbot, AI Framework & MCP for WordPress v3.1.2
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 / image.php
ai-engine / classes / services Last commit date
image.php 8 months ago message-builder.php 8 months ago model-environment.php 8 months ago response-id-manager.php 11 months ago session.php 11 months ago usage-stats.php 8 months ago
image.php
229 lines
1 <?php
2
3 class Meow_MWAI_Services_Image {
4 private $core;
5
6 public function __construct( $core ) {
7 $this->core = $core;
8 }
9
10 public function is_image( $mimeType ) {
11 return strpos( $mimeType, 'image/' ) === 0;
12 }
13
14 public function get_image_resolution( $imageData ) {
15 try {
16 $tempFile = tmpfile();
17 $tempFilePath = stream_get_meta_data( $tempFile )['uri'];
18 fwrite( $tempFile, $imageData );
19 $imageSize = getimagesize( $tempFilePath );
20 fclose( $tempFile );
21 if ( $imageSize !== false ) {
22 return $imageSize[0] . 'x' . $imageSize[1];
23 }
24 }
25 catch ( Exception $e ) {
26 throw new Exception( 'Failed to get image resolution.' );
27 }
28 return null;
29 }
30
31 public function get_mime_type( $file, $fileData = null ) {
32 $mimeType = null;
33
34 // If we have file data, let's use it
35 if ( !empty( $fileData ) ) {
36 $f = finfo_open();
37 $mimeType = finfo_buffer( $f, $fileData, FILEINFO_MIME_TYPE );
38 }
39
40 // Try to use mime_content_type for local files
41 if ( !$mimeType ) {
42 $isUrl = filter_var( $file, FILTER_VALIDATE_URL );
43 if ( !$isUrl && function_exists( 'mime_content_type' ) && file_exists( $file ) ) {
44 $mimeType = mime_content_type( $file );
45 }
46 }
47
48 // Otherwise, let's check the file extension (which can actually also be an URL)
49 if ( !$mimeType ) {
50 $extension = pathinfo( $file, PATHINFO_EXTENSION );
51 $extension = strtolower( $extension );
52 $mimeTypes = [
53 'jpg' => 'image/jpeg',
54 'jpeg' => 'image/jpeg',
55 'png' => 'image/png',
56 'gif' => 'image/gif',
57 'webp' => 'image/webp',
58 'bmp' => 'image/bmp',
59 'tiff' => 'image/tiff',
60 'tif' => 'image/tiff',
61 'svg' => 'image/svg+xml',
62 'ico' => 'image/x-icon',
63 'pdf' => 'application/pdf',
64 ];
65 $mimeType = isset( $mimeTypes[$extension] ) ? $mimeTypes[$extension] : null;
66 }
67
68 return $mimeType;
69 }
70
71 public function download_image( $url ) {
72 // Handle data URLs (base64-encoded images from Google Gemini, etc.)
73 if ( strpos( $url, 'data:' ) === 0 ) {
74 // Extract base64 data from data URL
75 // Format: data:image/png;base64,iVBORw0KGgoAAAANS...
76 $parts = explode( ',', $url, 2 );
77 if ( count( $parts ) !== 2 ) {
78 throw new Exception( 'Invalid data URL format.' );
79 }
80
81 // Validate it's an image data URL
82 if ( stripos( $parts[0], 'image/' ) === false ) {
83 throw new Exception( 'Data URL is not an image.' );
84 }
85
86 // Decode base64 data
87 $image_data = base64_decode( $parts[1] );
88 if ( $image_data === false ) {
89 throw new Exception( 'Failed to decode base64 image data.' );
90 }
91
92 return $image_data;
93 }
94
95 // Validate URL scheme (only allow http/https)
96 $parsed_url = parse_url( $url );
97 if ( empty( $parsed_url['scheme'] ) || !in_array( $parsed_url['scheme'], [ 'http', 'https' ] ) ) {
98 throw new Exception( 'Invalid URL scheme. Only HTTP and HTTPS are allowed.' );
99 }
100
101 // Disable redirects to prevent bypass attacks
102 $response = wp_safe_remote_get( $url, [
103 'timeout' => 60,
104 'redirection' => 0 // Prevent redirect-based bypass
105 ] );
106
107 if ( is_wp_error( $response ) ) {
108 throw new Exception( $response->get_error_message() );
109 }
110
111 // Validate response is actually an image
112 $content_type = wp_remote_retrieve_header( $response, 'content-type' );
113 if ( empty( $content_type ) || stripos( $content_type, 'image/' ) !== 0 ) {
114 throw new Exception( 'URL did not return an image. Content-Type: ' . $content_type );
115 }
116
117 return wp_remote_retrieve_body( $response );
118 }
119
120 /**
121 * Add an image from a URL to the Media Library.
122 * @param string $url The URL of the image to be downloaded.
123 * @param string $filename The filename of the image, if not set, it will be the basename of the URL.
124 * @param string $title The title of the image.
125 * @param string $description The description of the image.
126 * @param string $caption The caption of the image.
127 * @param string $alt The alt text of the image.
128 * @param array $ai_metadata AI-related metadata (model, latency, env_id).
129 * @return int The attachment ID of the image.
130 */
131 public function add_image_from_url( $url, $filename = null, $title = null, $description = null, $caption = null, $alt = null, $attachedPost = null, $post_status = 'inherit', $post_type = 'attachment', $ai_metadata = [] ) {
132 $path_parts = pathinfo( parse_url( $url, PHP_URL_PATH ) );
133 $url_filename = $path_parts['basename'];
134 $file_type = wp_check_filetype( $url_filename, null );
135 $allowed_types = get_allowed_mime_types();
136
137 // For URLs without file extensions (like Google Gemini), default to PNG
138 $extension = 'png';
139 if ( $file_type && $file_type['ext'] && in_array( $file_type['type'], $allowed_types ) ) {
140 $extension = $file_type['ext'];
141 }
142
143 if ( !empty( $filename ) ) {
144 $custom_file_type = wp_check_filetype( $filename, null );
145 if ( !$custom_file_type || !in_array( $custom_file_type['type'], $allowed_types ) ) {
146 throw new Exception( 'Invalid custom file type.' );
147 }
148 // Use the extension from the custom filename if valid
149 $extension = $custom_file_type['ext'];
150 }
151
152 $image_data = $this->download_image( $url );
153 if ( !$image_data ) {
154 throw new Exception( 'Could not download the image.' );
155 }
156 $upload_dir = wp_upload_dir();
157
158 // Filename handling including 'generated_' prefix scenario
159 if ( empty( $filename ) ) {
160 $filename = sanitize_file_name( $url_filename );
161 if ( empty( $extension ) ) { // This condition might now be redundant
162 $extension = $file_type['ext'];
163 }
164 // Filename length check and prepend if conditions met
165 if ( strlen( $filename ) > 32 || strlen( $filename ) < 4 || strpos( $filename, 'generated_' ) === 0 ) {
166 $filename = uniqid( 'ai_', true ) . '.' . $extension;
167 }
168 if ( strpos( $filename, '.' ) === false ) {
169 $filename .= '.' . $extension;
170 }
171 }
172
173 // Directory and file path handling
174 if ( wp_mkdir_p( $upload_dir['path'] ) ) {
175 $file = $upload_dir['path'] . '/' . $filename;
176 }
177 else {
178 $file = $upload_dir['basedir'] . '/' . $filename;
179 }
180
181 // Ensure file name uniqueness in the directory
182 $i = 1;
183 $parts = pathinfo( $file );
184 while ( file_exists( $file ) ) {
185 $file = $parts['dirname'] . '/' . $parts['filename'] . '-' . $i . '.' . $parts['extension'];
186 $i++;
187 }
188
189 // Write file to filesystem
190 file_put_contents( $file, $image_data );
191
192 // Prepare and insert attachment
193 $wp_filetype = wp_check_filetype( basename( $file ), null );
194 $attachment = [
195 'post_mime_type' => $wp_filetype['type'],
196 'post_title' => !is_null( $title ) ? $title : preg_replace( '/\.[^.]+$/', '', basename( $file ) ),
197 'post_content' => !is_null( $description ) ? $description : '',
198 'post_status' => $post_status,
199 'post_excerpt' => !is_null( $caption ) ? $caption : '',
200 'post_type' => $post_type,
201 ];
202
203 // Use wp_insert_post instead of wp_insert_attachment to allow custom post types
204 $attach_id = wp_insert_post( $attachment );
205
206 // Set the attached file manually since we're not using wp_insert_attachment
207 update_attached_file( $attach_id, $file );
208 require_once( ABSPATH . 'wp-admin/includes/image.php' );
209 $attach_data = wp_generate_attachment_metadata( $attach_id, $file );
210 wp_update_attachment_metadata( $attach_id, $attach_data );
211 if ( !is_null( $alt ) ) {
212 update_post_meta( $attach_id, '_wp_attachment_image_alt', $alt );
213 }
214
215 // Store AI-related metadata
216 if ( !empty( $ai_metadata['model'] ) ) {
217 update_post_meta( $attach_id, 'mwai_model', sanitize_text_field( $ai_metadata['model'] ) );
218 }
219 if ( !empty( $ai_metadata['latency'] ) ) {
220 update_post_meta( $attach_id, 'mwai_latency', floatval( $ai_metadata['latency'] ) );
221 }
222 if ( !empty( $ai_metadata['env_id'] ) ) {
223 update_post_meta( $attach_id, 'mwai_env_id', sanitize_text_field( $ai_metadata['env_id'] ) );
224 }
225
226 return $attach_id;
227 }
228 }
229