Skip to content

Commit

Permalink
Merge pull request #926 from Automattic/add/support-sub-sub-directory…
Browse files Browse the repository at this point in the history
…-ms-file-uploads

Support sub-sub directory multisite file uploads
  • Loading branch information
mjangda committed Jun 25, 2018
2 parents 96707c5 + e589f0a commit da7fcd2
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 9 deletions.
29 changes: 20 additions & 9 deletions a8c-files.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@

define( 'ALLOW_UNFILTERED_UPLOADS', false );

require_once( __DIR__ . '/files/class-path-utils.php' );

use Automattic\VIP\Files\Path_Utils;

class A8C_Files {

function __construct() {
Expand Down Expand Up @@ -276,7 +280,7 @@ public function filter_unique_filename( $filename, $ext, $dir, $unique_filename_

if ( 200 == $check['http_code'] ) {
$obj = json_decode( $check['content'] );
if ( isset( $obj->filename ) && basename( $obj->filename ) != basename( $post_url ) ) {
if ( isset( $obj->filename ) && basename( $obj->filename ) != basename( $filename ) ) {
$filename = $obj->filename;
}
}
Expand Down Expand Up @@ -329,9 +333,12 @@ private function _check_uniqueness_with_backend( $filename ) {

$url_parts = parse_url( $uploads['url'] . '/' . $filename );
$file_path = $url_parts['path'];
if ( is_multisite() &&
preg_match( '/^\/[_0-9a-zA-Z-]+\/' . str_replace( '/', '\/', $this->get_upload_path() ) . '\/sites\/[0-9]+\//', $file_path ) ) {
$file_path = preg_replace( '/^\/[_0-9a-zA-Z-]+/', '', $file_path );
if ( is_multisite() ) {
$sanitized_file_path = Path_Utils::trim_leading_multisite_directory( $file_path, $this->get_upload_path() );
if ( false !== $sanitized_file_path ) {
$file_path = $sanitized_file_path;
unset( $sanitized_file_path );
}
}

$post_url = $this->get_files_service_hostname() . $file_path;
Expand Down Expand Up @@ -362,10 +369,14 @@ function upload_file( $details, $upload_type ) {
} else {
$url_parts = parse_url( $details['url'] );
$file_path = $url_parts['path'];
if ( is_multisite() &&
preg_match( '/^\/[_0-9a-zA-Z-]+\/' . str_replace( '/', '\/', $this->get_upload_path() ) . '\/sites\/[0-9]+\//', $file_path ) ) {
$file_path = preg_replace( '/^\/[_0-9a-zA-Z-]+/', '', $file_path );
$details['url'] = $url_parts['scheme'] . '://' . $url_parts['host'] . $file_path;
if ( is_multisite() ) {
$sanitized_file_path = Path_Utils::trim_leading_multisite_directory( $file_path, $this->get_upload_path() );
if ( false !== $sanitized_file_path ) {
$file_path = $sanitized_file_path;
unset( $sanitized_file_path );

$details['url'] = $url_parts['scheme'] . '://' . $url_parts['host'] . $file_path;
}
}
$post_url = $this->get_files_service_hostname() . $file_path;
}
Expand Down Expand Up @@ -770,7 +781,7 @@ public static function strip_dimensions_from_url_path( $url ) {
}

return $url;
}
}
}

function a8c_files_init() {
Expand Down
39 changes: 39 additions & 0 deletions files/class-path-utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Automattic\VIP\Files;

class Path_Utils {
public static function is_subdirectory_multisite_path( $file_path, $uploads_path ) {
$pattern = '#^/[_0-9a-zA-Z-]+/' . $uploads_path . '/sites/[0-9]+/#';

return preg_match( $pattern, $file_path );
}

public static function is_sub_subdirectory_multisite_path( $file_path, $uploads_path ) {
$pattern = '#^/[_0-9a-zA-Z-]+/[_0-9a-zA-Z-]+/' . $uploads_path . '/sites/[0-9]+/#';

return preg_match( $pattern, $file_path );
}

/**
* Strips off sub- and sub-subdirectory from a valid, multisite file path.
*
* For example, given a URL like `/subsite-1/subsite_2/wp-content/uploads/sites/1/file.jpg`.
* We will get back `/wp-content/uploads/sites/1/file.jpg`.
*
* Note: only supports 2 levels of subdirectories.
*
* @param string $file_path The file path to sanitize
* @param string $uploads_path The relative path from ABSPATH to `uploads`, minus leading and trailing slashes (e.g. `wp-content/uploads`)
* @return string|bool The sanitized path if it's a valid path, otherwise `false`.
*/
public static function trim_leading_multisite_directory( $file_path, $uploads_path ) {
if ( self::is_sub_subdirectory_multisite_path( $file_path, $uploads_path ) ) {
return preg_replace( '#^/[_0-9a-zA-Z-]+/[_0-9a-zA-Z-]+#', '', $file_path );
} elseif ( self::is_subdirectory_multisite_path( $file_path, $uploads_path ) ) {
return preg_replace( '#^/[_0-9a-zA-Z-]+#', '', $file_path );
}

return false;
}
}
154 changes: 154 additions & 0 deletions tests/files/test-path-utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php

namespace Automattic\VIP\Files;

class Path_Utils_Test extends \PHPUnit_Framework_TestCase {
public static function setUpBeforeClass() {
parent::setUpBeforeClass();

require_once( __DIR__ . '/../../files/class-path-utils.php' );
}

public function get_test_data__is_subdirectory_multisite_path__nope() {
return [
'missing_leading_slash' => [
'subsite1/wp-content/uploads/sites/1/file.jpg',
],
'no_subdirectory' => [
'/wp-content/uploads/sites/1/file.jpg',
],
'invalid_subdirectory' => [
'/subsité1/wp-content/uploads/sites/1/file.jpg',
],
'empty_subdirectory' => [
'//wp-content/uploads/sites/1/file.jpg',
],
'no_wp-content-uploads' => [
'/subsite1/sites/1/file.jpg',
],
'no_sites' => [
'/subsite1/wp-content/uploads/1/file.jpg',
],
'invalid_site-id' => [
'/subsite1/wp-content/uploads/sites/xyz/file.jpg',
],
];
}

/**
* @dataProvider get_test_data__is_subdirectory_multisite_path__nope
*/
public function test__is_subdirectory_multisite_path__nope( $test_path ) {
$actual_return = Path_Utils::is_subdirectory_multisite_path( $test_path, 'wp-content/uploads' );

$this->assertEquals( 0, $actual_return );
}

public function get_test_data__is_subdirectory_multisite_path__yep() {
return [
'valid_path' => [
'/subsite1/wp-content/uploads/sites/1/file.jpg',
],
'all_allowed_characters' => [
'/1s_u-bSIT2e/wp-content/uploads/sites/4567/file.jpg',
],
];
}

/**
* @dataProvider get_test_data__is_subdirectory_multisite_path__yep
*/
public function test__is_subdirectory_multisite_path__yep( $test_path ) {
$actual_return = Path_Utils::is_subdirectory_multisite_path( $test_path, 'wp-content/uploads' );

$this->assertEquals( 1, $actual_return );
}

public function get_test_data__is_sub_subdirectory_multisite_path__nope() {
return [
'missing_leading_slash' => [
'subsite1/subsite2/wp-content/uploads/sites/1/file.jpg',
],
// Can't test for no_subdirectory :)
'no_sub_subdirectory' => [
'/subsite1/wp-content/uploads/sites/1/file.jpg',
],
'invalid_subdirectory' => [
'/subsité1/wp-content/uploads/sites/1/file.jpg',
],
'invalid_sub_subdirectory' => [
'/subsite1/subsité2/wp-content/uploads/sites/1/file.jpg',
],
'empty_subdirectory' => [
'//subsite2/wp-content/uploads/sites/1/file.jpg',
],
'empty_sub_subdirectory' => [
'/subsite1//wp-content/uploads/sites/1/file.jpg',
],
'no_wp-content-uploads' => [
'/subsite1/subsite2/sites/1/file.jpg',
],
'no_sites' => [
'/subsite1/subsite2/wp-content/uploads/1/file.jpg',
],
'invalid_site-id' => [
'/subsite1/subsite2/wp-content/uploads/sites/xyz/file.jpg',
],
];
}

/**
* @dataProvider get_test_data__is_sub_subdirectory_multisite_path__nope
*/
public function test__is_sub_subdirectory_multisite_path__nope( $test_path ) {
$actual_return = Path_Utils::is_sub_subdirectory_multisite_path( $test_path, 'wp-content/uploads' );

$this->assertEquals( 0, $actual_return );
}

public function get_test_data__is_sub_subdirectory_multisite_path__yep() {
return [
'valid_path' => [
'/subsite1/subsite2/wp-content/uploads/sites/1/file.jpg',
],
'all_allowed_characters' => [
'/1s_u-bSIT2e/bSIT2e_1s_u-/wp-content/uploads/sites/4567/file.jpg',
],
];
}

/**
* @dataProvider get_test_data__is_sub_subdirectory_multisite_path__yep
*/
public function test__is_sub_subdirectory_multisite_path__yep( $test_path ) {
$actual_return = Path_Utils::is_sub_subdirectory_multisite_path( $test_path, 'wp-content/uploads' );

$this->assertEquals( 1, $actual_return );
}

public function get_test_data__trim_leading_multisite_directory() {
return [
'sub_subdirectory' => [
'/subsite1/subsite2/wp-content/uploads/sites/10/file.jpg',
'/wp-content/uploads/sites/10/file.jpg',
],
'subdirectory' => [
'/subsite1/wp-content/uploads/sites/2/file.jpg',
'/wp-content/uploads/sites/2/file.jpg',
],
'other' => [
'',
false,
]
];
}

/**
* @dataProvider get_test_data__trim_leading_multisite_directory
*/
public function test__trim_leading_multisite_directory( $test_path, $expected_result ) {
$actual_result = Path_Utils::trim_leading_multisite_directory( $test_path, 'wp-content/uploads' );

$this->assertEquals( $expected_result, $actual_result );
}
}

0 comments on commit da7fcd2

Please sign in to comment.