703
社区成员
// dbx_EnsureFolders: first builds up a FolderSorting tree. Then calls
// the above function to copy it into the flat global vector.
//
void dbx_EnsureFolders(const string name,const string ver)
{ string testn=name, testv=ver;
if (testn=="") testn="_"; if (testv=="") testv="_";
if (dbx_DoneFoldersName==testn && dbx_DoneFoldersVer==testv) return;
dbx_DoneFoldersName=testn; dbx_DoneFoldersVer=testv;
dbx_Folders.clear();
if (dbx_DoneStores=="") dbx_EnsureStores(name);
//
// Now let's find the path for this name/ver
string path="";
for (vector<TDbxStore>::iterator i=dbx_Stores.begin(); i!=dbx_Stores.end(); i++)
{ if (name=="" && ver=="") {path=i->path; break;}
else if (name==i->name && ver==i->ver) {path=i->path; break;}
}
if (path=="") return;
string ffn = path+"\\Folders.dbx";
if (!FileExists(ffn)) return;
// Folders.dbx is an index of the folders in this profile.
// It is a flat list. Each entry has:
// id, parent-id, fn, name, regstring, subindex, value, id0b
// The id/parent-id determine the structure of the tree
// name is the folder name, and fn is the dbx filename (as a sub of path)
// For top-level ones, we look at regstring to determine the index:
// "LocalStore" is 1, some others also have numbers, some are empty (in which case we use index in the file)
// For sub-level ones, some have subindex set, some are empty (in which case we use index in the file)
// If value is 1, or if id0b is 0x2F0000, then this might be an offlineable folder.
vector<TDbxFolderSorting*> folders;
map<unsigned int,TDbxFolderSorting*> i2f;
//
// I'm using my specialised memory-mapped class here.
// Arne's code was written to use an istream instead, so you'll
// have to either change this to an istream, or change Arne's code.
imemfile inm(ffn.c_str());
if (!inm.okay()) return;
DbxFileHeader fileHeader(inm);
const int4 treeNumber=2; // This third tree stores pointers to all folder informations
int4 address = fileHeader.GetValue(fhTreeRootNodePtr+treeNumber);
int4 entries = fileHeader.GetValue(fhEntries +treeNumber);
if(address && entries)
{ DbxTree tree(inm,address,entries); // Read in the tree with all pointers
for(int4 filepos=0; filepos<entries; ++filepos)
{ int1 *ptr; int4 length;
address = tree.GetValue(filepos); // Get the address of the folder info
DbxFolderInfo folderInfo(inm,address); // Read in the folder info
unsigned int index = folderInfo.GetValue(0);
unsigned int pindex = folderInfo.GetValue(1);
string fn; ptr=folderInfo.GetValue(3,&length); if (ptr!=0 && length>0) fn=string((char*)ptr,length-1);
string name; ptr=folderInfo.GetValue(2,&length); if (ptr!=0 && length>0) name=string((char*)ptr,length-1);
string reg; ptr=folderInfo.GetValue(5,&length); if (ptr!=0 && length>0) reg=string((char*)ptr,length-1);
unsigned int pos = folderInfo.GetValue(9);
unsigned int value = folderInfo.GetValue(10);
unsigned int id0b = folderInfo.GetValue(11);
unsigned int num_to_download = folderInfo.GetValue(18);
//
bool isexplicitpos=false;
if (strncmp(reg.c_str(),"LocalStore",10)==0) reg="1";
if (reg!="" && pindex==0)
{ bool isnum=true; for (unsigned int i=0; i<reg.length(); i++) {if (reg[i]<'0' || reg[i]>'9') isnum=false;}
if (isnum) sscanf(reg.c_str(),"%lu",&pos);
}
if (pos!=0) isexplicitpos=true; else pos=filepos;
TDbxFolderSorting *parent=0;
if (pindex!=0)
{ map<unsigned int,TDbxFolderSorting*>::const_iterator i = i2f.find(pindex);
if (i!=i2f.end()) parent=i->second;
}
bool isnews = (value==0);
//
if (!isnews)
{ TDbxFolderSorting *f = new TDbxFolderSorting;
f->index = index;
f->isexplicitpos = isexplicitpos;
f->position = pos;
f->info.name = name;
f->parent = parent;
if (parent==0 || parent->parent==0) f->info.path="";
else if (parent->info.path=="") f->info.path=parent->info.name;
else f->info.path=parent->info.path+"\\"+parent->info.name;
if (parent==0) f->info.account="";
else if (parent->parent==0) f->info.account=parent->info.name;
else f->info.account=parent->info.account;
string sf="";
if (f->info.account!="") sf += f->info.account+"\\";
if (f->info.path!="") sf += f->info.path+"\\";
sf += f->info.name;
f->info.full = sf;
f->info.fn = fn;
f->info.fpath = path;
if (parent==0) f->info.depth=0; else f->info.depth=parent->info.depth+1;
if (parent==0) f->info.type=dftStructure;
else if (!f->isexplicitpos) f->info.type=dftMail;
else if (pos==1) f->info.type=dftInbox;
else if (pos==2) f->info.type=dftOutbox;
else if (pos==3) f->info.type=dftSent;
else if (pos==4) f->info.type=dftDeleted;
else if (pos==5) f->info.type=dftDrafts;
else f->info.type=dftMail;
f->info.might_contain_offlines = (value==0 || value==1 || id0b==0x2F || num_to_download>0);
// That 'num to download' isn't reliable.
//
if (parent==0) folders.push_back(f);
else parent->c.push_back(f);
i2f.insert(pair<unsigned int,TDbxFolderSorting*>(index,f));
}
}
}
inm.close();
// Now sort them and save copies into the main list ane delete them
RecEnsureFolders(folders,true);
}
imemfile::imemfile(const char *fn)
{ buf=0; isokay=false; pos=0; hf=0; hmap=0; size=0; DWORD high;
if (fn!=0) hf = CreateFile(fn,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
if (hf==INVALID_HANDLE_VALUE) isokay=false;
else size=GetFileSize(hf,&high);
if (high!=0 || size==0) {CloseHandle(hf);hf=0; isokay=false;}
else hmap=CreateFileMapping(hf,NULL,PAGE_READONLY,0,0,NULL);
if (hmap==NULL) {CloseHandle(hf); hf=0; isokay=false;}
else buf=(const char*)MapViewOfFile(hmap,FILE_MAP_READ,0,0,0);
if (buf==0) {CloseHandle(hmap); hmap=0; CloseHandle(hf); hf=0; isokay=false;}
else isokay=true;
}
imemfile::~imemfile()
{ close();
}
void imemfile::seekg(unsigned int apos)
{ if (buf==0 || apos>size) isokay=false; else pos=apos;
}
void imemfile::read(char *dst,unsigned int rsize)
{ if (buf==0 || pos+rsize>size) isokay=false;
else {memcpy(dst,buf+pos,rsize); pos+=rsize;}
}
bool imemfile::okay()
{ return isokay;
}
void imemfile::close()
{ if (buf!=0) UnmapViewOfFile(buf); buf=0;
if (hmap!=0) CloseHandle(hmap); hmap=0;
if (hf!=0) CloseHandle(hf); hf=0;
size=0; pos=0; isokay=false;
}
// IsValidDbxFile: to tell whether a given file is a valid DBX file
//
bool IsValidDbxFile(const string fn,bool wantfolders)
{ HANDLE hf = CreateFile(fn.c_str(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
if (hf==INVALID_HANDLE_VALUE) return false;
DWORD high; DWORD size=GetFileSize(hf,&high);
CloseHandle(hf);
if (high!=0 || size<FileHeaderSize) return false;
bool okay=false;
imemfile inm(fn.c_str());
try
{ DbxFileHeader fileHeader(inm);
int4 length = fileHeader.GetValue(fhFileInfoLength);
int4 address = FileHeaderSize;
if (length+FileHeaderSize<=size)
{ DbxFileInfo inf(inm,address,length);
if (wantfolders) okay=fileHeader.isFolders();
else okay=!fileHeader.isFolders();
}
}
catch (...)
{
}
inm.close();
return okay;
}
bool IsValidDbxFolderFile(const string fn) {return IsValidDbxFile(fn,true);}
bool IsValidDbxMessageFile(const string fn) {return IsValidDbxFile(fn,false);}
#include <windows.h>
#include <string>
#include <list>
#include <vector>
using namespace std;
#pragma hdrstop // precompiled headers stop here
#include "dbx_utilsh"
vector<TDbxStore> dbx_Stores;
void dbx_EnsureStores()
{ dbx_Stores.clear();
// We'll build up a list of things in
// HKEY_CURRENT_USER\Identities\<id>\Software\Microsoft\Outlook Express\<ver>
// Except we start the default identity first, and the rest in order;
// and we store the versions in descending order.
// So: MainIdentity\5.0
// MainIdentity\4.0
// SecondIdentity\5.0
// SecondIdentity\4.0
// Incidentally, if multiple different versions all point to the same store
// folder, then we collapse them, and only display the most recent version.
HKEY key; LONG res;
res = RegOpenKeyEx(HKEY_CURRENT_USER,"Identities",0,KEY_READ,&key);
if (res!=ERROR_SUCCESS) return;
list<string> ids = RegQuerySubkeys(key);
string defid = RegQueryString(key,"Default User ID");
RegCloseKey(key);
if (ids.size()==0) return;
//
for (int pas=0; pass<2; pass++)
{ // in pass=0 we add only the default id.
// in pass=1 we add all other ids
for (list<string>::iterator i=ids.begin(); i!=ids.end(); i++)
{ string id = *i; string name; string s; list<string> vers; bool okay = true;
if (okay)
{ if (pass==0 && id!=defid) okay=false;
else if (pass==1 && id==defid) okay=false;
}
if (okay)
{ res = RegOpenKeyEx(HKEY_CURRENT_USER,("Identities\\"+id).c_str(),0,KEY_READ,&key);
if (res!=ERROR_SUCCESS) okay=false;
else {name = RegQueryString(key,"Username"); RegCloseKey(key);}
if (name=="") okay=false;
}
if (okay)
{ s = "Identities\\"+id+"\\Software\\Microsoft\\Outlook Express";
res = RegOpenKeyEx(HKEY_CURRENT_USER,s.c_str(),0,KEY_READ,&key);
if (res!=ERROR_SUCCESS) okay=false;
else {vers = RegQuerySubkeys(key); RegCloseKey(key);}
}
for (list<string>::reverse_iterator j=vers.rbegin(); okay && j!=vers.rend(); j++)
{ string ver = *j; string path="";
res = RegOpenKeyEx(HKEY_CURRENT_USER,(s+"\\"+ver).c_str(),0,KEY_READ,&key);
if (res==ERROR_SUCCESS) {path = RemoveTrailingSlash(RegQueryString(key,"Store Root")); RegCloseKey(key);}
if (path!="")
{ // If different versions of OE are installed, but all point to the same location,
// then we won't add them
bool alreadythere=false;
if (dbx_Stores.size()>0)
{ TDbxStore &prevdbx = dbx_Stores.back();
if (prevdbx.id==id && StringLower(prevdbx.path)==StringLower(path)) alreadythere=true;
}
if (!alreadythere && FileExists(path+"\\Folders.dbx"))
{ TDbxStore dbxs;
dbxs.id = id;
dbxs.name = name;
dbxs.ver = ver;
dbxs.path = path;
dbx_Stores.push_back(dbxs);
}
}
}
}
}
}
vector<TDbxFolderInfo> dbx_Folders;
string dbx_DoneFoldersName, dbx_DoneFoldersVer;
// This 'TDbxFolderSorting' is used to sort the folders we retrieve
// into an order approximating that of Outlook Express
class TDbxFolderSorting
{ public:
unsigned int index;
bool isexplicitpos; unsigned int position;
TDbxFolderInfo info;
vector<TDbxFolderSorting*> c;
TDbxFolderSorting *parent;
};
bool comp(const TDbxFolderSorting *a,const TDbxFolderSorting *b)
{ if (b==NULL) return true; else if (a==NULL) return false;
if (a->isexplicitpos && !b->isexplicitpos) return true; else if (!a->isexplicitpos && b->isexplicitpos) return false;
if (a->position<b->position) return true; else if (a->position>b->position) return false;
return true;
}
// RecEnsureFolders -- this function recursively deletes the
// FolderSorting, and at the same time copies bits into the dbx_Folders
// global vector
void RecEnsureFolders(vector<TDbxFolderSorting*> &s,bool addit)
{ sort(s.begin(), s.end(), comp);
for (vector<TDbxFolderSorting*>::iterator i=s.begin(); i!=s.end(); i++)
{ TDbxFolderSorting *f = *i;
bool laddit=addit;
if (f->info.type==dftOutbox || f->info.type==dftDeleted || f->info.type==dftDrafts) laddit=false;
if (laddit) dbx_Folders.push_back(f->info);
RecEnsureFolders(f->c,laddit); // we need to call it recursively, even if !addit, since that will also delete stuff
delete f;
}
}
#define dbx_utilsH
// dbx_EnsureStores: call this function to build up the global
// variable 'dbx_Stores', which is a vector containing all the
// Outlook Express identities on this machine.
void dbx_EnsureStores();
// dbx_EnsureFolders: call this to build up the global
// variable 'dbx_Folders', a vector containing all the
// folders for a given OE idententy
void dbx_EnsureFolders(const string name,const string ver);
// These functions test whether a file is a valid dbx file
bool IsValidDbxFolderFile(const string fn);
bool IsValidDbxMessageFile(const string fn);
enum TDbxFolderType {dftStructure, dftInbox, dftOutbox, dftSent, dftDeleted, dftDrafts, dftMail};
typedef struct
{ string id; // e.g. {CAA16-...}
string name; // the username, e.g. "Main Identity"
string ver; // the version of Outlook Express, e.g. "5.0"
string path; // the path to the store root, e.g. "c:\\store"
} TDbxStore;
typedef struct
{ string name; // e.g. "Joseph"
string path; // e.g. "Friends"
string account; // e.g. "Cambridge email";
string full; // e.g. "Cambridge Email\\Friends\\Joseph"
string fn; // e.g. "cambridge email - inbox.dbx"
string fpath; // e.g. "c:\\localmessagestore"
unsigned int depth; // e.g. 2 (Cam.Email=0, Friends=1, Joseph=2)
TDbxFolderType type; // see the above enumeration
bool might_contain_offlines; // just a hint
DWORD filesize; // this gets set at a later time, during processing
} TDbxFolderInfo;
bool operator <(const TDbxFolderInfo &a,const TDbxFolderInfo &b);
extern vector<TDbxStore> dbx_Stores;
extern vector<TDbxFolderInfo> dbx_Folders;
// imemfile: this is similar to the STL 'istream' class, but
// is for Windows and is for memory-mapping a read-only file.
// I tweaked Arne's dbx code a little, to make it use these
// memory-mapped files, for an appreciable speed gain.
//
class imemfile
{ public:
const char *buf; DWORD size;
imemfile(const char *fn);
~imemfile();
void close();
void seekg(unsigned int apos);
void read(char *dst, unsigned int size);
bool okay();
protected:
unsigned int pos;
bool isokay;
HANDLE hf;
HANDLE hmap;
};