引言
在中國Foxmail早已成為收發郵件的首選軟體,其功能也早已超越Outlook,但是Foxmail也並非盡善盡美,尤其是在對郵件夾內郵件的匯出、備份的處理上做的不是太好,只能手工一封一封將信匯出,如果信件數目較少這樣處理還可以應付,但如果對於成百上千封郵件仍這樣處理顯然是相當困難的。為此,本文通過對Foxmail郵件夾檔案的分析而給出了一種通過程式自動將某個郵件夾中的所有郵件匯出到指定檔案夾的方法。
Foxmail郵件夾組成分析
為了能編程實現對Foxmail郵件夾的處理,首先要對Foxmail的郵件夾和內部郵件的組成方式及其採取的存放格式有一個清楚的認識。進入到Foxmail的安裝目錄,然後發一封郵件給自己,把信接收後,查看安裝目錄下各個檔案的時間屬性,發現只有fmnmtk.dat檔案是最新的,但該檔案大小遠遠小於所發郵件,因此應當排除。mail檔案夾中的子目錄對應Foxmail帳號。進入某個帳號所對應的檔案夾,檔案夾中的in、out、send和trash等檔案分別對應收件夾、發件夾、已傳送檔案夾和廢件箱。
在明確了郵件夾所對應的隱藏檔後,可以通過二進位查看軟體來進一步分析郵件夾的內部結構組成。根據目前的情況可以斷定某個郵件夾內的郵件均存放在同一個檔案中,因此本文對郵件夾內郵件的大量匯出問題可以轉化為從郵件夾檔案中分離出各個郵件並將其輸出到外部檔案的問題。這樣,在對郵件夾檔案內部結構組成的分析時,只需關心兩封郵件的邊界即可。開啟某個郵件夾檔案,可以看到開始一段16進位內容為:
| 10 10 10 10 |
10 10 10 11 |
11 11 11 11 |
11 53 0D 0A |
| 52 65 74 75 |
72 6E 2D 50 |
61 74 68 3A |
20 3C 7A 68 |
這裡,開始一段代碼非常規整,因此可以猜測為檔案的格式頭或是各個郵件的格式頭,在檔案內對前7個10和6個11共13個字元進行搜尋,可以發現存在有許多相同字元,而且搜尋到的字元鄰近幾個字元"53"、"0D"和"0A"也同時出現,其後的字元則不固定,因此可以進一步假定這16個字元為郵件的格式頭而非檔案的格式頭(檔案格式頭通常只在檔案開始處出現一次),再在郵件夾檔案中對這16個字元進行搜尋並記錄搜尋到的次數,可以發現正好同郵件夾中的郵件數目相吻合,而且在刪除或增添郵件後,該數目也會相應變化。根據以上情況可以斷定"10 10 10 10 10 10 10 11 11 11 11 11 11 53 0D 0A"是郵件的格式頭,根據此頭可以分離出郵件。
程式的設計實現
在弄清楚Foxmail郵件夾檔案的組成結構後,對內部郵件的提取、匯出就不是什麼難事了,編程的中心思想是對郵件格式頭的判別,並對位於其後郵件內容資料的檔案匯出。為此,可以對全部郵件夾檔案內容搜尋ASCII碼10(16進位),一旦發現該字元說明其後15個位元組有可能是郵件格式頭,然後繼續判斷其後15個位元組是否滿足郵件格式頭,一旦某個位元組不符合則表明其並非郵件格式頭而不予處理,否則開始將郵件資料寫入匯出檔案,同時關閉上一封開啟過的匯出檔案:
//從郵件夾檔案讀取字元
c = fgetc(fp1);
while (!feof(fp1))
{
//發現有分隔特徵串的開頭字元, 則進入check()函數進行分析
if (c == 0x10)
check();
//產生輸出檔案名
j++;
filename.Format("%d",j);
//確定待匯出檔案的存放路徑
char exeFullPath[MAX_PATH];
CString strPath;
GetModuleFileName(NULL, exeFullPath, MAX_PATH);
strPath = CString(exeFullPath);
strPath = strPath.Left(strPath.ReverseFind('//'));
strPath = strPath + "//mail//" + CString(filename) + ".eml";
strcpy(fullname, strPath);
//以寫方式開啟匯出檔案,如果檔案不存在則建立之
fp2 = fopen(fullname, "w");
//沒有到輸入檔案的結尾, 又不是分隔特徵串的開頭字元, 則將字元寫入輸出檔案
while (!feof(fp1) && (c = fgetc(fp1)) != 0x10)
fputc(c, fp2);
} |
其中,對於郵件格式頭的判斷是在check()函數中完成的,依次對0x10之後的15個位元組進行判斷,一旦發現不是郵件格式頭字元,立即返回,並將其當作郵件內容資料進行處理只有在斷定為郵件格式頭後才將上一封匯出檔案關閉:
for (int i = 0; i < 6; i++)
{
c = fgetc(fp1);
//不符合分隔特徵串,則返回
if(c != 0x10)
return;
}
for (i = 0; i < 6; i++)
{
c = fgetc(fp1);
//不符合分隔特徵串,則返回 由此可以斷定郵件夾的內容
if (c != 0x11)
return;
}
if ((c = fgetc(fp1)) == 0x53)
if ((c = fgetc(fp1)) == 0x0D)
if ((c = fgetc(fp1)) == 0x0A)
{
c = fgetc(fp1);
//完全符合特徵串,則當前郵件輸出完畢, 關閉輸出檔案
fclose(fp2);
} |
由於本程式的設計目的在於自動大量匯出郵件,因此處理資料量一般是比較大的,為避免程式阻塞,可以將此處理過程放置於線程之中進行。經實驗,由上述程式匯出的eml檔案可以為OutLook檔案識別並開啟,達到了預期目的。
小結
本文通過對Foxmail郵件夾的組成結構進行分析而最終實現了對其郵件的大量匯出,極大方便了對Foxmail郵件的備份、管理等工作,而且對不同版本的Foxmail均有很好的支援(當前最新版本為4.2,本文方法適用),因此不失為一款實用的郵件輔助管理工具。本文所述代碼在Windows 2000 Professional下由Microsoft Visual C++ 6.0編譯通過。