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 dokuwiki\Extension\PluginInterface; 12use IXR\Server\Server; 13use IXR\Message\Error; 14 15if (!defined('DOKU_INC')) 16 define('DOKU_INC', realpath(dirname(__FILE__) . '/../../../../') . '/'); 17 18require_once (DOKU_INC . 'inc/init.php'); 19 20require_once (DOKU_PLUGIN . 'linkback/tools.php'); 21require_once (DOKU_PLUGIN . 'linkback/http.php'); 22 23// Pingback Faultcodes 24const PINGBACK_ERROR_GENERIC = 0; 25const PINGBACK_ERROR_SOURCEURI_DOES_NOT_EXIST = 16; 26const PINGBACK_ERROR_SOURCEURI_DOES_NOT_CONTAIN_LINK = 17; 27const PINGBACK_ERROR_TARGETURI_DOES_NOT_EXIST = 32; 28const PINGBACK_ERROR_TARGETURI_CANNOT_BE_USED = 33; 29const PINGBACK_ERROR_PINGBACK_ALREADY_MADE = 48; 30const PINGBACK_ERROR_ACCESS_DENIED = 49; 31const PINGBACK_ERROR_NO_UPSTREAM = 50; 32 33class PingbackServer extends Server{ 34 35 // helper instance 36 /** @var PluginInterface|tools_plugin_linkback */ 37 var $tools; 38 39 /** 40 * Register service and construct helper 41 */ 42 function __construct() { 43 $this->tools = plugin_load('tools', 'linkback'); //TODO type 'tools'? is that possible? 44 parent::__construct(array ( 45 'pingback.ping' => 'this:ping', 46 47 )); 48 } 49 50 /** 51 * @param $sourceUri 52 * @param $targetUri 53 * @return Error|void 54 */ 55 function ping($sourceUri, $targetUri) { 56 // Plugin not enabled? Quit 57 if (plugin_isdisabled('linkback')) 58 return new Error(PINGBACK_ERROR_TARGETURI_CANNOT_BE_USED, ''); 59 60 // pingback disabled? Quit 61 if (!$this->tools->getConf('enable_pingback')) 62 return new Error(PINGBACK_ERROR_TARGETURI_CANNOT_BE_USED, ''); 63 64 // Given URLs are no urls? Quit 65 if (!preg_match("#^([a-z0-9\-\.+]+?)://.*#i", $sourceUri)) 66 return new Error(PINGBACK_ERROR_GENERIC, ''); 67 if (!preg_match("#^([a-z0-9\-\.+]+?)://.*#i", $targetUri)) 68 return new Error(PINGBACK_ERROR_GENERIC, ''); 69 70 // Source URL does not exist? Quit 71 $page = $this->tools->getPage($sourceUri); 72 if (!$page['success'] && ($page['status'] < 200 || $page['status'] >= 300)) 73 return new Error(PINGBACK_ERROR_SOURCEURI_DOES_NOT_EXIST, ''); 74 75 // Target URL does not match with request? Quit 76 $ID = substr($_SERVER['PATH_INFO'], 1); 77 if ($targetUri != wl($ID, '', true)) 78 return new Error(PINGBACK_ERROR_GENERIC, ''); 79 80 $file = metaFN($ID, '.linkbacks'); 81 $data = array ( 82 'send' => false, 83 'receive' => false, 84 'display' => false, 85 'sentpings' => array (), 86 'receivedpings' => array (), 87 'number' => 0, 88 89 ); 90 91 if (@ file_exists($file)) 92 $data = unserialize(io_readFile($file, false)); 93 94 // Target URL is not pingback enabled? Quit 95 if (!$data['receive']) 96 return new Error(PINGBACK_ERROR_TARGETURI_CANNOT_BE_USED, ''); 97 98 // Pingback already done? Quit 99 if ($data['receivedpings'][md5($sourceUri)]) 100 return new Error(PINGBACK_ERROR_PINGBACK_ALREADY_MADE, ''); 101 102 // Retrieve data from source 103 $linkback = $this->_getTrackbackData($sourceUri, $targetUri, $page); 104 105 // Source URL does not contain link to target? Quit 106 if (!$linkback) 107 return new Error(PINGBACK_ERROR_SOURCEURI_DOES_NOT_CONTAIN_LINK, ''); 108 109 // Prepare event for Antispam plugins 110 $evt_data = array ( 111 'linkback' => $linkback, 112 'page' => $page, 113 'target' => $targetUri, 114 'show' => true, 115 'log' => array( 116 date('Y/m/d H:i', time()) . ': Received pingback from ' . $linkback['url'] . ' (' . $linkback['lid'] . ')', 117 ), 118 ); 119 $event = new Doku_Event('ACTION_LINKBACK_RECEIVED', $evt_data); 120 if ($event->advise_before()) { 121 $linkback['show'] = $evt_data['show']; 122 if ($this->tools->getConf('usefavicon')) { 123 $linkback['favicon'] = $this->tools->getFavicon($linkback['url'], $page['body']); 124 } 125 126 // add pingback 127 $data['receivedpings'][$linkback['lid']] = $linkback; 128 if ($linkback['show']) 129 $data['number']++; 130 131 io_saveFile($file, serialize($data)); 132 $this->tools->addLogEntry($linkback['received'], $ID, 'cl', '', $linkback['lid']); 133 $this->tools->notify($ID, $linkback); 134 if ($this->tools->getConf('log_processing')) 135 $this->tools->addProcessLogEntry($evt_data['log']); 136 $event->advise_after(); 137 } else { 138 // Pingback was denied 139 if ($this->tools->getConf('log_processing')) 140 $this->tools->addProcessLogEntry($evt_data['log']); 141 $event->advise_after(); 142 return new Error(PINGBACK_ERROR_ACCESS_DENIED, $this->tools->getLang('error_noreason')); 143 } 144 } 145 146 /** 147 * Constructs linkback data and checks if source contains a link to target and a title. 148 */ 149 function _getTrackbackData($sourceUri, $targetUri, $page) { 150 // construct unique id for pingback 151 $lid = md5($sourceUri); 152 $linkback = array ( 153 'lid' => $lid, 154 'title' => '', 155 'url' => $sourceUri, 156 'excerpt' => '', 157 'raw_excerpt' => '', 158 'blog_name' => '', 159 'received' => time(), 160 'submitter_ip' => $_SERVER['REMOTE_ADDR'], 161 'submitter_useragent' => $_SERVER['HTTP_USER_AGENT'], 162 'submitter_referer' => $_SERVER['HTTP_REFERER'], 163 'type' => 'pingback', 164 'show' => true, 165 ); 166 167 $searchurl = preg_quote($targetUri, '!'); 168 $regex = '!<a[^>]+?href="' . $searchurl . '"[^>]*?>(.*?)</a>!is'; 169 $regex2 = '!\s(' . $searchurl . ')\s!is'; 170 if (!preg_match($regex, $page['body'], $match) && !preg_match($regex2, $page['body'], $match)) { 171 if ($this->tools->getConf('ping_internal') && (strstr($targetUri, DOKU_URL) == $targetUri)) { 172 $ID = substr($_SERVER['PATH_INFO'], 1); 173 $searchurl = preg_quote(wl($ID), '!'); 174 175 $regex = '!<a[^>]+?href="' . $searchurl . '"[^>]*?>(.*?)</a>!is'; 176 $regex2 = '!\s(' . $searchurl . ')\s!is'; 177 if (!preg_match($regex, $page['body'], $match) && !preg_match($regex2, $page['body'], $match)) 178 return false; 179 } else { 180 return false; 181 } 182 } 183 $linkback['raw_excerpt'] = '[...] ' . $match[1] . ' [...]'; 184 $linkback['excerpt'] = '[...] ' . strip_tags($match[1]) . ' [...]'; 185 186 $regex = '!<title.*?>(.*?)</title>!is'; 187 if (!preg_match($regex, $page['body'], $match)) 188 return false; 189 $linkback['title'] = strip_tags($match[1]); 190 191 return $linkback; 192 } 193 194} 195 196$server = new PingbackServer(); 197