PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.3
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.3
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
← All changes | front-end/default-fields/upload/upload_helper_functions.php +345 -55 3.9.8 → 4.0.3 View file →
@@ -1,34 +1,34 @@
1 1 <?php
2 -/* Set up upload field for frontend */
3 -/* overwrite the two functions for when an upload is made from the frontend so they don't check for a logged in user */
4 -if( strpos( wp_get_referer(), 'wp-admin' ) === false && isset( $_REQUEST['action'] ) && 'upload-attachment' == $_REQUEST['action'] ){
5 - if( !function_exists( 'check_ajax_referer' ) ){
6 - function check_ajax_referer( ) {
7 - return true;
8 - }
2 +// Exit if accessed directly
3 +if ( ! defined( 'ABSPATH' ) ) exit;
4 +
5 +/** Simple file input when the field is set to it, or the user cannot upload_files. */
6 +function wppb_use_simple_upload_field( $field ) {
7 + if ( ! empty( $field['simple-upload'] ) && $field['simple-upload'] === 'yes' ) {
8 + return true;
9 9 }
10 10
11 - if( !function_exists( 'auth_redirect' ) ){
12 - function auth_redirect() {
13 - return true;
14 - }
11 + return ! current_user_can( 'upload_files' );
12 +}
13 +
14 +/**
15 + * Whether this request includes a simple-upload for the field.
16 + * Checkout and some payment forms post the hidden attachment ID without $_FILES.
17 + */
18 +function wppb_simple_upload_was_submitted( $field, $request_data ) {
19 + $meta = wppb_handle_meta_name( $field['meta-name'] );
20 + $file_key = 'simple_upload_' . $meta;
21 +
22 + if ( isset( $_FILES[ $file_key ] ) ) {
23 + return true;
15 24 }
16 -}
17 25
18 -/* create a fake user with the "upload_posts" capability and assign him to the global $current_user. this is used to bypass the checks for current_user_can('upload_files') in async-upload.php */
19 -add_action( 'current_screen', 'wppb_create_fake_user_when_uploading_and_not_logged_in' );
20 -if( !function_exists( 'wppb_create_fake_user_when_uploading_and_not_logged_in' ) ) {
21 - function wppb_create_fake_user_when_uploading_and_not_logged_in()
22 - {
23 - if ( isset($_REQUEST['action']) && 'upload-attachment' == $_REQUEST['action'] && isset($_REQUEST['wppb_upload']) && 'true' == $_REQUEST['wppb_upload'] ) {
24 - if (!is_user_logged_in() || !current_user_can('upload_files') || !current_user_can('edit_posts')) {
25 - global $current_user;
26 - $current_user = new WP_User(0, 'frontend_uploader');
27 - $current_user->allcaps = array("upload_files" => true, "edit_posts" => true, "edit_others_posts" => true, "edit_pages" => true, "edit_others_pages" => true);
28 - }
29 - }
26 + if ( isset( $request_data['pay_gate'] ) && in_array( $request_data['pay_gate'], array( 'stripe_connect', 'paypal_connect' ), true ) ) {
27 + return true;
30 28 }
29 +
30 + return array_key_exists( $meta, $request_data );
31 31 }
32 32
33 33 /* for a request of a upload from the frontend and no user is logged in don't query for attachments */
34 34 add_action( 'after_setup_theme', 'wppb_modify_query_attachements_when_not_logged_in' );
@@ -34,9 +34,9 @@
34 34 add_action( 'after_setup_theme', 'wppb_modify_query_attachements_when_not_logged_in' );
35 35 if( !function_exists( 'wppb_modify_query_attachements_when_not_logged_in' ) ) {
36 36 function wppb_modify_query_attachements_when_not_logged_in()
37 37 {
38 - if (strpos(wp_get_referer(), 'wp-admin') === false && !is_user_logged_in()) {
38 + if ( strpos(wp_get_referer(), 'wp-admin') === false && !is_user_logged_in() ) {
39 39 add_action('wp_ajax_query-attachments', 'wppb_wp_ajax_not_loggedin_query_attachments', 0);
40 40 add_action('wp_ajax_nopriv_query-attachments', 'wppb_wp_ajax_not_loggedin_query_attachments', 0);
41 41 function wppb_wp_ajax_not_loggedin_query_attachments()
42 42 {
@@ -50,9 +50,9 @@
50 50 add_filter('wp_handle_upload_prefilter', 'wppb_upload_file_type');
51 51 if( !function_exists( 'wppb_upload_file_type' ) ) {
52 52 function wppb_upload_file_type($file)
53 53 {
54 - if( isset( $_POST['wppb_upload'] ) && $_POST['wppb_upload'] == 'true' ) {
54 + if( isset( $_POST['wppb_upload'] ) && $_POST['wppb_upload'] == 'true' && isset( $_POST['_wpnonce'] ) && wp_verify_nonce( sanitize_text_field( $_POST['_wpnonce'] ), 'media-form' ) ) {
55 55
56 56 // file size limits.
57 57 $size = $file['size'];
58 58 $limit = apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize')));
@@ -67,9 +67,21 @@
67 67 $all_fields = apply_filters( 'wppb_form_fields', get_option('wppb_manage_fields'), array( 'context' => 'upload_helper', 'upload_meta_name' => $meta_name ) );
68 68 if (!empty($all_fields)) {
69 69 foreach ($all_fields as $field) {
70 70 if ($field['meta-name'] == $meta_name) {
71 +
72 + // per-field file size limit
73 + if ( !empty( $field['max-file-size'] ) && is_numeric( $field['max-file-size'] ) && floatval( $field['max-file-size'] ) > 0 ) {
74 + $field_limit = floatval( $field['max-file-size'] ) * 1024 * 1024;
75 + $effective_limit = min( $field_limit, $limit );
76 + if ( $size > $effective_limit ) {
77 + $file['error'] = __( "Files must be smaller than ", "profile-builder" ) . floatval( $field['max-file-size'] ) . 'MB';
78 + return $file;
79 + }
80 + }
81 +
71 82 $allowed_upload_extensions = '';
83 +
72 84 if ($field['field'] == 'Upload' && !empty($field['allowed-upload-extensions']))
73 85 $allowed_upload_extensions = $field['allowed-upload-extensions'];
74 86 if ($field['field'] == 'Avatar' && !empty($field['allowed-image-extensions'])) {
75 87 if (trim($field['allowed-image-extensions']) == '.*')
@@ -93,9 +105,12 @@
93 105 foreach (get_allowed_mime_types() as $key => $value) {
94 106 if (strpos($key, $ext) !== false || $key == $ext)
95 107 return $file;
96 108 }
109 +
97 110 $file['error'] = __("Sorry, you cannot upload this file type for this field.", 'profile-builder');
111 +
112 + break;
98 113 }
99 114 }
100 115 }
101 116 }
@@ -122,8 +137,13 @@
122 137 $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
123 138 if ( !empty( $all_fields ) ) {
124 139 foreach ( $all_fields as $form_field ) {
125 140 if ($form_field[ 'meta-name' ] == $field[ 'meta-name' ] ) {
141 + // apply per-field size limit if set
142 + if ( !empty( $form_field['max-file-size'] ) && is_numeric( $form_field['max-file-size'] ) && floatval( $form_field['max-file-size'] ) > 0 ) {
143 + $field_limit = floatval( $form_field['max-file-size'] ) * 1024 * 1024;
144 + $limit = min( $field_limit, $limit );
145 + }
126 146 $allowed_upload_extensions = '';
127 147 if ( $form_field[ 'field' ] == 'Upload' && !empty( $form_field[ 'allowed-upload-extensions' ] ) ) {
128 148 $allowed_upload_extensions = $form_field[ 'allowed-upload-extensions' ];
129 149 }
@@ -139,23 +159,20 @@
139 159 $allowed_upload_extensions = str_replace( '.', '', array_map( 'trim', explode( ",", strtolower( $allowed_upload_extensions ) ) ) );
140 160 } else {
141 161 $allowed = true;
142 162 }
143 - $allowed_by_wordpress = false;
144 - foreach ( $allowed_mime_types as $key => $val ){
145 - if ( $val == $upload[ 'type' ] ){
146 - $possible_extensions = explode( '|', $key );
147 - $allowed_by_wordpress = true;
148 - }
163 + if ( empty( $upload['tmp_name'] ) || empty( $upload['name'] ) ) {
164 + return false;
149 165 }
150 - if ( isset( $possible_extensions ) && $allowed_by_wordpress == true ){
166 +
167 + $checked = wp_check_filetype_and_ext( $upload['tmp_name'], $upload['name'] );
168 + $detected_type = ! empty( $checked['type'] ) ? $checked['type'] : '';
169 + $detected_ext = ! empty( $checked['ext'] ) ? strtolower( $checked['ext'] ) : '';
170 + $allowed_by_wordpress = ( $detected_type !== '' && in_array( $detected_type, $allowed_mime_types, true ) );
171 +
172 + if ( $allowed_by_wordpress && $detected_ext !== '' ) {
151 173 if ( !isset( $allowed ) ){
152 - $allowed = false;
153 - foreach ( $allowed_upload_extensions as $extension ){
154 - if ( in_array( $extension, $possible_extensions ) ){
155 - $allowed = true;
156 - }
157 - }
174 + $allowed = in_array( $detected_ext, $allowed_upload_extensions, true );
158 175 }
159 176 if ( $upload[ 'size' ] > $limit ){
160 177 $allowed = false;
161 178 }
@@ -218,9 +235,9 @@
218 235 }
219 236 else{
220 237 $repeater_count = count( $repeater_group );
221 238 for ( $i = 0; $i < $repeater_count; $i++ ){
222 - if ( $repeater_group[ $i ][ 'field' ] == 'Upload' && isset( $repeater_group[ $i ][ 'simple-upload' ] ) && $repeater_group[ $i ][ 'simple-upload' ] == 'yes' && isset( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] ) ){
239 + if ( $repeater_group[ $i ][ 'field' ] == 'Upload' && wppb_use_simple_upload_field( $repeater_group[ $i ] ) && isset( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] ) ){
223 240 $groups = absint( $_REQUEST[ $form_field[ 'meta-name' ] . '_extra_groups_count' ] );
224 241 for ( $j = 0; $j <= $groups; $j++ ){
225 242 $name = $repeater_group[ $i ][ 'meta-name' ];
226 243 if ( $j != 0 ){
@@ -238,14 +255,19 @@
238 255 }
239 256 return false;
240 257 }
241 258
242 -function wppb_make_upload_button( $field, $input_value, $extra_attr = '' ){
243 - // change the upload limit. This is not functional.
244 - // just for display in the upload window. see upload_helper_functions.php for the actual restriction.
245 - add_filter('upload_size_limit', function($limit, $u, $p){
246 - return apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize')));
247 - }, 10, 3);
259 +function wppb_default_fields_make_upload_button( $field, $input_value, $extra_attr = '' ){
260 + // change the upload limit displayed in the upload window (per-field aware)
261 + $per_field_max = $field;
262 + add_filter('upload_size_limit', function($wp_limit) use ($per_field_max) {
263 + $server_limit = apply_filters('wppb_server_max_upload_size_byte_constant', wppb_return_bytes(ini_get('upload_max_filesize')));
264 + if ( !empty( $per_field_max['max-file-size'] ) && is_numeric( $per_field_max['max-file-size'] ) && floatval( $per_field_max['max-file-size'] ) > 0 ) {
265 + $field_limit = floatval( $per_field_max['max-file-size'] ) * 1024 * 1024;
266 + return min( $field_limit, $server_limit );
267 + }
268 + return $server_limit;
269 + }, 10, 1);
248 270
249 271 $upload_button = '';
250 272 $upload_input_id = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'] ) );
251 273
@@ -258,16 +280,16 @@
258 280 $thumbnail = wp_get_attachment_image($value, array(80, 80), true);
259 281 $file_name = get_the_title($value);
260 282 $file_type = get_post_mime_type($value);
261 283 $attachment_url = wp_get_attachment_url($value);
262 - $upload_button .= '<div id="' . esc_attr($upload_input_id) . '_info_container" class="upload-field-details" data-attachment_id="' . $value . '">';
284 + $upload_button .= '<div id="' . esc_attr($upload_input_id) . '_info_container" class="upload-field-details" data-attachment_id="' . esc_attr( $value ) . '">';
263 285 $upload_button .= '<div class="file-thumb">';
264 - $upload_button .= "<a href='{$attachment_url}' target='_blank' class='wppb-attachment-link'>" . $thumbnail . "</a>";
286 + $upload_button .= "<a href='" . esc_url( $attachment_url ) . "' target='_blank' class='wppb-attachment-link'>" . $thumbnail . "</a>";
265 287 $upload_button .= '</div>';
266 288 $upload_button .= '<p><span class="file-name">';
267 - $upload_button .= $file_name;
289 + $upload_button .= esc_html( $file_name );
268 290 $upload_button .= '</span><span class="file-type">';
269 - $upload_button .= $file_type;
291 + $upload_button .= esc_html( $file_type );
270 292 $upload_button .= '</span>';
271 293 $upload_button .= '<span class="wppb-remove-upload" tabindex="0">' . apply_filters( 'wppb_upload_button_remove_label', __( 'Remove', 'profile-builder' ) ) . '</span>';
272 294 $upload_button .= '</p></div>';
273 295 }
@@ -277,11 +299,11 @@
277 299 else{
278 300 $hide_upload_button = '';
279 301 }
280 302
281 - if ( isset( $field[ 'simple-upload' ] ) && $field[ 'simple-upload' ] == 'yes' ){
303 + if ( wppb_use_simple_upload_field( $field ) ){
282 304 //If selected accordingly in form fields, generate a simple upload button
283 - $upload_button .= '<input type="file" id="upload_' . esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) . '_button" name="simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'"';
305 + $upload_button .= '<input type="file" id="upload_' . esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) . '_button" class="wppb_simple_upload" data-field_type="'. esc_attr( $field['field'] ) .'" name="simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'"';
284 306 $upload_button .= $hide_upload_button . '>';
285 307 $upload_button .= '<p id="p_simple_upload_'. esc_attr(Wordpress_Creation_Kit_PB::wck_generate_slug($field['meta-name'], $field)) .'"></p>';
286 308 $limit = apply_filters( 'wppb_server_max_upload_size_byte_constant', wppb_return_bytes( ini_get( 'upload_max_filesize' ) ) );
287 309 $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
@@ -287,8 +309,13 @@
287 309 $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $field[ 'meta-name' ] ) );
288 310 if ( !empty( $all_fields ) ) {
289 311 foreach ( $all_fields as $form_field ) {
290 312 if ($form_field[ 'meta-name' ] == $field[ 'meta-name' ] ) {
313 + // apply per-field size limit if set
314 + if ( !empty( $form_field['max-file-size'] ) && is_numeric( $form_field['max-file-size'] ) && floatval( $form_field['max-file-size'] ) > 0 ) {
315 + $field_limit = floatval( $form_field['max-file-size'] ) * 1024 * 1024;
316 + $limit = min( $field_limit, $limit );
317 + }
291 318 $allowed_upload_extensions = '';
292 319 if ( $form_field[ 'field' ] == 'Upload' && !empty( $form_field[ 'allowed-upload-extensions' ] ) ) {
293 320 $allowed_upload_extensions = $form_field[ 'allowed-upload-extensions' ];
294 321 }
@@ -308,9 +335,10 @@
308 335 $allowed_extensions = '';
309 336 }
310 337 }
311 338 }
312 - $upload_button .= '<input id="allowed_extensions_simple_upload_'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="allowed_extensions_simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. $allowed_extensions .'"/>';
339 + $upload_button .= '<input id="allowed_extensions_simple_upload_'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="allowed_extensions_simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. esc_attr( $allowed_extensions ) .'"/>';
340 + $upload_button .= '<input id="size_limit_simple_upload_'. esc_attr( $upload_input_id ) .'" type="hidden" name="size_limit_simple_upload_'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. esc_attr( $limit ) .'"/>';
313 341 $allowed_mime_types = get_allowed_mime_types();
314 342 $allowed_types = '';
315 343 if ( !empty( $allowed_mime_types ) ) {
316 344 foreach ($allowed_mime_types as $key => $val){
@@ -341,9 +369,9 @@
341 369
342 370 $upload_button .= '>' . apply_filters( 'wppb_upload_button_select_label', __( 'Upload ', 'profile-builder' ) ) . '</a>';
343 371 }
344 372
345 - $upload_button .= '<input id="'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. $input_value .'"/>';
373 + $upload_button .= '<input id="'. esc_attr( $upload_input_id ) .'" type="hidden" size="36" name="'. esc_attr( Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) ) .'" value="'. esc_attr( wp_unslash( $input_value ) ) .'"/>';
346 374 return $upload_button;
347 375 }
348 376
349 377 /**
@@ -350,9 +378,9 @@
350 378 * Function to save an attachment from the simple upload field
351 379 * @param $field_name
352 380 * @return string|WP_Error
353 381 */
354 -function wppb_save_simple_upload_file ( $field_name ){
382 +function wppb_default_fields_save_simple_upload_file( $field_name ) {
355 383 require_once(ABSPATH . 'wp-admin/includes/file.php');
356 384 $upload_overrides = array('test_form' => false);
357 385
358 386 if( isset( $_FILES[$field_name] ) )
@@ -377,5 +405,267 @@
377 405 return trim($attachment_id);
378 406 } else {
379 407 return '';
380 408 }
409 +}
410 +
411 +/**
412 + * Converts a legacy file URL stored in user meta (versions that predate attachment IDs)
413 + * into an attachment owned by the user and stores the new ID in its place.
414 + *
415 + * The URL must resolve to an existing file inside the uploads directory with an allowed
416 + * mime type; anything else is discarded. Only call this with a value read from user meta,
417 + * never with request data, so that rendering a field cannot persist attacker-controlled input.
418 + *
419 + * @param string $file_url Legacy file URL read from user meta.
420 + * @param array $field Field definition array (must contain 'meta-name').
421 + * @param int $user_id User the attachment and meta belong to.
422 + *
423 + * @return int|string Attachment ID, or '' when the URL could not be converted.
424 + */
425 +function wppb_legacy_file_url_to_attachment( $file_url, $field, $user_id ) {
426 + $wp_upload_dir = wp_upload_dir();
427 + $base_dir = realpath( $wp_upload_dir['basedir'] );
428 + $file_path = str_replace( $wp_upload_dir['baseurl'], $wp_upload_dir['basedir'], $file_url );
429 + $file_path = is_file( $file_path ) ? realpath( $file_path ) : false;
430 +
431 + if ( ! $base_dir || ! $file_path ) {
432 + return '';
433 + }
434 +
435 + $base_dir = trailingslashit( wp_normalize_path( $base_dir ) );
436 + $file_path = wp_normalize_path( $file_path );
437 +
438 + if ( strpos( $file_path, $base_dir ) !== 0 ) {
439 + return '';
440 + }
441 +
442 + $file_type = wp_check_filetype( basename( $file_path ), null );
443 + if ( empty( $file_type['type'] ) ) {
444 + return '';
445 + }
446 +
447 + $attachment_id = wp_insert_attachment( array(
448 + 'guid' => trailingslashit( $wp_upload_dir['baseurl'] ) . substr( $file_path, strlen( $base_dir ) ),
449 + 'post_mime_type' => $file_type['type'],
450 + 'post_title' => sanitize_text_field( preg_replace( '/\.[^.]+$/', '', basename( $file_path ) ) ),
451 + 'post_content' => '',
452 + 'post_status' => 'inherit',
453 + 'post_author' => $user_id,
454 + ), $file_path );
455 +
456 + if ( empty( $attachment_id ) || is_wp_error( $attachment_id ) ) {
457 + return '';
458 + }
459 +
460 + // Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
461 + require_once ABSPATH . 'wp-admin/includes/image.php';
462 + wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $file_path ) );
463 + update_user_meta( $user_id, $field['meta-name'], $attachment_id );
464 +
465 + return $attachment_id;
466 +}
467 +
468 +// Deferred to plugins_loaded so older Profile Builder Pro versions (which declare
469 +// wppb_verify_attachment_id unconditionally during their own file load) win the
470 +// declaration race and our function_exists guard then skips — avoiding a fatal.
471 +add_action( 'plugins_loaded', 'wppb_register_attachment_ownership_helpers', 20 );
472 +function wppb_register_attachment_ownership_helpers() {
473 +
474 + /**
475 + * Verifies if an attachment either doesn't exist or already belongs to the user.
476 + * Used for IDOR protection on both Upload and Avatar fields.
477 + *
478 + * @param string|int $attachment_id The attachment post ID to verify.
479 + * @param int|null $user_id The user ID to check ownership against.
480 + *
481 + * @return bool True if the attachment is valid for this user, false otherwise.
482 + */
483 + if ( !function_exists( 'wppb_verify_attachment_id' ) ) {
484 + function wppb_verify_attachment_id( $attachment_id, $user_id = null ) {
485 + if ( $attachment_id !== '' && is_numeric( $attachment_id ) ) {
486 + $attachment = get_post( absint( trim( $attachment_id ) ) );
487 + if ( $attachment && $attachment->post_type === 'attachment' ) {
488 +
489 + // Get current user info for admin bypass checks
490 + $current_user_id = get_current_user_id();
491 + $current_user = $current_user_id ? get_userdata( $current_user_id ) : null;
492 + $is_admin = $current_user && current_user_can( 'manage_options' );
493 +
494 + if ( $user_id ) {
495 + // Allow admins to upload files for users
496 + if ( $is_admin ) {
497 + return true;
498 + }
499 + // The attachment is claimable when it already belongs to the target
500 + // user, or to the user performing the request. An author-less
501 + // attachment (post_author == 0) is only claimable by an
502 + // unauthenticated request (e.g. a visitor registering, whose upload
503 + // has no author yet). This prevents an authenticated user from
504 + // claiming (IDOR) an author-0 attachment created by someone else's
505 + // anonymous/nopriv upload.
506 + if ( $attachment->post_author == $user_id
507 + || ( $current_user_id && $attachment->post_author == $current_user_id )
508 + || ( 0 === (int) $current_user_id && 0 === (int) $attachment->post_author ) ) {
509 + return true;
510 + }
511 + } else {
512 + // If no user ID is provided, check if current user is admin
513 + if ( $is_admin ) {
514 + return true;
515 + }
516 + // Without an explicit target user, an authenticated user may only
517 + // reference an attachment they already own; an author-less
518 + // attachment is only claimable by an unauthenticated request.
519 + if ( ( $current_user_id && $attachment->post_author == $current_user_id )
520 + || ( 0 === (int) $current_user_id && 0 === (int) $attachment->post_author ) ) {
521 + return true;
522 + }
523 + }
524 + }
525 + }
526 + return false;
527 + }
528 + }
529 +
530 + /**
531 + * Validates attachment ownership and updates the user meta and post author.
532 + * Used for IDOR-safe saving on both Upload and Avatar fields.
533 + *
534 + * @param string|int $attachment_id The attachment post ID.
535 + * @param array $field The field definition array (must contain 'meta-name').
536 + * @param int $user_id The user ID to save for.
537 + */
538 + if ( !function_exists( 'wppb_save_attachment_id' ) ) {
539 + function wppb_save_attachment_id( $attachment_id, $field, $user_id ) {
540 + // Verify that the attachment either doesn't exist or already belongs to the user
541 + if ( wppb_verify_attachment_id( $attachment_id, $user_id ) ) {
542 + update_user_meta( $user_id, $field['meta-name'], absint( $attachment_id ) );
543 + wp_update_post( array(
544 + 'ID' => absint( trim( $attachment_id ) ),
545 + 'post_author' => $user_id
546 + ) );
547 + } else {
548 + update_user_meta( $user_id, $field['meta-name'], '' );
549 + }
550 + }
551 + }
552 +}
553 +
554 +/**
555 + * Resolves a simple-upload AJAX `name` parameter to a configured form field.
556 + *
557 + * @param string $post_name Sanitized value of $_POST['name'] from the AJAX request.
558 + * @param string|array $field_type Expected field type(s), e.g. 'Avatar' or 'Upload'.
559 + *
560 + * @return array|false Field definition array, or false when not found or not simple-upload.
561 + */
562 +function wppb_resolve_simple_upload_ajax_field( $post_name, $field_type ) {
563 + if ( empty( $post_name ) ) {
564 + return false;
565 + }
566 +
567 + $field_types = is_array( $field_type ) ? $field_type : array( $field_type );
568 + $all_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'simple_upload_ajax', 'upload_post_name' => $post_name ) );
569 +
570 + if ( empty( $all_fields ) ) {
571 + return false;
572 + }
573 +
574 + foreach ( $all_fields as $field ) {
575 + if ( ! in_array( $field['field'], $field_types, true ) ) {
576 + continue;
577 + }
578 + if ( ! wppb_use_simple_upload_field( $field ) ) {
579 + continue;
580 + }
581 +
582 + $field_slug = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $field['meta-name'], $field ) );
583 + if ( $field_slug === $post_name ) {
584 + return $field;
585 + }
586 + }
587 +
588 + // The field was not found among the top-level form fields. Repeater fields store
589 + // their inner Upload fields in a separate option keyed by the repeater's
590 + // meta-name, so those fields are never part of the wppb_manage_fields list scanned
591 + // above. Scan the repeater groups as well, otherwise Simple Upload inside a
592 + // Repeater field is silently rejected (the lookup fails and the file input clears).
593 + return wppb_resolve_simple_upload_ajax_field_in_repeater( $post_name, $field_types, $all_fields );
594 +}
595 +
596 +/**
597 + * Resolves a simple-upload AJAX `name` parameter to an Upload field nested inside a
598 + * Repeater field.
599 + *
600 + * Repeater sub-fields are stored unindexed in an option keyed by the repeater's
601 + * meta-name. On the front-end each group posts either "<slug>" (the first group) or
602 + * "<slug>_N" (the Nth extra group), where <slug> is the dash-normalized wck slug of
603 + * the inner field's meta-name.
604 + *
605 + * @param string $post_name Sanitized value of $_POST['name'] from the AJAX request.
606 + * @param array $field_types Expected field type(s), e.g. array( 'Upload' ).
607 + * @param array $all_fields The already-resolved top-level form fields.
608 + *
609 + * @return array|false Inner field definition array, or false when not found.
610 + */
611 +function wppb_resolve_simple_upload_ajax_field_in_repeater( $post_name, $field_types, $all_fields ) {
612 + foreach ( $all_fields as $form_field ) {
613 + if ( empty( $form_field['field'] ) || $form_field['field'] !== 'Repeater' ) {
614 + continue;
615 + }
616 +
617 + $repeater_group = get_option( $form_field['meta-name'], 'not_set' );
618 + if ( $repeater_group === 'not_set' || ! is_array( $repeater_group ) ) {
619 + continue;
620 + }
621 +
622 + foreach ( $repeater_group as $inner_field ) {
623 + if ( empty( $inner_field['field'] ) || ! in_array( $inner_field['field'], $field_types, true ) ) {
624 + continue;
625 + }
626 + if ( ! wppb_use_simple_upload_field( $inner_field ) ) {
627 + continue;
628 + }
629 +
630 + $base_slug = str_replace( '-', '_', Wordpress_Creation_Kit_PB::wck_generate_slug( $inner_field['meta-name'], $inner_field ) );
631 + if ( $base_slug === $post_name || preg_match( '/^' . preg_quote( $base_slug, '/' ) . '_[0-9]+$/', $post_name ) ) {
632 + return $inner_field;
633 + }
634 + }
635 + }
636 +
637 + return false;
638 +}
639 +
640 +function wppb_check_that_field_is_defined( $meta_name, $field_types = array() ){
641 +
642 + if( empty( $meta_name ) )
643 + return false;
644 +
645 + $defined_fields = apply_filters( 'wppb_form_fields', get_option( 'wppb_manage_fields' ), array( 'context' => 'upload_helper', 'upload_meta_name' => $meta_name ) );
646 +
647 + if( empty( $defined_fields ) )
648 + return false;
649 + else {
650 +
651 + if( empty( $field_types ) ){
652 + foreach( $defined_fields as $field ){
653 +
654 + if( $field['meta-name'] == $meta_name )
655 + return true;
656 +
657 + }
658 + } else {
659 + foreach( $defined_fields as $field ){
660 +
661 + if( in_array( $field['field'], $field_types ) && $field['meta-name'] == $meta_name )
662 + return true;
663 +
664 + }
665 + }
666 +
667 + }
668 +
669 + return false;
670 +
381 671 }