PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/xoops_trust_path/modules/d3blog/class/trackback.class.php

http://xoopscube-modules.googlecode.com/
PHP | 569 lines | 456 code | 74 blank | 39 comment | 80 complexity | 958bf81c5c4e541f2eaed40a807a1a12 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, AGPL-1.0
  1. <?php
  2. /**
  3. * @version $Id: trackback.class.php 638 2010-06-26 05:31:52Z hodaka $
  4. * @author Takeshi Kuriyama <kuri@keynext.co.jp>
  5. */
  6. if( ! defined( 'XOOPS_TRUST_PATH' ) ) die( 'set XOOPS_TRUST_PATH into mainfile.php' );
  7. require_once dirname(dirname(__FILE__)).'/lib/object.php';
  8. require_once dirname(dirname(__FILE__)).'/include/filter/TrackbackFilter.class.php';
  9. require_once dirname(dirname(__FILE__)).'/include/blacklist.class.php';
  10. require_once XOOPS_ROOT_PATH.'/class/snoopy.php';
  11. if( ! class_exists('d3blogTrackbackObjectBase') ){
  12. class d3blogTrackbackObjectBase extends myXoopsObject {
  13. var $mydirname_;
  14. var $_encoding = 'UTF-8';
  15. var $tbkey_;
  16. var $_data = array('title' => '', 'blog_name' => '', 'excerpt' => '', 'url' => '');
  17. function d3blogTrackbackObjectBase($id=null)
  18. {
  19. $this->initVar("tid", XOBJ_DTYPE_INT, 0, true);
  20. $this->initVar("bid", XOBJ_DTYPE_INT, 0, true);
  21. $this->initVar("blog_name", XOBJ_DTYPE_TXTBOX, null, false, 255);
  22. $this->initVar("url", XOBJ_DTYPE_TXTBOX, null, false, 150);
  23. $this->initVar("trackback_url", XOBJ_DTYPE_TXTBOX, null, false, 150);
  24. $this->initVar("title", XOBJ_DTYPE_TXTBOX, null, false, 255);
  25. $this->initVar("excerpt", XOBJ_DTYPE_TXTAREA, null, false);
  26. $this->initVar("direction", XOBJ_DTYPE_INT, 0, false);
  27. $this->initVar("host", XOBJ_DTYPE_TXTBOX, null, false, 15);
  28. $this->initVar("tbkey", XOBJ_DTYPE_TXTBOX, null, false, 12);
  29. $this->initVar("approved", XOBJ_DTYPE_INT, 0, false);
  30. $this->initVar("created", XOBJ_DTYPE_INT, time(), false);
  31. if ( is_array ( $id ) )
  32. $this->assignVars ( $id );
  33. }
  34. function &getStructure($type='s')
  35. {
  36. $ret =& parent::getStructure($type);
  37. return $ret;
  38. }
  39. function isApproved() {
  40. return $this->getVar('approved');
  41. }
  42. function autodiscover() {
  43. // get file contents.
  44. if (!$contents = $this->_getContents($this->getVar('trackback_url'))) {
  45. $this->setErrors('Could not get contents of '.$this->getVar('trackback_url'));
  46. return false;
  47. }
  48. // get trackback identifier
  49. if (!preg_match('@dc:identifier\s*=\s*["\'](http:[^"\']+)"@i', $contents, $matches)) {
  50. $this->setErrors('No trackback RDF found in "'.$this->getVar('trackback_url').'".');
  51. return false;
  52. }
  53. $identifier = trim($matches[1]);
  54. // get trackback URI
  55. if (!preg_match('@trackback:ping\s*=\s*["\'](http:[^"\']+)"@i', $contents, $matches)) {
  56. $this->setErrors('No trackback URI found in "'.$this->getVar('trackback_url').'".');
  57. return false;
  58. }
  59. $trackbackUrl = trim($matches[1]);
  60. // check if the URL to trackback matches the identifier from the autodiscovery code
  61. if ($identifier != $this->getVar('trackback_url')) {
  62. $this->setErrors('URLs mismatch. "'.$this->getVar('trackback_url').'" not equal to "'.$identifier.'".');
  63. return false;
  64. }
  65. $this->setVar('trackback_url', $trackbackUrl);
  66. return true;
  67. }
  68. function send() {
  69. $snoopy = new Snoopy();
  70. if (!$snoopy->submit($this->getVar('trackback_url'), $this->_data)) {
  71. $this->setErrors('Host returned Error:'.$snoopy->error);
  72. return false;
  73. }
  74. return $this->_interpreteTrackbackResponse($snoopy->results);
  75. }
  76. function receive() {
  77. if ( extension_loaded('mbstring') ) {
  78. $internalEncoding = mb_internal_encoding();
  79. $this->_encoding = mb_detect_encoding( serialize($_POST) );
  80. mb_convert_variables( $internalEncoding, $this->_encoding, $_POST );
  81. }
  82. $this->setVars($_POST);
  83. $this->setVar('trackback_url', '');
  84. if(!$this->_checkItems()) {
  85. return false;
  86. }
  87. return true;
  88. }
  89. function spamCheck() {
  90. $myModule = call_user_func(array($this->mydirname_, 'getInstance'));
  91. $check_items = $myModule->getConfig('spam_check');
  92. if(is_array($check_items) && in_array('n', $check_items)) {
  93. return true;
  94. }
  95. // multibytes characters check
  96. if(XOOPS_USE_MULTIBYTES == 1 && function_exists('mb_strlen') && in_array('l', $check_items) && $myModule->getConfig('regex_pattern')) {
  97. $elements = array('title', 'blog_name', 'excerpt');
  98. $string = mb_convert_encoding($this->_makeString($elements), 'UTF-8');
  99. $pattern = mb_convert_encoding($myModule->getConfig('regex_pattern'), 'UTF-8');
  100. $matched = '';
  101. if(preg_match_all("/($pattern)/ux", $string, $matches)) {
  102. $matched = join("", $matches[1]);
  103. }
  104. if(mb_strlen($matched) < $myModule->getConfig('letters')) {
  105. $this->setErrors('Our language characters are not found in your comment. ');
  106. return false;
  107. }
  108. }
  109. // word check
  110. if(in_array('w', $check_items) && $myModule->getConfig('wordlist')) {
  111. $elements = array('title', 'blog_name', 'excerpt');
  112. $patterns = preg_split("/[\s]+/", $myModule->getConfig('wordlist'));
  113. $string = $this->_makeString($elements);
  114. foreach($patterns as $pattern) {
  115. // if(false !== stripos($string, $pattern)) {
  116. if(preg_match("/$pattern/i", $string)) {
  117. $this->setErrors('Your trackback contains a banned word. ');
  118. return false;
  119. }
  120. }
  121. }
  122. // regex check
  123. if(in_array('r', $check_items) && $myModule->getConfig('regex')) {
  124. $elements = array('title', 'blog_name', 'excerpt', 'url');
  125. $pattern = '('.implode('|', preg_split("/[\s]+/", $myModule->getConfig('regex'))).')';
  126. if (preg_match('/'. $pattern. '/i', $this->_makeString($elements))) {
  127. $this->setErrors('Your trackback contains a spammy word. ');
  128. return false;
  129. }
  130. }
  131. // dnsbl check
  132. if(in_array('d', $check_items) && $myModule->getConfig('dnsbl')) {
  133. $dnsbls = preg_split("/[\s]+/", $myModule->getConfig('dnsbl'));
  134. if(is_array($dnsbls) && !empty($dnsbls)) {
  135. $blacklist = new Blacklist();
  136. if($blacklist->setBlacklists($dnsbls)) {
  137. $senderIP = $blacklist->getIP();
  138. if($senderIP == 'unknown' || !($blacklist->checkIP($senderIP))) {
  139. $this->setErrors('Your IP address is unknown.');
  140. return false;
  141. }
  142. if($blacklist->isListed($blacklist->reverseIP($senderIP))) {
  143. $this->setErrors('Your IP address, '.htmlspecialchars($senderIP, ENT_QUOTES).', are listed on a blacklist db.');
  144. return false;
  145. }
  146. $this->setVar('host', $senderIP);
  147. }
  148. unset($blacklist);
  149. }
  150. }
  151. // SURBL check
  152. if(in_array('s', $check_items) && $myModule->getConfig('surbl')) {
  153. $elements = array('title', 'excerpt', 'blog_name');
  154. $urls = $this->_extractURLs($this->_makeString($elements));
  155. if(count($urls) > 0) {
  156. $blacklist = new Blacklist();
  157. // strip domain prefix to get a domain
  158. $blacklist->buildLookupDomain();
  159. // check if a domain is ip address
  160. if(!empty($blacklist->_ips)) {
  161. if($blacklist->setBlacklists(preg_split("/[\s]+/", $myModule->getConfig('dnsbl')))) {
  162. foreach($blacklist->_ips as $ip) {
  163. if($blacklist->isListed($blacklist->reverseIP($ip))) {
  164. $this->setErrors('Your contents are contaminated with spammy domain:'.htmlspecialchars($ip), ENT_QUOTES);
  165. return false;
  166. }
  167. }
  168. }
  169. }
  170. // check if domains exist
  171. if(!empty($blacklist->_domains)) {
  172. if($blacklist->setBlacklists(preg_split("/[\s]+/", $myModule->getConfig('surbl')))) {
  173. foreach($blacklist->_domains as $domain) {
  174. if($blacklist->isListed($domain)) {
  175. $this->setErrors('Your contents are contaminated with spammy domain name:'.htmlspecialchars($domain), ENT_QUOTES);
  176. return false;
  177. }
  178. }
  179. }
  180. }
  181. }
  182. unset($blacklist);
  183. }
  184. // reference check
  185. if(in_array('f', $check_items)) {
  186. $references = array(
  187. XOOPS_URL.'/modules/'.$this->mydirname_.'/details.php?bid='.$this->getVar('bid'),
  188. XOOPS_URL.'/modules/'.$this->mydirname_.'/tb.php/'.$this->tbkey_
  189. );
  190. // check the remote site
  191. $contents = $this->_getContents($this->getVar('url'));
  192. if(!$contents) {
  193. $this->setErrors('Could not reach your blog url.');
  194. return false;
  195. }
  196. $ref = false;
  197. foreach($references as $reference) {
  198. if(false !== strpos($contents, $reference)) {
  199. $ref = true;
  200. break;
  201. }
  202. }
  203. if(!$ref) {
  204. $this->setErrors('No reference to me in your blog. ');
  205. return false;
  206. }
  207. }
  208. return true;
  209. }
  210. function sendWeblogUpdateping($parsed_url, $timeout = 0) {
  211. $rpc_server = $parsed_url['scheme'].$parsed_url['host'].$parsed_url['path'];
  212. $fp = fsockopen($parsed_url['host'], 80, $errno, $errstr, $timeout);
  213. if (!$fp) {
  214. $this->setErrors('Connection to RPC server '
  215. . htmlspecialchars($rpc_server, ENT_QUOTES) . ':' . 80
  216. . ' failed. ' . $errstr);
  217. return false;
  218. }
  219. if($timeout) {
  220. stream_set_timeout($fp, $timeout);
  221. }
  222. $updateping_body = $this->_getUpdatepingBody();
  223. $updateping_headers = $this->_getUpdatepingHeader($parsed_url, strlen($updateping_body));
  224. $op = $updateping_headers . "\r\n\r\n" . $updateping_body;
  225. if(!fputs($fp, $op, strlen($op))) {
  226. $this->setErrors('Transmission to RPC server '
  227. . htmlspecialchars($rpc_server, ENT_QUOTES) . ':' . 80
  228. . ' failed. ');
  229. return false;
  230. }
  231. // $response = fread($fp , 4095);
  232. $meta = stream_get_meta_data($fp);
  233. if ($meta['timed_out']) {
  234. fclose($fp);
  235. $this->setErrors('RPC server:'.htmlspecialchars($rpc_server, ENT_QUOTES).' did not send response before timeout.');
  236. return false;
  237. }
  238. //error_log($response . "\n" , 3 , 'd:/tmp/debug.log');
  239. fclose($fp);
  240. return true;
  241. }
  242. function _getUpdatepingBody() {
  243. // each rss
  244. $body =<<<EOD
  245. <?xml version="1.0" encoding="UTF-8" ?>
  246. <methodCall>
  247. <methodName>weblogUpdates.ping</methodName>
  248. <params>
  249. <param>
  250. <value>%s</value>
  251. </param>
  252. <param>
  253. <value>%s</value>
  254. </param>
  255. </params>
  256. </methodCall>
  257. EOD;
  258. return str_replace("\n", "\r\n", sprintf($body, xoops_utf8_encode($this->getVar('blog_name')), xoops_utf8_encode($this->getVar('url'))));
  259. }
  260. function _getUpdatepingHeader($parsed_url, $length) {
  261. $headers = 'POST ';
  262. $headers .= $parsed_url['path']. " HTTP/1.0\r\n";
  263. $headers .= "User-Agent: XOOPSCUBE D3BLOG\r\n";
  264. $headers .= 'Host: ' . $parsed_url['host'] . "\r\n";
  265. $headers .= "Content-Type: text/xml\r\n";
  266. $headers .= 'Content-Length: ' . $length;
  267. return $headers;
  268. }
  269. function _extractURLs($string) {
  270. $urls = '(?:http|file|ftp)';
  271. $ltrs = 'a-z0-9';
  272. $gunk = '.-';
  273. $punc = $gunk;
  274. $any = "$ltrs$gunk";
  275. /* $regex = "{
  276. $urls ://
  277. [$any]+
  278. (?=
  279. [$punc]*
  280. [^$any]
  281. |
  282. )
  283. }x";*/
  284. $regex = "/(?:http|https|file|ftp)://[A-Za-z0-9.-]+(?=[.-]*[^A-Za-z0-9.-]|)/x";
  285. $urls = array();
  286. if (0 !== preg_match_all($regex, $string, $matches)) {
  287. foreach ($matches[0] as $match) {
  288. $urls[] = $match;
  289. }
  290. }
  291. return array_unique($urls);
  292. }
  293. function _makeString($elements) {
  294. $str = '';
  295. foreach($elements as $element) {
  296. if($this->vars[$element] && $this->getVar($element)) {
  297. $str .= $this->getVar($element);
  298. }
  299. }
  300. return $str;
  301. }
  302. function _checkItems() {
  303. foreach(array_keys($this->_data) as $key) {
  304. if(!isset($this->vars[$key]) || !$this->getVar($key)) {
  305. $this->setErrors($key.' is not found in your trackback. ');
  306. return false;
  307. }
  308. }
  309. if(!d3blog_validateUrl($this->getVar('url'))) {
  310. $this->setErrors('Invalid url. ');
  311. return false;
  312. }
  313. return true;
  314. }
  315. function _getContents($url) {
  316. // check the remote site
  317. $snoopy = new Snoopy();
  318. if($snoopy->fetch($url)) {
  319. return $snoopy->results;
  320. } else {
  321. $this->setErrors($snoopy->error);
  322. unset($snoopy);
  323. return false;
  324. }
  325. }
  326. function _interpreteTrackbackResponse($response)
  327. {
  328. if (!preg_match('@<error>([0-9]+)</error>@', $response, $matches)) {
  329. $this->setErrors('Invalid trackback response, error code not found.');
  330. return false;
  331. }
  332. $errorCode = $matches[1];
  333. // Error code 0 means no error.
  334. if ($errorCode == 0) {
  335. return true;
  336. }
  337. if (!preg_match('@<message>([^<]+)</message>@', $response, $matches)) {
  338. $this->setErrors('Error code '.$errorCode.', no message received.');
  339. }
  340. $this->setErrors('Error code '.$errorCode.', message "'.$matches[1].'" received.');
  341. return false;
  342. }
  343. }
  344. class d3blogTrackbackObjectHandlerBase extends myXoopsObjectHandler
  345. {
  346. var $mydirname_;
  347. var $filter_;
  348. function d3blogTrackbackObjectHandlerBase($db,$classname=null) {
  349. parent::myXoopsObjectHandler($db,$classname);
  350. $this->mydirname_ = '';
  351. }
  352. function getId() {
  353. if (array_key_exists('PATH_INFO', $_SERVER)) {
  354. $pathinfo = $_SERVER['PATH_INFO'];
  355. } else {
  356. $pathinfo = '';
  357. }
  358. if (substr($pathinfo, 0, 1) == '/') {
  359. $id = substr($pathinfo, 1);
  360. } else {
  361. return false;
  362. }
  363. $pos = strpos($id, '/');
  364. if ($pos != false) {
  365. $id = substr($id, 0, $pos);
  366. }
  367. return str_replace("\0", "", $id);
  368. }
  369. function getDefaultCriteria() {
  370. // get a default trackback criteria thru filter
  371. $this->filter_ = call_user_func(array($this->mydirname_.'TrackbackFilter', 'getInstance'));
  372. $criteria =& $this->filter_->getDefaultCriteria();
  373. return $criteria;
  374. }
  375. function getCriteria() {
  376. // get trackback criteria thru filter
  377. $this->filter_ = call_user_func(array($this->mydirname_.'TrackbackFilter', 'getInstance'));
  378. $criteria =& $this->filter_->getCriteria();
  379. return $criteria;
  380. }
  381. function getTrackback($bid, $boundfor=null, $as_object=false) {
  382. $myModule = call_user_func(array($this->mydirname_, 'getInstance'));
  383. $return = array();
  384. if(empty($bid))
  385. return $return;
  386. $this->filter_ = call_user_func(array($this->mydirname_.'TrackbackFilter', 'getInstance'));
  387. $criteria = $this->filter_->getCriteria();
  388. $criteria->add(new criteria('bid', intval($bid)));
  389. if($boundfor)
  390. $criteria->add(new criteria('direction', intval($boundfor)));
  391. else
  392. $criteria->add(new criteria('direction', '0', '<>'));
  393. $objs =& $this->getObjects($criteria);
  394. if(count($objs)) {
  395. foreach($objs as $obj) {
  396. $direction = $obj->getVar('direction')==1? 'outbound' : 'inbound';
  397. if(!$as_object) {
  398. $return[$direction][$obj->getVar('tid')] = $obj->getStructure();
  399. } else {
  400. $return[$direction][$obj->getVar('tid')] = $obj;
  401. }
  402. }
  403. }
  404. return $return;
  405. }
  406. function getByTbkey($id) {
  407. $myts =& d3blogTextSanitizer:: getInstance();
  408. $criteria = new criteriaCompo(new criteria('tbkey', $myts->addSlashes($id)));
  409. return $this->getOne($criteria);
  410. }
  411. // purge old trackback key. works every 10 times by default
  412. function garbageTrackbackKey($per=10, $expiration=86400){
  413. $per=intval($per);
  414. if(rand(0,100) > $per){
  415. return null ;
  416. }
  417. $expiration = inyval($expiration);
  418. $sql = sprintf('delete from %s where direction=\'0\' and created<%d',
  419. $this->db->prefix($this->mydirname_.'_trackback'), time()-$expiration ); // default 1 day
  420. $this->db->query($sql);
  421. }
  422. function getPingList($rss)
  423. {
  424. // each rss
  425. $items = '';
  426. if(isset($rss['items']) && is_array($rss['items'])) {
  427. $item = <<<EOF
  428. <item>
  429. <title>%s</title>
  430. <link>%s</link>
  431. <description>%s</description>
  432. </item>
  433. EOF;
  434. foreach($rss['items'] as $r) {
  435. $items .= sprintf($item, $r['title'], $r['url'], $r['excerpt']);
  436. }
  437. }
  438. $res = <<<EOD
  439. <?xml version="1.0" encoding="%s"?>
  440. <response>
  441. <error>0</error>
  442. <rss version="0.91">
  443. <channel>
  444. <title>%s</title>
  445. <link>%s</link>
  446. <description>%s</description>
  447. <language>%s</language>
  448. %s
  449. </channel>
  450. </rss>
  451. </response>
  452. EOD;
  453. return sprintf($res, $rss['encoding'], $rss['title'], $rss['url'], $rss['excerpt'],
  454. $rss['language'], $items);
  455. }
  456. function insert(&$obj, $force=false)
  457. {
  458. return parent::insert($obj, $force);
  459. }
  460. function delete(&$obj, $force=false) {
  461. return parent::delete($obj, $force);
  462. }
  463. function deletes($criteria = null)
  464. {
  465. parent::deletes($criteria);
  466. }
  467. }
  468. }
  469. if( ! class_exists($object_class_name) ) {
  470. require dirname(dirname(__FILE__)).'/include/filter/TrackbackFilter.class.php';
  471. eval('
  472. class '. $object_class_name .' extends d3blogTrackbackObjectBase {
  473. function '. $object_class_name .'($id=null) {
  474. $this->mydirname_ = "'.$mydirname.'";
  475. $this->d3blogTrackbackObjectBase();
  476. }
  477. function getTableInfo() {
  478. $tinfo = new myTableInfomation("'.$mydirname.'_trackback", "tid");
  479. return ($tinfo);
  480. }
  481. }
  482. ');
  483. eval('
  484. class '. $object_class_name .'Handler extends d3blogTrackbackObjectHandlerBase {
  485. var $mydirname_;
  486. function '. $object_class_name .'Handler($db) {
  487. parent::myXoopsObjectHandler($db, "'. $object_class_name .'");
  488. $this->mydirname_ = "'.$mydirname.'";
  489. }
  490. }
  491. ');
  492. }
  493. ?>