/public/wp-content/plugins/better-wp-security/core/lib/class-itsec-lib-file.php

https://gitlab.com/kath.de/cibedo_cibedo.de · PHP · 368 lines · 192 code · 79 blank · 97 comment · 52 complexity · b4a8cb9ad1e22fcffed51407fc5e27f1 MD5 · raw file

  1. <?php
  2. /**
  3. * iThemes Security file library.
  4. *
  5. * Contains the ITSEC_Lib_File class.
  6. *
  7. * @package iThemes_Security
  8. */
  9. /**
  10. * iThemes Security File Library class.
  11. *
  12. * Utility class for managing files.
  13. *
  14. * @package iThemes_Security
  15. * @since 1.15.0
  16. */
  17. class ITSEC_Lib_File {
  18. /**
  19. * Read requested file and return the contents.
  20. *
  21. * @since 1.15.0
  22. *
  23. * @param string $file Full path to config file to update.
  24. * @return string|WP_Error String of the file contents or a WP_Error object otherwise.
  25. */
  26. public static function read( $file ) {
  27. if ( ! self::is_file( $file ) ) {
  28. return new WP_Error( 'itsec-lib-file-read-non-file', sprintf( __( '%s could not be read. It does not appear to be a file.', 'better-wp-security' ), $file ) );
  29. }
  30. $callable = array();
  31. if ( ITSEC_Lib_Utility::is_callable_function( 'file_get_contents' ) ) {
  32. $callable[] = 'file_get_contents';
  33. }
  34. if ( ITSEC_Lib_Utility::is_callable_function( 'fopen' ) && ITSEC_Lib_Utility::is_callable_function( 'feof' ) && ITSEC_Lib_Utility::is_callable_function( 'fread' ) && ITSEC_Lib_Utility::is_callable_function( 'flock' ) ) {
  35. $callable[] = 'fopen';
  36. }
  37. if ( empty( $callable ) ) {
  38. return new WP_Error( 'itsec-lib-file-read-no-callable-functions', sprintf( __( '%s could not be read. Both the fopen/feof/fread/flock and file_get_contents functions are disabled on the server.', 'better-wp-security' ), $file ) );
  39. }
  40. $contents = false;
  41. // Different permissions to try in case the starting set of permissions are prohibiting read.
  42. $trial_perms = array(
  43. false,
  44. 0644,
  45. 0664,
  46. 0666,
  47. );
  48. foreach ( $trial_perms as $perms ) {
  49. if ( false !== $perms ) {
  50. if ( ! isset( $original_file_perms ) ) {
  51. $original_file_perms = self::get_permissions( $file );
  52. }
  53. self::chmod( $file, $perms );
  54. }
  55. if ( in_array( 'fopen', $callable ) ) {
  56. if ( false !== ( $fh = fopen( $file, 'rb' ) ) ) {
  57. flock( $fh, LOCK_SH );
  58. $contents = '';
  59. while ( ! feof( $fh ) ) {
  60. $contents .= fread( $fh, 1024 );
  61. }
  62. flock( $fh, LOCK_UN );
  63. fclose( $fh );
  64. }
  65. }
  66. if ( ( false === $contents ) && in_array( 'file_get_contents', $callable ) ) {
  67. $contents = file_get_contents( $file );
  68. }
  69. if ( false !== $contents ) {
  70. if ( isset( $original_file_perms ) && is_int( $original_file_perms ) ) {
  71. // Reset the original file permissions if they were modified.
  72. self::chmod( $file, $original_file_perms );
  73. }
  74. return $contents;
  75. }
  76. }
  77. return new WP_Error( 'itsec-lib-file-read-cannot-read', sprintf( __( '%s could not be read due to an unknown error.', 'better-wp-security' ), $file ) );
  78. }
  79. /**
  80. * Update or append the requested file with the supplied contents.
  81. *
  82. * @since 1.15.0
  83. *
  84. * @param string $file Full path to config file to update.
  85. * @param string $contents Contents to write to the file.
  86. * @param bool $append Optional. Set to true to append contents to the file. Defaults to false.
  87. * @return bool|WP_Error Boolean true on success, WP_Error object otherwise.
  88. */
  89. public static function write( $file, $contents, $append = false ) {
  90. $callable = array();
  91. if ( ITSEC_Lib_Utility::is_callable_function( 'fopen' ) && ITSEC_Lib_Utility::is_callable_function( 'fwrite' ) && ITSEC_Lib_Utility::is_callable_function( 'flock' ) ) {
  92. $callable[] = 'fopen';
  93. }
  94. if ( ITSEC_Lib_Utility::is_callable_function( 'file_put_contents' ) ) {
  95. $callable[] = 'file_put_contents';
  96. }
  97. if ( empty( $callable ) ) {
  98. return new WP_Error( 'itsec-lib-file-write-no-callable-functions', sprintf( __( '%s could not be written. Both the fopen/fwrite/flock and file_put_contents functions are disabled on the server. This is a server configuration issue that must be resolved before iThemes Security can write files.', 'better-wp-security' ), $file ) );
  99. }
  100. if ( ITSEC_Lib_Directory::is_dir( $file ) ) {
  101. return new WP_Error( 'itsec-lib-file-write-path-exists-as-directory', sprintf( __( '%s could not be written as a file. The requested path already exists as a directory. The directory must be removed or a new file name must be chosen before the file can be written.', 'better-wp-security' ), $file ) );
  102. }
  103. if ( ! ITSEC_Lib_Directory::is_dir( dirname( $file ) ) ) {
  104. $result = ITSEC_Lib_Directory::create( dirname( $file ) );
  105. if ( is_wp_error( $result ) ) {
  106. return $result;
  107. }
  108. }
  109. $file_existed = self::is_file( $file );
  110. $success = false;
  111. // Different permissions to try in case the starting set of permissions are prohibiting write.
  112. $trial_perms = array(
  113. false,
  114. 0644,
  115. 0664,
  116. 0666,
  117. );
  118. foreach ( $trial_perms as $perms ) {
  119. if ( false !== $perms ) {
  120. if ( ! isset( $original_file_perms ) ) {
  121. $original_file_perms = self::get_permissions( $file );
  122. }
  123. self::chmod( $file, $perms );
  124. }
  125. if ( in_array( 'fopen', $callable ) ) {
  126. if ( $append ) {
  127. $mode = 'ab';
  128. } else {
  129. $mode = 'wb';
  130. }
  131. if ( false !== ( $fh = @fopen( $file, $mode ) ) ) {
  132. flock( $fh, LOCK_EX );
  133. mbstring_binary_safe_encoding();
  134. $data_length = strlen( $contents );
  135. $bytes_written = @fwrite( $fh, $contents );
  136. reset_mbstring_encoding();
  137. @flock( $fh, LOCK_UN );
  138. @fclose( $fh );
  139. if ( $data_length === $bytes_written ) {
  140. $success = true;
  141. }
  142. }
  143. }
  144. if ( ! $success && in_array( 'file_put_contents', $callable ) ) {
  145. if ( $append ) {
  146. $flags = FILE_APPEND;
  147. } else {
  148. $flags = 0;
  149. }
  150. mbstring_binary_safe_encoding();
  151. $data_length = strlen( $contents );
  152. $bytes_written = @file_put_contents( $file, $contents, $flags );
  153. reset_mbstring_encoding();
  154. if ( $data_length === $bytes_written ) {
  155. $success = true;
  156. }
  157. }
  158. if ( $success ) {
  159. if ( ! $file_existed ) {
  160. // Set default file permissions for the new file.
  161. self::chmod( $file, self::get_default_permissions() );
  162. } else if ( isset( $original_file_perms ) && ! is_wp_error( $original_file_perms ) ) {
  163. // Reset the original file permissions if they were modified.
  164. self::chmod( $file, $original_file_perms );
  165. }
  166. return true;
  167. }
  168. if ( ! $file_existed ) {
  169. // If the file is new, there is no point attempting different permissions.
  170. break;
  171. }
  172. }
  173. return new WP_Error( 'itsec-lib-file-write-file-put-contents-failed', sprintf( __( '%s could not be written. This could be due to a permissions issue. Ensure that PHP runs as a user that has permission to write to this location.', 'better-wp-security' ), $file ) );
  174. }
  175. /**
  176. * Create or append the requested file with the supplied contents.
  177. *
  178. * @since 1.15.0
  179. *
  180. * @param string $file Full path to config file to update.
  181. * @param string $contents Contents to append to the file.
  182. * @return bool|WP_Error Boolean true on success, WP_Error object otherwise.
  183. */
  184. public static function append( $file, $contents ) {
  185. return self::write( $file, $contents, true );
  186. }
  187. /**
  188. * Remove the supplied file.
  189. *
  190. * @since 1.15.0
  191. *
  192. * @return bool|WP_Error Boolean true on success or a WP_Error object if an error occurs.
  193. */
  194. public static function remove( $file ) {
  195. if ( ! self::exists( $file ) ) {
  196. return true;
  197. }
  198. if ( ! ITSEC_Lib_Utility::is_callable_function( 'unlink' ) ) {
  199. return new WP_Error( 'itsec-lib-file-remove-unlink-is-disabled', sprintf( __( 'The file %s could not be removed as the unlink() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $file ) );
  200. }
  201. $result = @unlink( $file );
  202. @clearstatcache( true, $file );
  203. if ( $result ) {
  204. return true;
  205. }
  206. return new WP_Error( 'itsec-lib-file-remove-unknown-error', sprintf( __( 'Unable to remove %s due to an unknown error.', 'better-wp-security' ), $file ) );
  207. }
  208. /**
  209. * Change file permissions.
  210. *
  211. * @since 1.15.0
  212. *
  213. * @param string $file Full path to the file to change permissions for.
  214. * @param int $perms New permissions to set.
  215. * @return bool|WP_Error Boolean true if successful, false if not successful, or WP_Error if the chmod() function
  216. * is unavailable.
  217. */
  218. public static function chmod( $file, $perms ) {
  219. if ( ! is_int( $perms ) ) {
  220. return new WP_Error( 'itsec-lib-file-chmod-invalid-perms', sprintf( __( 'The file %1$s could not have its permissions updated as non-integer permissions were sent: (%2$s) %3$s', 'better-wp-security' ), $file, gettype( $perms ), $perms ) );
  221. }
  222. if ( ! ITSEC_Lib_Utility::is_callable_function( 'chmod' ) ) {
  223. return new WP_Error( 'itsec-lib-file-chmod-chmod-is-disabled', sprintf( __( 'The file %s could not have its permissions updated as the chmod() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $file ) );
  224. }
  225. return @chmod( $file, $perms );
  226. }
  227. /**
  228. * Determine if a file or directory exists.
  229. *
  230. * @since 1.15.0
  231. *
  232. * @param string $file Full path to test for existence.
  233. * @return bool|WP_Error Boolean true if it exists, false if it does not.
  234. */
  235. public static function exists( $file ) {
  236. @clearstatcache( true, $file );
  237. return @file_exists( $file );
  238. }
  239. /**
  240. * Determine if a file exists.
  241. *
  242. * @since 1.15.0
  243. *
  244. * @param string $file Full path to file to test for existence.
  245. * @return bool|WP_Error Boolean true if it exists, false if it does not.
  246. */
  247. public static function is_file( $file ) {
  248. @clearstatcache( true, $file );
  249. return @is_file( $file );
  250. }
  251. /**
  252. * Get file permissions from the requested file.
  253. *
  254. * @since 1.15.0
  255. *
  256. * @param string $file Full path to the file to retrieve permissions from.
  257. * @return int|WP_Error The permissions as an int or a WP_Error object if an error occurs.
  258. */
  259. public static function get_permissions( $file ) {
  260. if ( ! self::is_file( $file ) ) {
  261. return new WP_Error( 'itsec-lib-file-get-permissions-missing-file', sprintf( __( 'Permissions for the file %s could not be read as the file could not be found.', 'better-wp-security' ), $file ) );
  262. }
  263. if ( ! ITSEC_Lib_Utility::is_callable_function( 'fileperms' ) ) {
  264. return new WP_Error( 'itsec-lib-file-get-permissions-fileperms-is-disabled', sprintf( __( 'Permissions for the file %s could not be read as the fileperms() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $file ) );
  265. }
  266. @clearstatcache( true, $file );
  267. return fileperms( $file ) & 0777;
  268. }
  269. /**
  270. * Get default file permissions to use for new files.
  271. *
  272. * @since 1.15.0
  273. * @uses FS_CHMOD_FILE Define that sets default file permissions.
  274. *
  275. * @return int|WP_Error The default permissions as an int or a WP_Error object if an error occurs.
  276. */
  277. public static function get_default_permissions() {
  278. if ( defined( 'FS_CHMOD_FILE' ) ) {
  279. return FS_CHMOD_FILE;
  280. }
  281. $perms = self::get_permissions( ABSPATH . 'index.php' );
  282. if ( ! is_wp_error( $perms ) ) {
  283. return $perms;
  284. }
  285. return 0644;
  286. }
  287. }
  288. require_once( dirname( __FILE__ ) . '/class-itsec-lib-utility.php' );
  289. require_once( dirname( __FILE__ ) . '/class-itsec-lib-directory.php' );