| @@ -219,8 +219,16 @@ | ||
| 219 | 219 | if ( strpos( $_image_url, $content_url ) !== 0 ) { |
| 220 | 220 | return new WP_Error( 'invalid_image', __( 'Invalid image url.', 'userswp' ) ); |
| 221 | 221 | } |
| 222 | 222 | |
| 223 | + // Only allow cropping the image the current user just uploaded (normalized like $image_url). | |
| 224 | + $pending_key = '_uwp_pending_' . $type . '_upload'; | |
| 225 | + $pending_url = get_user_meta( get_current_user_id(), $pending_key, true ); | |
| 226 | + $pending_url = $pending_url ? str_replace( array( 'https://', 'http://' ), '', $this->normalize_url( esc_url( $pending_url ) ) ) : ''; | |
| 227 | + if ( empty( $pending_url ) || $pending_url !== $_image_url ) { | |
| 228 | + return new WP_Error( 'crop_session_expired', __( 'Your image upload could not be verified. Please upload the image again.', 'userswp' ) ); | |
| 229 | + } | |
| 230 | + | |
| 223 | 231 | $filetype = wp_check_filetype( $image_url ); |
| 224 | 232 | |
| 225 | 233 | if ( empty( $filetype['ext'] ) ) { |
| 226 | 234 | return new WP_Error( 'invalid_image', __( 'Invalid image type.', 'userswp' ) ); |
| @@ -281,8 +289,15 @@ | ||
| 281 | 289 | wp_die( esc_html__( 'Something went wrong. Please contact site admin.', 'userswp' ), 403 ); |
| 282 | 290 | } |
| 283 | 291 | |
| 284 | 292 | $cropped = uwp_resizeThumbnailImage( $thumb_image_location, $image_path, $x, $y, $w, $h, $scale ); |
| 293 | + | |
| 294 | + // Resize returns a path even on failure; bail before touching meta or files so the crop can be retried. | |
| 295 | + clearstatcache( true, $thumb_image_location ); | |
| 296 | + if ( ! is_file( $thumb_image_location ) ) { | |
| 297 | + return new WP_Error( 'crop_failed', __( 'Could not crop the image. Please try again.', 'userswp' ) ); | |
| 298 | + } | |
| 299 | + | |
| 285 | 300 | $cropped = str_replace( $upload_path, $upload_url, $cropped ); |
| 286 | 301 | |
| 287 | 302 | // Remove previous avatar/banner |
| 288 | 303 | $unlink_img = ''; |
| @@ -297,13 +312,34 @@ | ||
| 297 | 312 | } else { |
| 298 | 313 | uwp_update_usermeta( $user_id, 'banner_thumb', $cropped ); |
| 299 | 314 | } |
| 300 | 315 | |
| 301 | - if ( $unlink_img && $unlink_img != $thumb_image_location && is_file( $unlink_img ) && file_exists( $unlink_img ) ) { | |
| 302 | - @unlink( $unlink_img ); | |
| 303 | - $unlink_ori_img = str_replace( '_uwp_' . $type . '_thumb' . '.', '.', $unlink_img ); | |
| 304 | - if ( is_file( $unlink_ori_img ) && file_exists( $unlink_ori_img ) ) { | |
| 305 | - @unlink( $unlink_ori_img ); | |
| 316 | + $original_key = '_uwp_' . $type . '_original'; | |
| 317 | + $prev_original = get_user_meta( $user_id, $original_key, true ); | |
| 318 | + | |
| 319 | + delete_user_meta( get_current_user_id(), $pending_key ); | |
| 320 | + $relative_original = ltrim( wp_normalize_path( str_replace( wp_normalize_path( untrailingslashit( $upload_path ) ), '', wp_normalize_path( $image_path ) ) ), '/' ); | |
| 321 | + update_user_meta( $user_id, $original_key, $relative_original ); | |
| 322 | + | |
| 323 | + // Enforce containment inside uploads before deleting, matching upload_file_remove(). | |
| 324 | + $real_upload_path = realpath( $upload_path ); | |
| 325 | + $real_unlink_img = $unlink_img ? realpath( $unlink_img ) : false; | |
| 326 | + | |
| 327 | + if ( $real_upload_path && $real_unlink_img && realpath( $thumb_image_location ) !== $real_unlink_img | |
| 328 | + && false !== strpos( basename( $real_unlink_img ), $thumb_postfix . '.' ) | |
| 329 | + && 0 === strpos( $real_unlink_img, $real_upload_path . DIRECTORY_SEPARATOR ) | |
| 330 | + && is_file( $real_unlink_img ) ) { | |
| 331 | + wp_delete_file( $real_unlink_img ); | |
| 332 | + | |
| 333 | + // Delete the previous source only if it is the exact file this user cropped. | |
| 334 | + $unlink_ori_img = str_replace( $thumb_postfix . '.', '.', $real_unlink_img ); | |
| 335 | + $real_unlink_ori_img = realpath( $unlink_ori_img ); | |
| 336 | + $expected_original = $prev_original ? realpath( untrailingslashit( $upload_path ) . '/' . $prev_original ) : false; | |
| 337 | + if ( $expected_original && $real_unlink_ori_img && $expected_original === $real_unlink_ori_img | |
| 338 | + && realpath( $image_path ) !== $real_unlink_ori_img | |
| 339 | + && 0 === strpos( $real_unlink_ori_img, $real_upload_path . DIRECTORY_SEPARATOR ) | |
| 340 | + && is_file( $real_unlink_ori_img ) ) { | |
| 341 | + wp_delete_file( $real_unlink_ori_img ); | |
| 306 | 342 | } |
| 307 | 343 | } |
| 308 | 344 | } |
| 309 | 345 | |
| @@ -390,8 +426,13 @@ | ||
| 390 | 426 | } else { |
| 391 | 427 | // Do nothing |
| 392 | 428 | } |
| 393 | 429 | |
| 430 | + if ( in_array( $type, array( 'avatar', 'banner' ), true ) ) { | |
| 431 | + delete_user_meta( $user_id, '_uwp_' . $type . '_original' ); | |
| 432 | + delete_user_meta( get_current_user_id(), '_uwp_pending_' . $type . '_upload' ); | |
| 433 | + } | |
| 434 | + | |
| 394 | 435 | if ( is_admin() ) { |
| 395 | 436 | if ( $user_id == get_current_user_id() ) { |
| 396 | 437 | $redirect_url = admin_url( 'profile.php' ); |
| 397 | 438 | } else { |
| @@ -2637,19 +2678,29 @@ | ||
| 2637 | 2678 | if ( $real_upload_path && $real_unlink_file && is_file( $real_unlink_file ) |
| 2638 | 2679 | && strpos( $real_unlink_file, $real_upload_path . DIRECTORY_SEPARATOR ) === 0 ) { |
| 2639 | 2680 | wp_delete_file( $real_unlink_file ); |
| 2640 | 2681 | |
| 2641 | - // For avatar/banner, also remove the original (non-thumb) file. | |
| 2682 | + // For avatar/banner, also remove the original (non-thumb) file, only if it is the exact file this user cropped. | |
| 2642 | 2683 | if ( $type ) { |
| 2643 | - $unlink_ori_file = str_replace( '_uwp_' . $type . '_thumb' . '.', '.', $real_unlink_file ); | |
| 2684 | + $unlink_ori_file = str_replace( '_uwp_' . $type . '_thumb' . '.', '.', $real_unlink_file ); | |
| 2644 | 2685 | $real_unlink_ori_file = realpath( $unlink_ori_file ); |
| 2686 | + $prev_original = get_user_meta( $user_id, '_uwp_' . $type . '_original', true ); | |
| 2687 | + $expected_original = $prev_original ? realpath( untrailingslashit( $upload_path ) . '/' . $prev_original ) : false; | |
| 2645 | 2688 | |
| 2646 | - if ( $real_unlink_ori_file && is_file( $real_unlink_ori_file ) | |
| 2689 | + if ( $expected_original && $real_unlink_ori_file && $expected_original === $real_unlink_ori_file | |
| 2690 | + && $real_unlink_ori_file !== $real_unlink_file | |
| 2691 | + && is_file( $real_unlink_ori_file ) | |
| 2647 | 2692 | && strpos( $real_unlink_ori_file, $real_upload_path . DIRECTORY_SEPARATOR ) === 0 ) { |
| 2648 | 2693 | wp_delete_file( $real_unlink_ori_file ); |
| 2649 | 2694 | } |
| 2650 | 2695 | } |
| 2651 | 2696 | } |
| 2697 | + } | |
| 2698 | + | |
| 2699 | + // Clear crop bookkeeping meta (pending upload is stored against the uploader). | |
| 2700 | + if ( $type ) { | |
| 2701 | + delete_user_meta( $user_id, '_uwp_' . $type . '_original' ); | |
| 2702 | + delete_user_meta( get_current_user_id(), '_uwp_pending_' . $type . '_upload' ); | |
| 2652 | 2703 | } |
| 2653 | 2704 | |
| 2654 | 2705 | wp_send_json_success(); |
| 2655 | 2706 | |