Make WordPress Core

Ticket #30245: 30245.2.diff

File 30245.2.diff, 20.9 KB (added by dd32, 7 years ago)
  • src/wp-admin/includes/class-wp-upgrader-skins.php

     
    2828        }
    2929
    3030        public function set_upgrader(&$upgrader) {
    3131                if ( is_object($upgrader) )
    3232                        $this->upgrader =& $upgrader;
    3333                $this->add_strings();
    3434        }
    3535
    3636        public function add_strings() {
    3737        }
    3838
    3939        public function set_result($result) {
    4040                $this->result = $result;
    4141        }
    4242
    43         public function request_filesystem_credentials($error = false) {
     43        public function request_filesystem_credentials( $error = false, $context = false, $allow_relaxed_file_ownership = false ) {
    4444                $url = $this->options['url'];
    45                 $context = $this->options['context'];
    46                 if ( !empty($this->options['nonce']) )
     45                if ( ! $context ) {
     46                        $context = $this->options['context'];
     47                }
     48                if ( !empty($this->options['nonce']) ) {
    4749                        $url = wp_nonce_url($url, $this->options['nonce']);
    48                 return request_filesystem_credentials($url, '', $error, $context); //Possible to bring inline, Leaving as is for now.
     50                }
     51
     52                $extra_fields = array();
     53
     54                return request_filesystem_credentials( $url, '', $error, $context, $extra_fields, $allow_relaxed_file_ownership );
    4955        }
    5056
    5157        public function header() {
    5258                if ( $this->done_header ) {
    5359                        return;
    5460                }
    5561                $this->done_header = true;
    5662                echo '<div class="wrap">';
    5763                echo '<h2>' . $this->options['title'] . '</h2>';
    5864        }
    5965        public function footer() {
    6066                if ( $this->done_footer ) {
    6167                        return;
    6268                }
    6369                $this->done_footer = true;
     
    687693}
    688694
    689695/**
    690696 * Upgrader Skin for Automatic WordPress Upgrades
    691697 *
    692698 * This skin is designed to be used when no output is intended, all output
    693699 * is captured and stored for the caller to process and log/email/discard.
    694700 *
    695701 * @package WordPress
    696702 * @subpackage Upgrader
    697703 * @since 3.7.0
    698704 */
    699705class Automatic_Upgrader_Skin extends WP_Upgrader_Skin {
    700706        protected $messages = array();
    701707
    702         public function request_filesystem_credentials( $error = false, $context = '' ) {
    703                 if ( $context )
     708        public function request_filesystem_credentials( $error = false, $context = '', $allow_relaxed_file_ownership = false ) {
     709                if ( $context ) {
    704710                        $this->options['context'] = $context;
     711                }
    705712                // TODO: fix up request_filesystem_credentials(), or split it, to allow us to request a no-output version
    706713                // This will output a credentials form in event of failure, We don't want that, so just hide with a buffer
    707714                ob_start();
    708                 $result = parent::request_filesystem_credentials( $error );
     715                $result = parent::request_filesystem_credentials( $error, $context, $allow_relaxed_file_ownership );
    709716                ob_end_clean();
    710717                return $result;
    711718        }
    712719
    713720        public function get_upgrade_messages() {
    714721                return $this->messages;
    715722        }
    716723
    717724        public function feedback( $data ) {
    718725                if ( is_wp_error( $data ) )
    719726                        $string = $data->get_error_message();
    720727                else if ( is_array( $data ) )
    721728                        return;
    722729                else
    723730                        $string = $data;
  • src/wp-admin/includes/class-wp-upgrader.php

     
    4949                $this->strings['fs_no_themes_dir'] = __('Unable to locate WordPress Theme directory.');
    5050                /* translators: %s: directory name */
    5151                $this->strings['fs_no_folder'] = __('Unable to locate needed folder (%s).');
    5252
    5353                $this->strings['download_failed'] = __('Download failed.');
    5454                $this->strings['installing_package'] = __('Installing the latest version&#8230;');
    5555                $this->strings['no_files'] = __('The package contains no files.');
    5656                $this->strings['folder_exists'] = __('Destination folder already exists.');
    5757                $this->strings['mkdir_failed'] = __('Could not create directory.');
    5858                $this->strings['incompatible_archive'] = __('The package could not be installed.');
    5959
    6060                $this->strings['maintenance_start'] = __('Enabling Maintenance mode&#8230;');
    6161                $this->strings['maintenance_end'] = __('Disabling Maintenance mode&#8230;');
    6262        }
    6363
    64         public function fs_connect( $directories = array() ) {
     64        public function fs_connect( $directories = array(), $allow_relaxed_file_ownership = false ) {
    6565                global $wp_filesystem;
    6666
    67                 if ( false === ($credentials = $this->skin->request_filesystem_credentials()) )
     67                if ( false === ( $credentials = $this->skin->request_filesystem_credentials( false, $directories[0], $allow_relaxed_file_ownership ) ) ) {
    6868                        return false;
     69                }
    6970
    70                 if ( ! WP_Filesystem($credentials) ) {
     71                if ( ! WP_Filesystem( $credentials, $directories[0], $allow_relaxed_file_ownership ) ) {
    7172                        $error = true;
    7273                        if ( is_object($wp_filesystem) && $wp_filesystem->errors->get_error_code() )
    7374                                $error = $wp_filesystem->errors;
    74                         $this->skin->request_filesystem_credentials($error); //Failed to connect, Error and request again
     75                        // Failed to connect, Error and request again
     76                        $this->skin->request_filesystem_credentials( $error, $directories[0], $allow_relaxed_file_ownership );
    7577                        return false;
    7678                }
    7779
    7880                if ( ! is_object($wp_filesystem) )
    7981                        return new WP_Error('fs_unavailable', $this->strings['fs_unavailable'] );
    8082
    8183                if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )
    8284                        return new WP_Error('fs_error', $this->strings['fs_error'], $wp_filesystem->errors);
    8385
    8486                foreach ( (array)$directories as $dir ) {
    8587                        switch ( $dir ) {
    8688                                case ABSPATH:
    8789                                        if ( ! $wp_filesystem->abspath() )
    8890                                                return new WP_Error('fs_no_root_dir', $this->strings['fs_no_root_dir']);
    8991                                        break;
     
    14441446                $this->strings['start_rollback'] = __( 'Attempting to roll back to previous version.' );
    14451447                $this->strings['rollback_was_required'] = __( 'Due to an error during updating, WordPress has rolled back to your previous version.' );
    14461448        }
    14471449
    14481450        public function upgrade( $current, $args = array() ) {
    14491451                global $wp_filesystem;
    14501452
    14511453                include( ABSPATH . WPINC . '/version.php' ); // $wp_version;
    14521454
    14531455                $start_time = time();
    14541456
    14551457                $defaults = array(
    14561458                        'pre_check_md5'    => true,
    14571459                        'attempt_rollback' => false,
    14581460                        'do_rollback'      => false,
     1461                        'allow_relaxed_file_ownership' => false,
    14591462                );
    14601463                $parsed_args = wp_parse_args( $args, $defaults );
    14611464
    14621465                $this->init();
    14631466                $this->upgrade_strings();
    14641467
    14651468                // Is an update available?
    14661469                if ( !isset( $current->response ) || $current->response == 'latest' )
    14671470                        return new WP_Error('up_to_date', $this->strings['up_to_date']);
    14681471
    1469                 $res = $this->fs_connect( array(ABSPATH, WP_CONTENT_DIR) );
     1472                $res = $this->fs_connect( array( ABSPATH, WP_CONTENT_DIR ), $parsed_args['allow_relaxed_file_ownership'] );
    14701473                if ( ! $res || is_wp_error( $res ) ) {
    14711474                        return $res;
    14721475                }
    14731476
    14741477                $wp_dir = trailingslashit($wp_filesystem->abspath());
    14751478
    14761479                $partial = true;
    14771480                if ( $parsed_args['do_rollback'] )
    14781481                        $partial = false;
    14791482                elseif ( $parsed_args['pre_check_md5'] && ! $this->check_files() )
    14801483                        $partial = false;
    14811484
    14821485                /*
    14831486                 * If partial update is returned from the API, use that, unless we're doing
    14841487                 * a reinstall. If we cross the new_bundled version number, then use
     
    18991902         * @since 3.7.0
    19001903         *
    19011904         * @param string $type    The type of update being checked: 'core', 'theme',
    19021905         *                        'plugin', 'translation'.
    19031906         * @param object $item    The update offer.
    19041907         * @param string $context The filesystem context (a path) against which filesystem
    19051908         *                        access and status should be checked.
    19061909         */
    19071910        public function should_update( $type, $item, $context ) {
    19081911                // Used to see if WP_Filesystem is set up to allow unattended updates.
    19091912                $skin = new Automatic_Upgrader_Skin;
    19101913
    19111914                if ( $this->is_disabled() )
    19121915                        return false;
    19131916
     1917                // Only relax the filesystem checks when the update doesn't include new files
     1918                $allow_relaxed_file_ownership = false;
     1919                if ( 'core' == $type && isset( $item->new_files ) && ! $item->new_files ) {
     1920                        $allow_relaxed_file_ownership = true;
     1921                } /* elseif ( 'translation' == $type && $translation_is_installed ) {
     1922                        $allow_relaxed_file_ownership = true;
     1923                }*/
     1924
    19141925                // If we can't do an auto core update, we may still be able to email the user.
    1915                 if ( ! $skin->request_filesystem_credentials( false, $context ) || $this->is_vcs_checkout( $context ) ) {
     1926                if ( ! $skin->request_filesystem_credentials( false, $context, $allow_relaxed_file_ownership ) || $this->is_vcs_checkout( $context ) ) {
    19161927                        if ( 'core' == $type )
    19171928                                $this->send_core_update_notification_email( $item );
    19181929                        return false;
    19191930                }
    19201931
    19211932                // Next up, is this an item we can update?
    19221933                if ( 'core' == $type )
    19231934                        $update = Core_Upgrader::should_update_to_version( $item->current );
    19241935                else
    19251936                        $update = ! empty( $item->autoupdate );
    19261937
    19271938                /**
    19281939                 * Filter whether to automatically update core, a plugin, a theme, or a language.
    19291940                 *
    19301941                 * The dynamic portion of the hook name, $type, refers to the type of update
     
    20602071                                $skin->feedback( __( 'Updating theme: %s' ), $item_name );
    20612072                                break;
    20622073                        case 'plugin':
    20632074                                $upgrader_item = $item->plugin;
    20642075                                $plugin_data = get_plugin_data( $context . '/' . $upgrader_item );
    20652076                                $item_name = $plugin_data['Name'];
    20662077                                $skin->feedback( __( 'Updating plugin: %s' ), $item_name );
    20672078                                break;
    20682079                        case 'translation':
    20692080                                $language_item_name = $upgrader->get_name_for_update( $item );
    20702081                                $item_name = sprintf( __( 'Translations for %s' ), $language_item_name );
    20712082                                $skin->feedback( sprintf( __( 'Updating translations for %1$s (%2$s)&#8230;' ), $language_item_name, $item->language ) );
    20722083                                break;
    20732084                }
    20742085
     2086                $allow_relaxed_file_ownership = false;
     2087                if ( 'core' == $type && isset( $item->new_files ) && ! $item->new_files ) {
     2088                        $allow_relaxed_file_ownership = true;
     2089                }/* elseif ( 'translation' == $type && $translation_is_installed ) {
     2090                        $allow_relaxed_file_ownership = true;
     2091                }*/
     2092
    20752093                // Boom, This sites about to get a whole new splash of paint!
    20762094                $upgrade_result = $upgrader->upgrade( $upgrader_item, array(
    20772095                        'clear_update_cache' => false,
    20782096                        // Always use partial builds if possible for core updates.
    20792097                        'pre_check_md5'      => false,
    20802098                        // Only available for core updates.
    20812099                        'attempt_rollback'   => true,
     2100                        // Allow relaxed file ownership in some scenarios
     2101                        'allow_relaxed_file_ownership' => $allow_relaxed_file_ownership,
     2102                       
    20822103                ) );
    20832104
    20842105                // If the filesystem is unavailable, false is returned.
    20852106                if ( false === $upgrade_result ) {
    20862107                        $upgrade_result = new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) );
    20872108                }
    20882109
    20892110                // Core doesn't output this, so lets append it so we don't get confused.
    20902111                if ( 'core' == $type ) {
    20912112                        if ( is_wp_error( $upgrade_result ) ) {
    20922113                                $skin->error( __( 'Installation Failed' ), $upgrade_result );
    20932114                        } else {
    20942115                                $skin->feedback( __( 'WordPress updated successfully' ) );
    20952116                        }
    20962117                }
  • src/wp-admin/includes/file.php

     
    797797                }
    798798        }
    799799        return true;
    800800}
    801801
    802802/**
    803803 * Initialises and connects the WordPress Filesystem Abstraction classes.
    804804 * This function will include the chosen transport and attempt connecting.
    805805 *
    806806 * Plugins may add extra transports, And force WordPress to use them by returning the filename via the 'filesystem_method_file' filter.
    807807 *
    808808 * @since 2.5.0
    809809 *
    810810 * @param array $args (optional) Connection args, These are passed directly to the WP_Filesystem_*() classes.
    811811 * @param string $context (optional) Context for get_filesystem_method(), See function declaration for more information.
     812 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable.
    812813 * @return null|boolean false on failure, true on success
    813814 */
    814 function WP_Filesystem( $args = false, $context = false ) {
     815function WP_Filesystem( $args = false, $context = false, $allow_relaxed_file_ownership = false ) {
    815816        global $wp_filesystem;
    816817
    817818        require_once(ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php');
    818819
    819         $method = get_filesystem_method($args, $context);
     820        $method = get_filesystem_method( $args, $context, $allow_relaxed_file_ownership );
    820821
    821822        if ( ! $method )
    822823                return false;
    823824
    824825        if ( ! class_exists("WP_Filesystem_$method") ) {
    825826
    826827                /**
    827828                 * Filter the path for a specific filesystem method class file.
    828829                 *
    829830                 * @since 2.6.0
    830831                 *
    831832                 * @see get_filesystem_method()
    832833                 *
    833834                 * @param string $path   Path to the specific filesystem method class file.
    834835                 * @param string $method The filesystem method to use.
     
    867868
    868869/**
    869870 * Determines which Filesystem Method to use.
    870871 * The priority of the Transports are: Direct, SSH2, FTP PHP Extension, FTP Sockets (Via Sockets class, or fsockopen())
    871872 *
    872873 * Note that the return value of this function can be overridden in 2 ways
    873874 *  - By defining FS_METHOD in your <code>wp-config.php</code> file
    874875 *  - By using the filesystem_method filter
    875876 * Valid values for these are: 'direct', 'ssh2', 'ftpext' or 'ftpsockets'
    876877 * Plugins may also define a custom transport handler, See the WP_Filesystem function for more information.
    877878 *
    878879 * @since 2.5.0
    879880 *
    880881 * @param array $args Connection details.
    881882 * @param string $context Full path to the directory that is tested for being writable.
     883 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable.
    882884 * @return string The transport to use, see description for valid return values.
    883885 */
    884 function get_filesystem_method($args = array(), $context = false) {
     886function get_filesystem_method( $args = array(), $context = false, $allow_relaxed_file_ownership = false ) {
    885887        $method = defined('FS_METHOD') ? FS_METHOD : false; // Please ensure that this is either 'direct', 'ssh2', 'ftpext' or 'ftpsockets'
    886888
    887         if ( ! $method && function_exists('getmyuid') && function_exists('fileowner') ){
    888                 if ( !$context )
     889        if ( ! $method ) {
     890                if ( !$context ) {
    889891                        $context = WP_CONTENT_DIR;
     892                }
    890893
    891894                // If the directory doesn't exist (wp-content/languages) then use the parent directory as we'll create it.
    892                 if ( WP_LANG_DIR == $context && ! is_dir( $context ) )
     895                if ( WP_LANG_DIR == $context && ! is_dir( $context ) ) {
    893896                        $context = dirname( $context );
     897                }
    894898
    895899                $context = trailingslashit($context);
     900
    896901                $temp_file_name = $context . 'temp-write-test-' . time();
    897902                $temp_handle = @fopen($temp_file_name, 'w');
    898903                if ( $temp_handle ) {
    899                         if ( getmyuid() == @fileowner($temp_file_name) )
     904                        /**
     905                         * getmyuid() will return the User ID of the current script, equiv to fileowner( __FILE__ ).
     906                         * getmygid() will return the Group ID of the current script, equiv to filegroup( __FILE__ ).
     907                         *
     908                         * The below tests are used to select the appropriate WP_Filesystem_* class, allowing for
     909                         * created files to be owned by the correct user. If no new files are being created, then
     910                         * group / world writable is enough to modify core files.
     911                         */
     912
     913                        $owner_writable_functions_exist = function_exists('getmyuid') && function_exists('fileowner');
     914                        $group_writable_functions_exist = function_exists('getmygid') && function_exists('filegroup');
     915
     916                        if ( $owner_writable_functions_exist && getmyuid() == @fileowner( $temp_file_name ) ) {
     917                                $method = 'direct';
     918                        } elseif ( $allow_relaxed_file_ownership && $group_writable_functions_exist && getmygid() === @filegroup( $temp_file_name ) ) {
    900919                                $method = 'direct';
     920                        } elseif ( $allow_relaxed_file_ownership ) {
     921                                // wp_is_writable( $context ) is assumed based on fopen() suceeding.
     922                                $method = 'direct';
     923                        }
    901924                        @fclose($temp_handle);
    902925                        @unlink($temp_file_name);
    903926                }
    904927        }
    905928
    906929        if ( ! $method && isset($args['connection_type']) && 'ssh' == $args['connection_type'] && extension_loaded('ssh2') && function_exists('stream_get_contents') ) $method = 'ssh2';
    907930        if ( ! $method && extension_loaded('ftp') ) $method = 'ftpext';
    908931        if ( ! $method && ( extension_loaded('sockets') || function_exists('fsockopen') ) ) $method = 'ftpsockets'; //Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread
    909932
    910933        /**
    911934         * Filter the filesystem method to use.
    912935         *
    913936         * @since 2.6.0
    914937         *
    915938         * @param string $method Filesystem method to return.
     
    921944/**
    922945 * Displays a form to the user to request for their FTP/SSH details in order to connect to the filesystem.
    923946 * All chosen/entered details are saved, Excluding the Password.
    924947 *
    925948 * Hostnames may be in the form of hostname:portnumber (eg: wordpress.org:2467) to specify an alternate FTP/SSH port.
    926949 *
    927950 * Plugins may override this form by returning true|false via the <code>request_filesystem_credentials</code> filter.
    928951 *
    929952 * @since 2.5.0
    930953 *
    931954 * @param string $form_post the URL to post the form to
    932955 * @param string $type the chosen Filesystem method in use
    933956 * @param boolean $error if the current request has failed to connect
    934957 * @param string $context The directory which is needed access to, The write-test will be performed on this directory by get_filesystem_method()
    935958 * @param string $extra_fields Extra POST fields which should be checked for to be included in the post.
     959 * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable.
    936960 * @return boolean False on failure. True on success.
    937961 */
    938 function request_filesystem_credentials($form_post, $type = '', $error = false, $context = false, $extra_fields = null) {
     962function request_filesystem_credentials($form_post, $type = '', $error = false, $context = false, $extra_fields = null, $allow_relaxed_file_ownership = false ) {
    939963
    940964        /**
    941965         * Filter the filesystem credentials form output.
    942966         *
    943967         * Returning anything other than an empty string will effectively short-circuit
    944968         * output of the filesystem credentials form, returning that value instead.
    945969         *
    946970         * @since 2.5.0
    947971         *
    948972         * @param mixed  $output       Form output to return instead. Default empty.
    949973         * @param string $form_post    URL to POST the form to.
    950974         * @param string $type         Chosen type of filesystem.
    951975         * @param bool   $error        Whether the current request has failed to connect.
    952976         *                             Default false.
    953977         * @param string $context      Full path to the directory that is tested for
    954978         *                             being writable.
     979         * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable.
    955980         * @param array  $extra_fields Extra POST fields.
    956981         */
    957         $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields );
     982        $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership );
    958983        if ( '' !== $req_cred )
    959984                return $req_cred;
    960985
    961         if ( empty($type) )
    962                 $type = get_filesystem_method(array(), $context);
     986        if ( empty($type) ) {
     987                $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership );
     988        }
    963989
    964990        if ( 'direct' == $type )
    965991                return true;
    966992
    967993        if ( is_null( $extra_fields ) )
    968994                $extra_fields = array( 'version', 'locale' );
    969995
    970996        $credentials = get_option('ftp_credentials', array( 'hostname' => '', 'username' => ''));
    971997
    972998        // 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)
    973999        $credentials['hostname'] = defined('FTP_HOST') ? FTP_HOST : (!empty($_POST['hostname']) ? wp_unslash( $_POST['hostname'] ) : $credentials['hostname']);
    9741000        $credentials['username'] = defined('FTP_USER') ? FTP_USER : (!empty($_POST['username']) ? wp_unslash( $_POST['username'] ) : $credentials['username']);
    9751001        $credentials['password'] = defined('FTP_PASS') ? FTP_PASS : (!empty($_POST['password']) ? wp_unslash( $_POST['password'] ) : '');
    9761002
    9771003        // Check to see if we are setting the public/private keys for ssh
  • src/wp-includes/update.php

     
    130130                return false;
    131131
    132132        $offers = $body['offers'];
    133133
    134134        foreach ( $offers as &$offer ) {
    135135                foreach ( $offer as $offer_key => $value ) {
    136136                        if ( 'packages' == $offer_key )
    137137                                $offer['packages'] = (object) array_intersect_key( array_map( 'esc_url', $offer['packages'] ),
    138138                                        array_fill_keys( array( 'full', 'no_content', 'new_bundled', 'partial', 'rollback' ), '' ) );
    139139                        elseif ( 'download' == $offer_key )
    140140                                $offer['download'] = esc_url( $value );
    141141                        else
    142142                                $offer[ $offer_key ] = esc_html( $value );
    143143                }
    144144                $offer = (object) array_intersect_key( $offer, array_fill_keys( array( 'response', 'download', 'locale',
    145                         'packages', 'current', 'version', 'php_version', 'mysql_version', 'new_bundled', 'partial_version', 'notify_email', 'support_email' ), '' ) );
     145                        'packages', 'current', 'version', 'php_version', 'mysql_version', 'new_bundled', 'partial_version', 'notify_email', 'support_email', 'new_files' ), '' ) );
    146146        }
    147147
    148148        $updates = new stdClass();
    149149        $updates->updates = $offers;
    150150        $updates->last_checked = time();
    151151        $updates->version_checked = $wp_version;
    152152
    153153        if ( isset( $body['translations'] ) )
    154154                $updates->translations = $body['translations'];
    155155
    156156        set_site_transient( 'update_core', $updates );
    157157
    158158        if ( ! empty( $body['ttl'] ) ) {
    159159                $ttl = (int) $body['ttl'];
    160160                if ( $ttl && ( time() + $ttl < wp_next_scheduled( 'wp_version_check' ) ) ) {