PageRenderTime 53ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/links/trackback.php

https://github.com/agnesrambaud/yacs
PHP | 406 lines | 215 code | 70 blank | 121 comment | 53 complexity | 4a567d01d47b4dc67fdeb8bd5c77f337 MD5 | raw file
  1. <?php
  2. /**
  3. * process trackback requests
  4. *
  5. * TrackBack is a framework for peer-to-peer communication and notifications between web sites.
  6. * The central idea behind TrackBack is the idea of a TrackBack ping, a request saying, essentially, &quot;resource A is related/linked to resource B.&quot;
  7. * A TrackBack &quot;resource&quot; is represented by a TrackBack Ping URL, which is just a standard URI.
  8. *
  9. * Here is an example of TrackBack. Note that line breaks have been inserted for readability.
  10. *
  11. * [snippet]
  12. * POST http://www.foo.com/yacs/links/trackback.php?anchor=article:123
  13. * Content-Type: application/x-www-form-urlencoded
  14. *
  15. * title=Foo+Bar
  16. * &url=http://www.bar.com/
  17. * &excerpt=My+Excerpt
  18. * &blog_name=Foo
  19. * [/snippet]
  20. *
  21. * Using TrackBack, sites can communicate about related resources.
  22. * For example, if Weblogger A wishes to notify Weblogger B that he has written something interesting/related/shocking, A sends a TrackBack ping to B.
  23. *
  24. * This script can be triggered either remotely, by some weblog software POSTing trackback attributes, or locally,
  25. * through some GET access to a form aiming to collect these attributes.
  26. *
  27. * Basically, the form will be used by those who want to be referenced by your server but that don't have a trackback-enable platform.
  28. * Others will operate remotely and silently.
  29. *
  30. * Note: it's useless to ask for surfers to be authenticated, since trackback posts are anonymous by construction.
  31. * Check how to harden this script agains spammers and hackers.
  32. *
  33. * @link http://www.movabletype.org/docs/mttrackback.html TrackBack Technical Specification
  34. *
  35. * Note that YACS also has a client implementation of the trackback specification into [script]links/links.php[/script].
  36. *
  37. * Accepted calls:
  38. * - trackback.php/&lt;type&gt;/&lt;id&gt; create a new link for this anchor
  39. * - trackback.php?anchor=&lt;type&gt;:&lt;id&gt; create a new link for this anchor
  40. * - trackback.php/&lt;id&gt; create a new link for the anchor 'article:&lt;id&gt;'
  41. *
  42. * If the anchor for this item specifies a specific skin (option keyword '[code]skin_xyz[/code]'),
  43. * or a specific variant (option keyword '[code]variant_xyz[/code]'), they are used instead default values.
  44. *
  45. * @see links/links.php#ping
  46. * @see articles/view.php
  47. * @see articles/publish.php
  48. * @see sections/view.php
  49. * @see categories/view.php
  50. *
  51. * @author Bernard Paques
  52. * @author GnapZ
  53. * @tester NickR
  54. * @reference
  55. * @license http://www.gnu.org/copyleft/lesser.txt GNU Lesser General Public License
  56. */
  57. // common definitions and initial processing
  58. include_once '../shared/global.php';
  59. include_once 'links.php';
  60. // the source url
  61. $source = NULL;
  62. if(isset($_REQUEST['url']))
  63. $source = $_REQUEST['url'];
  64. $source = strip_tags($source);
  65. // link title
  66. $title = NULL;
  67. if(isset($_REQUEST['title']))
  68. $title = $_REQUEST['title'];
  69. $title = strip_tags($title);
  70. // if title is not provided, the value for url will be set as the title
  71. if(!$title)
  72. $title = $source;
  73. // the excerpt
  74. $excerpt = NULL;
  75. if(isset($_REQUEST['excerpt']))
  76. $excerpt = $_REQUEST['excerpt'];
  77. $excerpt = strip_tags($excerpt);
  78. // the blog name
  79. $blog_name = NULL;
  80. if(isset($_REQUEST['blog_name']))
  81. $blog_name = $_REQUEST['blog_name'];
  82. $blog_name = strip_tags($blog_name);
  83. // look for the anchor reference
  84. $anchor = NULL;
  85. if(isset($_REQUEST['anchor']))
  86. $anchor = $_REQUEST['anchor'];
  87. elseif(isset($context['arguments'][1]))
  88. $anchor = $context['arguments'][0].':'.$context['arguments'][1];
  89. elseif(isset($context['arguments'][0]))
  90. $anchor = 'article:'.$context['arguments'][0];
  91. $anchor = strip_tags($anchor);
  92. // get the related anchor, if any
  93. if($anchor)
  94. $anchor =& Anchors::get($anchor);
  95. // load the skin, maybe with a variant
  96. load_skin('links', $anchor);
  97. // clear the tab we are in, if any
  98. if(is_object($anchor))
  99. $context['current_focus'] = $anchor->get_focus();
  100. // the path to this page
  101. if(is_object($anchor))
  102. $context['path_bar'] = $anchor->get_path_bar();
  103. else
  104. $context['path_bar'] = array( 'links/' => i18n::s('Links') );
  105. // the title of the page
  106. if(is_object($anchor) && ($title = $anchor->get_title()))
  107. $context['page_title'] = sprintf(i18n::s('Reference: %s'), $title);
  108. // stop crawlers
  109. if(Surfer::is_crawler()) {
  110. Safe::header('Status: 401 Forbidden', TRUE, 401);
  111. Logger::error(i18n::s('You are not allowed to perform this operation.'));
  112. // process uploaded data
  113. } elseif(isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'POST')) {
  114. // save the request if debug mode
  115. if(isset($context['debug_trackback']) && ($context['debug_trackback'] == 'Y'))
  116. Logger::remember('links/trackback.php', 'trackback request', $_REQUEST, 'debug');
  117. include_once $context['path_to_root'].'links/link.php';
  118. // do we have a valid target to track?
  119. if(!$anchor || !is_object($anchor))
  120. $response = array('faultCode' => 1, 'faultString' => 'Nothing to track');
  121. // check the source has not already been registered
  122. elseif(Links::have($source, $anchor->get_reference()))
  123. $response = array('faultCode' => 1, 'faultString' => 'The source has already been registered');
  124. // read the source file
  125. elseif(($content = Link::fetch($source, '', '', 'links/trackback.php')) === FALSE)
  126. $response = array('faultCode' => 1, 'faultString' => 'Cannot read source address '.$source);
  127. // we have to find a reference to the target here
  128. else {
  129. // ensure enough execution time
  130. Safe::set_time_limit(30);
  131. // we are coming from this form -- stop robots
  132. if(strpos($_SERVER['HTTP_REFERER'], $context['script_url']) !== FALSE) {
  133. if(Surfer::may_be_a_robot())
  134. $response = array('faultCode' => 1, 'faultString' => 'Please prove you are not a robot');
  135. // remote call -- get network address a.b.c.d of caller
  136. } elseif(!isset($_SERVER['REMOTE_ADDR']) || (!$ip = preg_replace('/[^0-9.]/', '', $_SERVER['REMOTE_ADDR'])))
  137. $response = array('faultCode' => 1, 'faultString' => 'Invalid request');
  138. // remote call -- get host name for referencing page www.foo.bar
  139. elseif((!$items = @parse_url($source)) || !isset($items['host']))
  140. $response = array('faultCode' => 1, 'faultString' => 'Invalid request');
  141. // remote call -- only accepted from referenced server (by network address or by name)
  142. elseif(($items['host'] != $ip) && is_callable('gethostbyname') && (gethostbyname($items['host']) != $ip))
  143. $response = array('faultCode' => 1, 'faultString' => 'Invalid request');
  144. // we already have an error
  145. if(isset($response))
  146. ;
  147. // look for a reference to us in the target page
  148. elseif(($position = strpos($content, $anchor->get_url())) === FALSE)
  149. $response = array('faultCode' => 17, 'faultString' => 'No reference found in the source to the target address '.$target);
  150. // register the source link back from the target page
  151. else {
  152. // link title
  153. $fields['title'] = $title;
  154. // link description (the excerpt combined with the blog name)
  155. $fields['description'] = $excerpt;
  156. if($blog_name)
  157. $fields['description'] .= ' ('.$blog_name.')';
  158. // save in the database
  159. $fields['anchor'] = $anchor->get_reference();
  160. $fields['link_url'] = $source;
  161. if(!$fields['id'] = Links::post($fields))
  162. $response = array('faultCode' => 1, 'faultString' => Logger::error_pop());
  163. else
  164. Links::clear($fields);
  165. }
  166. }
  167. // display results if we are coming from this form
  168. if(strpos($_SERVER['HTTP_REFERER'], $context['script_url']) !== FALSE) {
  169. // an error has been encountered
  170. if(is_array($response))
  171. Logger::error($response['faultString'].' ('.$response['faultCode'].')');
  172. // everything's going fine
  173. else
  174. $context['text'] = '<p>'.i18n::s('Thank you for your contribution')."</p>\n";
  175. // send some XML
  176. } else {
  177. // an error has been encountered
  178. if(is_array($response)) {
  179. $response = '<?xml version="1.0" encoding="'.$context['charset'].'"?>'
  180. ."\n".'<response>'
  181. ."\n".'<error>'.$response['faultCode'].'</error>'
  182. ."\n".'<message>'.$response['faultString'].'</message>'
  183. ."\n".'</response>';
  184. // everything's going fine
  185. } else {
  186. $response = '<?xml version="1.0" encoding="'.$context['charset'].'"?>'
  187. ."\n".'<response>'
  188. ."\n".'<error>0</error>'
  189. ."\n".'</response>';
  190. }
  191. // save the response if debug mode
  192. if(isset($context['debug_trackback']) && ($context['debug_trackback'] == 'Y'))
  193. Logger::remember('links/trackback.php', 'trackback response', $response, 'debug');
  194. // send the response
  195. Safe::header('Content-Type: text/xml');
  196. Safe::header('Content-Length: '.strlen($response));
  197. echo $response;
  198. return;
  199. }
  200. // we don't know which resource is tracked back
  201. } elseif(!$anchor || !is_object($anchor)) {
  202. Safe::header('Status: 404 Not Found', TRUE, 404);
  203. Logger::error(i18n::s('No resource to track back.'));
  204. // ensure that access is allowed
  205. } elseif(is_object($anchor) && !$anchor->is_viewable()) {
  206. Safe::header('Status: 401 Forbidden', TRUE, 401);
  207. Logger::error(i18n::s('You are not allowed to perform this operation.'));
  208. // we have a valid reference, collect other information
  209. } else {
  210. // internal reference, but only to authenticated surfers
  211. //
  212. if(Surfer::is_logged()) {
  213. $label = i18n::s('At any place of this site, use the following code to reference the target page:');
  214. $value = '['.str_replace(':', '=', $anchor->get_reference()).']';
  215. $text = '<p>'.$label.' <code>'.$value.'</code></p>'."\n";
  216. $context['text'] .= Skin::build_box(i18n::s('Internal reference'), $text);
  217. }
  218. // external reference
  219. //
  220. $text = '';
  221. // compute the summary
  222. $summary = '<a href="'.$context['url_to_home'].$context['url_to_root'].$anchor->get_url().'">'
  223. ."\n".$anchor->get_title()."\n".'</a>';
  224. if(is_object($anchor) && ($excerpt = $anchor->get_teaser('basic')))
  225. $summary .= ' &mdash; '.$excerpt;
  226. // a suggestion of text to be pasted in referencing page
  227. $text .= '<p>'.i18n::s('We suggest you to cut and paste the following piece of text to reference this page:').'</p>'
  228. .Skin::build_block(encode_field($summary), 'code');
  229. // permalink
  230. $label = i18n::s('Permament address (permalink):');
  231. $value = $context['url_to_home'].$context['url_to_root'].$anchor->get_url();
  232. $text .= '<p>'.$label.BR.'<code>'.$value.'</code></p>'."\n";
  233. // other links
  234. $other = array();
  235. if($link = $anchor->get_named_url())
  236. $other[] = $context['url_to_home'].$context['url_to_root'].$link;
  237. if($link = $anchor->get_short_url())
  238. $other[] = $context['url_to_home'].$context['url_to_root'].$link;
  239. if($other) {
  240. $label = i18n::ns('Other address:', 'Other addresses:', count($other));
  241. $text .= '<p>'.$label.BR.'<code>'.join(BR, $other).'</code></p>'."\n";
  242. }
  243. $context['text'] .= Skin::build_box(i18n::s('External reference'), $text);
  244. // trackback
  245. //
  246. $text = '';
  247. // splash screen
  248. $text .= '<p>'.i18n::s('You can use the form below to manually trackback any of your pages to this site. Of course, use this capability only if your weblogging software is not able to do it automatically.')."</p>\n";
  249. // the form to edit a link
  250. $text .= '<form method="post" action="'.$context['script_url'].'" onsubmit="return validateDocumentPost(this)" id="main_form"><div>';
  251. // the link url
  252. if(!isset($url) || !$url)
  253. $url = 'http://'.i18n::s('your_server/your_page').'...';
  254. $label = i18n::s('Referencing address');
  255. $input = '<input type="text" name="url" id="url" size="45" value="'.encode_field($url).'" maxlength="255" />';
  256. $hint = i18n::s('The remote address that is referencing content at this site');
  257. $fields[] = array($label, $input, $hint);
  258. // the title
  259. $label = i18n::s('Its title');
  260. $input = '<input type="text" name="title" size="45" maxlength="255" />';
  261. $hint = i18n::s('The title of your page');
  262. $fields[] = array($label, $input, $hint);
  263. // the excerpt
  264. $label = i18n::s('Excerpt or description');
  265. $input = '<textarea name="excerpt" rows="5" cols="50"></textarea>';
  266. $hint = i18n::s('As this field may be searched by surfers, please choose adequate searchable words');
  267. $fields[] = array($label, $input, $hint);
  268. // the blog name
  269. $label = i18n::s('Blog name or section');
  270. $input = '<input type="text" name="blog_name" size="45" value="'.encode_field($blog_name).'" maxlength="64" />';
  271. $hint = i18n::s('To complement the excerpt');
  272. $fields[] = array($label, $input, $hint);
  273. // random string to stop robots
  274. if(!Surfer::is_logged() && ($field = Surfer::get_robot_stopper()))
  275. $fields[] = $field;
  276. // build the form
  277. $text .= Skin::build_form($fields);
  278. // the submit button
  279. $text .= '<p>'.Skin::build_submit_button(i18n::s('Submit'), i18n::s('Press [s] to submit data'), 's').'</p>';
  280. // other hidden fields
  281. $text .= '<input type="hidden" name="anchor" value="'.$anchor->get_reference().'" />';
  282. // end of the form
  283. $text .= '</div></form>';
  284. // the script used for form handling at the browser
  285. $text .= JS_PREFIX
  286. .'// check that main fields are not empty'."\n"
  287. .'func'.'tion validateDocumentPost(container) {'."\n"
  288. ."\n"
  289. .' // url is mandatory'."\n"
  290. .' if(!container.url.value) {'."\n"
  291. .' alert("'.i18n::s('Please type a valid link.').'");'."\n"
  292. .' Yacs.stopWorking();'."\n"
  293. .' return false;'."\n"
  294. .' }'."\n"
  295. ."\n"
  296. .' // title is mandatory'."\n"
  297. .' if(!container.title.value) {'."\n"
  298. .' alert("'.i18n::s('Please provide a meaningful title.').'");'."\n"
  299. .' Yacs.stopWorking();'."\n"
  300. .' return false;'."\n"
  301. .' }'."\n"
  302. ."\n"
  303. .' // exceprt is mandatory'."\n"
  304. .' if(!container.exceprt.value) {'."\n"
  305. .' alert("'.i18n::s('You must type an excerpt of the referencing page.').'");'."\n"
  306. .' Yacs.stopWorking();'."\n"
  307. .' return false;'."\n"
  308. .' }'."\n"
  309. ."\n"
  310. .' // blog_name is mandatory'."\n"
  311. .' if(!container.blog_name.value) {'."\n"
  312. .' alert("'.i18n::s('You must name the originating blog.').'");'."\n"
  313. .' Yacs.stopWorking();'."\n"
  314. .' return false;'."\n"
  315. .' }'."\n"
  316. ."\n"
  317. .' // successful check'."\n"
  318. .' return true;'."\n"
  319. .'}'."\n"
  320. ."\n"
  321. .'// set the focus on first form field'."\n"
  322. .'$("url").focus();'."\n"
  323. .JS_SUFFIX."\n";
  324. // trackback link
  325. $label = i18n::s('Trackback address:');
  326. $value = $context['url_to_home'].$context['url_to_root'].'links/trackback.php?anchor='.$anchor->get_reference();
  327. $text .= '<p>'.$label.' <code>'.$value.'</code></p>'."\n";
  328. $context['text'] .= Skin::build_box(i18n::s('Trackback'), $text);
  329. // general help on this form
  330. $help = '<p>'.sprintf(i18n::s('This server supports the %s created by Ben Trott and Mena Trott. Please note that any %s system attempts to trackback up to seven links from each published page.'), Skin::build_link('http://www.movabletype.org/docs/mttrackback.html', i18n::s('trackback specification'), 'external'), Skin::build_link('http://www.yacs.fr/', 'Yacs', 'external')).'</p>'
  331. .'<p>'.i18n::s('You can use this form to manually trackback your pages to this site.').'</p>';
  332. $context['components']['boxes'] = Skin::build_box(i18n::s('Help'), $help, 'boxes', 'help');
  333. }
  334. // render the skin
  335. render_skin();
  336. ?>