1<?php 2 3/** 4 * Pingback server for use with the DokuWiki Linkback Plugin. 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 * @author Gina Haeussge <osd@foosel.net> 8 * @link http://wiki.foosel.net/snippets/dokuwiki/linkback 9 */ 10 11use IXR\Server\Server; 12use IXR\Message\Error; 13use dokuwiki\HTTP\DokuHTTPClient; 14 15if (!defined('DOKU_INC')) 16 define('DOKU_INC', realpath(dirname(__FILE__) . '/../../../../') . '/'); 17 18require_once (DOKU_INC . 'inc/init.php'); 19 20// Pingback Faultcodes 21const PINGBACK_ERROR_GENERIC = 0; 22const PINGBACK_ERROR_SOURCEURI_DOES_NOT_EXIST = 16; 23const PINGBACK_ERROR_SOURCEURI_DOES_NOT_CONTAIN_LINK = 17; 24const PINGBACK_ERROR_TARGETURI_DOES_NOT_EXIST = 32; 25const PINGBACK_ERROR_TARGETURI_CANNOT_BE_USED = 33; 26const PINGBACK_ERROR_PINGBACK_ALREADY_MADE = 48; 27const PINGBACK_ERROR_ACCESS_DENIED = 49; 28const PINGBACK_ERROR_NO_UPSTREAM = 50; 29 30/** 31 * Class PingbackServer 32 */ 33class PingbackServer extends Server { 34 35 /** @var helper_plugin_blogtng_linkback */ 36 var $tools; 37 38 /** 39 * Register service and construct helper 40 */ 41 function __construct() { 42 $this->tools = plugin_load('helper', 'blogtng_linkback'); 43 parent::__construct(array('pingback.ping' => 'this:ping')); 44 } 45 46 /** 47 * Send a HTTP get request to @$sourceUri and extract linkback 48 * data for @$targetUri from the response. 49 * 50 * @param $sourceUri 51 * @param $targetUri 52 * @return Error|void 53 */ 54 function ping($sourceUri, $targetUri) { 55 global $ID; 56 $ID = substr($_SERVER['PATH_INFO'], 1); 57 58 if (is_null($this->tools) || !$this->tools->linkbackAllowed()) { 59 return new Error(PINGBACK_ERROR_TARGETURI_CANNOT_BE_USED, ''); 60 } 61 62 // Given URLs are no urls? Quit 63 if (!preg_match("#^([a-z0-9\-\.+]+?)://.*#i", $sourceUri)) 64 return new Error(PINGBACK_ERROR_GENERIC, ''); 65 if (!preg_match("#^([a-z0-9\-\.+]+?)://.*#i", $targetUri)) 66 return new Error(PINGBACK_ERROR_GENERIC, ''); 67 68 // Source URL does not exist? Quit 69 $http = new DokuHTTPClient; 70 $page = $http->get($sourceUri); 71 if ($page === false) 72 return new Error(PINGBACK_ERROR_SOURCEURI_DOES_NOT_EXIST, ''); 73 74 // Target URL does not match with request? Quit 75 if ($targetUri != wl($ID, '', true)) 76 return new Error(PINGBACK_ERROR_GENERIC, ''); 77 78 // Retrieve data from source 79 $linkback = $this->_getTrackbackData($sourceUri, $targetUri, $page); 80 81 // Source URL does not contain link to target? Quit 82 if (!$linkback) 83 return new Error(PINGBACK_ERROR_SOURCEURI_DOES_NOT_CONTAIN_LINK, ''); 84 85 if (!$this->tools->saveLinkback('pingback', $linkback['title'], 86 $sourceUri, $linkback['excerpt'], $ID)) { 87 return new Error(PINGBACK_ERROR_PINGBACK_ALREADY_MADE, ''); 88 } 89 } 90 91 /** 92 * Constructs linkback data and checks if source contains a link to target and a title. 93 * 94 * @param $sourceUri 95 * @param $targetUri 96 * @param $page 97 * @return array|bool 98 */ 99 function _getTrackbackData($sourceUri, $targetUri, $page) { 100 $linkback = array (); 101 102 $searchurl = preg_quote($targetUri, '!'); 103 $regex = '!<a[^>]+?href="' . $searchurl . '"[^>]*?>(.*?)</a>!is'; 104 $regex2 = '!\s(' . $searchurl . ')\s!is'; 105 if (!preg_match($regex, $page, $match) && !preg_match($regex2, $page, $match)) { 106 // FIXME internal pings 107 if ((strstr($targetUri, DOKU_URL) == $targetUri)) { 108 $ID = substr($_SERVER['PATH_INFO'], 1); 109 $searchurl = preg_quote(wl($ID, '', false), '!'); 110 111 $regex = '!<a[^>]+?href="' . $searchurl . '"[^>]*?>(.*?)</a>!is'; 112 $regex2 = '!\s(' . $searchurl . ')\s!is'; 113 if (!preg_match($regex, $page, $match) && !preg_match($regex2, $page, $match)) 114 return false; 115 } else { 116 return false; 117 } 118 } 119 $linkback['excerpt'] = '[…] ' . strip_tags($match[1]) . ' […]'; 120 121 $regex = '!<title.*?>(.*?)</title>!is'; 122 if (!preg_match($regex, $page, $match)) 123 return false; 124 $linkback['title'] = strip_tags($match[1]); 125 126 return $linkback; 127 } 128} 129 130$server = new PingbackServer(); 131