PluginProbe
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent / 3.5.4
SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent v3.5.4
3.5.4 3.5.3 3.5.2 3.5.1 3.4.9 3.5.0 3.4.8 3.4.7 trunk 2.3.1 3.3.6 3.3.7 3.3.8 3.3.9 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6
supportcandy / includes / rest-api / class-wpsc-rest-attachment.php

class-wpsc-rest-attachment.php in SupportCandy – AI Customer Support Ticket System & Live Chatbot Agent 3.5.4, at includes/rest-api/class-wpsc-rest-attachment.php

326 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 exit; // Exit if accessed directly!
4 }
5
6 if ( ! class_exists( 'WPSC_REST_Attachment' ) ) :
7
8 final class WPSC_REST_Attachment {
9
10 /**
11 * Initialize this class
12 *
13 * @return void
14 */
15 public static function init() {
16
17 add_action( 'init', array( __CLASS__, 'check_download_file' ), 99 );
18 add_action( 'wpsc_rest_register_routes', array( __CLASS__, 'register_routes' ) );
19 }
20
21 /**
22 * Register routes
23 *
24 * @return void
25 */
26 public static function register_routes() {
27
28 // list statuses.
29 register_rest_route(
30 'supportcandy/v2',
31 '/attachments',
32 array(
33 'methods' => 'POST',
34 'callback' => array( __CLASS__, 'new_attachment' ),
35 'permission_callback' => 'is_user_logged_in',
36 ),
37 );
38
39 // list individual status.
40 register_rest_route(
41 'supportcandy/v2',
42 '/attachments/(?P<id>\d+)',
43 array(
44 'methods' => 'GET',
45 'callback' => array( __CLASS__, 'get_individual_attachment' ),
46 'args' => array(
47 'id' => array(
48 'validate_callback' => array( __CLASS__, 'validate_id' ),
49 ),
50 ),
51 'permission_callback' => 'is_user_logged_in',
52 ),
53 );
54 }
55
56 /**
57 * Create new attachment
58 *
59 * @param WP_REST_Request $request - request object.
60 * @return WP_Error|WP_REST_Response
61 */
62 public static function new_attachment( $request ) {
63
64 $file_parameters = $request->get_file_params();
65 if ( ! isset( $file_parameters['file'] ) ) {
66 return new WP_Error( 'rest_missing_callback_param', 'Missing parameter(s): file', array( 'status' => 400 ) );
67 }
68
69 $file = $file_parameters['file'];
70 $file_settings = get_option( 'wpsc-gs-file-attachments' );
71
72 $original_name = sanitize_file_name( $file['name'] );
73 $filename = time() . '_' . $original_name;
74 $extension = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) );
75 $today = new DateTime();
76
77 // Allowed extensions.
78 $allowed_file_extensions = explode( ',', $file_settings['allowed-file-extensions'] );
79 $allowed_file_extensions = array_map( 'trim', $allowed_file_extensions );
80 $allowed_file_extensions = array_map( 'strtolower', $allowed_file_extensions );
81
82 if ( ! in_array( $extension, $allowed_file_extensions, true ) ) {
83 wp_send_json_error( 'File extension not allowed!', 400 );
84 }
85
86 // Allowed file size.
87 $allowed_file_size = intval( $file_settings['attachments-max-filesize'] ) * 1000000;
88 if ( ! isset( $file['size'] ) || $file['size'] > $allowed_file_size ) {
89 wp_send_json_error( 'File size exceeds allowed limit!', 400 );
90 }
91
92 // Ensure wp_handle_upload is available.
93 if ( ! function_exists( 'wp_handle_upload' ) ) {
94 require_once ABSPATH . 'wp-admin/includes/file.php';
95 }
96
97 // Validate MIME using SAME custom mime rules.
98 add_filter( 'upload_mimes', array( 'WPSC_Attachment', 'wpsc_custom_upload_mimes' ) );
99 $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'] );
100 remove_filter( 'upload_mimes', array( 'WPSC_Attachment', 'wpsc_custom_upload_mimes' ) );
101
102 if ( empty( $wp_filetype['ext'] ) || empty( $wp_filetype['type'] ) ) {
103 wp_send_json_error( 'Invalid file type!', 400 );
104 }
105
106 // Strict extension match check.
107 if ( strtolower( $wp_filetype['ext'] ) !== $extension ) {
108 wp_send_json_error( 'File extension mismatch detected!', 400 );
109 }
110
111 if ( ! in_array( strtolower( $wp_filetype['ext'] ), $allowed_file_extensions, true ) ) {
112 wp_send_json_error( 'File type does not match allowed extension!', 400 );
113 }
114
115 $data = array(
116 'name' => $original_name,
117 'date_created' => $today->format( 'Y-m-d H:i:s' ),
118 );
119
120 // List of safe image extensions.
121 $safe_images = array( 'png', 'jpeg', 'jpg', 'bmp', 'gif', 'webp' );
122
123 if ( in_array( $extension, $safe_images, true ) && ! wp_get_image_mime( $file['tmp_name'] ) ) {
124 wp_send_json_error( 'Invalid image file!', 400 );
125 }
126
127 // PDF validation.
128 if ( 'pdf' === $extension && 'application/pdf' !== $wp_filetype['type'] ) {
129 wp_send_json_error( 'Invalid PDF file!', 400 );
130 }
131
132 if ( in_array( $extension, $safe_images, true ) || 'pdf' === $extension ) {
133 $data['is_image'] = 1;
134 } else {
135 $data['is_image'] = 0;
136 }
137
138 $ext = pathinfo( $filename, PATHINFO_EXTENSION );
139 $file_name = substr( $filename, 0, -( strlen( $ext ) + 1 ) );
140 $filename = $file_name . '.' . strtolower( $ext );
141
142 $filepath_short = '/wpsc/' . $today->format( 'Y' ) . '/' . $today->format( 'm' ) . '/' . $filename;
143 $data['file_path'] = $filepath_short;
144
145 $upload_overrides = array(
146 'test_form' => false,
147 );
148
149 $file['name'] = $filename;
150
151 add_filter( 'upload_dir', array( 'WPSC_Attachment', 'wpsc_upload_dir' ) );
152 add_filter( 'upload_mimes', array( 'WPSC_Attachment', 'wpsc_custom_upload_mimes' ) );
153
154 $uploaded_file = wp_handle_upload( $file, $upload_overrides );
155
156 remove_filter( 'upload_dir', array( 'WPSC_Attachment', 'wpsc_upload_dir' ) );
157 remove_filter( 'upload_mimes', array( 'WPSC_Attachment', 'wpsc_custom_upload_mimes' ) );
158
159 if ( $uploaded_file && empty( $uploaded_file['error'] ) ) {
160
161 // Normalize path for cross-platform compatibility.
162 $normalized_path = wp_normalize_path( $uploaded_file['file'] );
163
164 $pos = strpos( $normalized_path, '/wpsc/' );
165 if ( $pos === false ) {
166 wp_send_json_error( 'Something went wrong!', 500 );
167 }
168
169 $new_path = substr( $normalized_path, $pos );
170
171 if ( $new_path[0] !== '/' ) {
172 $new_path = '/' . $new_path;
173 }
174
175 $data['file_path'] = $new_path;
176
177 $attachment = WPSC_Attachment::insert( $data );
178 if ( ! $attachment->id ) {
179 wp_send_json_error( 'Something went wrong!', 500 );
180 }
181
182 // Record who uploaded it so that only they can later bind it
183 // to a ticket.
184 WPSC_Attachment::set_uploader( $attachment );
185
186 return new WP_REST_Response(
187 array(
188 'id' => intval( $attachment->id ),
189 'name' => $attachment->name,
190 ),
191 200
192 );
193 }
194
195 $error_message = isset( $uploaded_file['error'] ) ? $uploaded_file['error'] : 'Something went wrong!';
196 wp_send_json_error( $error_message, 500 );
197 }
198
199 /**
200 * Single attachment
201 *
202 * @param WP_REST_Request $request - request object.
203 * @return WP_Error|WP_REST_Response
204 */
205 public static function get_individual_attachment( $request ) {
206
207 $current_user = WPSC_Current_User::$current_user;
208 $attachment = new WPSC_Attachment( $request->get_param( 'id' ) );
209 $url = home_url( '/' ) . '?wpsc_attachment=' . $attachment->id . '&user=' . $current_user->user->ID . '&auth_code=' . $current_user->get_attachment_auth();
210 $data = array(
211 'id' => intval( $attachment->id ),
212 'name' => $attachment->name,
213 'url' => $url,
214 );
215 return new WP_REST_Response( $data, 200 );
216 }
217
218 /**
219 * Validate id
220 *
221 * @param string $param - parameter value.
222 * @param WP_REST_Request $request - request object.
223 * @param string $key - filter key.
224 * @return boolean
225 */
226 public static function validate_id( $param, $request, $key ) {
227
228 $current_user = WPSC_Current_User::$current_user;
229 $attachment = new WPSC_Attachment( $param );
230 if ( ! $attachment->id || ! $attachment->is_active ) {
231 return new WP_Error( 'invalid_id', 'Invalid attachment id', array( 'status' => 400 ) );
232 }
233
234 switch ( $attachment->source ) {
235
236 case 'cf':
237 if ( in_array( $cf->field, array( 'ticket', 'agentonly' ) ) ) { // ticket field.
238
239 $ticket = new WPSC_Ticket( $attachment->ticket_id );
240 if ( ! $ticket->id ) {
241 return new WP_Error( 'invalid_id', 'Invalid attachment id', array( 'status' => 400 ) );
242 }
243
244 WPSC_Individual_Ticket::$ticket = $ticket;
245 if ( ! (
246 ( $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'view' ) ) ||
247 WPSC_Individual_Ticket::is_customer()
248 ) ) {
249 return new WP_Error( 'unauthorized', 'You are not authorized to access this attachment!', array( 'status' => 401 ) );
250 }
251 } else { // customer field.
252
253 $customer = new WPSC_Customer( intval( $attachment->customer_id ) );
254 $ticket_widgets = get_option( 'wpsc-ticket-widget', array() );
255 $raised_by = $ticket_widgets['raised-by'];
256
257 if ( ! (
258 $current_user->customer->id == $customer->id ||
259 (
260 $current_user->is_agent &&
261 in_array( $current_user->agent->role, $raised_by['allowed-agent-roles'] )
262 ) )
263 ) {
264 return new WP_Error( 'unauthorized', 'You are not authorized to access this attachment!', array( 'status' => 401 ) );
265 }
266 }
267 break;
268
269 case 'reply':
270 case 'report':
271 $ticket = new WPSC_Ticket( $attachment->ticket_id );
272 if ( ! $ticket->id ) {
273 return new WP_Error( 'invalid_id', 'Invalid attachment id', array( 'status' => 400 ) );
274 }
275
276 WPSC_Individual_Ticket::$ticket = $ticket;
277 if ( ! (
278 ( $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'view' ) ) ||
279 WPSC_Individual_Ticket::is_customer()
280 ) ) {
281 return new WP_Error( 'unauthorized', 'You are not authorized to access this attachment!', array( 'status' => 401 ) );
282 }
283 break;
284
285 case 'note':
286 $ticket = new WPSC_Ticket( $attachment->ticket_id );
287 if ( ! $ticket->id ) {
288 return new WP_Error( 'invalid_id', 'Invalid attachment id', array( 'status' => 400 ) );
289 }
290
291 WPSC_Individual_Ticket::$ticket = $ticket;
292 if ( ! (
293 $current_user->is_agent && WPSC_Individual_Ticket::has_ticket_cap( 'pn' )
294 ) ) {
295 return new WP_Error( 'unauthorized', 'You are not authorized to access this attachment!', array( 'status' => 401 ) );
296 }
297 break;
298 }
299
300 return true;
301 }
302
303 /**
304 * Check for rest attachment
305 *
306 * @return void
307 */
308 public static function check_download_file() {
309
310 // phpcs:disable
311 if ( isset( $_REQUEST['wpsc_attachment'] ) && isset( $_REQUEST['user'] ) && isset( $_REQUEST['auth_code'] ) ) {
312 $user = get_user_by( 'id', intval( $_REQUEST['user'] ) );
313 if ( $user ) {
314 $current_user = WPSC_Current_User::change_current_user( $user->user_email );
315 if ( $_REQUEST['auth_code'] != $current_user->get_attachment_auth() ) {
316 wp_send_json_error( __( 'Unauthorized access!', 'supportcandy' ), 401 );
317 }
318 }
319 }
320 // phpcs:enable
321 }
322 }
323 endif;
324
325 WPSC_REST_Attachment::init();
326