About Web Crawler

什麼是 Web Crawler?? 其實就是一般慣稱的 bot.
定期在網路上漫遊,將網頁的部分(全部)資料以及特徵值放到自身的資料庫裡面,做為 Search engine 的資料來源.
所以你大概會這麼想,使用一部或多部 server, 以 multiple thread 甚或 multiple process 的方式,去網路上漫遊.
當遇到網頁中有連結的時候,先排程到 Queue 裡面,讓其他有空的 thread/process 能從 Queue 裡面循序取出來,進行漫遊.
設計這樣的一個程式,所需要考慮的一些事情:
1. Multiple database server的考量:一個 database server 可能會承受不住這種情況,所以需要考慮 replication 或是更適合用來取資料的 database engine.
2. Multiple host + Multiple thread 的考量:網路上現今的資料量絕對超過你的想像,所以你不可能使用一台機器甚或一個 thread 去做漫遊,這樣會花費太多時間.另外也要考慮 lock, resource …等等的問題.
3. Queue 實作的方式: 有些立即性的連結,可能需要放在 thread/process 自身的 queue 裡面,比較不具立即性的連結,就可能放在 database server 上. (Priority Queues)
我想我考慮的可能還太淺.
其他一些可以參考的或可能用上的技術:
* Lucene
* Google Desktop search API
* MySQL replication
* Thread pool
* DDJ 2001 April: Web site searching & Indexing in Perl

MSN messenger+ foolbar2000

早先就知道可以讓 Windows Media Player 播放的曲目資訊顯示在 MSN messenger 上.
可是…我並不喜歡用 Windows Media Player 來播放歌曲啊.
之前也逛到讓 foolbar2000 播放的曲目資訊顯示在 MSN messenger 上的方法,可是我並沒有去試.
今天心血來潮了,上網搜尋了一下:
*PCDVD數位科技討論區 – [教學]如何不慣WMP9啟動MSN歌名同步
節錄步驟如下,照著作就對了.
1. 先到這裡 HKEY_CURRENT_USER\Software\Microsoft\MSNMessenger\PerPassportSettings000000000\
把 “ShowTransientPSM” 改成 dword:00000001.
2. 下載 MSN Messenger Now Playing Plugin (gen_msn), 解開以後,放到 foolbar2000 目錄下的 components.
3. 收工.

RFMaintainer 的 statechart (補)

昨天的RFMaintainer 的 statechart並沒有實作部份,今天補上…
你可以看到這些都是寫在 Form 裡面,並沒有跟 UI 切開….
這就是我疑問的地方…
不過對照圖之後,你會發現這樣的寫法很清楚…不會有補東牆挖西牆,東改一塊西改一塊的問題…

		#region State Chart Implementation.
private int _StateVariable = 0;
private int _StateVariable_A = 0;
private int _StateVariable_B = 0;
private int _StateVariable_C = 0;
/// 
/// State 1: Transient
/// 
private void go_state_1()
{
_StateVariable = 1;
if( txtConnectString.Text == "" )
go_state_2();
else
go_state_3();
}
/// 
/// State 2: Connection string null
/// 
private void go_state_2()
{
_StateVariable = 2;
// disable controls.
radioButton1.Enabled = false;
radioButton2.Enabled = false;
dtpFrom.Enabled = false;
dtpTo.Enabled = false;
cboType.Enabled = false;
txtPath.Enabled = false;
btnBrowse.Enabled = false;
btnExport.Enabled = false;
}
/// 
/// State 3: Connection string not null
/// 
private void go_state_3()
{
_StateVariable = 3;
// enable controls.
radioButton1.Enabled = true;
radioButton1.Checked = true;
radioButton2.Enabled = true;
cboType.Enabled = true;
txtPath.Enabled = true;
btnBrowse.Enabled = true;
go_state_4();
go_state_7();
go_state_10();
}
/// 
/// State 4: Region A: Transient
/// 
private void go_state_4()
{
_StateVariable_A = 4;
if( radioButton2.Checked )
go_state_5();
else if( radioButton1.Checked )
go_state_6();
}
/// 
/// State 5: Region A: "Period" checked
/// 
private void go_state_5()
{
_StateVariable_A = 5;
dtpFrom.Enabled = true;
dtpTo.Enabled = true;
}
/// 
/// State 6: Region A: "All" checked
/// 
private void go_state_6()
{
_StateVariable_A = 6;
dtpFrom.Enabled = false;
dtpTo.Enabled = false;
}
/// 
/// State 7: Region B: Transient
/// 
private void go_state_7()
{
_StateVariable_B = 7;
if( txtPath.Text == "" )
go_state_8();
else
go_state_9();
}
/// 
/// State 8: Region B: "output file" null
/// 
private void go_state_8()
{
btnExport.Enabled = false;
}
/// 
/// State 9: Region B: "output file" not null
/// 
private void go_state_9()
{
btnExport.Enabled = true;
}
/// 
/// State 10: Region C: "Export type" is XML
/// 
private void go_state_10()
{
_StateVariable_C = 10;
cboType.SelectedIndex = 0;
}
/// 
/// State 11: Export
/// 
/// 
private void go_state_11()
{
}
#endregion
#region UI independence routines
/// 
/// I don't consider this button in State chart.
/// And I am lazy to change....
/// 
/// 
private bool testConnection( string connectString )
{
}
private bool export( int type, DateTime dtFrom, DateTime dtTo, string outputFile )
{
}
#endregion
#region Event handler
private void btnExport_Click(object sender, System.EventArgs e)
{
int iType = 0;
if( radioButton1.Checked == true )
iType = 1;
else if( radioButton2.Checked == true )
iType = 2;
if( export( iType, dtpFrom.Value, dtpTo.Value, txtPath.Text ) )
MessageBox.Show( "Finished!" );
else
MessageBox.Show( "Fail!!" );
}
private void btnBrowse_Click(object sender, System.EventArgs e)
{
// Displays a SaveFileDialog so the user can save the Image
SaveFileDialog saveFileDialog1 = new SaveFileDialog();
saveFileDialog1.Filter = "XML file|*.xml";
saveFileDialog1.Title = "Save to XML File";
saveFileDialog1.ShowDialog();
saveFileDialog1.OverwritePrompt = true;
// If the file name is not an empty string open it for saving.
if(saveFileDialog1.FileName != "")
{
txtPath.Text = saveFileDialog1.FileName;
}
}
private void btnTest_Click(object sender, System.EventArgs e)
{
testConnection( txtConnectString.Text );
}
private void radioButton1_CheckedChanged(object sender, System.EventArgs e)
{
go_state_6();
}
private void radioButton2_CheckedChanged(object sender, System.EventArgs e)
{
go_state_5();
}
private void txtPath_TextChanged(object sender, System.EventArgs e)
{
go_state_7();
}
private void txtConnectString_TextChanged(object sender, System.EventArgs e)
{
go_state_1();
}
#endregion
}

RFMaintainer 的 statechart

Constructing the user interface with statecharts之後,應用到自己的小程式上…
書上建議先大致勾勒出畫面,然後寫出 screen rule.
我的畫面:
rfmaintainer screenshot
而 screen rule 大致如下:
*”Connection string” must be specified.
*如果”Connection string” is not specified, “All”, “Period”, “From”, “To”, “Export type”, “output file”, “browse”, “export” 都會被 disable.
*當按下 “Export” button 會開始進行 export 動作.
*當按下 “Browse” button 可以指定 output file
*如果 output file 未指定, “Export” 按鈕無法被 click.
*選擇 Option “All”, Option “Period” will be disabled.
*選擇 Option “Period”, Option “All” will be disabled.
*Export type 目前只有一個可以選擇: XML
所以,可以導出這樣的 statechart,只是不知道對不對…
RFMaintainer statechart
最後寫 code 的時候,我為每個 state 加上編號,如圖:
RFMaintainer statechart 為 state 加上編號
然後再寫,的確是比較容易寫 code, 也比較容易了解.
只是,看來並沒有跟 UI 分離,或許是我自己理解錯誤…

升級到 MovableType 2.661

拖拖拖….轉眼就拖了一年,Movable Type都已經到 3.17 版了,而我…還在用 2.64.
原因是因為我以為下載不到 2.661 了….
其實上官方網站免費加入會員以後,還是可以在帳戶裡面下載到 2.661 版.
再配合JediMovable Type完全手冊,一下子就搞定了.
升級上,沒啥問題. MySQL database 不用動,只要先備份 mt.cfg 以及 mt-db-passwd.cgi.
然後把新的檔案複製過去,最後再把剛剛備份的檔案覆蓋回來就好啦~~
收工.

如何判別是否為 DRM 的 ASF 檔案?

如何判別一個 ASF 檔案是否為 DRM 的 ASF 檔案?
詳情資料可以參考微軟提供的 Specification: Advanced Systems Format (ASF) Specification
大致上很簡單,一個 ASF 檔案主要有三個部分: Header Object, Data Object, Index Object.
而每個部分又各由不同的 Object 所組成.
要分辨是否為 DRM,主要是在 Header Object 裡面找尋是否有 Content Encryption Object.
那麼要怎麼找尋呢?? OK, 每個 Object 的最前面有唯一的 GUID,你可以依據 GUID 來找到.
而這些,你都可以在 Specification 的後面幾張找到 ( 或者你可以搜尋 ASF GUIDs ).
至此,寫起來並不太困難.程式碼如下:

typedef unsigned long UINT32;
typedef unsigned short UINT16;
typedef unsigned char UINT8;
typedef int BOOL;
enum BOOLEAN
{
FALSE = 0,
TRUE
};
typedef struct GUID_s
{
UINT32 id1;
UINT16 id2;
UINT16 id3;
UINT16 id4;
UINT8 id5[6];
}GUID;
//Content Encryption Object
GUID ASF_Content_Encryption_Object_guid =
{
0x2211b3fb,
0xbd23,
0x11d2,
0xb7b4,
{
0x00, 0xa0, 0xc9, 0x55, 0xfc, 0x6e
}
};
/*
typedef struct ContentEncryptionObject_s
{
GUID objectID;
UINT32 objectSize; // objectSize should be QWORD, 8 bytes, but CCS has not this type.
UINT32 objectSize1;
UINT32 secretDataLength;
BYTE* secretData; // according secretDataLength
UINT32 protectionTypeLength;
char* protectionType;
UINT32 keyIDLength;
char* keyID;
UINT32 licenseURLLength;
char* licenseURL;
}ContentEncryptionObject_t;
*/
//extern CF_FILE* filePtrR;
typedef FILE CF_FILE;
CF_FILE* filePtrR;
long
FILEr_ftell()
{
return ftell( filePtrR );
}
int
FILEr_fseek( long filepos, int whence)
{
return fseek( filePtrR, filepos, whence );
}
long
FILEr_fread( UINT8* ptr, long length )
{
return fread( ptr, 1, length, filePtrR );
}
char* SDRAM_HOLE = NULL;
BOOL
isASFDRM( )
{
long filePos;
char* spaceForParsing = (char*)( SDRAM_HOLE + 0x800000 ); // use the space after 8 MB, prevent conflict
int GUIDLen = sizeof( GUID );
int iter, len = 0;
BOOL bFound = FALSE, bResult = FALSE;
UINT32 headerObjectSize = 0;
UINT32 secretDataLength=0;
UINT32 protectionTypeLength=0;
char* protectionType=NULL;
long readLen=0;
char* encryptionPos = NULL;
// save file position
filePos = FILEr_ftell();
// seek to the start position
FILEr_fseek( 16, 0); //SEEK_SET
FILEr_fread( (UINT8*)&headerObjectSize, 4 ); // we should read 8 here, but ….
// read all header object
// then search the specified GUID, use pattern compare
FILEr_fseek( 0, 0 ); // SEEK_SET
readLen = FILEr_fread( spaceForParsing, headerObjectSize );
len = headerObjectSize – GUIDLen;
// searching.
for( iter = 0; iter objectID
encryptionPos = spaceForParsing + iter;
// ( encryptionPos + GUIDLen ) ==> objectSize
// ( encryptionPos + GUIDLen + 8 ) ==> secretDataLength
secretDataLength = (UINT32) *( encryptionPos + GUIDLen + 8 );
// ( spaceForParsing + iter + GUIDLen + 8 + 4 ) ==> secretData
// ( spaceForParsing + iter + GUIDLen + 12 + secretDataLength ) ==> protectionTypeLength;
protectionTypeLength = (UINT32) *(encryptionPos + GUIDLen + 12 + secretDataLength );
protectionType = (char*) (encryptionPos + GUIDLen + 12 + secretDataLength + 4 );
if( strncmp( protectionType, “DRM”, 3 ) == 0 )
bResult = TRUE;
OnFinally:
// restore file position
FILEr_fseek( filePos, 0); //SEEK_SET
// return result.
return bResult;
}
int main(int argc, char* argv[])
{
SDRAM_HOLE = (char*) malloc( 0x800000 * 2 );
filePtrR = fopen( “00 Cannot Play.wma”, “rb” );
if( isASFDRM() )
printf(“is a drm file.\n”);
else
printf(“not a drm file.\n”);
fclose( filePtrR );
return 0;
}

ok, 程式裡面你會發現一些奇怪的地方,是的,因為我需要在 embedded 環境下運作,所以為了符合環境,所以做了一些調整,不過你還是可以在 Visual C++ 上來運行這段代碼.
看不順眼的,就自己調整吧…
這份 specification 其實斷斷續續看一陣子了,都沒怎麼專心看,不過昨天看的時候,突然開竅了,於是就把這麼一段代碼搞定了.
真神奇啊….

跳槽到 CentOS 3.4

昨天把家裡的 server 從原來的WBEL 改為 CentOS 了.
跳槽的原因….嗯….沒什麼特別的,只因為 CentOS 的 mailing list 比較活躍,更新速度感覺上也比較快.
步驟也是簡單到不行….CentOS 官方網站上有提供FAQ.
照步驟作,網路夠快,裝的套件也不多的話,很快就裝完了…
下次的目標是從 3.4 升級到 4, 不過看起來有蠻多困難的. 畢竟 kernel 換到 2.6 ,處理 hardware 的方式也變了挺多的, 找了一下,大部分都建議燒成光碟片,以光碟開機來作昇級.
再找時間試試看吧.

Upgrade to MediaWiki 1.4.4

鬼混了很久,終於把家裡的 wiki 升級到 1.4.4 了.
升級方法還挺簡單的.
1. 備份你的 LocalSettings.php / AdminSettings.php
2. 把下載來的 MediaWiki 1.4.4 tarball 解壓以後整個蓋過去.
3. 切到 maintenance 目錄下, 執行 php update.php
4. 收工.
真的很簡單吧….
下個目標是把 VFP Wiki 升級到 1.4.4 …