pingback.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. /*
  3. Copyright (c) 2009-2017 F3::Factory/Bong Cosca, All rights reserved.
  4. This file is part of the Fat-Free Framework (http://fatfreeframework.com).
  5. This is free software: you can redistribute it and/or modify it under the
  6. terms of the GNU General Public License as published by the Free Software
  7. Foundation, either version 3 of the License, or later.
  8. Fat-Free Framework is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License along
  13. with Fat-Free Framework. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. namespace Web;
  16. //! Pingback 1.0 protocol (client and server) implementation
  17. class Pingback extends \Prefab {
  18. protected
  19. //! Transaction history
  20. $log;
  21. /**
  22. * Return TRUE if URL points to a pingback-enabled resource
  23. * @return bool
  24. * @param $url
  25. **/
  26. protected function enabled($url) {
  27. $web=\Web::instance();
  28. $req=$web->request($url);
  29. $found=FALSE;
  30. if ($req['body']) {
  31. // Look for pingback header
  32. foreach ($req['headers'] as $header)
  33. if (preg_match('/^X-Pingback:\h*(.+)/',$header,$href)) {
  34. $found=$href[1];
  35. break;
  36. }
  37. if (!$found &&
  38. // Scan page for pingback link tag
  39. preg_match('/<link\h+(.+?)\h*\/?>/i',$req['body'],$parts) &&
  40. preg_match('/rel\h*=\h*"pingback"/i',$parts[1]) &&
  41. preg_match('/href\h*=\h*"\h*(.+?)\h*"/i',$parts[1],$href))
  42. $found=$href[1];
  43. }
  44. return $found;
  45. }
  46. /**
  47. * Load local page contents, parse HTML anchor tags, find permalinks,
  48. * and send XML-RPC calls to corresponding pingback servers
  49. * @return NULL
  50. * @param $source string
  51. **/
  52. function inspect($source) {
  53. $fw=\Base::instance();
  54. $web=\Web::instance();
  55. $parts=parse_url($source);
  56. if (empty($parts['scheme']) || empty($parts['host']) ||
  57. $parts['host']==$fw->HOST) {
  58. $req=$web->request($source);
  59. $doc=new \DOMDocument('1.0',$fw->ENCODING);
  60. $doc->stricterrorchecking=FALSE;
  61. $doc->recover=TRUE;
  62. if (@$doc->loadhtml($req['body'])) {
  63. // Parse anchor tags
  64. $links=$doc->getelementsbytagname('a');
  65. foreach ($links as $link) {
  66. $permalink=$link->getattribute('href');
  67. // Find pingback-enabled resources
  68. if ($permalink && $found=$this->enabled($permalink)) {
  69. $req=$web->request($found,
  70. [
  71. 'method'=>'POST',
  72. 'header'=>'Content-Type: application/xml',
  73. 'content'=>xmlrpc_encode_request(
  74. 'pingback.ping',
  75. [$source,$permalink],
  76. ['encoding'=>$fw->ENCODING]
  77. )
  78. ]
  79. );
  80. if ($req['body'])
  81. $this->log.=date('r').' '.
  82. $permalink.' [permalink:'.$found.']'.PHP_EOL.
  83. $req['body'].PHP_EOL;
  84. }
  85. }
  86. }
  87. unset($doc);
  88. }
  89. }
  90. /**
  91. * Receive ping, check if local page is pingback-enabled, verify
  92. * source contents, and return XML-RPC response
  93. * @return string
  94. * @param $func callback
  95. * @param $path string
  96. **/
  97. function listen($func,$path=NULL) {
  98. $fw=\Base::instance();
  99. if (PHP_SAPI!='cli') {
  100. header('X-Powered-By: '.$fw->PACKAGE);
  101. header('Content-Type: application/xml; '.
  102. 'charset='.$charset=$fw->ENCODING);
  103. }
  104. if (!$path)
  105. $path=$fw->BASE;
  106. $web=\Web::instance();
  107. $args=xmlrpc_decode_request($fw->BODY,$method,$charset);
  108. $options=['encoding'=>$charset];
  109. if ($method=='pingback.ping' && isset($args[0],$args[1])) {
  110. list($source,$permalink)=$args;
  111. $doc=new \DOMDocument('1.0',$fw->ENCODING);
  112. // Check local page if pingback-enabled
  113. $parts=parse_url($permalink);
  114. if ((empty($parts['scheme']) ||
  115. $parts['host']==$fw->HOST) &&
  116. preg_match('/^'.preg_quote($path,'/').'/'.
  117. ($fw->CASELESS?'i':''),$parts['path']) &&
  118. $this->enabled($permalink)) {
  119. // Check source
  120. $parts=parse_url($source);
  121. if ((empty($parts['scheme']) ||
  122. $parts['host']==$fw->HOST) &&
  123. ($req=$web->request($source)) &&
  124. $doc->loadhtml($req['body'])) {
  125. $links=$doc->getelementsbytagname('a');
  126. foreach ($links as $link) {
  127. if ($link->getattribute('href')==$permalink) {
  128. call_user_func_array($func,[$source,$req['body']]);
  129. // Success
  130. die(xmlrpc_encode_request(NULL,$source,$options));
  131. }
  132. }
  133. // No link to local page
  134. die(xmlrpc_encode_request(NULL,0x11,$options));
  135. }
  136. // Source failure
  137. die(xmlrpc_encode_request(NULL,0x10,$options));
  138. }
  139. // Doesn't exist (or not pingback-enabled)
  140. die(xmlrpc_encode_request(NULL,0x21,$options));
  141. }
  142. // Access denied
  143. die(xmlrpc_encode_request(NULL,0x31,$options));
  144. }
  145. /**
  146. * Return transaction history
  147. * @return string
  148. **/
  149. function log() {
  150. return $this->log;
  151. }
  152. /**
  153. * Instantiate class
  154. * @return object
  155. **/
  156. function __construct() {
  157. // Suppress errors caused by invalid HTML structures
  158. libxml_use_internal_errors(TRUE);
  159. }
  160. }