Ticket #31616: 31616.3.diff
File 31616.3.diff, 20.5 KB (added by , 10 years ago) |
---|
-
src/wp-admin/includes/file.php
910 910 911 911 $context = trailingslashit( $context ); 912 912 913 if ( ! $method ) {913 static $results = array(); 914 914 915 $temp_file_name = $context . 'temp-write-test-' . time();916 $temp_handle = @fopen($temp_file_name, 'w');917 if ( $temp_handle) {915 // saving hash for static cache; file reads/writes are to be avoided when possible 916 $hash = md5(serialize($args) . $context . $allow_relaxed_file_ownership); 917 if ( ! isset($results[$hash])) { 918 918 919 // Attempt to determine the file owner of the WordPress files, and that of newly created files 920 $wp_file_owner = $temp_file_owner = false; 921 if ( function_exists('fileowner') ) { 922 $wp_file_owner = @fileowner( __FILE__ ); 923 $temp_file_owner = @fileowner( $temp_file_name ); 924 } 919 if ( ! $method ) { 925 920 926 if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) { 927 // WordPress is creating files as the same owner as the WordPress files, 928 // this means it's safe to modify & create new files via PHP. 929 $method = 'direct'; 930 $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner'; 931 } elseif ( $allow_relaxed_file_ownership ) { 932 // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files 933 // safely in this directory. This mode doesn't create new files, only alter existing ones. 934 $method = 'direct'; 935 $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership'; 921 $temp_file_name = $context . 'temp-write-test-' . time(); 922 $temp_handle = @fopen( $temp_file_name, 'w' ); 923 if ( $temp_handle ) { 924 925 // Attempt to determine the file owner of the WordPress files, and that of newly created files 926 $wp_file_owner = $temp_file_owner = false; 927 if ( function_exists( 'fileowner' ) ) { 928 $wp_file_owner = @fileowner( __FILE__ ); 929 $temp_file_owner = @fileowner( $temp_file_name ); 930 } 931 932 if ( $wp_file_owner !== false && $wp_file_owner === $temp_file_owner ) { 933 // WordPress is creating files as the same owner as the WordPress files, 934 // this means it's safe to modify & create new files via PHP. 935 $method = 'direct'; 936 $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner'; 937 } elseif ( $allow_relaxed_file_ownership ) { 938 // The $context directory is writable, and $allow_relaxed_file_ownership is set, this means we can modify files 939 // safely in this directory. This mode doesn't create new files, only alter existing ones. 940 $method = 'direct'; 941 $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership'; 942 } 943 944 @fclose( $temp_handle ); 945 @unlink( $temp_file_name ); 936 946 } 947 } 937 948 938 @fclose($temp_handle); 939 @unlink($temp_file_name); 949 if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2'; 950 if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext'; 951 if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread 952 953 /** 954 * Filter the filesystem method to use. 955 * 956 * @since 2.6.0 957 * 958 * @param string $method Filesystem method to return. 959 * @param array $args An array of connection details for the method. 960 * @param string $context Full path to the directory that is tested for being writable. 961 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. 962 */ 963 $results[$hash] = apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership ); 964 } 965 966 return $results[$hash]; 967 } 968 969 /** 970 * Get the filesystem credentials needed to connect to the site 971 * 972 * @param string $form_post the URL to post the form to 973 * @param string $type the chosen Filesystem method in use 974 * @param boolean $error if the current request has failed to connect 975 * @param string $context The directory which is needed access to, 976 * The write-test will be performed on this 977 * directory by get_filesystem_method() 978 * @param array $extra_fields Extra POST fields which should be checked for to be included in the post. 979 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. 980 * @return mixed Credentials that are found, not guaranteed to contain all information 981 */ 982 function get_filesystem_credentials($form_post, $type = '', $error = false, $context = false, $extra_fields = null, $allow_relaxed_file_ownership = false) { 983 984 /** This filter is documented in wp-includes/file.php */ 985 $credentials = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); 986 if ( '' !== $credentials ) { 987 return $credentials; 988 } 989 990 if ( empty($type) ) { 991 $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); 992 } 993 994 if ( 'direct' == $type ) { 995 return array( 'connection_type' => 'direct' ); 996 } 997 998 $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => '')); 999 1000 // If defined, set it to that, Else, If POST'd, set it to that, If not, Set it to whatever it previously was(saved details in option) 1001 $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? wp_unslash( $_POST['hostname'] ) : $credentials['hostname']); 1002 $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? wp_unslash( $_POST['username'] ) : $credentials['username']); 1003 $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? wp_unslash( $_POST['password'] ) : ''); 1004 1005 // Check to see if we are setting the public/private keys for ssh 1006 $credentials['public_key'] = defined('FTP_PUBKEY') ? FTP_PUBKEY : (!empty($_POST['public_key']) ? wp_unslash( $_POST['public_key'] ) : ''); 1007 $credentials['private_key'] = defined('FTP_PRIKEY') ? FTP_PRIKEY : (!empty($_POST['private_key']) ? wp_unslash( $_POST['private_key'] ) : ''); 1008 1009 // Sanitize the hostname, Some people might pass in odd-data: 1010 $credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off 1011 1012 if ( strpos($credentials['hostname'], ':') ) { 1013 list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2); 1014 if ( ! is_numeric($credentials['port']) ) 1015 unset($credentials['port']); 1016 } else { 1017 unset($credentials['port']); 1018 } 1019 1020 if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' == FS_METHOD ) ) { 1021 $credentials['connection_type'] = 'ssh'; 1022 } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' == $type ) { //Only the FTP Extension understands SSL 1023 $credentials['connection_type'] = 'ftps'; 1024 } elseif ( ! empty( $_POST['connection_type'] ) ) { 1025 $credentials['connection_type'] = wp_unslash( $_POST['connection_type'] ); 1026 } elseif ( ! isset( $credentials['connection_type'] ) ) { //All else fails (And it's not defaulted to something else saved), Default to FTP 1027 $credentials['connection_type'] = 'ftp'; 1028 } 1029 1030 return $credentials; 1031 } 1032 1033 /** 1034 * Check credentials for required parameters 1035 * 1036 * @param array $credentials Credentials to check required parameters 1037 * 1038 * @return bool True when all required parameters have been set for supplied type, False when anything is missing 1039 */ 1040 function usable_filesystem_credentials($credentials) { 1041 // We need something to check against 1042 if ( empty( $credentials ) ) { 1043 return false; 1044 } 1045 1046 // All the checks required an array for parameters; exit if we got something else 1047 if ( ! is_array( $credentials ) ) { 1048 return false; 1049 } 1050 1051 if ( 'direct' == $credentials['connection_type'] ) { 1052 return true; 1053 } 1054 1055 // SSH needs public and private key 1056 if ( isset($credentials['connection_type']) && 'ssh' == $credentials['connection_type'] ) { 1057 if ( ! isset( $credentials['public_key'], $credentials['private_key'] ) ) { 1058 return false; 940 1059 } 941 }942 1060 943 if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2'; 944 if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext'; 945 if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread 1061 return ( ! empty( $credentials['public_key'] ) && ! empty( $credentials['private_key'] ) ); 1062 } 946 1063 947 /** 948 * Filter the filesystem method to use. 949 * 950 * @since 2.6.0 951 * 952 * @param string $method Filesystem method to return. 953 * @param array $args An array of connection details for the method. 954 * @param string $context Full path to the directory that is tested for being writable. 955 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. 956 */ 957 return apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership ); 1064 // Other connection methods need hostname, password and username 1065 if ( ! isset( $credentials['password'], $credentials['username'], $credentials['hostname'] ) ) { 1066 return false; 1067 } 1068 1069 return ( ! empty( $credentials['password'] ) && ! empty( $credentials['username'] ) && ! empty( $credentials['hostname'] ) ); 958 1070 } 959 1071 960 1072 /** … … 982 1094 * @return boolean False on failure. True on success. 983 1095 */ 984 1096 function request_filesystem_credentials($form_post, $type = '', $error = false, $context = false, $extra_fields = null, $allow_relaxed_file_ownership = false ) { 985 986 1097 /** 987 1098 * Filter the filesystem credentials form output. 988 1099 * … … 1001 1112 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. 1002 1113 * @param array $extra_fields Extra POST fields. 1003 1114 */ 1004 $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); 1005 if ( '' !== $req_cred ) 1006 return $req_cred; 1115 $credentials = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); 1116 if ( '' !== $credentials ) { 1117 return $credentials; 1118 } 1007 1119 1008 if ( empty( $type) ) {1120 if ( empty( $type ) ) { 1009 1121 $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); 1010 1122 } 1011 1123 1012 if ( 'direct' == $type ) 1124 if ( 'direct' == $type ) { 1013 1125 return true; 1126 } 1014 1127 1015 if ( is_null( $extra_fields ) ) 1016 $extra_fields = array( 'version', 'locale' ); 1128 $credentials = get_filesystem_credentials( $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); 1017 1129 1018 $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => '')); 1130 if ( 'direct' == $credentials['connection_type'] ) { 1131 return true; 1132 } 1019 1133 1020 // If defined, set it to that, Else, If POST'd, set it to that, If not, Set it to whatever it previously was(saved details in option) 1021 $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? wp_unslash( $_POST['hostname'] ) : $credentials['hostname']); 1022 $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? wp_unslash( $_POST['username'] ) : $credentials['username']); 1023 $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? wp_unslash( $_POST['password'] ) : ''); 1134 if ( ! $error && usable_filesystem_credentials( $credentials ) ) { 1024 1135 1025 // Check to see if we are setting the public/private keys for ssh1026 $credentials['public_key'] = defined('FTP_PUBKEY') ? FTP_PUBKEY : (!empty($_POST['public_key']) ? wp_unslash( $_POST['public_key'] ) : '');1027 $credentials['private_key'] = defined('FTP_PRIKEY') ? FTP_PRIKEY : (!empty($_POST['private_key']) ? wp_unslash( $_POST['private_key'] ) : '');1028 1029 // Sanitize the hostname, Some people might pass in odd-data:1030 $credentials['hostname'] = preg_replace('|\w+://|', '', $credentials['hostname']); //Strip any schemes off1031 1032 if ( strpos($credentials['hostname'], ':') ) {1033 list( $credentials['hostname'], $credentials['port'] ) = explode(':', $credentials['hostname'], 2);1034 if ( ! is_numeric($credentials['port']) )1035 unset($credentials['port']);1036 } else {1037 unset($credentials['port']);1038 }1039 1040 if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' == FS_METHOD ) ) {1041 $credentials['connection_type'] = 'ssh';1042 } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' == $type ) { //Only the FTP Extension understands SSL1043 $credentials['connection_type'] = 'ftps';1044 } elseif ( ! empty( $_POST['connection_type'] ) ) {1045 $credentials['connection_type'] = wp_unslash( $_POST['connection_type'] );1046 } elseif ( ! isset( $credentials['connection_type'] ) ) { //All else fails (And it's not defaulted to something else saved), Default to FTP1047 $credentials['connection_type'] = 'ftp';1048 }1049 if ( ! $error &&1050 (1051 ( !empty($credentials['password']) && !empty($credentials['username']) && !empty($credentials['hostname']) ) ||1052 ( 'ssh' == $credentials['connection_type'] && !empty($credentials['public_key']) && !empty($credentials['private_key']) )1053 ) ) {1054 1136 $stored_credentials = $credentials; 1055 if ( !empty($stored_credentials['port']) ) //save port as part of hostname to simplify above code. 1137 if ( ! empty( $stored_credentials['port'] ) ) //save port as part of hostname to simplify retrievement code 1138 { 1056 1139 $stored_credentials['hostname'] .= ':' . $stored_credentials['port']; 1140 } 1057 1141 1058 unset($stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key']); 1142 unset( $stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key'] ); 1143 1059 1144 if ( ! defined( 'WP_INSTALLING' ) ) { 1060 1145 update_option( 'ftp_credentials', $stored_credentials ); 1061 1146 } 1147 1062 1148 return $credentials; 1063 1149 } 1150 1151 // Display the credentials form to the user 1152 request_filesystem_credentials_form( $form_post, $credentials, $type, $error, $context, $extra_fields ); 1153 1154 // Nothing usable has been found yet. 1155 return false; 1156 } 1157 1158 /** 1159 * Write the form needed to input credentials needed to connect for the specified type 1160 * 1161 * @param string $form_post the URL to post the form to 1162 * @param $credentials 1163 * @param string $type the chosen Filesystem method in use 1164 * @param boolean $error if the current request has failed to connect 1165 * @param string $context The directory which is needed access to, The write-test will be performed on this directory by get_filesystem_method() 1166 * @param array $extra_fields Extra POST fields which should be checked for to be included in the post. 1167 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. 1168 */ 1169 function request_filesystem_credentials_form($form_post, $credentials, $type = '', $error = false, $context = null, $extra_fields = null, $allow_relaxed_file_ownership = false) { 1170 if ( empty( $type ) ) { 1171 $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); 1172 } 1173 1174 if ( ! is_array($credentials)) { 1175 $credentials = array(); 1176 } 1177 1064 1178 $hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : ''; 1065 1179 $username = isset( $credentials['username'] ) ? $credentials['username'] : ''; 1066 1180 $public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : ''; … … 1068 1182 $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; 1069 1183 $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; 1070 1184 1185 if ( is_null( $extra_fields ) ) 1186 $extra_fields = array( 'version', 'locale' ); 1187 1071 1188 if ( $error ) { 1072 1189 $error_string = __('<strong>ERROR:</strong> There was an error connecting to the server, Please verify the settings are correct.'); 1073 1190 if ( is_wp_error($error) ) … … 1190 1307 </div> 1191 1308 </form> 1192 1309 <?php 1193 return false; 1310 1194 1311 } -
tests/phpunit/tests/filesystem/credentials.php
1 <?php 2 3 /** 4 * @group filesystem 5 * @group wp-filesystem 6 */ 7 8 class Tests_Filesystem_Credentials extends WP_UnitTestCase { 9 /** 10 * 11 */ 12 function test_get_filesystem_credentials() { 13 // test direct type 14 $credentials = get_filesystem_credentials('', 'direct'); 15 $this->assertInternalType( 'array', $credentials ); 16 $this->assertArrayHasKey( 'connection_type', $credentials ); 17 $this->assertEquals( 'direct', $credentials['connection_type'] ); 18 19 // test SSH 20 $credentials = get_filesystem_credentials('', 'ssh'); 21 $this->assertInternalType( 'array', $credentials ); 22 23 $this->assertArrayHasKey( 'connection_type', $credentials ); 24 $this->assertArrayHasKey( 'private_key', $credentials ); 25 $this->assertArrayHasKey( 'public_key', $credentials ); 26 $this->assertArrayHasKey( 'hostname', $credentials ); 27 $this->assertArrayHasKey( 'username', $credentials ); 28 $this->assertArrayHasKey( 'password', $credentials ); 29 30 $this->assertEquals( '', $credentials['private_key'] ); 31 $this->assertEquals( '', $credentials['public_key'] ); 32 $this->assertEquals( '', $credentials['hostname'] ); 33 $this->assertEquals( '', $credentials['username'] ); 34 $this->assertEquals( '', $credentials['password'] ); 35 36 $_POST['hostname'] = rand_str(); 37 $_POST['private_key'] = rand_str(); 38 $_POST['public_key'] = rand_str(); 39 $_POST['username'] = rand_str(); 40 $_POST['password'] = rand_str(); 41 42 $credentials = get_filesystem_credentials('', 'ftp'); 43 44 $this->assertEquals( $_POST['private_key'], $credentials['private_key'] ); 45 $this->assertEquals( $_POST['public_key'], $credentials['public_key'] ); 46 $this->assertEquals( $_POST['hostname'], $credentials['hostname'] ); 47 $this->assertEquals( $_POST['username'], $credentials['username'] ); 48 $this->assertEquals( $_POST['password'], $credentials['password'] ); 49 50 unset($_POST['hostname'], $_POST['private_key'], $_POST['public_key'], $_POST['username'], $_POST['password']); 51 52 53 // test filter override: 54 add_filter( 'request_filesystem_credentials', array('Tests_Filesystem_Credentials', '__filesystem_credentials'), 10, 3); 55 56 $credentials = get_filesystem_credentials('', 'ssh'); 57 $_credentials = Tests_Filesystem_Credentials::__filesystem_credentials('', '', 'ssh'); 58 59 $this->assertEquals( $_credentials, $credentials); 60 61 // clean up filter 62 remove_filter( 'request_filesystem_credentials', array('Tests_Filesystem_Credentials', '__filesystem_credentials'), 10, 3); 63 } 64 65 /** 66 * 67 */ 68 function test_usable_filesystem_credentials() { 69 $credentials = array('connection_type' => 'direct'); 70 71 $this->assertTrue( usable_filesystem_credentials( $credentials ) ); 72 73 // ssh: only one that needs public/private keys 74 $credentials = get_filesystem_credentials( '', 'ssh' ); 75 $credentials['connection_type'] = 'ssh'; 76 77 $this->assertFalse( usable_filesystem_credentials( $credentials ) ); 78 79 $credentials['private_key'] = $credentials['public_key'] = 'a_value'; 80 $this->assertTrue( usable_filesystem_credentials( $credentials ) ); 81 82 // ssh2 83 $credentials['connection_type'] = 'ssh2'; 84 $this->assertFalse( usable_filesystem_credentials( $credentials ) ); 85 86 $credentials = array( 87 'hostname' => 'a', 88 'username' => 'b', 89 'password' => 'c', 90 'connection_type' => 'ssh2' 91 ); 92 $this->assertTrue( usable_filesystem_credentials( $credentials ) ); 93 94 95 // ftp 96 $credentials = array( 97 'hostname' => '', 98 'username' => '', 99 'password' => '', 100 'connection_type' => 'ftp' 101 ); 102 $this->assertFalse( usable_filesystem_credentials( $credentials ) ); 103 104 $credentials = array( 105 'hostname' => 'a', 106 'username' => 'b', 107 'password' => 'c', 108 'connection_type' => 'ftp' 109 ); 110 $this->assertTrue( usable_filesystem_credentials( $credentials ) ); 111 } 112 113 /** Helper functions */ 114 public static function __filesystem_credentials($empty, $form_url, $type) { 115 switch ($type) { 116 case 'ssh': 117 $credentials = array( 118 'connection_type' => 'ssh', 119 'private_key' => 'b', 120 'public_key' => 'c' 121 ); 122 break; 123 default: 124 $credentials = array( 125 'hostname' => 'a', 126 'username' => 'b', 127 'password' => 'c', 128 'connection_type' => 'ftp' 129 ); 130 break; 131 } 132 133 return $credentials; 134 } 135 }