PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.6.0
WP 2FA – Two-factor authentication for WordPress v2.6.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / Helpers / class-file-writer.php
wp-2fa / includes / classes / Admin / Helpers Last commit date
class-ajax-helper.php 2 years ago class-classes-helper.php 2 years ago class-file-writer.php 2 years ago class-methods-helper.php 2 years ago class-php-helper.php 2 years ago class-user-helper.php 2 years ago class-wp-helper.php 2 years ago index.php 2 years ago
class-file-writer.php
791 lines
1 <?php
2 /**
3 * Responsible for the File writing operations
4 *
5 * @package wp2fa
6 * @subpackage helpers
7 * @since 2.4.0
8 * @copyright %%YEAR%% Melapress
9 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
10 * @link https://wordpress.org/plugins/wp-2fa/
11 */
12
13 namespace WP2FA\Admin\Helpers;
14
15 defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
16
17 /**
18 * File writer settings class
19 */
20 if ( ! class_exists( '\WP2FA\Admin\Helpers\File_Writer' ) ) {
21
22 /**
23 * All the file operations must go trough this class.
24 *
25 * @since 2.4.0
26 */
27 class File_Writer {
28
29 public const SECRET_NAME = 'WP2FA_ENCRYPT_KEY';
30
31 public const WP2FA_UPLOADS_DIR = 'wp-2fa-data';
32
33 /**
34 * Saves a secret key in `wp-config.php`.
35 *
36 * @param string $secret The secret key to save.
37 *
38 * @return bool
39 *
40 * @since 2.4.0
41 */
42 public static function save_secret_key( string $secret ): bool {
43 if ( ! self::can_write_to_file( self::get_wp_config_file_path() ) ) {
44 return false;
45 }
46
47 $file = self::get_wp_config_file_path();
48 $contents = self::read( $file );
49
50 if ( false === $contents ) {
51 return false;
52 }
53
54 set_error_handler(
55 function ( $err_severity, $err_msg, $err_file, $err_line, array $err_context ) {
56 throw new \Error( $err_msg, 0, $err_severity, $err_file, $err_line ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
57 },
58 E_WARNING
59 );
60
61 try {
62 $current_secret = constant( self::SECRET_NAME );
63 } catch ( \Error $e ) {
64 $current_secret = null;
65 }
66
67 restore_error_handler();
68
69 $matches_found = $current_secret ? substr_count( $contents, $current_secret ) : 0;
70
71 if ( ! $current_secret || ! $matches_found ) {
72 if ( substr_count( $contents, self::SECRET_NAME ) ) {
73
74 $line_ending = self::get_line_ending( $contents );
75
76 $contents = explode( $line_ending, $contents );
77
78 foreach ( $contents as $key => $line ) {
79 if ( stristr( $line, self::SECRET_NAME ) ) {
80 unset( $contents[ $key ] );
81 }
82 }
83
84 $contents = implode( $line_ending, array_values( $contents ) );
85 self::write( $file, $contents );
86 }
87 self::write_wp_config( '/** WP 2FA plugin data encryption key. For more information please visit melapress.com */' . "\n" . 'define( \'' . self::SECRET_NAME . '\', \'' . $secret . '\' );' );
88 return true;
89 }
90
91 if ( $matches_found > 1 ) {
92 return false;
93 }
94
95 $replaced = str_replace( $current_secret, $secret, $contents );
96
97 if ( ! $replaced ) {
98 return false;
99 }
100
101 $written = self::write( $file, $replaced );
102
103 if ( false === $written ) {
104 return false;
105 }
106
107 return true;
108 }
109
110 /**
111 * Gets the permissions of given directory
112 *
113 * @param string $dir - The name of the directory to check.
114 *
115 * @return bool|int
116 *
117 * @since 2.4.0
118 */
119 public static function get_permissions( string $dir ) {
120 if ( ! is_dir( $dir ) ) {
121 return false;
122 }
123
124 if ( ! PHP_Helper::is_callable( 'fileperms' ) ) {
125 return false;
126 }
127
128 $dir = rtrim( $dir, '/' );
129 // phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.
130 @clearstatcache( true, $dir );
131
132 return fileperms( $dir ) & 0777;
133 }
134
135 /**
136 * Writes a content to a given file
137 *
138 * @param string $file - The file to write to.
139 * @param string $contents - The contents of the file to write.
140 * @param boolean $append - Append the contents of the file or overwrite.
141 *
142 * @return mixed
143 *
144 * @since 2.4.0
145 */
146 public static function write( string $file, string $contents, $append = false ) {
147 $callable = array();
148
149 if ( PHP_Helper::is_callable( 'fopen' ) && PHP_Helper::is_callable( 'fwrite' ) && PHP_Helper::is_callable( 'flock' ) ) {
150 $callable[] = 'fopen';
151 }
152 if ( PHP_Helper::is_callable( 'file_put_contents' ) ) {
153 $callable[] = 'file_put_contents';
154 }
155
156 if ( empty( $callable ) ) {
157 return false;
158 }
159
160 if ( is_dir( $file ) ) {
161 return false;
162 }
163
164 if ( ! is_dir( dirname( $file ) ) ) {
165 $result = self::create_dir( dirname( $file ) );
166
167 if ( false === $result ) {
168 return false;
169 }
170 }
171
172 $file_existed = is_file( $file );
173 $success = false;
174
175 // Different permissions to try in case the starting set of permissions are prohibiting write.
176 $trial_perms = array(
177 false,
178 0644,
179 0664,
180 0666,
181 );
182
183 foreach ( $trial_perms as $perms ) {
184 if ( false !== $perms ) {
185 if ( ! isset( $original_file_perms ) ) {
186 $original_file_perms = self::get_permissions( $file );
187 }
188
189 self::chmod( $file, $perms );
190 }
191
192 if ( in_array( 'fopen', $callable, true ) ) {
193 if ( $append ) {
194 $mode = 'ab';
195 } else {
196 $mode = 'wb';
197 }
198
199 if ( false !== ( $fh = @fopen( $file, $mode ) ) ) { // phpcs:ignore -- Ignored the assignment on the same line
200 flock( $fh, LOCK_EX );
201
202 mbstring_binary_safe_encoding();
203
204 $data_length = strlen( $contents );
205 $bytes_written = @fwrite( $fh, $contents ); // phpcs:ignore -- Ignored the error silencing
206
207 reset_mbstring_encoding();
208
209 @flock( $fh, LOCK_UN ); // phpcs:ignore -- Ignored the error silencing
210 @fclose( $fh ); // phpcs:ignore -- Ignored the error silencing
211
212 if ( $data_length === $bytes_written ) {
213 $success = true;
214 }
215 }
216 }
217
218 if ( ! $success && in_array( 'file_put_contents', $callable, true ) ) {
219 if ( $append ) {
220 $flags = FILE_APPEND;
221 } else {
222 $flags = 0;
223 }
224
225 mbstring_binary_safe_encoding();
226
227 $data_length = strlen( $contents );
228 $bytes_written = @file_put_contents( $file, $contents, $flags ); // phpcs:ignore -- Ignored the silencing warning
229
230 reset_mbstring_encoding();
231
232 if ( $data_length === $bytes_written ) {
233 $success = true;
234 }
235 }
236
237 if ( $success ) {
238 if ( ! $file_existed ) {
239 // Set default file permissions for the new file.
240 self::chmod( $file, self::get_default_permissions() );
241 } elseif ( isset( $original_file_perms ) && ! is_wp_error( $original_file_perms ) ) {
242 // Reset the original file permissions if they were modified.
243 self::chmod( $file, $original_file_perms );
244 }
245
246 return true;
247 }
248
249 if ( ! $file_existed ) {
250 // If the file is new, there is no point attempting different permissions.
251 break;
252 }
253 }
254
255 return false;
256 }
257
258 /**
259 * Adds index.php and .htaccess files to the given directory
260 *
261 * @param string $dir - The directory to protect.
262 *
263 * @return bool
264 *
265 * @since 2.4.0
266 */
267 public static function add_file_listing_protection( string $dir ) {
268 $dir = rtrim( $dir, \DIRECTORY_SEPARATOR );
269
270 if ( ! is_dir( $dir ) ) {
271 return false;
272 }
273
274 if ( self::exists( $dir . \DIRECTORY_SEPARATOR . 'index.php' ) ) {
275 return true;
276 }
277
278 return self::write( $dir . \DIRECTORY_SEPARATOR . '.htaccess', 'Deny from all' ) &&
279 self::write( $dir . \DIRECTORY_SEPARATOR . 'index.php', "<?php\n// Silence is golden." );
280 }
281
282 /**
283 * Checks if given file exists
284 *
285 * @param string $file - The name of the file to check.
286 *
287 * @return bool
288 *
289 * @since 2.4.0
290 */
291 public static function exists( string $file ): bool {
292
293 @clearstatcache( true, $file ); // phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.
294
295 return @file_exists( $file ); // phpcs:ignore -- Have Tide ignore the following line. We use arguments that don't exist in early versions, but these versions ignore the arguments.
296 }
297
298 /**
299 * Check the setting that allows writing files.
300 *
301 * @param string $filename - The name of the file and path.
302 *
303 * @since 2.4.0
304 *
305 * @return bool True if files can be written to, false otherwise.
306 */
307 public static function can_write_to_file( string $filename ) {
308 return is_writable( $filename );
309 }
310
311 /**
312 * Get full file path to the site's wp-config.php file.
313 *
314 * @since 2.4.0
315 *
316 * @return string Full path to the wp-config.php file or a blank string if modifications for the file are disabled.
317 */
318 public static function get_wp_config_file_path() {
319 if ( file_exists( ABSPATH . 'wp-config.php' ) ) {
320 $path = ABSPATH . 'wp-config.php';
321 } else {
322 $path = '';
323 }
324
325 return $path;
326 }
327
328 /**
329 * Creates a directory structure
330 *
331 * @param string $dir - The directory to create.
332 *
333 * @return boolean
334 *
335 * @since 2.4.0
336 */
337 public static function create_dir( string $dir ): bool {
338 $dir = rtrim( $dir, '/' );
339
340 if ( is_dir( $dir ) ) {
341 self::add_file_listing_protection( $dir );
342
343 return true;
344 }
345
346 if ( self::exists( $dir ) ) {
347 return false;
348 }
349
350 if ( ! PHP_Helper::is_callable( 'mkdir' ) ) {
351 return false;
352 }
353
354 $parent = dirname( $dir );
355
356 while ( ! empty( $parent ) && ! is_dir( $parent ) && dirname( $parent ) !== $parent ) {
357 $parent = dirname( $parent );
358 }
359
360 if ( empty( $parent ) ) {
361 return false;
362 }
363
364 $perms = self::get_permissions( $parent );
365
366 if ( ! is_int( $perms ) ) {
367 $perms = self::get_default_permissions();
368 }
369
370 $cached_umask = umask( 0 );
371 $result = @mkdir( $dir, $perms, true ); // phpcs:ignore -- We don't want to have fatalities here.
372 umask( $cached_umask );
373
374 if ( $result ) {
375 self::add_file_listing_protection( $dir );
376
377 return true;
378 }
379
380 return false;
381 }
382
383 /**
384 * Retrieves the full path to plugin's working directory. Returns a folder path with a trailing slash. It also
385 * creates the folder unless the $skip_creation parameter is set to true.
386 *
387 * Default path is "{uploads folder}/self::WP2FA_UPLOADS_DIR/"
388 *
389 * @param string $path Optional path relative to the working directory.
390 * @param bool $skip_creation If true, the folder will not be created.
391 * @param bool $ignore_site If true, there will be no sub-site specific subfolder in multisite context.
392 *
393 * @return string|\WP_Error
394 *
395 * @since 2.6.0
396 */
397 public static function get_upload_path( $path = '', $skip_creation = false, $ignore_site = false ) {
398 $result = '';
399
400 $upload_dir = wp_upload_dir( null, false );
401 if ( is_array( $upload_dir ) && array_key_exists( 'basedir', $upload_dir ) ) {
402 $result = $upload_dir['basedir'] . \DIRECTORY_SEPARATOR . self::WP2FA_UPLOADS_DIR . \DIRECTORY_SEPARATOR;
403 } elseif ( defined( 'WP_CONTENT_DIR' ) ) {
404 // Fallback in case there is a problem with filesystem.
405 $result = WP_CONTENT_DIR . \DIRECTORY_SEPARATOR . 'uploads' . \DIRECTORY_SEPARATOR . self::WP2FA_UPLOADS_DIR . \DIRECTORY_SEPARATOR;
406 }
407
408 if ( empty( $result ) ) {
409 // Empty result here means invalid custom path or a problem with WordPress (uploads folder issue or mission WP_CONTENT_DIR).
410 return new \WP_Error( '2fa_uplaods_dir_missing', __( 'The base of WSAL working directory cannot be determined. Custom path is invalid or there is some other issue with your WordPress installation.', 'wp-2fa' ) );
411 }
412
413 // Append site specific subfolder in multisite context.
414 if ( ! $ignore_site && WP_Helper::is_multisite() ) {
415 $site_id = \get_current_blog_id();
416 if ( $site_id > 0 ) {
417 $result .= 'sites' . \DIRECTORY_SEPARATOR . $site_id . \DIRECTORY_SEPARATOR;
418 }
419 }
420
421 // Append optional path passed as a parameter.
422 if ( $path && is_string( $path ) ) {
423 $result .= $path . \DIRECTORY_SEPARATOR;
424 }
425
426 if ( ! file_exists( $result ) ) {
427 if ( ! $skip_creation ) {
428 if ( ! \wp_mkdir_p( $result ) ) {
429 return new \WP_Error(
430 'mkdir_failed',
431 sprintf(
432 /* translators: %s: Directory path. */
433 __( 'Unable to create directory %s. Is its parent directory writable by the server?', 'wp-2fa' ),
434 esc_html( $result )
435 )
436 );
437 }
438 }
439
440 self::add_file_listing_protection( $result );
441 }
442
443 return $result;
444 }
445
446 /**
447 * Remove the supplied file.
448 *
449 * @param string $file - The name of the file and path.
450 *
451 * @return bool|WP_Error Boolean true on success or a WP_Error object if an error occurs.
452 *
453 * @since 2.6.0
454 */
455 public static function remove( $file ) {
456 if ( ! self::exists( $file ) ) {
457 return true;
458 }
459
460 if ( ! PHP_Helper::is_callable( 'unlink' ) ) {
461 return new \WP_Error(
462 'wp-2fa',
463 // translators: the name of the file.
464 sprintf( __( 'The file %s could not be removed as the unlink() function is disabled. This is a system configuration issue.', 'wp-2fa' ), $file )
465 );
466 }
467
468 $result = @unlink( $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.unlink_unlink
469
470 @clearstatcache( true, $file ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
471
472 if ( $result ) {
473 return true;
474 }
475
476 return new \WP_Error(
477 'wp-2fa',
478 sprintf(
479 // translators: the name of the file.
480 __( 'Unable to remove %s due to an unknown error.', 'wp-2fa' ),
481 $file
482 )
483 );
484 }
485
486 /**
487 * Gets the content of a file
488 *
489 * @param string $file - The name of the file.
490 *
491 * @return bool|string
492 *
493 * @since 2.4.0
494 */
495 protected static function get_file_contents( string $file ) {
496 if ( ! self::exists( $file ) ) {
497 return '';
498 }
499
500 $contents = self::read( $file );
501
502 if ( is_wp_error( $contents ) ) {
503 return false;
504 }
505
506 return $contents;
507 }
508
509 /**
510 * Write the supplied modification to the wp-config.php file.
511 *
512 * @since 2.4.0
513 *
514 * @param string $modification - The modification to add to the wp-config.php file.
515 *
516 * @return bool
517 */
518 private static function write_wp_config( $modification ) {
519 $file_path = self::get_wp_config_file_path();
520
521 return self::update( $file_path, $modification );
522 }
523
524 /**
525 * Updates the content of a file
526 *
527 * @param string $file - The name of the file to update.
528 * @param string $modification - The modification to be added to the file.
529 *
530 * @return boolean
531 *
532 * @since 2.4.0
533 */
534 private static function update( string $file, string $modification ): bool {
535 // Check to make sure that the settings give permission to write files.
536 if ( ! self::can_write_to_file( $file ) ) {
537
538 return false;
539 }
540
541 $contents = self::read( $file );
542
543 if ( is_wp_error( $contents ) ) {
544 return $contents;
545 }
546
547 if ( ! $contents ) {
548 return false;
549 }
550
551 $modification = ltrim( $modification, "\x0B\r\n\0" );
552 $modification = rtrim( $modification, " \t\x0B\r\n\0" );
553
554 if ( empty( $modification ) ) {
555 // If there isn't a new modification, write the content without any modification and return the result.
556
557 if ( empty( $contents ) ) {
558 $contents = PHP_EOL;
559 }
560
561 return false;
562 }
563
564 $placeholder = self::get_placeholder();
565
566 // Ensure that the generated placeholder can be uniquely identified in the contents.
567 while ( false !== strpos( $contents, $placeholder ) ) {
568 $placeholder = self::get_placeholder();
569 }
570
571 // Put the placeholder at the beginning of the file, after the <?php tag.
572 $contents = preg_replace( '/^(.*?<\?(?:php)?)\s*(?:\r\r\n|\r\n|\r|\n)/', "\${1}$placeholder", $contents, 1 );
573
574 if ( false === strpos( $contents, $placeholder ) ) {
575 $contents = preg_replace( '/^(.*?<\?(?:php)?)\s*(.+(?:\r\r\n|\r\n|\r|\n))/', "\${1}$placeholder$2", $contents, 1 );
576 }
577
578 if ( false === strpos( $contents, $placeholder ) ) {
579 $contents = "<?php$placeholder?" . ">$contents";
580 }
581
582 // Pad away from existing sections when adding iThemes Security modifications.
583 $line_ending = self::get_line_ending( $contents );
584
585 while ( ! preg_match( "/(?:^|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n))$placeholder/", $contents ) ) {
586 $contents = preg_replace( "/$placeholder/", "$line_ending$placeholder", $contents );
587 }
588 while ( ! preg_match( "/$placeholder(?:$|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n))/", $contents ) ) {
589 $contents = preg_replace( "/$placeholder/", "$placeholder$line_ending", $contents );
590 }
591
592 // Ensure that the file ends in a newline if the placeholder is at the end.
593 $contents = preg_replace( "/$placeholder$/", "$placeholder$line_ending", $contents );
594
595 if ( ! empty( $modification ) ) {
596 // Normalize line endings of the modification to match the file's line endings.
597 $modification = self::normalize_line_endings( $modification, $line_ending );
598
599 // Exchange the placeholder with the modification.
600 $contents = preg_replace( "/$placeholder/", $modification, $contents );
601 }
602
603 // Write the new contents to the file and return the results.
604 return self::write( $file, $contents );
605 }
606
607 /**
608 * Generates unique placeholder to be used in the string
609 *
610 * @return string
611 *
612 * @since 2.4.0
613 */
614 private static function get_placeholder(): string {
615 $characters = str_split( 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' );
616
617 $string = '';
618
619 for ( $x = 0; $x < 100; $x++ ) {
620 $string .= array_rand( $characters );
621 }
622
623 return $string;
624 }
625
626 /**
627 * Returns to proper line endings of a given content
628 *
629 * @param string $contents - The text to be checked.
630 *
631 * @return string
632 *
633 * @since 2.4.0
634 */
635 private static function get_line_ending( string $contents ) {
636 if ( empty( $contents ) ) {
637 return PHP_EOL;
638 }
639
640 $count["\n"] = preg_match_all( "/(?<!\r)\n/", $contents, $matches );
641 $count["\r"] = preg_match_all( "/\r(?!\n)/", $contents, $matches );
642 $count["\r\n"] = preg_match_all( "/(?<!\r)\r\n/", $contents, $matches );
643 $count["\r\r\n"] = preg_match_all( "/\r\r\n/", $contents, $matches );
644
645 if ( 0 === array_sum( $count ) ) {
646 return PHP_EOL;
647 }
648
649 $maxes = array_keys( $count, max( $count ), true );
650
651 if ( in_array( "\r\r\n", $maxes, true ) ) {
652 return "\r\r\n";
653 }
654
655 return $maxes[0];
656 }
657
658 /**
659 * Normalizing fileendings for different platforms
660 *
661 * @param string $content - The file content to be checked.
662 * @param string $line_ending - Line endings to be used.
663 *
664 * @return string
665 *
666 * @since 2.4.0
667 */
668 private static function normalize_line_endings( string $content, string $line_ending = "\n" ): string {
669 return preg_replace( '/(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n/', $line_ending, $content );
670 }
671
672 /**
673 * Reads the content of a file
674 *
675 * @param string $file - The file to read.
676 *
677 * @return bool|string
678 *
679 * @since 2.4.0
680 */
681 private static function read( string $file ) {
682 if ( ! is_file( $file ) ) {
683 return false;
684 }
685
686 $callable = array();
687
688 if ( PHP_Helper::is_callable( 'file_get_contents' ) ) {
689 $callable[] = 'file_get_contents';
690 }
691 if ( PHP_Helper::is_callable( 'fopen' ) && PHP_Helper::is_callable( 'feof' ) && PHP_Helper::is_callable( 'fread' ) && PHP_Helper::is_callable( 'flock' ) ) {
692 $callable[] = 'fopen';
693 }
694
695 if ( empty( $callable ) ) {
696 return false;
697 }
698
699 $contents = false;
700
701 // Different permissions to try in case the starting set of permissions are prohibiting read.
702 $trial_perms = array(
703 false,
704 0644,
705 0664,
706 0666,
707 );
708
709 foreach ( $trial_perms as $perms ) {
710 if ( false !== $perms ) {
711 if ( ! isset( $original_file_perms ) ) {
712 $original_file_perms = self::get_permissions( $file );
713 }
714
715 self::chmod( $file, $perms );
716 }
717
718 if ( in_array( 'fopen', $callable, true ) ) {
719 if ( false !== ( $fh = fopen( $file, 'rb' ) ) ) { // phpcs:ignore -- Ignored the assigned on the same line error
720 flock( $fh, LOCK_SH );
721
722 $contents = '';
723
724 while ( ! feof( $fh ) ) {
725 $contents .= fread( $fh, 1024 ); // phpcs:ignore -- Ignored the file operation notification
726 }
727
728 flock( $fh, LOCK_UN );
729 fclose( $fh ); // phpcs:ignore -- Ignored the file operation notification
730 }
731 }
732
733 if ( ( false === $contents ) && in_array( 'file_get_contents', $callable, true ) ) {
734 $contents = file_get_contents( $file ); // phpcs:ignore -- Ignored the wp_remote_get usage
735 }
736
737 if ( false !== $contents ) {
738 if ( isset( $original_file_perms ) && is_int( $original_file_perms ) ) {
739 // Reset the original file permissions if they were modified.
740 self::chmod( $file, $original_file_perms );
741 }
742
743 return $contents;
744 }
745 }
746
747 return false;
748 }
749
750 /**
751 * Changes the permissions of a file
752 *
753 * @param string $file - The file to change permissions to.
754 * @param mixed $perms - The permissions to be set.
755 *
756 * @return bool
757 *
758 * @since 2.4.0
759 */
760 private static function chmod( string $file, $perms ): bool {
761 if ( ! is_int( $perms ) ) {
762 return \CURLOPT_SSL_FALSESTART;
763 }
764
765 if ( ! PHP_Helper::is_callable( 'chmod' ) ) {
766 return false;
767 }
768
769 return @chmod( $file, $perms ); // phpcs:ignore -- Don't need fatalities here.
770 }
771
772 /**
773 * Returns the default filesystem permissions
774 *
775 * @return integer
776 *
777 * @since 2.4.0
778 */
779 private static function get_default_permissions() {
780
781 $perms = self::get_permissions( ABSPATH );
782
783 if ( ! is_wp_error( $perms ) ) {
784 return $perms;
785 }
786
787 return 0755;
788 }
789 }
790 }
791