Tommy 碎碎念
https://blog.teatime.com.tw/
Tommy Wu's blog
tommy
2024-03-19T17:34:37Z
-
限制 phpBB3 的驗證碼只輸入前四碼
https://blog.teatime.com.tw/1/post/411
<p>基本上, 這個修改與<a href="http://blog.teatime.com.tw/1/post/378" target="_blank">之前改給 SMF 用的</a>是同一個, 只是針對 <a href="http://www.phpbb.com/" target="_blank">phpBB3</a> 來改.</p><pre class="diff"><div class="insertcode"><pre class="diff" style="font-family: monospace">diff -Nur phpBB3.orig//includes/captcha/plugins/captcha_abstract.php phpBB3/includes/captcha/plugins/captcha_abstract.php<br /><span class="re3">--- phpBB3.orig//includes/captcha/plugins/captcha_abstract.php <span class="nu0">2012</span>-01-01 <span class="nu0">23</span>:00:<span class="nu0">17.000000000</span> +0800</span><br /><span class="re4">+++ phpBB3/includes/captcha/plugins/captcha_abstract.php <span class="nu0">2012</span>-01-<span class="nu0">18</span> <span class="nu0">12</span>:<span class="nu0">17</span>:<span class="nu0">38.838436361</span> +0800</span><br /><span class="re6">@@ -<span class="nu0">326</span>,<span class="nu0">7</span> +<span class="nu0">326</span>,<span class="nu0">10</span> @@</span><br /> <br /> function check_code<span class="br0">(</span><span class="br0">)</span><br /> <span class="br0">{</span><br /><span class="re7">- return <span class="br0">(</span>strcasecmp<span class="br0">(</span>$this->code, $this->confirm_code<span class="br0">)</span> === 0<span class="br0">)</span>;</span><br /><span class="re8">+ //return <span class="br0">(</span>strcasecmp<span class="br0">(</span>$this->code, $this->confirm_code<span class="br0">)</span> === 0<span class="br0">)</span>;</span><br /><span class="re8">+ // we only need first <span class="nu0">4</span> letters, if we got more, reject it</span><br /><span class="re8">+ if <span class="br0">(</span>strlen<span class="br0">(</span>$this->confirm_code<span class="br0">)</span> != 4<span class="br0">)</span> return false;</span><br /><span class="re8">+ return <span class="br0">(</span>strcasecmp<span class="br0">(</span>substr<span class="br0">(</span>$this->code, 0, 4<span class="br0">)</span>, $this->confirm_code<span class="br0">)</span> === 0<span class="br0">)</span>;</span><br /> <span class="br0">}</span><br /> <br /> function delete_code<span class="br0">(</span><span class="br0">)</span><br />diff -Nur phpBB3.orig//includes/constants.php phpBB3/includes/constants.php<br /><span class="re3">--- phpBB3.orig//includes/constants.php <span class="nu0">2012</span>-01-01 <span class="nu0">23</span>:00:<span class="nu0">17.000000000</span> +0800</span><br /><span class="re4">+++ phpBB3/includes/constants.php <span class="nu0">2012</span>-01-<span class="nu0">18</span> <span class="nu0">12</span>:<span class="nu0">20</span>:<span class="nu0">43.563129711</span> +0800</span><br /><span class="re6">@@ -<span class="nu0">205</span>,<span class="nu0">7</span> +<span class="nu0">205</span>,<span class="nu0">7</span> @@</span><br /> @define<span class="br0">(</span>'CHMOD_EXECUTE', 1<span class="br0">)</span>;<br /> <br /> // Captcha code length<br /><span class="re7">-define<span class="br0">(</span>'CAPTCHA_MIN_CHARS', 4<span class="br0">)</span>;</span><br /><span class="re8">+define<span class="br0">(</span>'CAPTCHA_MIN_CHARS', 6<span class="br0">)</span>;</span><br /> define<span class="br0">(</span>'CAPTCHA_MAX_CHARS', 7<span class="br0">)</span>;<br /> <br /> // Additional constants<br />diff -Nur phpBB3.orig//language/en/common.php phpBB3/language/en/common.php<br /><span class="re3">--- phpBB3.orig//language/en/common.php <span class="nu0">2012</span>-01-<span class="nu0">18</span> <span class="nu0">12</span>:<span class="nu0">10</span>:<span class="nu0">36.848677118</span> +0800</span><br /><span class="re4">+++ phpBB3/language/en/common.php <span class="nu0">2012</span>-01-01 <span class="nu0">23</span>:00:<span class="nu0">17.000000000</span> +0800</span><br /><span class="re6">@@ -<span class="nu0">123</span>,<span class="nu0">8</span> +<span class="nu0">123</span>,<span class="nu0">8</span> @@</span><br /> 'COLOUR_SWATCH' => 'Colour swatch',<br /> 'COMMA_SEPARATOR' => ', ', // Used in pagination of ACP & prosilver, use localised comma if appropriate, eg: Ideographic or Arabic<br /> 'CONFIRM' => 'Confirm',<br /><span class="re7">- 'CONFIRM_CODE' => 'Confirmation code <span class="br0">(</span>first <span class="nu0">4</span> letters<span class="br0">)</span>',</span><br /><span class="re7">- 'CONFIRM_CODE_EXPLAIN' => 'Enter the code exactly as it appears <span class="br0">(</span>first <span class="nu0">4</span> letters<span class="br0">)</span>. All letters are case insensitive.',</span><br /><span class="re8">+ 'CONFIRM_CODE' => 'Confirmation code',</span><br /><span class="re8">+ 'CONFIRM_CODE_EXPLAIN' => 'Enter the code exactly as it appears. All letters are case insensitive.',</span><br /> 'CONFIRM_CODE_WRONG' => 'The confirmation code you entered was incorrect.',<br /> 'CONFIRM_OPERATION' => 'Are you sure you wish to carry out this operation?',<br /> 'CONGRATULATIONS' => 'Congratulations to',<br />diff -Nur phpBB3.orig//language/zh_cmn_hant/common.php phpBB3/language/zh_cmn_hant/common.php<br /><span class="re3">--- phpBB3.orig//language/zh_cmn_hant/common.php <span class="nu0">2012</span>-01-<span class="nu0">18</span> <span class="nu0">12</span>:<span class="nu0">10</span>:<span class="nu0">01.745087469</span> +0800</span><br /><span class="re4">+++ phpBB3/language/zh_cmn_hant/common.php <span class="nu0">2012</span>-01-<span class="nu0">18</span> <span class="nu0">12</span>:08:<span class="nu0">56.437944350</span> +0800</span><br /><span class="re6">@@ -<span class="nu0">123</span>,<span class="nu0">8</span> +<span class="nu0">123</span>,<span class="nu0">8</span> @@</span><br /> 'COLOUR_SWATCH' => '顏色取樣',<br /> 'COMMA_SEPARATOR' => ',',<br /> 'CONFIRM' => '確認',<br /><span class="re7">- 'CONFIRM_CODE' => '確認代碼',</span><br /><span class="re7">- 'CONFIRM_CODE_EXPLAIN' => '請正確輸入它所顯示的代碼,不必區分大小寫。',</span><br /><span class="re8">+ 'CONFIRM_CODE' => '確認代碼<span class="br0">(</span>前四碼<span class="br0">)</span>',</span><br /><span class="re8">+ 'CONFIRM_CODE_EXPLAIN' => '請正確輸入它所顯示的代碼<span class="br0">(</span>前四碼<span class="br0">)</span>,不必區分大小寫。',</span><br /> 'CONFIRM_CODE_WRONG' => '您輸入的確認代碼有誤。',<br /> 'CONFIRM_OPERATION' => '您確認要執行這個操作嗎?',<br /> 'CONGRATULATIONS' => '恭喜您',</pre></div></pre><p>修改後, 建議把產生的圖檔改成 "簡單" 的那一個, 因為反正 bot 都看的懂, 沒必要弄那麼複雜, 反而讓人很難看懂. </p>
Patch
phpBB
PHP
2012-01-18T12:35:18Z
tommy
-
讓 PhpBB2 也用中文數字吧! Part II
https://blog.teatime.com.tw/1/post/206
加上動態使用背景圖案並把顯示的文字做些旋轉.<p>請參考前面提到的這篇文章, 並在 chinese_img 的目錄下頭, 再建立一個子目錄 backgrounds, 然後把你所要使用的背景圖案的 gif 檔案, 放到這個目錄下頭.</p><p>接著把 usercp_captcha_chinese.php 改成下面這個樣子:</p><pre class="php"><div class="insertcode"><span class="kw2"><?php</span><br /> <br /><span class="kw1">if</span> <span class="br0">(</span> !<a href="http://www.php.net/defined"><span class="kw3">defined</span></a><span class="br0">(</span><span class="st0">'IN_PHPBB'</span><span class="br0">)</span> <span class="br0">)</span><br /><span class="br0">{</span><br /> <a href="http://www.php.net/die"><span class="kw3">die</span></a><span class="br0">(</span><span class="st0">'Hacking attempt'</span><span class="br0">)</span>;<br /> <a href="http://www.php.net/exit"><span class="kw3">exit</span></a>;<br /><span class="br0">}</span><br /><span class="co1">// Do we have an id? No, then just exit</span><br /><span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/empty"><span class="kw3">empty</span></a><span class="br0">(</span><span class="re0">$HTTP_GET_VARS</span><span class="br0">[</span><span class="st0">'id'</span><span class="br0">]</span><span class="br0">)</span><span class="br0">)</span><br /><span class="br0">{</span><br /> <a href="http://www.php.net/exit"><span class="kw3">exit</span></a>;<br /><span class="br0">}</span><br /> <br /><span class="re0">$confirm_id</span> = <a href="http://www.php.net/htmlspecialchars"><span class="kw3">htmlspecialchars</span></a><span class="br0">(</span><span class="re0">$HTTP_GET_VARS</span><span class="br0">[</span><span class="st0">'id'</span><span class="br0">]</span><span class="br0">)</span>;<br /> <br /><span class="kw1">if</span> <span class="br0">(</span>!<a href="http://www.php.net/preg_match"><span class="kw3">preg_match</span></a><span class="br0">(</span><span class="st0">'/^[A-Za-z0-9]+$/'</span>, <span class="re0">$confirm_id</span><span class="br0">)</span><span class="br0">)</span><br /><span class="br0">{</span><br /> <span class="re0">$confirm_id</span> = <span class="st0">''</span>;<br /><span class="br0">}</span><br /> <br /><span class="re0">$gd</span> = <span class="kw2">true</span>;<br /><span class="kw1">if</span><span class="br0">(</span>!<a href="http://www.php.net/extension_loaded"><span class="kw3">extension_loaded</span></a><span class="br0">(</span><span class="st0">"gd"</span><span class="br0">)</span><span class="br0">)</span><span class="br0">{</span><br /> <span class="co1">//Gd isn't loaded</span><br /> <span class="co1">//Display error messages always, or not?</span><br /> <span class="re0">$gd</span> = <span class="kw2">false</span>;<br /><span class="br0">}</span><br /><span class="kw1">if</span><span class="br0">(</span>!<a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">"gd_info"</span><span class="br0">)</span> || !<a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">'imagettftext'</span><span class="br0">)</span><span class="br0">)</span><span class="br0">{</span><br /> <span class="co1">//GD function, gd_info don't exists. GD isn't loaded correctly.</span><br /> <span class="co1">//Freetype function imagettftext doesn't exists. This is required by this mod.</span><br /> <span class="re0">$gd</span> = <span class="kw2">false</span>;<br /><span class="br0">}</span><br /><span class="kw1">if</span><span class="br0">(</span><span class="re0">$gd</span> === <span class="kw2">false</span><span class="br0">)</span><span class="br0">{</span><br /> <span class="co1">//GD not loaded, or one of the needed functions aren't there. Require usercp_confirm ;)</span><br /> <span class="kw1">require</span><span class="br0">(</span><span class="re0">$phpbb_root_path</span> . <span class="st0">'includes/usercp_confirm.'</span> . <span class="re0">$phpEx</span><span class="br0">)</span>;<br /> <a href="http://www.php.net/die"><span class="kw3">die</span></a>;<br /><span class="br0">}</span><br /> <br /><span class="co1">// Try and grab code for this id and session</span><br /><span class="re0">$sql</span> = <span class="st0">'SELECT code<br /> FROM '</span> . CONFIRM_TABLE . <span class="st0">"<br /> WHERE session_id = '"</span> . <span class="re0">$userdata</span><span class="br0">[</span><span class="st0">'session_id'</span><span class="br0">]</span> . <span class="st0">"'<br /> AND confirm_id = '$confirm_id'"</span>;<br /><span class="re0">$result</span> = <span class="re0">$db</span>-><span class="me1">sql_query</span><span class="br0">(</span><span class="re0">$sql</span><span class="br0">)</span>;<br /> <br /><span class="co1">// If we have a row then grab data else create a new id</span><br /><span class="kw1">if</span> <span class="br0">(</span><span class="re0">$row</span> = <span class="re0">$db</span>-><span class="me1">sql_fetchrow</span><span class="br0">(</span><span class="re0">$result</span><span class="br0">)</span><span class="br0">)</span><br /><span class="br0">{</span><br /> <span class="re0">$db</span>-><span class="me1">sql_freeresult</span><span class="br0">(</span><span class="re0">$result</span><span class="br0">)</span>;<br /> <span class="re0">$code</span> = <span class="re0">$row</span><span class="br0">[</span><span class="st0">'code'</span><span class="br0">]</span>;<br /><span class="br0">}</span><br /><span class="kw1">else</span><br /><span class="br0">{</span><br /> <a href="http://www.php.net/exit"><span class="kw3">exit</span></a>;<br /><span class="br0">}</span><br /> <br /><span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">'ImageRotate'</span><span class="br0">)</span><span class="br0">)</span><br /> <span class="re0">$size</span> = <span class="nu0">36</span>;<br /><span class="kw1">else</span><br /> <span class="re0">$size</span> = <span class="nu0">24</span>;<br /> <br /><span class="re0">$bg_files</span> = <a href="http://www.php.net/glob"><span class="kw3">glob</span></a><span class="br0">(</span><span class="re0">$phpbb_root_path</span>.<span class="st0">'includes/chinese_img/backgrounds/*.gif'</span><span class="br0">)</span>;<br /><span class="re0">$bg_file</span> = <span class="re0">$bg_files</span><span class="br0">[</span><a href="http://www.php.net/rand"><span class="kw3">rand</span></a><span class="br0">(</span><span class="nu0">0</span>, <a href="http://www.php.net/count"><span class="kw3">count</span></a><span class="br0">(</span><span class="re0">$bg_files</span><span class="br0">)</span><span class="nu0">-1</span><span class="br0">)</span><span class="br0">]</span>;<br /> <br /><span class="re0">$len</span> = <a href="http://www.php.net/strlen"><span class="kw3">strlen</span></a><span class="br0">(</span><span class="re0">$code</span><span class="br0">)</span>;<br /><span class="re0">$im</span> = ImageCreate<span class="br0">(</span><span class="nu0">24</span> * <span class="re0">$len</span>, <span class="nu0">24</span><span class="br0">)</span>;<br /><span class="re0">$background</span> = ImageColorAllocate<span class="br0">(</span><span class="re0">$im</span>, <span class="nu0">255</span>, <span class="nu0">255</span>, <span class="nu0">255</span><span class="br0">)</span>;<br /> <br /><span class="re0">$im_bg</span> = @ImageCreateFromGIF<span class="br0">(</span><span class="re0">$bg_file</span><span class="br0">)</span>;<br /><span class="kw1">if</span> <span class="br0">(</span><span class="re0">$im_bg</span><span class="br0">)</span> <span class="br0">{</span><br /> <a href="http://www.php.net/list"><span class="kw3">list</span></a><span class="br0">(</span><span class="re0">$width</span>, <span class="re0">$height</span>, <span class="re0">$type</span>, <span class="re0">$attr</span><span class="br0">)</span> = @<a href="http://www.php.net/getimagesize"><span class="kw3">GetImageSize</span></a><span class="br0">(</span><span class="re0">$bg_file</span><span class="br0">)</span>;<br /> <span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">'ImageCopyReSampled'</span><span class="br0">)</span><span class="br0">)</span><br /> ImageCopyReSampled<span class="br0">(</span><span class="re0">$im</span>, <span class="re0">$im_bg</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">24</span> * <span class="re0">$len</span>, <span class="nu0">24</span>, <span class="re0">$width</span>, <span class="re0">$height</span><span class="br0">)</span>;<br /> <span class="kw1">else</span><br /> ImageCopyReSized<span class="br0">(</span><span class="re0">$im</span>, <span class="re0">$im_bg</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">24</span> * <span class="re0">$len</span>, <span class="nu0">24</span>, <span class="re0">$width</span>, <span class="re0">$height</span><span class="br0">)</span>;<br /> ImageDestroy<span class="br0">(</span><span class="re0">$im_bg</span><span class="br0">)</span>;<br /><span class="br0">}</span><br /> <br /><span class="kw1">for</span> <span class="br0">(</span><span class="re0">$i</span> = <span class="nu0">0</span>; <span class="re0">$i</span> < <span class="re0">$len</span>; <span class="re0">$i</span>++<span class="br0">)</span> <span class="br0">{</span><br /> <span class="re0">$im_num</span> = @ImageCreateFromPNG<span class="br0">(</span><span class="re0">$phpbb_root_path</span> . <span class="st0">'includes/chinese_img/'</span>.<span class="re0">$code</span><span class="br0">[</span><span class="re0">$i</span><span class="br0">]</span>.<span class="st0">'.png'</span><span class="br0">)</span>;<br /> <span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">'ImageRotate'</span><span class="br0">)</span><span class="br0">)</span> <span class="br0">{</span><br /> <span class="re0">$im_rotate</span> = ImageRotate<span class="br0">(</span><span class="re0">$im_num</span>, <a href="http://www.php.net/rand"><span class="kw3">rand</span></a><span class="br0">(</span><span class="nu0">-45</span>, <span class="nu0">45</span><span class="br0">)</span>, <span class="nu0">0</span><span class="br0">)</span>;<br /> ImageDestroy<span class="br0">(</span><span class="re0">$im_num</span><span class="br0">)</span>;<br /> <span class="re0">$im_num</span> = ImageCreate<span class="br0">(</span><span class="re0">$size</span>, <span class="re0">$size</span><span class="br0">)</span>;<br /> <span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">'ImageCopyReSampled'</span><span class="br0">)</span><span class="br0">)</span><br /> ImageCopyReSampled<span class="br0">(</span><span class="re0">$im_num</span>, <span class="re0">$im_rotate</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="re0">$size</span>, <span class="re0">$size</span>, imagesx<span class="br0">(</span><span class="re0">$im_rotate</span><span class="br0">)</span>, imagesy<span class="br0">(</span><span class="re0">$im_rotate</span><span class="br0">)</span><span class="br0">)</span>;<br /> <span class="kw1">else</span><br /> ImageCopyReSized<span class="br0">(</span><span class="re0">$im_num</span>, <span class="re0">$im_rotate</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="re0">$size</span>, <span class="re0">$size</span>, imagesx<span class="br0">(</span><span class="re0">$im_rotate</span><span class="br0">)</span>, imagesy<span class="br0">(</span><span class="re0">$im_rotate</span><span class="br0">)</span><span class="br0">)</span>;<br /> ImageDestroy<span class="br0">(</span><span class="re0">$im_rotate</span><span class="br0">)</span>;<br /> <span class="br0">}</span><br /> <span class="kw1">if</span> <span class="br0">(</span><span class="re0">$im_num</span><span class="br0">)</span> <span class="br0">{</span><br /> ImageColorTransparent<span class="br0">(</span><span class="re0">$im_num</span>, ImageColorAt<span class="br0">(</span><span class="re0">$im_num</span>, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">)</span><span class="br0">)</span>;<br /> ImageCopy<span class="br0">(</span><span class="re0">$im</span>, <span class="re0">$im_num</span>, <span class="nu0">24</span> * <span class="re0">$i</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">24</span>, <span class="nu0">24</span><span class="br0">)</span>;<br /> ImageDestroy<span class="br0">(</span><span class="re0">$im_num</span><span class="br0">)</span>;<br /> <span class="br0">}</span><br /><span class="br0">}</span><br /> <br /><a href="http://www.php.net/header"><span class="kw3">header</span></a><span class="br0">(</span><span class="st0">'content-type:image/png'</span><span class="br0">)</span>;<br /><a href="http://www.php.net/header"><span class="kw3">header</span></a><span class="br0">(</span><span class="st0">'Cache-control: no-cache, no-store'</span><span class="br0">)</span>;<br /> <br />ImagePNG<span class="br0">(</span><span class="re0">$im</span><span class="br0">)</span>;<br />ImageDestroy<span class="br0">(</span><span class="re0">$im</span><span class="br0">)</span>;<br /><a href="http://www.php.net/die"><span class="kw3">die</span></a>;<br /> <br /><span class="kw2">?></span></div></pre><p>這樣子就應該可以了.</p><p>對了, 如果你的系統產生的字並沒有旋轉, 表示你的系統的 PHP 不支援 ImageRotate() 這個函式 (例如 Debian 本身內建). 如果你一定要使用這個功能, 就必須自行編譯一份有含這個功能的 php gd 模組 (在 Debian 中, 把 rules 的</p><pre>--with-gd=shared,/usr </pre><p>改成</p><pre>--with-gd=shared </pre><p>重新編譯後, 把 gd.so 覆蓋原本的就可以了. </p>
phpBB
PHP
2007-03-15T12:09:41Z
tommy
-
讓 PhpBB2 也用中文數字吧!
https://blog.teatime.com.tw/1/post/195
<a href="http://blog.teatime.com.tw/1/post/194" target="_blank">同樣的方式,</a> 我們也可以讓 phpBB 也使用中文數字的圖片.<p>首先, 利用前述的方式, 產生你的數字圖檔, 並選取一個背景圖檔. 在 include 的目錄下, 建立一個 chinese_img 的目錄, 把這些檔案放進去.</p><p>然後產生一個新的檔案 usercp_captcha_chinese.php, 放到 include, 內容如下:</p><pre class="php"><div class="insertcode"><span class="kw2"><?php</span><br /> <br /><span class="kw1">if</span> <span class="br0">(</span> !<a href="http://www.php.net/defined"><span class="kw3">defined</span></a><span class="br0">(</span><span class="st0">'IN_PHPBB'</span><span class="br0">)</span> <span class="br0">)</span><br /><span class="br0">{</span><br /> <a href="http://www.php.net/die"><span class="kw3">die</span></a><span class="br0">(</span><span class="st0">'Hacking attempt'</span><span class="br0">)</span>;<br /> <a href="http://www.php.net/exit"><span class="kw3">exit</span></a>;<br /><span class="br0">}</span><br /><span class="co1">// Do we have an id? No, then just exit</span><br /><span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/empty"><span class="kw3">empty</span></a><span class="br0">(</span><span class="re0">$HTTP_GET_VARS</span><span class="br0">[</span><span class="st0">'id'</span><span class="br0">]</span><span class="br0">)</span><span class="br0">)</span><br /><span class="br0">{</span><br /> <a href="http://www.php.net/exit"><span class="kw3">exit</span></a>;<br /><span class="br0">}</span><br /> <br /><span class="re0">$confirm_id</span> = <a href="http://www.php.net/htmlspecialchars"><span class="kw3">htmlspecialchars</span></a><span class="br0">(</span><span class="re0">$HTTP_GET_VARS</span><span class="br0">[</span><span class="st0">'id'</span><span class="br0">]</span><span class="br0">)</span>;<br /> <br /><span class="kw1">if</span> <span class="br0">(</span>!<a href="http://www.php.net/preg_match"><span class="kw3">preg_match</span></a><span class="br0">(</span><span class="st0">'/^[A-Za-z0-9]+$/'</span>, <span class="re0">$confirm_id</span><span class="br0">)</span><span class="br0">)</span><br /><span class="br0">{</span><br /> <span class="re0">$confirm_id</span> = <span class="st0">''</span>;<br /><span class="br0">}</span><br /> <br /><span class="re0">$gd</span> = <span class="kw2">true</span>;<br /><span class="kw1">if</span><span class="br0">(</span>!<a href="http://www.php.net/extension_loaded"><span class="kw3">extension_loaded</span></a><span class="br0">(</span><span class="st0">"gd"</span><span class="br0">)</span><span class="br0">)</span><span class="br0">{</span><br /> <span class="co1">//Gd isn't loaded</span><br /> <span class="co1">//Display error messages always, or not?</span><br /> <span class="re0">$gd</span> = <span class="kw2">false</span>;<br /><span class="br0">}</span><br /><span class="kw1">if</span><span class="br0">(</span>!<a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">"gd_info"</span><span class="br0">)</span> || !<a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">'imagettftext'</span><span class="br0">)</span><span class="br0">)</span><span class="br0">{</span><br /> <span class="co1">//GD function, gd_info don't exists. GD isn't loaded correctly.</span><br /> <span class="co1">//Freetype function imagettftext doesn't exists. This is required by this mod.</span><br /> <span class="re0">$gd</span> = <span class="kw2">false</span>;<br /><span class="br0">}</span><br /><span class="kw1">if</span><span class="br0">(</span><span class="re0">$gd</span> === <span class="kw2">false</span><span class="br0">)</span><span class="br0">{</span><br /> <span class="co1">//GD not loaded, or one of the needed functions aren't there. Require usercp_confirm ;)</span><br /> <span class="kw1">require</span><span class="br0">(</span><span class="re0">$phpbb_root_path</span> . <span class="st0">'includes/usercp_confirm.'</span> . <span class="re0">$phpEx</span><span class="br0">)</span>;<br /> <a href="http://www.php.net/die"><span class="kw3">die</span></a>;<br /><span class="br0">}</span><br /> <br /><span class="co1">// Try and grab code for this id and session</span><br /><span class="re0">$sql</span> = <span class="st0">'SELECT code<br /> FROM '</span> . CONFIRM_TABLE . <span class="st0">"<br /> WHERE session_id = '"</span> . <span class="re0">$userdata</span><span class="br0">[</span><span class="st0">'session_id'</span><span class="br0">]</span> . <span class="st0">"'<br /> AND confirm_id = '$confirm_id'"</span>;<br /><span class="re0">$result</span> = <span class="re0">$db</span>-><span class="me1">sql_query</span><span class="br0">(</span><span class="re0">$sql</span><span class="br0">)</span>;<br /> <br /><span class="co1">// If we have a row then grab data else create a new id</span><br /><span class="kw1">if</span> <span class="br0">(</span><span class="re0">$row</span> = <span class="re0">$db</span>-><span class="me1">sql_fetchrow</span><span class="br0">(</span><span class="re0">$result</span><span class="br0">)</span><span class="br0">)</span><br /><span class="br0">{</span><br /> <span class="re0">$db</span>-><span class="me1">sql_freeresult</span><span class="br0">(</span><span class="re0">$result</span><span class="br0">)</span>;<br /> <span class="re0">$code</span> = <span class="re0">$row</span><span class="br0">[</span><span class="st0">'code'</span><span class="br0">]</span>;<br /><span class="br0">}</span><br /><span class="kw1">else</span><br /><span class="br0">{</span><br /> <a href="http://www.php.net/exit"><span class="kw3">exit</span></a>;<br /><span class="br0">}</span><br /> <br /><span class="re0">$len</span> = <a href="http://www.php.net/strlen"><span class="kw3">strlen</span></a><span class="br0">(</span><span class="re0">$code</span><span class="br0">)</span>;<br /><span class="re0">$im</span> = ImageCreate<span class="br0">(</span><span class="nu0">24</span> * <span class="re0">$len</span>, <span class="nu0">24</span><span class="br0">)</span>;<br /><span class="re0">$background</span> = ImageColorAllocate<span class="br0">(</span><span class="re0">$im</span>, <span class="nu0">255</span>, <span class="nu0">255</span>, <span class="nu0">255</span><span class="br0">)</span>;<br /> <br /><span class="re0">$bg_file</span> = <span class="re0">$phpbb_root_path</span> . <span class="st0">'includes/chinese_img/background.gif'</span>;<br /><span class="re0">$im_bg</span> = @ImageCreateFromGIF<span class="br0">(</span><span class="re0">$bg_file</span><span class="br0">)</span>;<br /><span class="kw1">if</span> <span class="br0">(</span><span class="re0">$im_bg</span><span class="br0">)</span> <span class="br0">{</span><br /> <a href="http://www.php.net/list"><span class="kw3">list</span></a><span class="br0">(</span><span class="re0">$width</span>, <span class="re0">$height</span>, <span class="re0">$type</span>, <span class="re0">$attr</span><span class="br0">)</span> = @<a href="http://www.php.net/getimagesize"><span class="kw3">GetImageSize</span></a><span class="br0">(</span><span class="re0">$bg_file</span><span class="br0">)</span>;<br /> <span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/function_exists"><span class="kw3">function_exists</span></a><span class="br0">(</span><span class="st0">'ImageCopyReSampled'</span><span class="br0">)</span><span class="br0">)</span><br /> ImageCopyReSampled<span class="br0">(</span><span class="re0">$im</span>, <span class="re0">$im_bg</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">24</span> * <span class="re0">$len</span>, <span class="nu0">24</span>, <span class="re0">$width</span>, <span class="re0">$height</span><span class="br0">)</span>;<br /> <span class="kw1">else</span><br /> ImageCopyReSized<span class="br0">(</span><span class="re0">$im</span>, <span class="re0">$im_bg</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">24</span> * <span class="re0">$len</span>, <span class="nu0">24</span>, <span class="re0">$width</span>, <span class="re0">$height</span><span class="br0">)</span>;<br /> ImageDestroy<span class="br0">(</span><span class="re0">$im_bg</span><span class="br0">)</span>;<br /><span class="br0">}</span><br /> <br /><span class="kw1">for</span> <span class="br0">(</span><span class="re0">$i</span> = <span class="nu0">0</span>; <span class="re0">$i</span> < <span class="re0">$len</span>; <span class="re0">$i</span>++<span class="br0">)</span> <span class="br0">{</span><br /> <span class="re0">$im_num</span> = @ImageCreateFromPNG<span class="br0">(</span><span class="re0">$phpbb_root_path</span> . <span class="st0">'includes/chinese_img/'</span>.<span class="re0">$code</span><span class="br0">[</span><span class="re0">$i</span><span class="br0">]</span>.<span class="st0">'.png'</span><span class="br0">)</span>;<br /> <span class="kw1">if</span> <span class="br0">(</span><span class="re0">$im_num</span><span class="br0">)</span> <span class="br0">{</span><br /> ImageColorTransparent<span class="br0">(</span><span class="re0">$im_num</span>, ImageColorAt<span class="br0">(</span><span class="re0">$im_num</span>, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">)</span><span class="br0">)</span>;<br /> ImageCopy<span class="br0">(</span><span class="re0">$im</span>, <span class="re0">$im_num</span>, <span class="nu0">24</span> * <span class="re0">$i</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">0</span>, <span class="nu0">24</span>, <span class="nu0">24</span><span class="br0">)</span>;<br /> ImageDestroy<span class="br0">(</span><span class="re0">$im_num</span><span class="br0">)</span>;<br /> <span class="br0">}</span><br /><span class="br0">}</span><br /> <br /><a href="http://www.php.net/header"><span class="kw3">header</span></a><span class="br0">(</span><span class="st0">"content-type:image/png"</span><span class="br0">)</span>;<br /><a href="http://www.php.net/header"><span class="kw3">header</span></a><span class="br0">(</span><span class="st0">'Cache-control: no-cache, no-store'</span><span class="br0">)</span>;<br /> <br />ImagePNG<span class="br0">(</span><span class="re0">$im</span><span class="br0">)</span>;<br />ImageDestroy<span class="br0">(</span><span class="re0">$im</span><span class="br0">)</span>;<br /><a href="http://www.php.net/die"><span class="kw3">die</span></a>;<br /> <br /><span class="kw2">?></span></div></pre><p>然後修改 include/usercp_register.php 的內容, 找到: </p><pre class="php"><div class="insertcode"><span class="re0">$code</span> = dss_rand<span class="br0">(</span><span class="br0">)</span>;</div></pre><p> 把下頭的 $code = substr(... 這行改成: </p><pre class="php"><div class="insertcode"><span class="re0">$code</span> = <a href="http://www.php.net/substr"><span class="kw3">substr</span></a><span class="br0">(</span><a href="http://www.php.net/base_convert"><span class="kw3">base_convert</span></a><span class="br0">(</span><span class="re0">$code</span>, <span class="nu0">16</span>, <span class="nu0">10</span><span class="br0">)</span>, <span class="nu0">2</span>, <span class="nu0">6</span><span class="br0">)</span>;</div></pre><p>然後修改 profile.php 的內容, 找到:</p><pre class="php"><div class="insertcode"><span class="kw1">include</span><span class="br0">(</span><span class="re0">$phpbb_root_path</span> . <span class="st0">'includes/usercp_confirm.'</span>.<span class="re0">$phpEx</span><span class="br0">)</span>;</div></pre><p>把這行改成:</p><pre class="php"><div class="insertcode"><span class="kw1">include</span><span class="br0">(</span><span class="re0">$phpbb_root_path</span> . <span class="st0">'includes/usercp_captcha_chinese.'</span>.<span class="re0">$phpEx</span><span class="br0">)</span>;</div></pre><p>經過這樣子的修改, 應該就可以在註冊時看到那個圖片變成中文的數字了. </p><p>那個新增的檔案與圖型檔, 也可以由這兒抓取: <a href="http://www.teatime.com.tw/~tommy/files/phpbb2_captcha_chinese.tgz" target="_blank">http://www.teatime.com.tw/~tommy/files/phpbb2_captcha_chinese.tgz</a> </p><p>把這個檔案在 include 目錄中解開就可以了.</p>
phpBB
PHP
2007-03-01T16:02:17Z
tommy
-
PHPBB 之訪客發文請點我, part II
https://blog.teatime.com.tw/1/post/145
話說自從在 phpBB2 的<a href="http://blog.teatime.com.tw/post/1/49" target="_blank">註冊</a>與<a href="http://blog.teatime.com.tw/post/1/127" target="_blank">訪客發文</a>的 form 上頭, 加上一個自訂的 checkbox 之後, 就沒碰過有 bot 註冊成功的例子. 而訪客發文的部份, 一直也都沒碰到有成功的案例. 不過... 上星期發現有個 bot 居然成功的以訪客的身份發文. 難道... 這個是新的 bot, 專門對我修改的這個方法來處理嗎? 由於不太確定該 bot 是如何通過檢查的, 所以特地加上一些 log 來觀察所有訪客發文的記錄. 經過了幾天, 都還是只有看到舊型的 bot 來嘗試發文, 不過都無法通過. 不過... 今天終於看到了一個作法不同的 bot, 的確可以通過我之前的檢查.<p>之前的 bot, 在運作的時候, 會先抓取網站上頭的 form, 然後就存起來使用, 之後到該網站發文時, 並不會再次讀取網站的資料, 而是直接使用之前抓下來的 form 來發表文章 (因為之前修改的方式, 會加上一個 hidden 的欄位是 GMT 時間, 而依據這幾天的 log 來看, 絕大多數的 bot 居然這個欄位都是同一個時間). 這個 bot 只會填入一些標準的欄位, 因此, 我們新增加的 checkbox 的欄位, 在 post 之後, 那個變數並不會存在, 可以正確的阻擋這類的 bot.</p><p>不過, 後來看到另一種型式的 bot (不確定是否針對我們加上 checkbox 的作法, 還是原本這個 bot 就考慮的比較多一些), 看起來是比較即時的抓取 form 下來, 再送出 post 來發文 (因為上述的 GMT 時間欄位都很接近系統時間, 看起來很接近一般人用 browser 操作的模式), 而且, 送出的 post 內容中, 並非只有含有 phpBB 本身標準的欄位, 看起來是會把所有那個 form 上頭的欄位都傳回來, 自然之前的檢查方式, 只檢查是否有該變數存在時, 會通過檢查.</p><p>針對這個 bot 的作法, 修改一下我們之前的作法, 再加上另一個 checkbox, 也就是使用兩個額外的 checkbox, 要求使用者必須選取其中一個, 另一個則不要去選取.</p><p>整理一下, 修改的動作如下:</p><ul><li> includes/constants.php <br /></li></ul><p>加上下面這行, 請修改字串內容, 不要直接使用這個範例, 這個值後頭會用來計算, 基本上選擇一個比較不容易被猜到的字串會比較好一些.</p><pre>define('ANTISPAM', 'ANTISPAM Check!'); <br /></pre><ul><li>language/lang_chinese_traditional_taiwan/lang_main.php</li></ul><p>加上這幾行:</p><pre>$lang['ANTISPAM_Check'] = '請選擇此欄位';<br />$lang['ANTISPAM_NotCheck'] = '請勿選擇此欄位';<br />$lang['ANTISPAM_Check_Error'] = '[請選擇此欄位] 這個欄位未選擇 或 [請勿選擇此欄位] 這個欄位被選擇';<br />$lang['ANTISPAM_Timeout'] = '表單 逾時, 請再試一次!';<br />$lang['ANTISPAM_Error'] = 'ANTISPAM MOD 檢查失敗!';</pre><ul><li> language/lang_english/lang_main.php</li></ul><p>加上這幾行:</p><pre>$lang['ANTISPAM_Check'] = 'Choose this!';<br />$lang['ANTISPAM_NotCheck'] = 'Don\'t choose this!';<br />$lang['ANTISPAM_Check_Error'] = '[Choose this!] not choose or [Don\'t choose this!] be chosen';<br />$lang['ANTISPAM_Timeout'] = 'Form timeout, Please try again!';<br />$lang['ANTISPAM_Error'] = 'ANTISPAM MOD check error!';</pre><p>有關註冊時的修改:</p><ul><li>includes/usercp_register.php</li></ul><p>找到下頭的內容 (<span style="color: #ff0000">2007/03/25 修正</span>, 只在註冊時顯示欄位):</p><pre>$template->assign_block_vars('switch_confirm', array());<br /></pre><p>然後在這行之前加上:</p><pre>$template->assign_block_vars('switch_antispam_choose', array());<br /></pre><p>找到下頭的內容:</p><pre> $passwd_sql = ''; </pre><p>然後在這行之前加上:</p><pre> if ($mode == 'register') {<br /> $x_hcode_date = $HTTP_POST_VARS['date'];<br /> $hcode_item = $HTTP_POST_VARS['antispam_item'];<br /> if ($hcode_item == md5('1'.ANTISPAM.'1'.$x_hcode_date.'1')) {<br /> $hcode_check = '1';<br /> $hcode_notcheck = '2';<br /> }<br /> else {<br /> $hcode_check = '2';<br /> $hcode_notcheck = '1';<br /> }<br /> $now_date = gmdate('U');<br /> if ($now_date >= $x_hcode_date)<br /> $date_len = $now_date - $x_hcode_date;<br /> else<br /> $date_len = $x_hcode_date - $now_date;<br /> $hcode = md5(ANTISPAM.$x_hcode_date);<br /> $hcode_name = md5($x_hcode_date.ANTISPAM);<br /> if (!isset($HTTP_POST_VARS[$hcode_name]) ||<br /> empty($HTTP_POST_VARS[$hcode_name])) {<br /> $error = TRUE;<br /> $error_msg .= ( ( isset($error_msg) ) ? '<br />' : '' ) . $lang['ANTISPAM_Error'];<br /> }<br /> else if ($date_len > 86400) { // > 1 day?<br /> $error = TRUE;<br /> $error_msg .= ( ( isset($error_msg) ) ? '<br />' : '' ) . $lang['ANTISPAM_Timeout'];<br /> }<br /> else if ( !isset($HTTP_POST_VARS['antispam_'.$hcode_check.'_'.$hcode]) ||<br /> $HTTP_POST_VARS['antispam_'.$hcode_check.'_'.$hcode] !== 'on' ||<br /> isset($HTTP_POST_VARS['antispam_'.$hcode_notcheck.'_'.$hcode])) {<br /> $error = TRUE;<br /> $error_msg .= ( ( isset($error_msg) ) ? '<br />' : '' ) . $lang['ANTISPAM_Check_Error'];<br /> }<br /> }</pre><p>找到下頭的內容:</p><pre> //<br /> // Let's do an overall check for settings/versions which would prevent<br /> // us from doing file uploads....<br /> //<br /> $ini_val = ( phpversion() >= '4.0.0' ) ? 'ini_get' : 'get_cfg_var';<br /> $form_enctype = ( @$ini_val('file_uploads') == '0' || strtolower(@$ini_val('file_uploads') == 'off') || phpversion() == '4.0.4pl1' || !$board_config['allow_avatar_upload'] || ( phpversion() < '4.0.3' && @$ini_val('open_basedir') != '' ) ) ? '' : 'enctype="multipart/form-data"';<br /> <br /> $template->assign_vars(array(<br /> 'USERNAME' => isset($username) ? $username : '',<br /> 'CUR_PASSWORD' => isset($cur_password) ? $cur_password : '',<br /> 'NEW_PASSWORD' => isset($new_password) ? $new_password : '',<br /></pre><p>在 $template->assign_vars(array( 之前, 加上下面幾行:</p><pre> $hcode_date = gmdate('U');<br /> $hcode = md5(ANTISPAM.$hcode_date);<br /> $hcode_name = md5($hcode_date.ANTISPAM);<br /> $hcode_random = rand(1,100) % 2;<br /> if ($hcode_random == 1) {<br /> // 1 on, 2 off<br /> $hcode_item = md5('1'.ANTISPAM.'1'.$hcode_date.'1');<br /> $hcode_lang1 = $lang['ANTISPAM_Check'];<br /> $hcode_lang2 = $lang['ANTISPAM_NotCheck'];<br /> }<br /> else {<br /> // 1 off, 2 on<br /> $hcode_item = md5('2'.ANTISPAM.'2'.$hcode_date.'2');<br /> $hcode_lang1 = $lang['ANTISPAM_NotCheck'];<br /> $hcode_lang2 = $lang['ANTISPAM_Check'];<br /> }</pre><p>然後在 'NEW_PASSWORD' => isset($new_password) ? $new_password : '', 這行後面加上這幾行:</p><pre> 'HCODE' => $hcode,<br /> 'HCODENAME' => $hcode_name,<br /> 'HCODE_ITEM' => $hcode_item,<br /> 'L_ANTISPAM_CHECKBOX1' => $hcode_lang1,<br /> 'L_ANTISPAM_CHECKBOX2' => $hcode_lang2,<br /> 'DATE' => $hcode_date, </pre><ul><li>templates/subSilver/profile_add_body.tpl</li></ul><p>找到下頭的內容:</p><pre> <!-- Visual Confirmation --> </pre><p>然後在這行上面加上這幾行 (<span style="color: #ff0000">2007/03/25 修正</span>, 只在註冊時顯示):</p><pre> <!-- BEGIN switch_antispam_choose --> <br /> <input type="hidden" name="{HCODENAME}" value="1" /><br /> <input type="hidden" name="date" value="{DATE}" /><br /> <input type="hidden" name="antispam_item" value="{HCODE_ITEM}" /><br /> <tr><br /> <td class="row1"><span class="gen">{L_ANTISPAM_CHECKBOX1}: * </span></td><br /> <td class="row2"><input type="checkbox" name="antispam_1_{HCODE}" /></td><br /> </tr><br /> <tr><br /> <td class="row1"><span class="gen">{L_ANTISPAM_CHECKBOX2}: * </span></td><br /> <td class="row2"><input type="checkbox" name="antispam_2_{HCODE}" /></td><br /> </tr> <br /> <!-- END switch_antispam_choose --> </pre><p>接著是有關於訪客發文的修改:</p><ul><li>posting.php</li></ul><p>找到下頭的內容:</p><pre> $bbcode_uid = '';<br /> <br /> prepare_post($mode, $post_data, $bbcode_on, $html_on, $smilies_on, $error_msg, $username, $bbcode_uid, $subject, $message, $poll_title, $poll_options, $poll_length); </pre><p>在 $bbcode_uid = ''; 之後加上這幾行:</p><pre> $hcode_date = $HTTP_POST_VARS['date'];<br /> $hcode_item = $HTTP_POST_VARS['antispam_item'];<br /> if ($userdata['user_id'] == ANONYMOUS) {<br /> $hcode = md5(ANTISPAM.$hcode_date);<br /> $hcode_name = md5($hcode_date.ANTISPAM);<br /> }<br /> else {<br /> $hcode = md5(ANTISPAM.$hcode_date.$userdata['username'].$userdata['user_regdate']);<br /> $hcode_name = md5($userdata['username'].$hcode_date.ANTISPAM);<br /> }<br /><br /> if ($hcode_item == md5('1'.ANTISPAM.'1'.$hcode_date.'1')) {<br /> $hcode_check = '1';<br /> $hcode_notcheck = '2';<br /> }<br /> else {<br /> $hcode_check = '2';<br /> $hcode_notcheck = '1';<br /> }<br /><br /> $now_date = gmdate('U');<br /> if ($now_date >= $hcode_date)<br /> $date_len = $now_date - $hcode_date;<br /> else<br /> $date_len = $hcode_date - $now_date;<br /><br /> if (!isset($HTTP_POST_VARS[$hcode_name]) ||<br /> empty($HTTP_POST_VARS[$hcode_name])) {<br /> $error_msg = $lang['ANTISPAM_Error'];<br /> }<br /> else if ($userdata['user_id'] == ANONYMOUS &&<br /> $date_len > 86400) { // > 1 day?<br /> $error_msg = $lang['ANTISPAM_Timeout'];<br /> }<br /> else if ($userdata['user_id'] == ANONYMOUS &&<br /> (!isset($HTTP_POST_VARS['antispam_'.$hcode_check.'_'.$hcode]) ||<br /> $HTTP_POST_VARS['antispam_'.$hcode_check.'_'.$hcode] !== 'on' ||<br /> isset($HTTP_POST_VARS['antispam_'.$hcode_notcheck.'_'.$hcode])) ) {<br /> $error_msg = $lang['ANTISPAM_Check_Error'];<br /> }<br /> else </pre><p>找到下頭的內容:</p><pre>//<br />// This enables the forum/topic title to be output for posting<br />// but not for privmsg (where it makes no sense)<br />//<br />$template->assign_block_vars('switch_not_privmsg', array());<br /><br />//<br />// Output the data to the template<br />//<br />$template->assign_vars(array(<br /> 'USERNAME' => $username,<br /> 'SUBJECT' => $subject,<br /> 'MESSAGE' => $message, </pre><p>在 $template->assign_vars(array( 這行之前, 加上這幾行:</p><pre>$hcode_date = gmdate('U');<br />if ($userdata['user_id'] == ANONYMOUS) {<br /> $hcode = md5(ANTISPAM.$hcode_date);<br /> $hcode_name = md5($hcode_date.ANTISPAM);<br />}<br />else {<br /> $hcode = md5(ANTISPAM.$hcode_date.$userdata['username'].$userdata['user_regdate']);<br /> $hcode_name = md5($userdata['username'].$hcode_date.ANTISPAM);<br />}<br />$hcode_random = rand(1,100) % 2;<br />if ($hcode_random == 1) {<br /> // 1 on, 2 off<br /> $hcode_item = md5('1'.ANTISPAM.'1'.$hcode_date.'1');<br /> $hcode_lang1 = $lang['ANTISPAM_Check'];<br /> $hcode_lang2 = $lang['ANTISPAM_NotCheck'];<br />}<br />else {<br /> // 1 off, 2 on<br /> $hcode_item = md5('2'.ANTISPAM.'2'.$hcode_date.'2');<br /> $hcode_lang1 = $lang['ANTISPAM_NotCheck'];<br /> $hcode_lang2 = $lang['ANTISPAM_Check'];<br />} </pre><p>然後在 'MESSAGE' => $message, 這行後面加上這幾行:</p><pre> 'HCODE' => $hcode,<br /> 'HCODENAME' => $hcode_name,<br /> 'HCODE_ITEM' => $hcode_item,<br /> 'L_ANTISPAM_CHECKBOX1' => $hcode_lang1,<br /> 'L_ANTISPAM_CHECKBOX2' => $hcode_lang2,<br /> 'DATE' => $hcode_date, </pre><ul><li>templates/subSilver/posting_body.tpl</li></ul><p>找到下頭的內容:</p><pre>{POST_PREVIEW_BOX}<br />{ERROR_BOX} </pre><p>在這之後加上這幾行:</p><pre><input type="hidden" name="date" value="{DATE}" /><br /><input type="hidden" name="{HCODENAME}" value="1" /><br /><input type="hidden" name="antispam_item" value="{HCODE_ITEM}" /> </pre><p>找到下頭的內容:</p><pre> <!-- BEGIN switch_username_select --><br /> <tr><br /> <td class="row1"><span class="gen"><b>{L_USERNAME}</b></span></td><br /> <td class="row2"><span class="genmed"><input type="text" class="post" tabindex="1" name="username" size="25" maxlength="25" value="{USERNAME}" /></span></td> </pre><p>在上頭最後一行的 </span></td> 之前, 加上這幾行:</p><pre><input type="checkbox" name="antispam_1_{HCODE}" />{L_ANTISPAM_CHECKBOX1}<br /><input type="checkbox" name="antispam_2_{HCODE}" />{L_ANTISPAM_CHECKBOX2} </pre><p>這樣子, 在發文的時候也會檢查.</p><p>另外, 如果有安裝快速回應之類的 MOD, 也要做下頭的修改:</p><ul><li>viewtopic.php</li></ul><p>找到下頭的內容:</p><pre>//<br />// If we've got a hightlight set pass it on to pagination,<br />// I get annoyed when I lose my highlight after the first page.<br />//<br />$pagination = ( $highlight != '' ) ? generate_pagination("viewtopic.$phpEx?" . POST_TOPIC_URL . "=$topic_id&postdays=$post_days&postorder=$post_order&highlight=$highlight", $total_replies, $board_config['posts_per_page'], $start) : generate_pagination("viewtopic.$phpEx?" . POST_TOPIC_URL . "=$topic_id&postdays=$post_days&postorder=$post_order", $total_replies, $board_config['posts_per_page'], $start);<br /><br />//<br />// Send vars to template<br />//<br />$template->assign_vars(array(<br /> 'FORUM_ID' => $forum_id,<br /> 'FORUM_NAME' => $forum_name,<br /> 'TOPIC_ID' => $topic_id,<br /> 'TOPIC_TITLE' => $topic_title, </pre><p>在 $template->assign_vars(array( 之前加上這幾行:</p><pre>$hcode_date = gmdate('U');<br />if ($userdata['user_id'] == ANONYMOUS) {<br /> $hcode = md5(ANTISPAM.$hcode_date);<br /> $hcode_name = md5($hcode_date.ANTISPAM);<br />}<br />else {<br /> $hcode = md5(ANTISPAM.$hcode_date.$userdata['username'].$userdata['user_regdate']);<br /> $hcode_name = md5($userdata['username'].$hcode_date.ANTISPAM);<br />}<br />$hcode_random = rand(1,100) % 2;<br />if ($hcode_random == 1) {<br /> // 1 on, 2 off<br /> $hcode_item = md5('1'.ANTISPAM.'1'.$hcode_date.'1');<br /> $hcode_lang1 = $lang['ANTISPAM_Check'];<br /> $hcode_lang2 = $lang['ANTISPAM_NotCheck'];<br />}<br />else {<br /> // 1 off, 2 on<br /> $hcode_item = md5('2'.ANTISPAM.'2'.$hcode_date.'2');<br /> $hcode_lang1 = $lang['ANTISPAM_NotCheck'];<br /> $hcode_lang2 = $lang['ANTISPAM_Check'];<br />} </pre><p>然後在 'TOPIC_TITLE' => $topic_title, 之後, 加上這幾行:</p><pre> 'HCODE' => $hcode,<br /> 'HCODENAME' => $hcode_name,<br /> 'HCODE_ITEM' => $hcode_item,<br /> 'L_ANTISPAM_CHECKBOX1' => $hcode_lang1,<br /> 'L_ANTISPAM_CHECKBOX2' => $hcode_lang2,<br /> 'DATE' => $hcode_date, </pre><ul><li>templates/subSilver/viewtopic_body.tpl</li></ul><p>找到下頭的內容:</p><pre> <!-- BEGIN switch_username_field --><br /> <span class='gensmall'><b>{L_USERNAME}</b></span><br /><br /> <span class='genmed'><input type='text' class='post' tabindex='1' name='username' size='25' maxlength='25' value='' /></span><br /><br /></pre><p>在上頭最後一行的 </span><br /> 之前, 加上這幾行:</p><pre><input type="hidden" name="antispam_item" value="{HCODE_ITEM}" /><br /><input type="checkbox" name="antispam_1_{HCODE}" />{L_ANTISPAM_CHECKBOX1}<br /><input type="checkbox" name="antispam_2_{HCODE}" />{L_ANTISPAM_CHECKBOX2} <br /></pre> <p>找到下頭的內容:</p><pre> <!-- BEGIN quick_reply --><br /> <textarea name="message" rows="7" cols="35" wrap="virtual" style="width:425px" class="post" onclick="{if(document.quick_reply.message.value=='{L_QUICK_REPLY_TOPIC}') document.quick_reply.message.value=''}">{L_QUICK_REPLY_TOPIC}</textarea><br /> </pre><p>在這之後加上這幾行:</p><pre><input type="hidden" name="quickreply" value="1" /><br /><input type="hidden" name="date" value="{DATE}" /><br /><input type="hidden" name="{HCODENAME}" value="1" /> </pre><p>這樣子修改後, 應該可以避免一些 bot 來註冊或使用訪客的身份來發文了. 至少... 那個 bot 必須要看的懂 form 上頭的文字, 選取應該選取的 checkbox, 並且不可以選擇不應該選的 checkbox, 才會通過這個檢查.</p><hr width="100%" size="2" /><p>2007/03/25:</p><p>修正一下, 只在註冊時顯示那些欄位. (上頭紅色部份)</p>
phpBB
2006-11-01T11:18:41Z
tommy
-
PHPBB 之訪客發文請點我....
https://blog.teatime.com.tw/1/post/127
這篇主要是之前的 "<a href="http://blog.teatime.com.tw/1/post/49" target="_blank">討厭的廣告 bot, 別再來我的 phpBB 了....</a>" 的延伸. 利用新增的一個 checkbox 欄位, 要求使用訪客身份發文時, 必須點選該欄位才允許發文. 對於一些專對 PHPBB 所寫的 bot 來說, 應該會有一定的效用.<p>這兒用到的一些變數, 在前文中有所描述, 請勿直接依照本文的內容修改, 而沒有依照前文的某些部份做修改, 否則... 那段程式碼可能會有問題.</p><p>首先, 修改 lang_main.php , 把之前所加上的那兩段語言定義修改一下, 原本為: </p><pre>$lang['ANTISPAM_Check'] = '在註冊時請選擇此欄位';<br />$lang['ANTISPAM_Check_Error'] = '[在註冊時請選擇此欄位] 這個欄位未選擇';</pre><p>改成:</p><pre>$lang['ANTISPAM_Check'] = '請選擇此欄位';<br />$lang['ANTISPAM_Check_Error'] = '[請選擇此欄位] 這個欄位未選擇';</pre><p>讓原本給註冊專用的訊息也能在這兒使用.</p><p>修改 posting_body.tpl, 找到下頭的字串:</p><pre> <td class="row2"><span class="genmed"><input type="text" class="post" tabindex="1" name="username" size="25" maxlength="25" value="{USERNAME}" /></span></td> </pre><p>改成:</p><pre> <td class="row2"><span class="genmed"><input type="text" class="post" tabindex="1" name="username" size="25" maxlength="25" value="{USERNAME}" /><br /><!-- add by twu2 20060919 begin --><br /><input type="checkbox" name="check_{HCODE}" />{L_ANTISPAM_CHECK}<br /><!-- add by twu2 20060919 end --><br /></span></td></pre><p>修改 posting.php, 找到下頭的字串:</p><pre> 'HCODE' => md5($userdata['username'] . $userdata['user_regdate']),<br /> 'HCODENAME' => md5($userdata['username'] . $hcode_date . ANTISPAM),<br /> 'DATE' => $hcode_date, </pre><p>在後頭加上一段, 變成:</p><pre> 'HCODE' => md5($userdata['username'] . $userdata['user_regdate']),<br /> 'HCODENAME' => md5($userdata['username'] . $hcode_date . ANTISPAM),<br /> 'DATE' => $hcode_date,<br /> 'L_ANTISPAM_CHECK' => $lang['ANTISPAM_Check'], </pre><p>找到下頭的字串:</p><pre> if (!isset($hcode) || empty($hcode) || $hcode != $hcode_old) {<br /> //debug_writelog("antispam for ".$userdata['user_id'].", ".$userdata['username']);<br /> message_die(GENERAL_MESSAGE, $lang['ANTISPAM_Error']);<br /> } </pre><p>在後頭加上一段, 變成:</p><pre> if (!isset($hcode) || empty($hcode) || $hcode != $hcode_old) {<br /> //debug_writelog("antispam for ".$userdata['user_id'].", ".$userdata['username']);<br /> message_die(GENERAL_MESSAGE, $lang['ANTISPAM_Error']);<br /> }<br /><br /> if (($username != '' || $userdata['user_id'] == ANONYMOUS) &&<br /> !isset($HTTP_POST_VARS['check_'.$hcode]) ) {<br /> //debug_writelog("antispam 1 for ".$userdata['user_id'].", ".$username);<br /> $error_msg = $lang['ANTISPAM_Check_Error'];<br /> }<br /> else </pre><p>這樣子就可以在訪客發文時, 檢查是否有點選那個非一般 PHPBB 使用的欄位, 來避免那些專對 PHPBB 所寫的 bot 使用訪客的身份來發文了.</p><p>如果有使用 QuickReply 的模組, 還要修改下頭的檔案.</p><p>修改 viewtopic_body.tpl, 找到下面的字串:</p><pre> <span class='genmed'><input type='text' class='post' tabindex='1' name='username' size='25' maxlength='25' value='' /></span><br /> </pre><p>改成:</p><pre> <span class='genmed'><input type='text' class='post' tabindex='1' name='username' size='25' maxlength='25' value='' /><br /><!-- add by twu2 20060419 begin --><br /><input type="checkbox" name="check_{HCODE}" />{L_ANTISPAM_CHECK}<br /><!-- add by twu2 20060419 end --><br /></span><br /> </pre><p>修改 viewtopic.php, 找到下頭的字串:</p><pre> 'HCODE' => md5($userdata['username'] . $userdata['user_regdate']),<br /> 'HCODENAME' => md5($userdata['username'] . $hcode_date . ANTISPAM),<br /> 'DATE' => $hcode_date, </pre><p>在後頭加上一段, 變成:</p><pre> 'HCODE' => md5($userdata['username'] . $userdata['user_regdate']),<br /> 'HCODENAME' => md5($userdata['username'] . $hcode_date . ANTISPAM),<br /> 'DATE' => $hcode_date,<br /> 'L_ANTISPAM_CHECK' => $lang['ANTISPAM_Check'], </pre><p>這樣子就可以了.</p><hr width="100%" size="2" />2006/10/29: 今天發現似乎有防客上來發廣告信, 看起來像是一個 bot (到目前為止唯一通過的廣告文章), 不知道是如何通過這個檢查的. 目前是先在訪客發文時, 加上一些記錄, 觀察一下這個 bot 是怎麼通過檢查的再說吧.<br /><p> </p>
phpBB
2006-09-19T14:03:13Z
tommy
-
討厭的廣告 bot, 別再來我的 phpBB 了....
https://blog.teatime.com.tw/1/post/49
<a href="http://www.phpbb.com" target="_blank">phpBB</a> 是一套在 internet 上頭, 極為普遍的討論區軟體, 但是, 也由於太多人使用它, 就有人針對這套軟體, 開發了專門用來註冊與發廣告的 bot. 而近來, 在我常出沒的<a href="http://phorum.study-area.org" target="_blank">酷學園</a>裡頭, 一星期總會出現數十篇廣告, 讓版主們砍文砍到手軟.<p>所以, 我們目前採用了下列幾個預防的措施, 用來減少這類的 bot 到 phpBB 來搗亂:</p> <ul> <li>把 phpBB 2.0.12 之後所內建的註冊確認碼功能打開. 可以在註冊時, 要求使用者輪入所出現圖案的文字, 以降低 bot 運作的可能性.</li> </ul> <ul> <li><strike>在觀察多數用來發廣告所註冊的 ID 之後, 發現他們都有設定個人網站這個欄位. 所以我們做下列的修改, 限制使用者註冊時, 無法同時輸入個人網站這個欄位. 如果要設定這個欄位, 可以在註冊成功後再修改.</strike></li> </ul> <blockquote><strike>修改 language/lang_chinese_traditional_taiwan/lang_main.php, 加上</strike></blockquote><blockquote><pre><strike>$lang['Website'] = '個人網站';<br />$lang['Website_explain'] = '在註冊時請將此欄位留空白, 等註冊後再更改這個欄位'; </strike> <br /></pre></blockquote> <blockquote><strike>修改 templates/subSilver/profile_add_body.tpl, 找到下頭的內容</strike></blockquote><blockquote><pre><strike><td class="row1"><span class="gen">{L_WEBSITE}:</span></td> </strike> <br /></pre></blockquote><blockquote><strike>改成</strike></blockquote><blockquote><pre><strike><td class="row1"><span class="gen">{L_WEBSITE}:</span><br /><br /> <span class="gensmall">{L_WEBSITE_EXPLAIN}</span></td></strike> <br /></pre></blockquote> <blockquote><strike>修改 includes/usercp_register.php, 找到下頭的內容</strike></blockquote><blockquote><pre><strike>'L_WEBSITE' => $lang['Website'], </strike> <br /></pre></blockquote><blockquote><strike>在後面加上</strike></blockquote><blockquote><pre><strike>'L_WEBSITE_EXPLAIN' => $lang['Website_explain'], </strike> <br /></pre></blockquote><blockquote><strike>再找下頭的內容</strike></blockquote><blockquote><pre><strike>if ( $website != '' )<br />{<br /> rawurlencode($website);<br />}</strike> <br /></pre></blockquote><blockquote><strike>在後面加上</strike></blockquote><blockquote><pre><strike>if ($mode == 'register') {<br /> if ($website != '') {<br /> $error = TRUE;<br /> $error_msg .= ( ( isset($error_msg) ) ? '<br />' : '' ) . $lang['ANTISPAM_Website'];<br /> }<br />} </strike> <br /></pre></blockquote> <blockquote><strike>經過上頭的修改, 如果使用者註冊時, 有輸入個人網站欄位, 就會顯示錯誤訊息, 無法成功註冊.</strike> 其實在後頭加上一個 checkbox, 讓使用者必須選取這個欄位才能註冊, 也能做到同樣的效果, 所以這個地方先移除不做了, 改用後頭的方式來防止.<br /></blockquote> <ul> <li>參考 <a href="http://www.phpbb.com/files/mods/antispam-1.0.3.mod" target="_blank">ANTISPAM 這個 MOD</a>. 由於上頭對於時間的檢查, 似乎沒有必要, 所以我們修改一下, 只使用其中的概念, 在發文所使用的 form 上頭, 加上兩個用來檢查的欄位.</li> </ul> <blockquote>修改 includes/constants.php, 加上下頭這個設定, 你可以改成你想用的字串</blockquote><blockquote><pre>define('ANTISPAM', 'ANTISPAM Check!'); <br /></pre></blockquote> <blockquote>修改 language/lang_chinese_traditional_taiwan/lang_main.php, 加上</blockquote><blockquote><pre>$lang['ANTISPAM_Error'] = 'ANTISPAM MOD 檢查失敗!'; <br /></pre></blockquote> <blockquote>修改 templates/subSilver/posting_body.tpl, 找到下頭的內容</blockquote><blockquote><pre>{ERROR_BOX} <br /></pre></blockquote><blockquote>在後頭加上</blockquote><blockquote><pre><input type="hidden" name="date" value="{DATE}" /><br /><input type="hidden" name="{HCODENAME}" value="{HCODE}" /> <br /></pre></blockquote> <blockquote>如果有使用快速回應的 MOD, 則修改 templates/subSilver/viewtopic_body.tpl, 找到下頭的內容</blockquote><blockquote><pre>{quick_reply.U_HIDDEN_FORM_FIELDS} <br /></pre></blockquote><blockquote>在前頭加上</blockquote><blockquote><pre><input type="hidden" name="date" value="{DATE}" /><br /><input type="hidden" name="{HCODENAME}" value="{HCODE}" /> <br /></pre></blockquote> <blockquote>修改 posting.php, 找到下頭的內容</blockquote><blockquote><pre>$bbcode_uid = ''; <br /></pre></blockquote><blockquote>在後頭加上</blockquote><blockquote><pre>$hcode = $HTTP_POST_VARS[md5($userdata['username'] . $HTTP_POST_VARS['date'] . ANTISPAM)];<br />$hcode_old = md5($userdata['username'] . $userdata['user_regdate']);<br />if (!isset($hcode) || empty($hcode) || $hcode != $hcode_old) {<br /> message_die(GENERAL_MESSAGE, $lang['ANTISPAM_Error']);<br />} <br /></pre></blockquote><blockquote>找到下頭的內容</blockquote><blockquote><pre>$template->assign_vars(array( <br /></pre></blockquote><blockquote>在前頭加上</blockquote><blockquote><pre>$hcode_date = gmdate('U'); <br /></pre></blockquote><blockquote>找到下頭的內容</blockquote><blockquote><pre>'L_BBCODE_CLOSE_TAGS' => $lang['Close_Tags'], <br /></pre></blockquote><blockquote>在前頭加上</blockquote><blockquote><pre>'HCODE' => md5($userdata['username'] . $userdata['user_regdate']),<br />'HCODENAME' => md5($userdata['username'] . $hcode_date . ANTISPAM),<br />'DATE' => $hcode_date, <br /></pre></blockquote> <blockquote>如果有使用快速回應的 MOD, 則修改 viewtopic.php, 找到下頭的內容</blockquote><blockquote><pre>$template->assign_vars(array( <br /></pre></blockquote><blockquote>在前頭加上</blockquote><blockquote><pre>$hcode_date = gmdate('U'); <br /></pre></blockquote><blockquote>找到下頭的內容</blockquote><blockquote><pre>'S_TOPIC_LINK' => POST_TOPIC_URL, <br /></pre></blockquote><blockquote>在前頭加上</blockquote><blockquote><pre>'HCODE' => md5($userdata['username'] . $userdata['user_regdate']),<br />'HCODENAME' => md5($userdata['username'] . $hcode_date . ANTISPAM),<br />'DATE' => $hcode_date, <br /></pre></blockquote> <blockquote>經過這樣的修改之後, 系統在產生發表文章的 form 時, 會加上兩個特別的欄位, 對每個使用者在不同的時間點, 這兩個欄位的內容都不相同. 而系統在使用者 submit 這個 form 的時候, 會檢查這兩個欄位是否存在, 且內容是否符合, 如果不符合, 則認定為 bot 所發的文章. 因為在正常的使用下, 應該都會符合才對.</blockquote> <ul> <li>由於上頭那個 ANTISPAM MOD 的作法, 每天的確擋下了不少的 bot 前來發文, 所以, 我們依照同樣的作法, 運用在註冊上頭, 避免 bot 前來註冊.</li> </ul> <blockquote>修改 language/lang_chinese_traditional_taiwan/lang_main.php, 加上</blockquote><blockquote><pre>$lang['ANTISPAM_Check'] = '在註冊時請選擇此欄位';<br />$lang['ANTISPAM_Check_Error'] = '[在註冊時請選擇此欄位] 這個欄位未選擇'; </pre></blockquote> <blockquote>修改 includes/usercp_register.php, 找到下頭的內容</blockquote><blockquote><pre>$passwd_sql = ''; <br /></pre></blockquote><blockquote>在前頭加上</blockquote><blockquote><pre>if ($mode == 'register') {<br /> $x_hcode_date = $HTTP_POST_VARS['date'];<br /> $hcode = $HTTP_POST_VARS[md5($x_hcode_date . ANTISPAM)];<br /> $hcode_old = md5($x_hcode_date);<br /> if (!isset($hcode) || empty($hcode) || $hcode != $hcode_old) {<br /> $error = TRUE;<br /> $error_msg .= ( ( isset($error_msg) ) ? '<br />' : '' ) . $lang['ANTISPAM_Error'];<br /> }<br /> if ( !isset($HTTP_POST_VARS['antispam_'.$hcode]) ) {<br /> $error = TRUE;<br /> $error_msg .= ( ( isset($error_msg) ) ? '<br />' : '' ) . $lang['ANTISPAM_Check_Error'];<br /> }<br />} <br /></pre></blockquote><blockquote>找到下頭的內容</blockquote><blockquote><pre>$template->assign_vars(array( <br /></pre></blockquote><blockquote>在前頭加上</blockquote><blockquote><pre>$hcode_date = gmdate('U'); <br /></pre></blockquote><blockquote>找到下頭的內容</blockquote><blockquote><pre>'CONFIRM_IMG' => $confirm_image, <br /></pre></blockquote><blockquote>在後頭加上</blockquote><blockquote><pre>'HCODE' => md5($hcode_date),<br />'HCODENAME' => md5($hcode_date . ANTISPAM),<br />'DATE' => $hcode_date,<br />'L_ANTISPAM_CHECK' => $lang['ANTISPAM_Check'], </pre></blockquote> <blockquote>修改 templates/subSilver/profile_add_body.tpl, 找到下頭的內容</blockquote><blockquote><pre><!-- Visual Confirmation --> <br /></pre></blockquote><blockquote>在前頭加上</blockquote><blockquote><pre><input type="hidden" name="{HCODENAME}" value="{HCODE}" /><br /><input type="hidden" name="date" value="{DATE}" /><br /><tr><br /> <td class="row1"><span class="gen">{L_ANTISPAM_CHECK}: * </span></td><br /> <td class="row2"><input type="checkbox" name="antispam_{HCODE}" /></td><br /></tr> <br /></pre></blockquote> <blockquote>經過這樣的修改後, 註冊時也可以使用同樣的方式來防止 bot 執行. <br /> </blockquote> <ul><li>在儲存文章之前, 如果是訪客或發文數在某個數字以下的使用者, 則使用 regexp 檢查內容.</li></ul><blockquote>修改 include/functions.php, 在第一個 function 之前, 加上下列這個 function</blockquote><blockquote><pre>function check_myfilter($message, $fname = "myfilter")<br />{<br /> global $phpEx, $phpbb_root_path;<br /><br /> $found = '';<br /> $fp = @fopen($phpbb_root_path."includes/$fname.txt", 'rt');<br /> if ($fp != 0) {<br /> while (!feof($fp)) {<br /> $rule = trim(fgets($fp, 1024));<br /> if ($rule == '') continue;<br /> if (preg_match($rule, $message)) {<br /> $found = $rule;<br /> break;<br /> }<br /> }<br /> fclose($fp);<br /> }<br /> return $found;<br />} <br /></pre></blockquote><blockquote>接著修改 include/functions_post.php, 找到下列的內容</blockquote><blockquote><pre>//<br />// Flood control<br />// <br /></pre></blockquote><blockquote>在這前頭加上</blockquote><blockquote><pre>// check message<br />// only check for posts < 30 or guest<br />if ($userdata['user_id'] == ANONYMOUS || $userdata['user_posts'] < 30) {<br /> $myfilter = check_myfilter($post_message);<br /> if ($myfilter != '') {<br /> message_die(GENERAL_MESSAGE, 'Message not allowed for token: '.$myfilter);<br /> }<br />} <br /></pre></blockquote><blockquote>然後把你要阻擋的內容, 用 regexp 的方式寫在 include/myfilter.txt 內, 如下</blockquote><blockquote><pre>/msi-team/i<br />/whymsi/i<br />/SEX ONLINE/i<br />/\[url=http:\/\/.*\.chatsms\.ru\]/i<br />/\.pharnu\.com/i <br /></pre></blockquote><blockquote>每行一個設定, 系統會在儲存之前, 檢查是否有你不要的內容的文章出現.</blockquote>
phpBB
2006-04-10T14:59:26Z
tommy
-
phpBB 由 BIG5 轉換到 UTF-8
https://blog.teatime.com.tw/1/post/48
以往, 當你的網站上來的使用者, 都來自台灣時, 自然大家都使用這兒最普遍的中文編碼 BIG5, 所以網頁用的文字以及資料庫的內容, 自然也就是使用 BIG5 碼來儲存.<p>可是, BIG5 碼只考慮到正體中文的使用, 並未考慮到其他文字的使用. 所以, 當有人使用不同編碼時, 在 BIG5 的系統上頭, 看起來就變成亂碼一樣. 為了解決這個問題, 所以有了 Unicode 的出現, 而 UTF-8 是目前在多數系統上頭都支援的 Unicode 方案. 有了 UTF-8 之後, 我們便可以在同一篇文章中, 同時打入各國的文字, 而不會變成亂碼.</p><p>目前網路上找一下, 可以發現有許多文章在說明如何轉換. 多數都是先把資料庫的內容 dump 出來, 然後使用 iconv 來轉換為 UTF-8, 然後再存入. 而 <a href="http://xoops.tnc.edu.tw/modules/wfdownloads/singlefile.php?cid=9&lid=23" target="_blank">ols3 有寫了一個直接讀出資料轉換後再存入另一個資料庫的程式</a>. 似乎只有上頭兩種方式. </p><p>第一種方式有些小問題, 使用 iconv 轉碼時, 如果同一篇文章只能由某一種內碼轉換到另一種內碼, 所以如果有些內容是同時存在多種內碼時, 就會發生錯誤. 而且, 似乎某些中文字的後頭會多出一個 \ 出來. 這些都要另外去修改. 不過對於一個很大的資料庫來說, 這個修改就變成一個很頭大的動作了.</p><p>第二種方式, 我下載後並沒有試過, 據說可以解決多出那個 \ 的問題. 但是多種內碼存在同一篇文章的情形, 並沒有說明是否有特別處理. 而且.... 那是一個 linux 的 ELF 格式的執行檔. 對我來說, 不知道裡頭到底做了那些動作, 所以不想直接拿來使用.</p><p>所以, 就自己寫了一個 script, 做出類似第二種方式的處理. 直接把資料庫的內容轉成一個 SQL 指令的檔案. 自己在裡頭做轉碼的動作. 這個後頭再來說明.</p><p>先說比較容易處理的部份. 有關 apache 與 phpbb 的 UTF-8 支援部份. 首先, 我們先使用 <a href="http://xoops.tnc.edu.tw/modules/wfdownloads/singlefile.php?cid=9&lid=32" target="_blank">ols3 所寫的另一個 b2u 的 php script</a> 來轉換 phpbb 的檔案為 UTF-8.</p><p>不過這個 script 有點小 bug, 它是依據檔名的後四個字元來判斷是否做轉碼的動作, 但是在 ".tpl" 與 ".css" 的部份, 卻不小心弄成了 "tpl" 與 "css", 所以實際上並沒有處理到 .tpl 與 .css 的檔案. 修改後的 script 如下:</p><pre>#!/usr/bin/php -q<br /><?php<br />dir_work($_SERVER["argv"][1]);<br /><br />function dir_work($toget_dir=""){<br /> if(substr($toget_dir,-1)=="/"){<br /> $toget_dir=substr($toget_dir,0,-1);<br /> }<br /> $d=explode("/",$toget_dir);<br /> $n=sizeof($d);<br /> $k=$n-1;<br /><br /> $utf8_dir=$d[$k]."_utf8";<br /> echo $utf8_dir."\n";<br /> $d[$k]=$utf8_dir;<br /> $new_dir=implode("/",$d);<br /><br /> dir_encode($toget_dir,$new_dir);<br /> return;<br />}<br /><br /><br />function dir_encode($toget_dir="",$new_dir=""){<br /> if(!is_dir($new_dir)){<br /> mkdir($new_dir);<br /> }<br /> if ($dir = @opendir($toget_dir)) {<br /> while (($file = readdir($dir)) !== false) {<br /> if($file=="." or $file==".."){<br /> continue;<br /> }elseif(is_dir($toget_dir."/".$file)){<br /> dir_encode($toget_dir."/".$file,$new_dir."/".$file);<br /> }else{<br /> $type=substr($file,-4);<br /> $ok_array=array(".txt",".php",".sql",".htm","html",".tpl",".css");<br /> if(in_array($type,$ok_array)){<br /> $exec="piconv -f BIG5 -t UTF-8 ".$toget_dir."/".$file." > ".$new_dir."/".$file;<br /> echo $exec."\n";<br /> exec($exec);<br /> }else{<br /> copy($toget_dir."/".$file,$new_dir."/".$file);<br /> }<br /> }<br /> }<br /> closedir($dir);<br /> }<br /> return;<br />}<br />?> <br /></pre><p>我們只要使用這個 script, 就可以把原本的 phpbb 所在的目錄, 複製一份到 _utf8 結尾的目錄, 而裡頭的檔案, 都被轉換為 UTF-8 編碼了.</p><p>接著修改 lang_main.php 中的</p><pre>$lang['ENCODING'] = 'big5'; <br /></pre><p>改成</p><pre>$lang['ENCODING'] = 'utf-8'; <br /></pre><p>然後修改 language 裡頭 email 下的目錄, 把所有出現 big5 的地方, 都改成 utf-8.</p><p>接著修改 <span class="postbody">include/emailer.php 的內容, 在 // Send message 之後加上</span></p><pre> $subject = '=?'.$this->encoding.'?B?'.base64_encode($this->subject).'?=';<br /> $this->subject = $subject;<span class="postbody"></span> </pre><p>把 email 的標題, 使用 base64 編碼來處理, 以避免收到信的時候, 標題的處理可能會出現的亂碼.</p><p>然後修改 db/mysql.php 與 db/mysql4.php (看你用那一個), 在 mysql_connect 之後, 傳回 id 之前, 加上 (如果用 mysql 4.0.x 或之前的版本, 這個動作加不加都一樣)</p><pre>$this->sql_query("SET NAMES 'utf8'"); <br /></pre><p>如果有使用 adv_top5.php 這個 MOD, 也要修改一下這個檔案內的 cutStr(), 改用 mb_strlen() 來抓 UTF-8 字串長度:</p><pre>function cutStr($str) {<br /><br /> global $MAX_STR_LEN;<br /><br />// check lenght use mb_strlen() for UTF-8<br /> $len = mb_strlen($str, "UTF-8");<br /> if ($len > $MAX_STR_LEN) {<br /> $str = mb_strcut($str, 0, $MAX_STR_LEN, "UTF-8")."...";<br /> }<br />/*<br /> $str = (strlen($str) > $MAX_STR_LEN) ? (substr($str, 0, $MAX_STR_LEN - 1) . "...") : $str;<br />*/<br /> return $str;<br />} <br /></pre><p>然後, 修改 apache 的設定, 在新的目錄的位置加上</p><pre><Directory /var/www/phpbb_utf8><br /> AllowOverride FileInfo<br /></Directory> </pre><p>至少要有 FileInfo 的權限. 然後在該目錄加上 .htaccess 檔案, 內容是</p><pre>AddDefaultCharset UTF-8 <br /></pre><p>這樣子可以使這個目錄的資料, 在 apache 送出的 header 中, 表示是使用 UTF-8 編碼的.</p><p>這樣子的處理之後, 我們就會有一個使用 UTF-8 的環境了, 剩下來的就是資料庫裡頭的內容了.</p><p>首先, 我們先使用下頭的指令, 把目前用的資料庫的 schema 抓出來</p><pre>mysqldump -d -p -u dbuser dbname > phpbb_schema.sql <br /></pre><p>產生的 phpbb_schema.sql 檔案, 就是目前我們使用的資料庫的 schema, 接著, 修改這個檔案的內容, 在 MyISAM 之後, 加上 DEFAULT CHARSET=utf8, 用來表示資料是存放為 UTF-8 的格式. (如果你使用的是 mysql 4.0.x 或之前的版本, 系統並未真的支援 UTF-8 存放於資料庫中, 一般來說, 仍是用 latin1 編碼來儲存 UTF-8 的文字, 則上一個改 CHARSET 的動作可能沒有作用)</p><p><strike>接著修改裡頭某些 char 欄位的長度, 因為 UTF-8 並非固定長度, 所以不修改的話, 並無法存入原本你想像的那麼多的字元, 至於想改多大, 你就自行決定吧</strike> (在 MySQL 4.1 或之後有直接支援 UTF-8 字元的版本, 並不需要做這個修改, 依據 <a href="http://dev.mysql.com/doc/refman/5.0/en/charset-unicode.html" target="_blank">MySQL 的文件</a>指出, 對於 UTF-8 的欄位, varchar 或 char 都是用 3 個 bytes 來存一個 char) . 另外, 部份欄位使用 text 格式, 在 mysql 的限制中, 這個格式最大為 65535 bytes, 如果你希望同一篇文章的長度超過這個限制, 可以改用 mediumtext 格式.</p><p>修改後, 建立一個新的資料庫叫 dbname_utf8, 然後建立這些 table</p><pre>mysql -f -p -u dbuser dbname_utf8 < phpbb_schema.sql <br /></pre><p>這樣子就會建立一個新的, 使用 UTF-8 的資料庫了. 接著把 UTF-8 環境下的 phpbb 的 config.php 中資料庫的設定指到新的這個資料庫.</p> <p>目前所有的動作, 都是可以先行處理的, 並不需要停止系統的服務來處理. 而接下來的資料庫內容轉碼的動作, 為了避免資料轉換過程中, 有新的資料產生, 所以需要停止 phpbb 的運作才能進行.</p><p>我們先使用下面的 script, 執行這個 script 後, 應該會產生一個 utf8.sql 的檔案:</p><pre><?php<br /><br />set_time_limit(0);<br /><br />$phpname = 'phpbb_';<br />$dbuser = 'dbuser';<br />$dbpass = 'dbpass';<br />$dbhost = 'localhost';<br />$dbname = 'dbname';<br /><br />$encoding_cnt = 0;<br />$encoding[$encoding_cnt++] = 'BIG5';<br />$encoding[$encoding_cnt++] = 'GB2312';<br />$encoding[$encoding_cnt++] = 'GBK';<br />$encoding[$encoding_cnt++] = 'SHIFT_JIS';<br />$encoding[$encoding_cnt++] = 'GB18030';<br /><br />$conn = mysql_connect($dbhost, $dbuser, $dbpass);<br />if (!$conn) {<br /> echo "Can't connect to mysql at $dbhost!";<br /> exit;<br />}<br /><br />if (!mysql_select_db($dbname, $conn)) {<br /> echo "Can't use database $dbname!";<br /> mysql_close($conn);<br /> exit;<br />}<br /><br />//mysql_query("SET NAMES 'big5'", $conn);<br />if (!($result = mysql_list_tables($dbname, $conn))) {<br /> echo "Can't get table for $dbname!";<br /> mysql_close($conn);<br /> exit;<br />}<br />while ($row = mysql_fetch_row($result))<br /> $tables[] = $row[0];<br />mysql_free_result($result);<br /><br />$fp = fopen("utf8.sql", "wt");<br />if ($fp == 0) {<br /> echo "Can't open utf8.sql!";<br /> mysql_close($conn);<br /> exit;<br />}<br /><br />fputs($fp, "SET NAMES 'utf8';\n");<br /><br />$cnt = 0;<br />foreach ($tables as $tblname) {<br /> $cnt++;<br /> if ($tblname == $phpname.'search_wordlist' ||<br /> $tblname == $phpname.'search_results' ||<br /> $tblname == $phpname.'search_wordmatch') {<br /> echo "skip for $tblname<br>\n";<br /> continue;<br /> }<br /> fputs($fp, "delete from `$tblname`;\n");<br /> echo "Table: $tblname<br>\n";<br /> echo "<blockquote>\n";<br /> $sql = "select * from `$tblname`";<br /> $result = mysql_query($sql, $conn);<br /> if (!$result) {<br /> echo "Query failed: $sql\n";<br /> }<br /> else {<br /> $fields = array();<br /> $num = mysql_num_fields($result);<br /> for ($i = 0; $i < $num; $i++) {<br /> $meta = mysql_fetch_field($result);<br /> if (!$meta) {<br /> echo "No meta information!\n";<br /> }<br /> else {<br /> $name = $meta->name;<br /> $type = $meta->type;<br /> echo "'$name' => '$type'<br>\n";<br /> $fields[$i]['name'] = $name;<br /> $fields[$i]['type'] = $type;<br /> }<br /> }<br /> $row_cnt = 0;<br /> $err = 0;<br /> while ($row = mysql_fetch_row($result)) {<br /> $row_cnt++;<br /> $field_list = '';<br /> $value_list = '';<br /> $id = $row[0];<br /> for ($i = 0; $i < $num; $i++) {<br /> $type = $fields[$i]['type'];<br /> $value = $row[$i];<br /> if ($value == NULL) continue;<br /> if ($field_list == '') {<br /> $field_list = '`'.$fields[$i]['name'].'`';<br /> }<br /> else {<br /> $field_list .= ',`'.$fields[$i]['name'].'`';<br /> }<br /> if ($type == 'string' || $type == 'blob') {<br /> $lines = explode("\n", $value);<br /> $n = count($lines);<br /> $uvalue = '';<br /> for ($ln = 0; $ln < $n; $ln++) {<br /> $xline = $lines[$ln];<br /> $ok = 0;<br /> for ($x = 0; $x < $encoding_cnt; $x++) {<br /> $code = $encoding[$x];<br /> // I think we should remove //TRANSLIT here, return error to change another encoding<br /> //$xvalue = iconv($code, 'UTF-8//TRANSLIT', $xline);<br /> $xvalue = iconv($code, 'UTF-8', $xline);<br /> if ($xvalue === false) {<br /> //echo "$id, Can't convert $xline from $code<br>\n";<br /> }<br /> else {<br /> //echo "convert '$xline' to '$xvalue' for $code<br>\n";<br /> $ok = 1;<br /> break;<br /> }<br /> }<br /> if ($ok == 0) {<br /> $xvalue = $xline;<br /> //echo "failed for all encoding<br>\n";<br /> }<br /> if ($ln == 0)<br /> $uvalue = $xvalue;<br /> else<br /> $uvalue .= "\n".$xvalue;<br /> }<br /> if (preg_match("/&#\d{4,7};{0,1}/", $uvalue)) {<br /> $x = preg_replace_callback(<br /> "|(&#)(\d{4,7})(;{0,1})|",<br /> "num2utf",<br /> $uvalue);<br /> //echo "convert '$uvalue' to '$x'<br>\n";<br /> $uvalue = $x;<br /> }<br /> $str = mysql_escape_string($uvalue);<br /> if ($value_list == '') {<br /> $value_list = "'$str'";<br /> }<br /> else {<br /> $value_list .= ",'$str'";<br /> }<br /> }<br /> else {<br /> if ($value_list == '') {<br /> $value_list = $value;<br /> }<br /> else {<br /> $value_list .= ",$value";<br /> }<br /> }<br /> }<br /> $sql = "insert into $tblname ($field_list) values ($value_list)";<br /> fputs($fp, $sql);<br /> fputs($fp, ";\n");<br /> }<br /> }<br /> echo "</blockquote>\n";<br />}<br /><br />fclose($fp);<br />mysql_close($conn);<br /><br />function code2utf($num)<br />{<br /> //Returns the utf string corresponding to the unicode value<br /> //courtesy - romans@void.lv<br /> if ($num<128)<br /> return chr($num);<br /> if ($num<2048)<br /> return chr(($num>>6)+192).chr(($num&63)+128);<br /> if ($num<65536)<br /> return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128);<br /> if ($num<2097152)<br /> return chr(($num>>18)+240).chr((($num>>12)&63)+128).chr((($num>>6)&63)+128). chr(($num&63)+128);<br /> return '';<br />}<br /><br />// the callback function<br />function num2utf($matches) {<br /> // as usual: $matches[0] is the complete match<br /> // $matches[1] the match for the first subpattern<br /> // enclosed in '(...)' and so on<br /> $code = $matches[2];<br /> return code2utf($code);<br />}<br /><br />?> <br /></pre><p>然後把這個檔案的內容塞入新的資料庫</p><pre>mysql -f -p -u dbuser dbname_utf8 < utf8.sql <br /></pre><p>等資料轉好後, 把 apache 中指向 phpbb 的設定, 切換到新的目錄, 這樣子系統就變成 UTF-8 了. 你可以用 broswer 連上去看看, 應該是正確的 UTF-8 編碼了.</p><p>最後, 說明一下那個轉碼 script 的動作:</p><ol><li>先抓出所有的 table 名稱.</li><li>由於 search_wordlist, search_results, search_wordmatch 三個 table 只是搜尋的 cache, 可以清除而不影響系統運作, 所以不轉換這三個 table.</li><li>在 utf8.sql 一開頭, 先執行 SET NAMES 'utf8'; 以確定使用 UTF-8.</li><li>在每個 table 轉碼前先 delete 該 table 的資料.</li><li>抓取 table 的欄位名與資料格式. 我們只針對 string 與 blob 格式進行轉碼. (也許 blob 不應該也不需要轉碼, 在 phpBB 中沒有這類的格式, 如果你的系統有這類格式的欄位, 請先轉換測試看看是否有此需求)<br /></li><li>系統為避免同一篇文章內出現不同的編碼, 造成 iconv 轉換到該編碼後就停止, 所以每次只轉一行. 如此, 應該只有同一行出現不同編碼的情形才會轉換失敗.</li><li>把原本 &#nnnn; 之類的字元, 也轉換成 UTF-8 的字元. <br /></li><li>產生 insert 的 SQL 指令存入 utf8.sql.<br /></li></ol><p>這個轉碼與存入資料庫的動作, 並不會太久, 依據你的資料庫大小而定, 以<a href="http://phorum.study-area.org" target="_blank">酷學園</a>為例, 有 12000 的使用者與 160000 的文章, 轉換與存入的動作, 在 5-10 分鐘就可以完成. </p><hr width="100%" size="2" />2006/07/20: <a href="http://phorum.study-area.org/viewtopic.php?p=203275#203275" target="_blank">經 Darkhero 兄提醒</a>, 在 MySQL 5.0 (可能是 4.1 之後的版本) 中, 如果要轉換, 抓出來的型態中, date, datetime 兩個型態也是要比照字串辦理. 上述程式的 <span class="postbody">if ($type == 'string' || $type == 'blob') 要改成 </span><span class="postbody">if ($type == 'string' || $type == 'blob' || $type == 'date' || $type == 'datetime') 才可以.<br /></span><p> </p>
phpBB
2006-04-09T10:37:15Z
tommy