Page 2 of 2

Posted: Fri Jul 06, 2007 12:00 am
by blog.brockha.us
I inspected the code, that Slim first posted. This is no dead code at all, pingbacks are sent already by s9y, the function serendipity_pingback_autodiscover is already called in serendipity_reference_autodiscover directy after serendipity_trackback_autodiscover.

But the code seems to be just a "first investigation" of the pingback protocoll. The corrected XML code is already sent by s9y, if a pingback capable blog / ressource is found. But the XML is posted using GET instead of POST. (This is the call, Slim found debugging what s9y is sending on the tcp/ip stack, I guess). The return of this call is ignored at the moment (it is "XML-RPC server accepts POST requests only" at the moment). I will fix this in my s9y version and if it works, I will send a patch.

So no plugin is needed for this, the functionality is already there.

Posted: Fri Jul 06, 2007 3:49 am
by blog.brockha.us
Well okay. The problem was not, that s9y didn't POST the pingback (it did), but it posted with the wrong content type. Pingback needs text/xml.

So I patched the functions_trackbacks.inc.php to make pingbacks work:

Code: Select all

/**
 * Check a HTTP response if it is a valid XML pingback response
 *
 * @access public
 * @param   string  HTTP Response string
 * @return  mixed   Boolean or error message
 */
function serendipity_pingback_is_success($resp) {
    // This is very rudimentary, but the fault is printed later, so what..
    if (preg_match('@<fault>@', $resp, $matches)) {
        return false;
    }
    return true;
}

/**
 * Perform a HTTP query for autodiscovering a pingback URL
 *
 * @access public
 * @param   string  The HTML of the source URL
 * @param   string  The source URL
 * @param   string  The URL of our blog
 * @return
 */
function serendipity_pingback_autodiscover($res, $loc, $url) {
global $serendipity;
    if (!empty($_SERVER['X-PINGBACK'])) {
        $pingback = $_SERVER['X-PINGBACK'];
    } elseif (preg_match('@<link rel="pingback" href="([^"]+)" ?/?>@i', $res, $matches)) {
        $pingback = $matches[1];
    } else {
		echo '<div>• ' . sprintf('PINGBACK: ' . TRACKBACK_FAILED, TRACKBACK_NOT_FOUND) . '</div>';
        return false;
    }

    // xml-rpc pingback call
    $query = "
<?xml version=\"1.0\"?>
<methodCall>
  <methodName>pingback.ping</methodName>
  <params>
    <param>
      <name>sourceURI</name>
      <value><string>$url</string></value>
    </param>
    <param>
      <name>targetURI</name>
      <value><string>$loc</string></value>
    </param>
  </params>
</methodCall>";

	echo '<div>• ' . sprintf('PINGBACK: ' . TRACKBACK_SENDING, htmlspecialchars($pingback)) . '</div>';
    flush();

	$response =  _serendipity_send($pingback, $query,'text/html');
	$success  =	serendipity_pingback_is_success($response);
    if ($success == true) {
        echo '<div>• ' . 'PINGBACK: ' . TRACKBACK_SENT .'</div>';
    } else {
        echo '<div>• ' . sprintf('PINGBACK: ' . TRACKBACK_FAILED, $response) . '</div>';
    }
	return $success;
}

/**
 * Send a track/pingback ping
 *
 * @access public
 * @param   string  The URL to send a trackback to
 * @param   string  The XML data with the trackback contents
 * @return  string  Reponse
 */
function _serendipity_send($loc, $data, $contenttype=null) {
    global $serendipity;

    $target = parse_url($loc);
    if ($target['query'] != '') {
        $target['query'] = '?' . str_replace('&', '&', $target['query']);
    }

    if (!is_numeric($target['port'])) {
       $target['port'] = 80;
    }

    $uri = $target['scheme'] . '://' . $target['host'] . ':' . $target['port'] . $target['path'] . $target['query'];
    require_once S9Y_PEAR_PATH . 'HTTP/Request.php';
    $options = array('allowRedirects' => true, 'maxRedirects' => 5, 'method' => 'POST');
    serendipity_plugin_api::hook_event('backend_http_request', $options, 'trackback_send');
    serendipity_request_start();

    $req = &new HTTP_Request($uri, $options);
    if (isset($contenttype)){
    	$req->addHeader('Content-Type',$contenttype);
    }
    $req->addRawPostData($data, true);
    $res = $req->sendRequest();

    if (PEAR::isError($res)) {
        serendipity_request_end();
        return false;
    }

    $fContent = $req->getResponseBody();
    serendipity_request_end();
    return $fContent;
}
You will easily see the differences in the code, using the cvs diff, so I posted the complete functions.

Only pingback or trackback should be sent (actually s9y tries both), so first I try trackback, as it sends more infos, and if that fails, I try pingback.

Code: Select all

function serendipity_reference_autodiscover($loc, $url, $author, $title, $text) {

 // .. code ..

    serendipity_request_end();

    if (strlen($fContent) != 0) {
    	$pingresult = serendipity_trackback_autodiscover($fContent, $parsed_loc, $url, $author, $title, $text, $loc);
    	if ($pingresult == false){
        	serendipity_pingback_autodiscover($fContent, $parsed_loc, $url);
    	}
        flush();
    } else {
        echo '<div>• ' . TRACKBACK_NO_DATA . '</div>';
    }
    echo '<hr noshade="noshade" />';
}
There are two things, that should be changed:

1. As I don't want to change the languagefiles, I used the trackback language entries, and precede each entry with 'POSTBACK: ' inside the code. So 'PINGBACK: ' . TRACKBACK_ should be replaced with PINGBACK_ and the new language entries should be added.

2. serendipity_pingback_is_success is very simple, because I am not very firm to the php regular expressions (I used a lot of them in .NET and Java, but the PHP regEx seems to be somehow -hmm- other). I was not able to fetch something spreaded over more than one line. I know how the result XML looks like, so if someone introduces me in PHP regex's, I could make this more stable.

But now pingback works! :-)

Posted: Fri Jul 06, 2007 12:19 pm
by garvinhicking
Hi!

Whoa! Great work, and so fast. I'm very happy to have you aboard. :-)

I just branched serendipity 1.3 and committed your code there:

http://svn.berlios.de/viewcvs/serendipi ... 4&view=rev

I added the new language constants and modified the code a bit so that the old function call parameters are still used. Could you try if it works alright?

Regards,
Garvin

Posted: Fri Jul 06, 2007 5:59 pm
by blog.brockha.us
As we Germans say: Thanks a lot for the flowers ! :D
I like s9y a lot and am very willing to contribute.

I diffed your checked in code with my code. You changed the signature of serendipity_pingback_autodiscover and for that you had to change the code of this function.

The problem is: With your code (what is very similar to the old code) all pingbacks will have the sourceUri pointing to the main URL of the blog, not to the article URL. If too articles in my blog point to one article in the other blog, the second pingback will be rejected, because the other blog will see the same pingback on the same article. And of course: The pingback entry in the other block is pointing to the global blog URL instead of the article URL, that is referencing the other article.

Is serendipity_pingback_autodiscover used in any other place of the code? Why do you want to have the method signature downwardly compatible? I made the signature similar to the serendipity_trackback_autodiscover function.

If downward combatibility is needed (for plugins?), I think it is better to introduce a wrapper function like this:

Code: Select all

/*
 * Only for downward compatibility:
 */
function serendipity_pingback_autodiscover($loc, $body) {
  serendipity_pingback_autodiscover($body, $loc, {$serendipity['baseURL']});
}
and leave my function like it was. Old code using the old signature will have the same (small) problems described above but new code now has a completely working version.

LATER: Uuh.. I see, PHP is not able to define functions with the same name but different parameters.. :-( Hmm..

And I noticed: I missed a parameter description in _serendipity_send: contenttype.

Posted: Fri Jul 06, 2007 6:32 pm
by blog.brockha.us
P.S.: Is there an anonymous access to your version system (is it CVS or SVN?) on BerliOS? I think it is more comfortable fetching the files directly instead of copy and pasting "downlods" from the web interface..

Or will the trunk be in the next nightly build?

Posted: Fri Jul 06, 2007 11:00 pm
by Slim
Thanks, brockhaus, for investigating further. I was sure about the code being used (because I saw it on network trace). But I had no time to dig into it again. Now you've done it.

So s9y uses both trackbacks and pingbacks. Since the RDF part was missing in my tests to WP, there was no trackback action, s9y aborted it. But it still sent the pingback. But in the pingback there is only the base URI submitted. WP rejects that when searching for a link in the source uri document.

If you could add the real URI to the pingback code, it would be great. Then probably trackbacks to WP would work even without RDF code on the target side.

Posted: Sat Jul 07, 2007 12:11 am
by blog.brockha.us
In my code the real URI is sent via pingback. Garvin's code was changed a littel and now only the base url is transmitted.

If you use my patch above, everything works.

I did another patch on base of the source I found in the CVS, that is correcting this problem. If you are interested, I can send it to you (compatibel to 1.2/1.3 and I think earlier). This code is optimal in my eyes, as it has correct pingback, trackback and trackback fallback for WP boards with no RDF info. I did a blogentry today with many links to many blogs, all of the trackback/pingbacks worked very well, no failure. Two of them were WP boards without trackback functionality at all.

Posted: Sun Jul 08, 2007 4:21 pm
by blog.brockha.us
To maintain downward compatibility, the pingback_autodiscover schould look like that:

Code: Select all

/**
 * Perform a HTTP query for autodiscovering a pingback URL
 *
 * @access public
 * @param   string  The URL to try autodiscovery
 * @param   string  The HTML of the source URL
 * @param   string  The URL of our blog article
 * @return
 */
function serendipity_pingback_autodiscover($loc, $body, $url=null) {
global $serendipity;

	// This is the old way, sending pingbacks, for downward compatibility.
	// But this is wrong, as it does link from the main blog URL instead of the article URL
	if (!isset($url)){
		$url = $serendipity['baseURL'];
	}

    if (!empty($_SERVER['X-PINGBACK'])) {
        $pingback = $_SERVER['X-PINGBACK'];
    } elseif (preg_match('@<link rel="pingback" href="([^"]+)" ?/?>@i', $body, $matches)) {
        $pingback = $matches[1];
    } else {
		echo '<div>• ' . sprintf(PINGBACK_FAILED, PINGBACK_NOT_FOUND) . '</div>';
        return false;
    }

    // xml-rpc pingback call
    $query = "
<?xml version=\"1.0\"?>
<methodCall>
  <methodName>pingback.ping</methodName>
  <params>
    <param>
      <name>sourceURI</name>
      <value><string>$url</string></value>
    </param>
    <param>
      <name>targetURI</name>
      <value><string>$loc</string></value>
    </param>
  </params>
</methodCall>";

	echo '<div>• ' . sprintf(PINGBACK_SENDING, htmlspecialchars($pingback)) . '</div>';
	// Debug:
	//echo '<div>• DEBUG: ' . $query . '</div>';
    flush();

	$response =  _serendipity_send($pingback, $query,'text/html');
	$success  =	serendipity_pingback_is_success($response);
    if ($success == true) {
        echo '<div>• ' . PINGBACK_SENT .'</div>';
    } else {
        echo '<div>• ' . sprintf(PINGBACK_FAILED, $response) . '</div>';
    }
	return $success;
}
This function should be called in serendipity_reference_autodiscover like this:

Code: Select all

function serendipity_reference_autodiscover($loc, $url, $author, $title, $text) {

// .. code ..

    $fContent = $req->getResponseBody();
    serendipity_request_end();

    if (strlen($fContent) != 0) {
        $trackback_result = serendipity_trackback_autodiscover($fContent, $parsed_loc, $url, $author, $title, $text, $loc);
        if ($trackback_result == false) {
            serendipity_pingback_autodiscover($parsed_loc, $fContent, $url);
        }
    } else {
        echo '<div>• ' . TRACKBACK_NO_DATA . '</div>';
    }
    echo '<hr noshade="noshade" />';
}
In Garvin's code, I found in the BerliOS SVN, I found another problem: He called serendipity_pingback_autodiscover like that:

Code: Select all

serendipity_pingback_autodiscover($loc, $fContent);
instead of

Code: Select all

serendipity_pingback_autodiscover($parsed_loc, $fContent, $url);
The missing URL parameter results in the problem, I described above and Slim mentioned now, too. But Garvin also handed $loc instead $parsed_loc, what produces a link to the foreign blog's main URL instead of the foreign blogs article. So with Garvin's changes to my code a pingback from our blocks main URL to the foreign blocks main URL is sent always, what will be rejected after the first successfull pingback between these two blogs.

Remarks: You will need the new language files found at the BerliOS SVN for this code.

Posted: Sun Jul 08, 2007 8:11 pm
by garvinhicking
Hi!

Thanks a lot for bearing with me. Sorry I screwed up the patch, you made it now as I originally intended it to be. Many thanks, I committed your fix now.

BTW, for future patches: please try to use 4 spaces indenting for sourcecode changes - your patch contained 3 spaces in some places. Just a minor issue, but I wanted to mention that :)

Best regards,
Garvin

Posted: Sun Jul 08, 2007 10:41 pm
by blog.brockha.us
garvinhicking wrote:Thanks a lot for bearing with me
Here a smiley is missing, isn't it? Else: You are kidding. You are one of these open source scene diamonds, I can't give enough kudos to. :-)
garvinhicking wrote:Many thanks, I committed your fix now.
I looked at the BerliOS SVN, and now the code looks right.

BTW: I did another very little change to this file localy: I commented out line 268, as I'm always irritated by the message, that no pingback/trackback data is found in a link to a picture every time I'm resaving an article. As this is not even tested in the code, the message seems wrong to me. But this is just a little "beauty adjustment". :-)
garvinhicking wrote:BTW, for future patches: please try to use 4 spaces indenting for sourcecode changes - your patch contained 3 spaces in some places.
Oh well, did I? Hmm.. Strange. I told eclipse to use 4 space autoindenting and can't find a 3 space indenting in my code, but I will have a look on that in the future. Thanks for hinting!