最近因為 tt 的 trackback spam 發生的愈來愈嚴重了, 雖然有 eolin 的 antispam 在背後努力著, 但是還是進來了一堆 trackback 的 spam.
最近發生的大多是這個網址來的: 9hxofreeporn.info 還有 9hwifreeporn.info , 不知他是如何躲過 eolin 的 antispam plugin 的咧. 不過無論如此, 針對這樣的特性, 索性想寫個 plugin 來自己 antispam 一下, 結果在實測時, 發生了小狀況.
因為在 tt 裡, AddingTrackback 在 1.0.6 版本以上已成為可用的一個 event 了, 不過根據 tt 的 event 觸發行為是利用列舉 enable 的 function 來進行呼叫, 若是非 antispam 這種 plugin 倒沒什麼關係, 因為這些 event function 都是要被執行的, 不過像 antispam 這樣的應用時, 會發生 return 值要拿來做為是否加入 trackback 成功的要件的這種狀況下, 多個 event function 將會發生只回傳最後一個呼叫的 event function, tt 的 fireEvent 程式碼如下:
function fireEvent($event,$target=null,$mother=null,$condition=true){ global $service,$eventMappings,$pluginURL; if(!$condition) return $target; if(!isset($eventMappings[$event])) return $target; foreach($eventMappings[$event] as $mapping){ include_once ("../../plugins/{$mapping['plugin']}/index.php"); if(function_exists($mapping['listener'])){ $pluginURL="{$service['path']}/plugins/{$mapping['plugin']}"; $target=call_user_func($mapping['listener'],$target,$mother); } } return $target; }
要命的就是其中的 foreach 中, return 回來的這個 $target, 會因為多次呼叫 call_user_func 的結果而一直被覆蓋掉, 所以若是自行撰寫獨立的 antispam 必須確保在 eolin 的 antispam 後, 但再仔細看一下, 其實 $target 是會一直再傳入的, 也就是說, 我實測結果有問題的原因, 也就是 $target 被覆蓋的原因其實並不在這裡, 而是在 eolin 的 antispam 中的 EAS_AddingTrackback function:
function EAS_AddingTrackback($target, $mother) { return EAS_Call(2, $mother['site'], $mother['title'], $mother['url'], $mother['excerpt']); }
及 EAS_Call function:
function EAS_Call($type, $name, $title, $url, $content) { requireComponent('Eolin.PHP.Core'); requireComponent('Eolin.PHP.XMLRPC'); global $hostURL, $blogURL; $blogstr = $hostURL . $blogURL; $rpc = new XMLRPC(); $rpc->url = 'http://antispam.eolin.com/RPC/index.php'; if ($rpc->call('checkSpam', $blogstr, $type, $name, $title, $url, $content, $_SERVER['REMOTE_ADDR']) == false) { // call fail return true; } if (!is_null($rpc->fault)) { // EAS has some problem return true; } if ($rpc->result['result'] == true) { return false; // it's spam } return true; }
眼尖的人應該看出來了吧, 在 EAS_Call 中, 是判斷回呼 (RPC回 eolin antispam service)時的結果, 但是在 EAS_AddingTrackback 中, 卻不判斷原來傳入的 $target , 就直接回傳了 EAS_Call 的傳回結果, 這樣將會導致自行撰寫的 antispam plugin 若是在 eolin antispam 之前時, 就會被其回傳值給覆蓋掉, 解決方式若是要自行撰寫 plugin , 就要改寫 EAS_AddingTrackback 如下:
function EAS_AddingTrackback($target, $mother) { return EAS_Call(2, $mother['site'], $mother['title'], $mother['url'], $mother['excerpt']) && $target; }
也就是拿了 EAS_Call 的回傳結果, 仍需要和之前的 $target 比對後再傳回. 這裡先簡單解釋一下 $target true 及 false 的代表意義. 在 /blog/trackback/item.php 中, 預設為 true, 如下:
if(!fireEvent('AddingTrackback',true,array('entry'=>$entry,'url'=>$url,'site'=>$site,'title'=>$title,'excerpt'=>$excerpt)))
也就是說, 預設為非 spam , 要寫入 db, 若該 $target 為 false 時, 代表不寫入 db, (當然不一定是 spam, 這要看插件的意義來決定, 不過這篇文章指的就是 spam 啦). 所以在 EAS_AddingTrackback 的最後要回傳的值必須是累加的結果, 也就是若有一個 false 就是 false 囉, 這樣就用 && (and) 來做比對, 如此一來, 便能將這個不能自己撰寫 antispam plugin 的問題解決掉了. 看起來似乎沒什麼問題, 但實務上, 到底是有一個 true 即為 true或是有一個 false 即為 false 的定義, 必須看使用上的意義來決定, 若非是在做 antispam, 這樣的改寫似乎也不太對, 不過無論如此總比原來的 eolin 都不管來得強, 否則原來的設計框架就失去意義囉.
不過真的很晚啦, 改天再來寫這個 plugin, 今天先在這裡加一行擋著先:
function EAS_AddingTrackback($target, $mother) { if(strpos($mother['url'], 'freeporn.info')) return false; return EAS_Call(2, $mother['site'], $mother['title'], $mother['url'], $mother['excerpt']); }
由於是 url 過濾方式, 所以可以很容易地用 freeporn.info 來做為過濾關鍵字, 一下就可以濾掉不少. 先觀察看看囉, 另外也希望大家能給我一些撰寫 antispam 上的建議, 我個人是覺得 url 就能防掉不少..