XMLRPC – Pingbacks selbst implementieren

Da ich momentan dabei bin meine Homepage zu optimieren, möchte ich gerne Ping– und Trackbacks implementieren. Soweit ich es bisher gelesen habe, basieren beide Verfahren auf XMLRPC. Bei WordPress wird diese Funktionalität schon mitgeliefert. Da ich aber alles selbst programmiert habe muss ich das Ganze nun manuell nachziehen.

Pingbacks werden automatisch von WordPress versendet. Das heißt, sobald ich einen Link auf eine Seite in einen Artikel einbaue, wird an diese ein Pingback versendet. So teilt man anderen Seiten mit, dass man sich in einem Artikel auf diese bezieht. Bei WordPress passiert das alles komplett automatisch – doch was muss meine Seite nun alles können?

  • Versenden von Pingbacks (sobald ich in Artikeln einen Link verwende)
  • Empfangen von Pingbacks (sobald jemand anders meine Seite wo verlinkt)

Trackbacks möchte ich im ersten Schritt erstmal nicht berücksichtigen – sollte aber alles relativ ähnlich sein.

Möglichkeiten / Ansätze

Nun habe ich das Internet etwas durchforstet und bin auf einige Möglichkeiten gestoßen. Als erstes ist mir aufgefallen, dass es in PHP eine Reihe von XML-RPC-Funktionen gibt. Wie man diese implementieren könnte ist in diesem Artikel beschrieben – hier gibt es sogar ein Beispiel für einen Pingback. Leider sind diese auf meinem Server nicht verfügbar – also brauche ich einen anderen Weg. Die Klasse PHPTrackback auf Sourceforge hat mich auch nicht wirklich weiter gebracht. Diese ist zwar gut Dokumentiert, hatte für mich auf den ersten (kurzen) Blick aber wenig mit XML-RPC zu tun.

Etwas umfangreicher scheint mir da schon die XML-RPC for PHP Library zu sein. Diese wurde allerdings seit September 2009 nicht mehr modifiziert – das spricht entweder für einen fehlerfreien Stand, oder das Projekt ist eingeschlafen. Das Wörtchen „beta“ in der letzten Version lässt mich auf letzteres schließen. Trotzdem ist es sicher einen Versuch wert.

Die letzte Alternative wäre: Einfach den WordPress-Code ansehen und versuchen daraus eine eigene Klasse zu implementieren. Ich habe zwar schon mehrfach gelesen, dass der Code total undurchsichtig geschrieben ist, aber da muss man wohl durch. Eine bessere Code-Review-Alternative bietet angeblich Habari. Doch das habe ich mir direkt geschenkt – das Projekt ist schließlich auch seit Dezember 2011 nicht mehr aktualisiert worden.

Allerdings muss man dann wohl ein wenig Filtern, denn die XML-RPC-Schnittstelle für WordPress bietet einiges mehr als nur Ping- und Trackbacks zu empfangen. In WordPress ist es außerdem möglich Remote-Posts zu erstellen. So greift zum Beispiel die iOS-App auf diese Schnittstelle zu wenn man vom iPhone oder iPad einen Beitrag erstellt, bearbeitet oder löscht. Durch den Umfang und die Komplexität hat es in der Vergangenheit schon einige Sicherheitslücken gegeben. Dieser Artikel geht genauer auf die Problematik ein.

Entwicklung

Ich habe mich also für den Einsatz von XML-RPC for PHP entschieden. Geladen habe ich Version 3.0.0 Beta vom September 2009. Kann ja nicht schaden auf die letzte Version aufzusetzen – auch wenn es eine Beta ist. Außerdem habe ich einen Artikel gefunden der scheinbar genau diese Library nutzt. Den Code nehme ich also erstmal als Grundlage.

Als erstes lege ich also eine xmlrpc.php im Hauptverzeichnis meiner Webseite an. Mit dem RSS-Reader und der Sitemap habe ich es bereits so gemacht, dass ich in der .htaccess auf diese Dateien verweise wenn die Dateiendung fehlt. Das will ich nun auch so machen:

RewriteRule ^rss$ rss.php

RewriteRule ^sitemap$ sitemap.php
RewriteRule ^sitemap.xml$ sitemap.php

RewriteRule ^xmlrpc$ xmlrpc.php

Nun kann die Arbeit ja beginnen.

Um anderen Webseiten mitzuteilen wo meine XML-RPC-Schnittstelle zu finden ist, füge ich noch ein Meta-Attribut hinzu:

<link rel=“pingback“ href=“http://klein0r.de/xmlrpc“ />

Außerdem muss der Header um einen Eintrag erweitert werden (X-Pingback):

header("X-Pingback: http://klein0r.de/xmlrpc");

Als Test-Umgebung nutze ich MAMP auf meinem lokalen Rechner. So kann ich mir einfach eine Test-Klasse schreiben und so Pingbacks senden wenn ich es möchte. Außerdem ist der Ansatz sehr realitätsnah.

Als Grundlage ist der oben genannte Artikel schon sehr gut zu verwenden. Aber das Pingback versenden hat mir nicht so zugesagt und auch nicht richtig funktioniert. Daher hier nun eine Korrektur:

<!--?php 

require_once(dirname(__FILE__).'/lib/xmlrpc.inc');

function xmlrpc_send_pingback($myarticle, $url) {
	$parts = parse_url($url);

	if (!isset($parts['scheme'])) {
		trigger_error("xmlrpc_send_pingback: failed to get url scheme [".$url."]", E_USER_WARNING);
		return(1);
	}

	if ($parts['scheme'] != 'http') {
		trigger_error("xmlrpc_send_pingback: url scheme is not http [".$url."]", E_USER_WARNING);
		return(1);
	}

	if (!isset($parts['host'])) {
		trigger_error("xmlrpc_send_pingback: could not get host [".$url."]", E_USER_WARNING);               
		return(1);
	}

	$host = $parts['host'];
	$port = isset($parts['port']) ? $parts['port'] : 80;
	$path = isset($parts['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) &#038;& $fp &#038;& (!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)) {
		trigger_error("Could not get pingback url from [$url]", E_USER_WARNING);
		return(1);
	}

	$pbParts = parse_url($pburl);

	if (!isset($pbParts['scheme'])) {
		trigger_error("xmlrpc_send_pingback: failed to get pingback url scheme [".$pburl."]", E_USER_WARNING);
		return(1);
	}

	if ($pbParts['scheme'] != 'http') {
		trigger_error("xmlrpc_send_pingback: pingback url scheme is not http [".$pburl."]", E_USER_WARNING);
		return(1);
	}

	if (!isset($pbParts['host'])) {
		trigger_error("xmlrpc_send_pingback: could not get pingback host [".$pburl."]", E_USER_WARNING);
		return(1);
	}

	$host = $pbParts['host'];
	$port = isset($pbParts['port']) ? $pbParts['port'] : 80;
	$path = isset($pbParts['path']) ? $pbParts['path'] : "/";

	if (isset($pbParts['query']))
		$path .="?".$pbParts['query'];

	if (isset($parts['fragment']))
		$path .="#".$pbParts['fragment'];

	$m = new xmlrpcmsg("pingback.ping", array(new xmlrpcval($myarticle, "string"), new xmlrpcval($url, "string")));
	$c = new xmlrpc_client($path, $host, $port);

	$c-&gt;setRequestCompression(null);
	$c-&gt;setAcceptedCompression(null);

	//$c-&gt;setDebug(2);

	$r = $c-&gt;send($m);
	if (!$r-&gt;faultCode()) {
		trigger_error("Pingback to $url succeeded", E_USER_NOTICE);
	} else {
		$err = "code ".$r-&gt;faultCode()." message ".$r-&gt;faultString();
		trigger_error("Pingback to $url failed with error $err.", E_USER_ERROR);
	}
}

// Test:
xmlrpc_send_pingback('https://mkleine.de/2012/11/ssh-forwarding-u-a-fur-git/', 'http://klein0r.de/news-51-Portfolio-Relaunch.html');

?&gt;

Weitere Einsatzmöglichkeiten

Google bietet eine XML-RPC-Schnittstelle. An diese können neue Inhalte versendet werden um schneller in den Index aufgenommen zu werden. Auch andere Suchmaschinenanbieter bieten solche Schnittstellen an.


Beitrag veröffentlicht

in

, , , , ,

von

Schlagwörter: