4 PingBack [http://www.hixie.ch/specs/pingback/pingback] automatically
5 discovers when somebody inserts a URL into a page and pings the remote
6 server to take note about the mentioning of one of its articles. There
7 is a lot of auto-discovery involved, and this requires XML+RPC and the
8 HTTP extensions (see plugins/lib/) to be present.
9 This plugin implements client and server (Wiki can itself be pinged).
11 GOTCHAS: may only work for ordinarily named WikiPages (no non-word
12 characters inside). You should prepare EWIKI_SCRIPT_URL to yield nice
13 links to your pages as usual.
14 If possible you should add <link rel="pingback" href=".../z.php"> to
15 your site layout script.
20 #-- plugin registration
21 $wikiapi["pingback.ping"] = "ewiki_pingback_rpc";
22 $ewiki_plugins["edit_save"][] = "ewiki_pingback_newurls";
23 $ewiki_plugins["init"][] = "ewiki_pingback_header";
24 @$ewiki_config["ua"] .= " PingBack/0.2";
27 #-- server part (adding URLs to our pages) -------------------------------
28 function ewiki_pingback_rpc($source_url, $target_url) {
31 #-- does the target URL refer to a known WikiPage ?
32 $id = ewiki_url2id($target_url);
34 xmlrpc_send_response(xmlrpc_error(0x0021, "Could not determine PageName for the given target URL."));
36 if (!($data = ewiki_db::GET($id))) {
37 xmlrpc_send_response(xmlrpc_error(0x0020, "The given target page does not exist."));
41 #-- check if the caller really has a link as he claims
42 ini_set("user_agent", $ewiki_config["ua"]);
43 if ((strpos($source_url, "http://")===0) && ($test = ewiki_http_asis($source_url, 96256))) {
44 $test = strtolower($test);
45 $test_url = strtolower($target_url);
46 if (!strpos($test, $test_url)
47 and !strpos($test, htmlentities($test_url))) {
48 return xmlrpc_error(0x0011, "Sorry, but couldn't find a link to '$target_url' on your given '$source_url' page.");
52 return xmlrpc_error(0x0010, "Your given source URL does not exist, could not be retrieved.");
55 #-- reject other frivolous links
56 if (preg_match('#^http://[^/]+/?$#', $source_url)) {
57 return xmlrpc_error(0x0011, "Rejected '$source_url' as frivolous.");
59 #-- check write permissions
60 if ((EWIKI_DB_F_TEXT != $data["flags"] & EWIKI_DB_F_TYPE)
61 or ($data["flags"] & EWIKI_DB_F_READONLY)) {
62 return xmlrpc_error(0x0031, "Sorry, but this page is write-protected or not a system page.");
65 if (strpos($data["content"], $source_url)) {
66 return xmlrpc_error(0x0030, "The given link does already exist on this page.");
68 #-- other go-away cases
69 if (function_exists("ewiki_banned_url") && ewiki_banned_url($source_url) || function_exists("ewiki_blocked_url") && ewiki_blocked_url($source_url)) {
70 return xmlrpc_error(0x0100, "Your link is unwanted here (registered on BlockedLinks or BannedLinks).");
74 $data["content"] = rtrim($data["content"])
75 . "\n* $source_url (PingBack)\n";
76 ewiki_db::UPDATE($data);
78 $ok = ewiki_db::WRITE($data);
82 return("Link to '$source_url' was added to page '$id'.");
85 return xmlrpc_error(0x0101, "Seems like a database/writing error occoured.");
90 #-- page id from absolute URL
91 function ewiki_url2id($url) {
92 if (strpos($url, EWIKI_SCRIPT_URL)===0) {
93 $id = substr($url, strlen(EWIKI_SCRIPT_URL));
95 elseif ($l = strpos($url, "?id=")) {
96 $id = strtok(substr($url, $l+4), "&");
98 elseif ($l = strpos($url, "?")) {
99 $id = strtok(substr($url, $l+1), "&");
101 elseif (($l = strrpos($url, "/")) > 10) {
102 $id = strtok(substr($url, $l+1), "?&.");
108 #-- notify clients that we're also server
109 function ewiki_pingback_header() {
110 header("X-PingBack: " . EWIKI_BASE_URL . "z.php");
116 #-- client part (notify remote server of added URLs on current site) -----
117 function ewiki_pingback_ping($source, $target) {
119 #-- detect if $target URL is pingback-enabled, and go
120 if ($rpc_url = ewiki_pingback_discover($target)) {
122 $res = xmlrpc_request($rpc_url, "pingback.ping", array($source, $target));
123 // we don't care about the result, do we?
128 #-- short http request to discover X-Pingback header or <link> tag
129 function ewiki_pingback_discover($url) {
130 global $ewiki_config;
132 ini_set("user_agent", $ewiki_config["ua"]);
133 if ((strpos($url, "http://") === 0) && ($data = ewiki_http_asis($url, 4096))) {
134 if (preg_match('/\nX-Pingback:\s*([^\s,]+)/i', $data, $uu)) {
137 elseif (preg_match('/<link[^>]+rel=["\']?pingback["\']?[^>]+href=["\']?([^>"\'\s]+)/i', $data, $uu)) {
144 #-- undeciphered GET request
145 function ewiki_http_asis($url, $maxsize=8192) {
146 global $ewiki_config;
147 $c = parse_url($url);
149 $port = $port ? $port : 80;
150 $path .= $query ? "?$query" : "";
151 if ($f = fsockopen($host, $port, $errno, $errstr, $timeout=5)) {
152 fwrite($f, "GET $path HTTP/1.0\r\n"
154 . "Connection: close\r\n"
155 . "Accept: text/html, application/xml, text/xml, application/xhtml+xml, text/plain\r\n"
156 . "User-Agent: $ewiki_config[ua]\r\n"
158 socket_set_blocking($f, true);
160 while (!feof($f) && (strlen($data) < $maxsize)) {
161 $data .= fread($f, $maxsize);
170 #-- check for newly added urls
171 function ewiki_pingback_newurls(&$save, &$old) {
173 global $ewiki_plugins, $ewiki_config;
175 #-- check newly added links
176 $newlinks = array_diff(explode("\n", trim($save["refs"])), explode("\n", trim($old["refs"])));
177 foreach ($newlinks as $link) if (strpos($link, "://")) {
178 $ewiki_config["pingback"][] = $link;
180 if (@$ewiki_config["pingback"]) {
181 register_shutdown_function("ewiki_pingback_start");
186 #-- registers all previously added URLs
187 function ewiki_pingback_start() {
189 global $ewiki_plugins, $ewiki_config, $ewiki_id;
190 $source_url = ewiki_script_url("", $ewiki_id);
192 foreach ($ewiki_config["pingback"] as $target_url) {
193 ewiki_pingback_ping($source_url, $target_url);