The UHF of the film world.
Latest news

quietearth [General News 10.30.06]



Here's a tutorial (howto) on coding your own pingback mechanism for your blog using version 2.0 of the phpxmlrpc library available from sourceforge.

1. Receiving pingbacks
The pingback specs are available here:
http://www.hixie.ch/specs/pingback/pingback


When a client sends a pingback, it requests the page of the article being pinged and looks for either the X-Pingback header or a link the html header section of the page. This tells the client sending the pingback the url to actually send the pingback too. So first off we need to add these in to every page which serves articles.

This is part of the http header, so you need this before any html is served out:
header("X-Pingback: http://www.yoursite.com/pingback.php");
This is for the html header section:
<link rel="pingback" href="http://www.yoursite.com/pingback.php" />

The rest is simply making the pingback server itself. Here's the skeleton for pingback.php:
<?
header("Content-Type: application/xml");
require_once('/path/to/xmlrpc/lib/xmlrpc.inc');
require_once('/path/to/xmlrpc/lib/xmlrpcs.inc');

function pbprocess($m) {
        global $xmlrpcerruser;

        $x1 = $m->getParam(0);
        $x2 = $m->getParam(1);
        $source = $x1->scalarval(); # their article
        $dest = $x2->scalarval(); # your article

        # INSERT CODE
        # here we can check for valid urls in source and dest, security
        # lookup the dest article in our database etc.. 

        if (..) { # source uri does not exist
               return new xmlrpcresp(0, 16, "Source uri does not exist");
               }

        if (..) { # source uri does not have a link to target uri
               return new xmlrpcresp(0, 17, "Source uri does have link to target uri");
               }

        if (..) { # target uri does not exist
               return new xmlrpcresp(0, 32, "Target uri does not exist");
               }

        if (..) { # target uri cannot be used as target
               return new xmlrpcresp(0, 33, "Target uri cannot be used as target");
               }

        if (..) { # Pingback already registered
               return new xmlrpcresp(0, 48, "Target uri cannot be used as target");
               }

        if (..) { # Access denied
               return new xmlrpcresp(0, 49, "Access denied");
               }

        if (..) { # Could not communicate with upstream server or got error
               return new xmlrpcresp(0, 50, "Problem with upstream server");
               }

        if (..) { # Generic fault code if not applicable to above
               return new xmlrpcresp(0, 50, "Unkown error");
               }

        return new xmlrpcresp(new xmlrpcval("Pingback registered. May the force
be with you.", "string"));
        }

$a = array( "pingback.ping" => array( "function" => "pbprocess" ));

$s = new xmlrpc_server($a, false);
#$s->setdebug(3);
$s->service();
?>


The xmlrpc_server class from the xmlrpcphp module processes the post and uses pbprocess as a callback to return the data. From the pbprocess function we need to return a valid xmlrpcresp, and this gets passed off to the client. This is pretty straightforward. On a side note, I use the Access denied (error 49) as a generic error response in case none of the others fit.

2. Sending pingbacks
For sending pingbacks, we need to pass the entire text of a post put on the blog to a parsing routine. This routine send_pingback which is below, looks for all the html links and runs through each one of these pages looking for the X-Pingback header. (At this time I haven't implemented the html link reference.) If it finds this header, it will submit a pingback to the specified url.

<?
require_once('/path/to/xmlrpc/lib/xmlrpc.inc');

function do_send_pingback($myarticle, $url, $pdebug = 0) {
        $parts = parse_url($url);

        if (!isset($parts['scheme'])) {
                print "do_send_pingback: failed to get url scheme [".$url."]<br />\n";
                return(1);
                }
        if ($parts['scheme'] != 'http') {
                print "do_send_pingback: url scheme is not http [".$url."]<br />\n";
                return(1);
                }
        if (!isset($parts['host'])) {
                print "do_send_pingback: could not get host [".$url."]<br />\n";               
                return(1);
                }
        $host = $parts['host'];
        $port = 80;
        if (isset($parts['port'])) $port = $parts['port'];
        $path = "/";
        if (isset($parts['path'])) $path = $parts['path'];
        if (isset($parts['query'])) $path .="?".$parts['query'];
        if (isset($parts['fragment'])) $path .="#".$parts['fragment'];

        $fp = fsockopen($host, $port);
        fwrite($fp, "GET $path HTTP/1.0\r\nHost: $host\r\n\r\n");
        $response = "";
        while (is_resource($fp) && $fp && (!feof($fp))) {
                $response .= fread($fp, 1024);
                }
        fclose($fp);
        $lines = explode("\r\n", $response);
        foreach ($lines as $line) {
                if (ereg("X-Pingback: ", $line)) {
                        list($pburl) = sscanf($line, "X-Pingback: %s");
                        #print "pingback url is $pburl<br />\n";
                        }
                }

        if (empty($pburl)) {
                print "Could not get pingback url from [$url].<br />\n";
                return(1);
                }
        if (!isset($parts['scheme'])) {
                print "do_send_pingback: failed to get pingback url scheme [".$pburl."]<br />\n";
                return(1);
                }
        if ($parts['scheme'] != 'http') {
                print "do_send_pingback: pingback url scheme is not http[".$pburl."]<br />\n";
                return(1);
                }
        if (!isset($parts['host'])) {
                print "do_send_pingback: could not get pingback host [".$pburl."]<br />\n";
                return(1);
                }
        $host = $parts['host'];
        $port = 80;
        if (isset($parts['port'])) $port = $parts['port'];
        $path = "/";
        if (isset($parts['path'])) $path = $parts['path'];
        if (isset($parts['query'])) $path .="?".$parts['query'];
        if (isset($parts['fragment'])) $path .="#".$parts['fragment'];

        $m = new xmlrpcmsg("pingback.ping", array(new xmlrpcval($myarticle, "string"), new xmlrpcval($url, "string")));
        $c = new xmlrpc_client($path, $host, $port);
        $c->setRequestCompression(null);
        $c->setAcceptedCompression(null);
        if ($pdebug) $c->setDebug(2);
        $r = $c->send($m);
        if (!$r->faultCode()) {
                print "Pingback to $url succeeded.<br >\n";
                } else {
                $err = "code ".$r->faultCode()." message ".$r->faultString();
                print "Pingback to $url failed with error $err.<br >\n";
                }
        }

# call send_pingback() from your blog after adding a new post,
# $text will be the full text of your post
# $myurl will be the full url of your posting
function send_pingback($text, $myurl) {
        $m = array();
        preg_match_all ("/<a[^>]*href=[\"']([^\"']*)[\"'][^>]*>(.*?)<\/a>/i", $text, $m);
        $c = count($m[0]);
        for ($i = 0; $i < $c; $i++) {
                $ret = valid_url($m[1][$i]);
                if ($ret) do_send_pingback($myurl, $m[1][$i]);
                }
        }
?>


I have put up a page to allow you to test sending and receiving pingbacks from your blog:
http://www.quietearth.us/webtools

You might also like

avatar

obokaman (9 years ago) Reply

Hi! Thanks for sharing your ideas through your blog.

I've trying to implement your code in a hand-made blogging system, but I receive some errors. I see that at the final part of the ping sending part, you're using the post URL to send the request, so the XML client always receive the HTML code from the post as answer, not the XML pingback server response.

I've tried simply to add this line:

$parts = parse_url($pburl);

just after constructing the parameters for the call, and it works. I'm doing something wrong or was a mistake in the original code?

Thanks for your help & time ( and sorry about my poor english... :P )

avatar

ikari (9 years ago) Reply

Um... Where did u get the valid_url function?

avatar

quietearth (9 years ago) Reply

Sorry here's the valid_url code, it's not much in the way of security because it's you sending a pingback, not someone else:

function valid_url($url) {
if (!ereg('[()"'<>]', $url)) return(1);
return(0);
}

avatar

Mindloop Webdesign (8 years ago) Reply

Thanks,

This'll come in handy when adding the pingback class to the mindCMS

avatar

Mike (8 years ago) Reply

In the pingbacks.php, what goes in the "if (..)" portions of the script?

An example would be great for learning, thanks!

avatar

Mike (8 years ago) Reply

Sorry for the noob-i-ness, but shouldn't your valid_url() function be as follows:

function valid_url($url) {
if (!ereg('[()"'<>]', $url)) return(1);
return(0);
}

// Note the escaped ' character

avatar

Kestas (8 years ago) Reply

Hi,
i have some questions. How pbprocess() function is call out. Can you give source code. Thanks

avatar

Kestas (8 years ago) Reply

I found that function, but i have another problem. Script gives me 105 fault. What does it means, where can i find the problem?

avatar

quietearth (8 years ago) Reply

do you have the phpxmlrpc library installed and the paths for the requires set properly?

avatar

Kestas (8 years ago) Reply

I find that problem. It was some mistakes in code. Thanks for that great script :)

avatar

Tyler (8 years ago) Reply

For some reason I am having troubles understanding how to get this to work. I mean you've done the skeleton of it but the little bits I'm currently stumped on.

You don't have a small working version I could take a look at do you?

avatar

snydez (8 years ago) Reply

hi,
i try to implement your code,,
when i try to pingback to my site, it said the pingback.php is forbiden,
i've chmod the pingback.php to 777

avatar

scott klarr (8 years ago) Reply

Thanks for the code. I noticed that it was not working properly on sites that used x-header, but no html tag.. solution was that $parts needed to be reset to carry the values for the url provided by x-header:

foreach ($lines as $line) {
if (ereg("X-Pingback: ", $line)) {
list($pburl) = sscanf($line, "X-Pingback: %s");
$parts = parse_url($pburl);
//print "pingback url is $pburl<br />n";
}
}

avatar

vivevtvivas (8 years ago) Reply

I wrote this yesterday to get the X-Pingback header value.

In: URL of site that you would like to get the X-Pingback header value from.
Out: URL of X-Pingback server.



function GetXPingback($sURL) {
$clSession = curl_init();
$iTimeout = 30;

curl_setopt ($clSession, CURLOPT_URL, $sURL);
curl_setopt ($clSession, CURLOPT_RETURNTRANSFER, 1);
curl_setopt ($clSession, CURLOPT_CONNECTTIMEOUT, $iTimeout);
curl_setopt ($clSession, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt ($clSession, CURLOPT_HEADER, TRUE);
curl_setopt ($clSession, CURLOPT_NOBODY, TRUE);

$sScrape = curl_exec($clSession);
curl_close($clSession);

$sPingbackAddress = trim(TextBetween("X-Pingback: ", "n", $sScrape));

return $sPingbackAddress;
}

function TextBetween($s1,$s2,$s){
$s1 = strtolower($s1);
$s2 = strtolower($s2);
$L1 = strlen($s1);
$scheck = strtolower($s);
if($L1>0){$pos1 = strpos($scheck,$s1);} else {$pos1=0;}
if($pos1 !== false){
if($s2 == '') return substr($s,$pos1+$L1);
$pos2 = strpos(substr($scheck,$pos1+$L1),$s2);
if($pos2!==false) return substr($s,$pos1+$L1,$pos2);
}
return '';
}

avatar

Lazar (8 years ago) Reply

Thank you for this useful post and links to relevant tools. This page is now in my favorites!

avatar

rio (8 years ago) Reply

the other day I stumbled upon a pingback server/client implementation in php without the xmlrpc-bloat (see contact-url). I use the client in my blog, works flawlessly as far as I can tell.

avatar

LukasS (8 years ago) Reply

I receive an error when I send pingback:

Pingback to http://some.url/ failed with error code 2 message Invalid return payload: enable debugging to examine incoming payload (XML error: > required at line 104, column 24).

avatar

Trezub (8 years ago) Reply

okey, I remembe of this post fo future. $)

avatar

akifemre (8 years ago) Reply

Hey!
Firstly, thanks for the tutorial. But I have the same problem as LukasS and I couldn't debug it.
My XML-RPC version is 2.2.1. Here is the error output:
XML error: not well-formed (invalid token) at line 8, column 35Pingback to http://xxx.com/foo.htm failed with error code 2 message Invalid return payload: enable debugging to examine incoming payload (XML error: not well-formed (invalid token) at line 8, column 35).
And there is my code: http://pastebin.com/m3a49fa67

avatar

Allan Wirth (8 years ago) Reply

I wrote a simple function for scanning a block of HTML and sending pingbacks to all of the links that it contained, without any libraries or includes. Link is in contact URL.

avatar

Origin (7 years ago) Reply

Get the X-Pingback value using a nice neat regex:

preg_match('/bX-Pingback: ([^s]+)/m', $sScrape , $match);

Where $match[1] will be your pingback server if it was present.

avatar

Ramon Fincken (7 years ago) Reply

Thank you so much !

I needed to create a response pingback server for my phpbb2 board. I allready had a pingback module but not the server. Your first snipplet helped me out !

Regards,

Ramon
http://www.ramonfincken.com

avatar

Ramon Fincken (7 years ago) Reply

In addition to finding the right pingback/trackback url ...

> http://www.ramonfincken.com/permalink/topic120.html

avatar

angelxmoreno (5 years ago) Reply

is this still valid 4 years later?

avatar

jarmen (5 years ago) Reply

How fix http://www.mysite.com/wp-app.php/service error 401 ?

thanks for assist

avatar

Anonymous (5 years ago) Reply

How to get the infos into the database?

avatar

Dane (5 years ago) Reply

Hey, thanks for this great tutorial. But i can't get it to work. Always receiving a Code 5 error message.

"Failed with error code 5 message Didn't receive 200 OK from remote server."

Maybe it's because I'm just using this to ping and don't have implemented a pingback server on my site.

avatar

christian louboutin outlet (5 years ago) Reply

How to get the infos into the database?

avatar

bop da (4 years ago) Reply

[...]I had a few different options. I could have manually made a POST request to the endpoint, hand-coding the XML (this brute force method). Or I could stand on the shoulders of giants and leverage the power of open-source libraries. I chose the latter, in keeping with my new guiding principle of pursuing beauty, truth and goodness.[...]

avatar

Outlet Moncler (4 years ago) Reply

=

avatar

Haider M Rizvi (3 years ago) Reply

Thanks for the code....


Leave a comment