WordPress.org

Make WordPress Core

Ticket #34419: 34419.diff

File 34419.diff, 9.3 KB (added by dshanske, 4 years ago)

Refactoring of Pingback Code

  • src/wp-includes/class-wp-xmlrpc-server.php

    diff --git src/wp-includes/class-wp-xmlrpc-server.php src/wp-includes/class-wp-xmlrpc-server.php
    index 531dd50..0059840 100644
    class wp_xmlrpc_server extends IXR_Server { 
    62326232                        return $this->pingback_error( 0, __( 'Is there no link to us?' ) );
    62336233
    62346234                // let's find which post is linked to
    6235                 // FIXME: does url_to_postid() cover all these cases already?
    6236                 //        if so, then let's use it and drop the old code.
    6237                 $urltest = parse_url($pagelinkedto);
    6238                 if ( $post_ID = url_to_postid($pagelinkedto) ) {
    6239                         // $way
    6240                 } elseif ( isset( $urltest['path'] ) && preg_match('#p/[0-9]{1,}#', $urltest['path'], $match) ) {
    6241                         // the path defines the post_ID (archives/p/XXXX)
    6242                         $blah = explode('/', $match[0]);
    6243                         $post_ID = (int) $blah[1];
    6244                 } elseif ( isset( $urltest['query'] ) && preg_match('#p=[0-9]{1,}#', $urltest['query'], $match) ) {
    6245                         // the querystring defines the post_ID (?p=XXXX)
    6246                         $blah = explode('=', $match[0]);
    6247                         $post_ID = (int) $blah[1];
    6248                 } elseif ( isset($urltest['fragment']) ) {
    6249                         // an #anchor is there, it's either...
    6250                         if ( intval($urltest['fragment']) ) {
    6251                                 // ...an integer #XXXX (simplest case)
    6252                                 $post_ID = (int) $urltest['fragment'];
    6253                         } elseif ( preg_match('/post-[0-9]+/',$urltest['fragment']) ) {
    6254                                 // ...a post id in the form 'post-###'
    6255                                 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']);
    6256                         } elseif ( is_string($urltest['fragment']) ) {
    6257                                 // ...or a string #title, a little more complicated
    6258                                 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']);
    6259                                 $sql = $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title );
    6260                                 if (! ($post_ID = $wpdb->get_var($sql)) ) {
    6261                                         // returning unknown error '0' is better than die()ing
    6262                                         return $this->pingback_error( 0, '' );
    6263                                 }
    6264                         }
    6265                 } else {
    6266                         // TODO: Attempt to extract a post ID from the given URL
     6235                $post_ID = url_to_postid($pagelinkedto);
     6236                if ( ! $post_ID ) {
    62676237                        return $this->pingback_error( 33, __('The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) );
    62686238                }
    6269                 $post_ID = (int) $post_ID;
    62706239
     6240                $post_ID = (int) $post_ID;
    62716241                $post = get_post($post_ID);
    62726242
    6273                 if ( !$post ) // Post_ID not found
     6243                if ( ! $post ) // Post_ID not found
    62746244                        return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) );
    62756245
    62766246                if ( $post_ID == url_to_postid($pagelinkedfrom) )
    62776247                        return $this->pingback_error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) );
    62786248
    62796249                // Check if pings are on
    6280                 if ( !pings_open($post) )
     6250                if ( ! pings_open( $post ) )
    62816251                        return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) );
    62826252
    62836253                // Let's check that the remote site didn't already pingback this entry
    6284                 if ( $wpdb->get_results( $wpdb->prepare("SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom) ) )
     6254                $dupe_args = array(
     6255                                'comment_post_ID' => $post_ID,
     6256                                'author_url' => $pagelinkedfrom
     6257                );
     6258                $comments = get_comments( $dupe_args );
     6259                if ( ! empty( $comments ) ) {
    62856260                        return $this->pingback_error( 48, __( 'The pingback has already been registered.' ) );
    6286 
     6261                }
    62876262                // very stupid, but gives time to the 'from' server to publish !
    62886263                sleep(1);
    62896264
    class wp_xmlrpc_server extends IXR_Server { 
    62966271                $http_api_args = array(
    62976272                        'timeout' => 10,
    62986273                        'redirection' => 0,
    6299                         'limit_response_size' => 153600, // 150 KB
     6274                        'limit_response_size' => 1048576, // 1mb
    63006275                        'user-agent' => "$user_agent; verifying pingback from $remote_ip",
    63016276                        'headers' => array(
    63026277                                'X-Pingback-Forwarded-For' => $remote_ip,
    63036278                        ),
    63046279                );
     6280                // Make sure the resource is a valid one before retrieving it
     6281                $request = wp_safe_remote_head( $pagelinkedfrom, $http_api_args );
     6282                $content_type = wp_remote_retrieve_header( $request, 'content-type' );
     6283    if ( is_wp_error( $request ) ) {
     6284      return $this->pingback_error( 16, __( 'The source URL does not exist.' ) );
     6285    }
     6286                // Protect Against Someone Trying to Make Us Download Media Files
     6287                if ( preg_match( '#(image|audio|video|model)/#is', $content_type ) ) {
     6288      return $this->pingback_error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) );
     6289                }
    63056290
    63066291                $request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args );
    63076292                $remote_source = $remote_source_original = wp_remote_retrieve_body( $request );
    63086293
    6309                 if ( ! $remote_source ) {
    6310                         return $this->pingback_error( 16, __( 'The source URL does not exist.' ) );
    6311                 }
    6312 
    63136294                /**
    63146295                 * Filter the pingback remote source.
    63156296                 *
    class wp_xmlrpc_server extends IXR_Server { 
    63206301                 */
    63216302                $remote_source = apply_filters( 'pre_remote_source', $remote_source, $pagelinkedto );
    63226303
    6323                 // Work around bug in strip_tags():
    6324                 $remote_source = str_replace( '<!DOC', '<DOC', $remote_source );
    6325                 $remote_source = preg_replace( '/[\r\n\t ]+/', ' ', $remote_source ); // normalize spaces
    6326                 $remote_source = preg_replace( "/<\/*(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $remote_source );
    6327 
    6328                 preg_match( '|<title>([^<]*?)</title>|is', $remote_source, $matchtitle );
    6329                 $title = $matchtitle[1];
    6330                 if ( empty( $title ) )
    6331                         return $this->pingback_error( 32, __('We cannot find a title on that page.' ) );
    6332 
    6333                 $remote_source = strip_tags( $remote_source, '<a>' ); // just keep the tag we need
    6334 
    6335                 $p = explode( "\n\n", $remote_source );
    6336 
    6337                 $preg_target = preg_quote($pagelinkedto, '|');
    6338 
    6339                 foreach ( $p as $para ) {
    6340                         if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link?
    6341                                 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context);
    6342 
    6343                                 // If the URL isn't in a link context, keep looking
    6344                                 if ( empty($context) )
    6345                                         continue;
    6346 
    6347                                 // We're going to use this fake tag to mark the context in a bit
    6348                                 // the marker is needed in case the link text appears more than once in the paragraph
    6349                                 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para);
     6304                $verified = strpos( $remote_source, str_replace( array( 'http://www.', 'http://', 'https://www.', 'https://' ), '', untrailingslashit( preg_replace( '/#.*/', '', $pagelinkedto ) ) ) );
     6305    /**
     6306     * Filter the verification to allow for stricter controls.
     6307     *
     6308     * @since 4.6.0
     6309     *
     6310                 * @param boolean $verified Is the provided
     6311     * @param string $remote_source Response source for the page linked from.
     6312     * @param string $pagelinkedto  URL of the page linked to.
     6313     * @param string $content_type  The content type of the remote source.
     6314     */
     6315    $verified = apply_filters( 'ping_source_verified', $verified, $remote_source, $pagelinkedto, $content_type );
     6316    if ( ! $verified ) { // Link to target not found
     6317      return $this->pingback_error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) );
     6318                }
    63506319
    6351                                 // prevent really long link text
    6352                                 if ( strlen($context[1]) > 100 )
    6353                                         $context[1] = substr($context[1], 0, 100) . '&#8230;';
     6320                // Sanitized for Your Protection
     6321                $remote_source = wp_kses_post( $remote_source );
    63546322
    6355                                 $marker = '<wpcontext>'.$context[1].'</wpcontext>';    // set up our marker
    6356                                 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker
    6357                                 $excerpt = strip_tags($excerpt, '<wpcontext>');        // strip all tags but our context marker
    6358                                 $excerpt = trim($excerpt);
    6359                                 $preg_marker = preg_quote($marker, '|');
    6360                                 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt);
    6361                                 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper
    6362                                 break;
    6363                         }
     6323                preg_match( '/<title>(.+)<\/title>/i', $remote_source, $matchtitle);
     6324                $title = trim( $matchtitle[1] );
     6325                // If there is no title use the domain name
     6326                if ( empty( $title ) ) {
     6327                        $host = wp_parse_url( $pagelinkedfrom );
     6328                        $title = preg_replace( '/^www\./', '', $host['host'] );
    63646329                }
    63656330
    6366                 if ( empty($context) ) // Link to target not found
    6367                         return $this->pingback_error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) );
     6331                $comment_content = sprintf( __( '<a href="%s">%s</a> linked to this.'), $pagelinkedfrom, $title );
    63686332
    63696333                $pagelinkedfrom = str_replace('&', '&amp;', $pagelinkedfrom);
    6370 
    6371                 $context = '[&#8230;] ' . esc_html( $excerpt ) . ' [&#8230;]';
    63726334                $pagelinkedfrom = $this->escape( $pagelinkedfrom );
    63736335
    63746336                $comment_post_ID = (int) $post_ID;
    class wp_xmlrpc_server extends IXR_Server { 
    63766338                $comment_author_email = '';
    63776339                $this->escape($comment_author);
    63786340                $comment_author_url = $pagelinkedfrom;
    6379                 $comment_content = $context;
     6341
    63806342                $this->escape($comment_content);
    63816343                $comment_type = 'pingback';
    63826344
    63836345                $commentdata = compact(
    63846346                        'comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email',
    6385                         'comment_content', 'comment_type', 'remote_source', 'remote_source_original'
     6347                        'comment_content', 'comment_type', 'remote_source', 'remote_source_original', 'content_type'
    63866348                );
    63876349
    63886350                $comment_ID = wp_new_comment($commentdata);