PageRenderTime 48ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/services/ping.php

https://github.com/agnesrambaud/yacs
PHP | 326 lines | 124 code | 46 blank | 156 comment | 51 complexity | d1945bf198a6c9e07357383821829fc1 MD5 | raw file
  1. <?php
  2. /**
  3. * process ping requests
  4. *
  5. * This script accepts following XML-RPC calls:
  6. * - monitor.ping
  7. * - pingback.ping
  8. * - weblogUpdates.ping
  9. *
  10. * Please note that technical specifications describe different ways to return information,
  11. * and this may lead to various error codes depending on situation.
  12. * Basically, we have implemented XML-RPC return codes where applicable, plus specific recommendations
  13. * where applicable.
  14. * For example, if no data is provided to this script, the sender will be returned a structure
  15. * with attributes '[code]faultCode[/code]' and '[code]faultString[/code]'.
  16. * But if inadequate parameters are given to a '[code]pingback.ping[/code]' call, a simple error code will be returned.
  17. *
  18. * [title]monitor.ping[/title]
  19. *
  20. * Syntax: [code]monitor.ping()[/code] returns [code](int, string)[/code]
  21. *
  22. * This entry point is used to centralize the monitoring of yacs servers.
  23. *
  24. * The returned structure is made of an integer and a string.
  25. * The value 0 means that the pinged server has checked its internal state and that this state is OK.
  26. * Else an error code and a printable label are returned.
  27. *
  28. * You can prevent a server to monitor you by disabling monitoring in the related server profile.
  29. *
  30. *
  31. * [title]pingback.ping[/title]
  32. *
  33. * Syntax: [code]pingback.ping(source_uri, target_uri)[/code] returns string or int
  34. *
  35. * This entry point is used to link a remote page (the source_uri) to one page at the server receiving the call
  36. * (the target_uri).
  37. * We are claiming to fully support the pingback server interface here, as described in the [link=pingback]http://www.hixie.ch/specs/pingback/pingback[/link] specification.
  38. *
  39. * According to the specification we are returning either the string 'Thank you for the ping', or an error code.
  40. * Following error codes have been defined:
  41. * - 0 generic fault code
  42. * - 16 The source URI does not exists
  43. * - 17 The source URI does not contain a link to the target URI, and so cannot be used as a source
  44. * - 32 The specific target URI does not exist
  45. * - 33 The specified target URI cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.
  46. * - 48 The pingback has already been registered
  47. * - 49 Access denied
  48. * - 50 The server could not communicate with an upstream server, or received an error from an upstream server.
  49. *
  50. * Note that YACS also has a client implementation of the pingback specification into [script]links/links.php[/script].
  51. *
  52. * @see articles/view.php
  53. * @see articles/publish.php
  54. * @see categories/view.php
  55. * @see links/links.php
  56. * @see sections/view.php
  57. *
  58. * How does pingback work?
  59. *
  60. * Ok, for those new to pingback here is an overview of the protocol:
  61. * [list=1]
  62. * [*] Alice posts to her blog, including the URL http://www.bob.com/post5.
  63. * [*] Alice's blogging system (AliceBlog) gets all external URLs
  64. * referenced in the post (in this case, just the one to Bob).
  65. * [*] AliceBlog requests http://www.bob.com/post5 and parses it for a
  66. * [code]&lt;link&gt;[/code] tag matching [code]&lt;link rel="pingback" href="http://foo/xmlrpcserver" /&gt;[/code].
  67. * [*] If it doesn't find one, or the response of the request doesn't seem
  68. * to support it, then abort the ping trial for this link.
  69. * [*] Perform an XML-RPC ping to the URL found in the [code]&lt;link&gt;[/code].
  70. * AliceBlog doesn't care if it succeeds or not, really; that's the end of its bit.
  71. * [*] BobBlog receives the XML-RPC ping, naming the URL of
  72. * Alice's post (AliceURL), and the URL it linked to (BobURL).
  73. * [*] BobBlog should check that BobURL is part of Bob's blog.
  74. * [*] BobBlog requests the URL of Alice's post, and confirms that it
  75. * mentions BobURL.
  76. * [*] BobBlog parses the title of AliceURL.
  77. * [*] BobBlog stashes the details of the ping somewhere in its database,
  78. * against the entry which is BobURL..
  79. * [/list]
  80. *
  81. * You can prevent a server to flood you by disabling pings in the related server profile.
  82. *
  83. * [title]weblogUpdates.ping[/title]
  84. *
  85. * Syntax: [code]weblogUpdates.ping(blog_name, blog_url)[/code] returns [code](flerror = boolean, message = string)[/code]
  86. *
  87. * It takes two parameters, both strings. The first is the name of the weblog, the second is its URL.
  88. *
  89. * It returns a struct that indicates success or failure. It has two elements, flerror and message.
  90. * If flerror is false, it worked. If it's true, message contains an English-language description of the reason
  91. * for the failure.
  92. *
  93. * If the call succeeds, an entry for this server will be created
  94. * or updated in the database (see [script]servers/index.php[/script]) for the provided URL.
  95. * Note that an existing profile is updated only if ping is still allowed for it.
  96. *
  97. * This entry point is used to centralize change information.
  98. * By letting the receiving server know about updates, efficient aggregation mechanisms can be put in place.
  99. *
  100. * To tell a YACS server that a weblog has changed, call [code]weblogUpdates.ping[/code] at path [code]/yacs/services/ping.php[/code].
  101. *
  102. * @see services/ping_test.php
  103. * @see servers/index.php
  104. *
  105. * You can prevent a server to flood you by disabling pings in the related server profile.
  106. *
  107. *
  108. * @author Bernard Paques
  109. * @reference
  110. * @license http://www.gnu.org/copyleft/lesser.txt GNU Lesser General Public License
  111. *
  112. * @see services/configure.php
  113. *
  114. * @link http://www.xmlrpc.com/weblogsCom Weblogs.Com XML-RPC interface
  115. * @link http://www.hixie.ch/specs/pingback/pingback Pingback specification
  116. */
  117. include_once '../shared/global.php';
  118. include_once '../links/link.php';
  119. include_once '../links/links.php';
  120. // load a skin engine
  121. load_skin('services');
  122. // process raw content
  123. $raw_data = file_get_contents("php://input");
  124. // save the raw request if debug mode
  125. if(isset($context['debug_ping']) && ($context['debug_ping'] == 'Y'))
  126. Logger::remember('services/ping.php', 'ping request', $raw_data, 'debug');
  127. // transcode to our internal charset
  128. if($context['charset'] == 'utf-8')
  129. $raw_data = utf8::encode($raw_data);
  130. // load the adequate codec
  131. include_once 'codec.php';
  132. include_once 'xml_rpc_codec.php';
  133. $codec = new xml_rpc_Codec();
  134. // parse xml parameters
  135. $result = $codec->import_request($raw_data);
  136. $status = @$result[0];
  137. $parameters = @$result[1];
  138. // nothing to parse
  139. if(!$raw_data) {
  140. $response = array('faultCode' => 5, 'faultString' => 'Empty request, please retry');
  141. // parse has failed
  142. } elseif(!$status) {
  143. $response = array('faultCode' => 5, 'faultString' => 'Impossible to parse parameters');
  144. // dispatch the request
  145. } else {
  146. // remember parameters if debug mode
  147. if(isset($context['debug_ping']) && ($context['debug_ping'] == 'Y'))
  148. Logger::remember('services/ping.php', 'ping '.$parameters['methodName'], $parameters['params'], 'debug');
  149. elseif(isset($context['debug_trackback']) && ($context['debug_trackback'] == 'Y') && ($parameters['methodName'] == 'pingback.ping'))
  150. Logger::remember('services/ping.php', 'ping '.$parameters['methodName'], $parameters['params'], 'debug');
  151. // depending on method name
  152. switch($parameters['methodName']) {
  153. // we are pinged from a monitoring yacs server
  154. case 'monitor.ping':
  155. // caller has been banned
  156. include_once $context['path_to_root'].'servers/servers.php';
  157. if($_SERVER['REMOTE_HOST'] && ($server =& Servers::get($_SERVER['REMOTE_HOST']) && ($server['process_monitor'] != 'Y')))
  158. $response = array('faultCode' => 49, 'faultString' => 'Access denied');
  159. // check we have a configuration file
  160. elseif(!file_exists($context['path_to_root'].'parameters/control.include.php'))
  161. $response = array('faultCode' => 16, 'faultString' => 'No parameter file parameters/control.include.php');
  162. // ok
  163. else
  164. $response = array('faultCode' => 0, 'faultString' => 'OK');
  165. break;
  166. // ping an external reference to some page on this site
  167. case 'pingback.ping':
  168. list($source, $target) = $parameters['params'];
  169. // we are linking to an article
  170. $anchor = NULL;
  171. if(preg_match('/\/articles\/view.php\/(\w+)/', $target, $matches))
  172. $anchor = 'article:'.$matches[1];
  173. elseif(preg_match('/\/articles\/view.php\?id=(\w+)/', $target, $matches))
  174. $anchor = 'article:'.$matches[1];
  175. // we are linking to a section
  176. if(preg_match('/\/sections\/view.php\/(\w+)/', $target, $matches))
  177. $anchor = 'section:'.$matches[1];
  178. elseif(preg_match('/\/sections\/view.php\?id=(\w+)/', $target, $matches))
  179. $anchor = 'section:'.$matches[1];
  180. // we are linking to a category
  181. if(preg_match('/\/categories\/view.php\/(\w+)/', $target, $matches))
  182. $anchor = 'category:'.$matches[1];
  183. elseif(preg_match('/\/categories\/view.php\?id=(\w+)/', $target, $matches))
  184. $anchor = 'category:'.$matches[1];
  185. // caller has been banned
  186. include_once $context['path_to_root'].'servers/servers.php';
  187. if(isset($_SERVER['REMOTE_HOST']) && ($server =& Servers::get($_SERVER['REMOTE_HOST']) && ($server['process_ping'] != 'Y')))
  188. $response = 49;
  189. // check we are linking on this site
  190. elseif(!preg_match('/^'.preg_quote($context['url_to_home'], '/').'/i', $target))
  191. $response = 33;
  192. elseif(!$anchor)
  193. $response = 33;
  194. // check that the source has not already been registered
  195. elseif(Links::have($source, $anchor))
  196. $response = 48;
  197. // check that the source actually has a link to us
  198. elseif(($content = Link::fetch($source, '', '', 'services/ping.php')) === FALSE)
  199. $response = 16;
  200. // we have to found a reference to the target here
  201. else {
  202. // ensure enough execution time
  203. Safe::set_time_limit(30);
  204. // we have to found a reference to the target here
  205. if(($position = strpos($content, $target)) === FALSE)
  206. $response = 17;
  207. // register the source link back from the target page
  208. else {
  209. // try to grab a title
  210. if(preg_match("/<h1>(.*)<\/h1>/i", $content, $matches))
  211. $fields['title'] = $matches[1];
  212. elseif(preg_match("/<title>(.*)<\/title>/i", $content, $matches))
  213. $fields['title'] = $matches[1];
  214. // try to extract some text around the link
  215. $extract = strip_tags(substr($content, max(0, $position-70), 210), '<a><b><i>');
  216. if(preg_match('/[^<]*>(.*)$/', $extract, $matches))
  217. $extract = $matches[1];
  218. if($extract)
  219. $fields['description'] = '...'.$extract.'...';
  220. // save in the database
  221. $fields['anchor'] = $anchor;
  222. $fields['link_url'] = $source;
  223. if(!$fields['id'] = Links::post($fields))
  224. $response = 0;
  225. else {
  226. $response = 'Thanks for the ping';
  227. Links::clear($fields);
  228. }
  229. }
  230. }
  231. break;
  232. // ping an external reference to some page on this site
  233. case 'weblogUpdates.ping':
  234. list($label, $url) = $parameters['params'];
  235. // caller has been banned
  236. include_once $context['path_to_root'].'servers/servers.php';
  237. if($_SERVER['REMOTE_HOST'] && ($server =& Servers::get($_SERVER['REMOTE_HOST']) && ($server['process_ping'] != 'Y')))
  238. $response = array('flerror' => 49, 'message' => 'Access denied');
  239. // do not accept local address
  240. elseif(preg_match('/\b(127\.0\.0\.1|localhost)\b/', $url))
  241. $response = array('flerror' => 1, 'message' => 'We don\'t accept local references '.$url);
  242. // check we can read the given address, or the same with an additional '/'
  243. elseif((($content = Link::fetch($url, '', '', 'services/ping.php')) === FALSE) && (($content = Link::fetch($url.'/', '', '', 'services/ping.php')) === FALSE))
  244. $response = array('flerror' => 1, 'message' => 'Cannot read source address '.$url);
  245. // create or update a server entry
  246. else {
  247. include_once $context['path_to_root'].'servers/servers.php';
  248. $response = Servers::ping(strip_tags($label), $url);
  249. if($response) {
  250. Logger::remember('services/ping.php', 'failing ping', $response, 'debug');
  251. $response = array('flerror' => 1, 'message' => $response);
  252. } else
  253. $response = array('flerror' => 0, 'message' => 'Thanks for the ping');
  254. }
  255. break;
  256. default:
  257. $response = array('faultCode' => 1, 'faultString' => 'Do not know how to process '.$parameters['methodName']);
  258. Logger::remember('services/ping.php', 'ping unsupported methodName', $parameters, 'debug');
  259. }
  260. }
  261. // no response yet
  262. if(!isset($response))
  263. $response = array('faultCode' => 1, 'faultString' => 'no response');
  264. // build a XML snippet
  265. $result = $codec->export_response($response);
  266. $status = @$result[0];
  267. $response = @$result[1];
  268. // handle the output correctly
  269. render_raw('text/xml; charset='.$context['charset']);
  270. // actual transmission except on a HEAD request
  271. if(isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] != 'HEAD'))
  272. echo $response;
  273. // save the response if debug mode
  274. if(isset($context['debug_ping']) && ($context['debug_ping'] == 'Y'))
  275. Logger::remember('services/ping.php', 'ping response', $response, 'debug');
  276. elseif(isset($context['debug_trackback']) && ($context['debug_trackback'] == 'Y') && ($parameters['methodName'] == 'pingback.ping'))
  277. Logger::remember('services/ping.php', 'ping response', $response, 'debug');
  278. // the post-processing hook
  279. finalize_page();
  280. ?>