From 9126d2b133d00b836fca640f847a0948f7579e02 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Mon, 23 Jul 2012 06:41:38 +0000 Subject: Db3x_mmap,Db_autobackups: changed folder structure git-svn-id: http://svn.miranda-ng.org/main/trunk@1107 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/Db3x_mmap/src/commonheaders.h | 62 +++ plugins/Db3x_mmap/src/database.cpp | 121 +++++ plugins/Db3x_mmap/src/database.h | 70 +++ plugins/Db3x_mmap/src/dbcache.cpp | 175 ++++++ plugins/Db3x_mmap/src/dbcontacts.cpp | 244 +++++++++ plugins/Db3x_mmap/src/dbevents.cpp | 355 ++++++++++++ plugins/Db3x_mmap/src/dbheaders.cpp | 65 +++ plugins/Db3x_mmap/src/dbintf.cpp | 174 ++++++ plugins/Db3x_mmap/src/dbintf.h | 289 ++++++++++ plugins/Db3x_mmap/src/dbmodulechain.cpp | 138 +++++ plugins/Db3x_mmap/src/dbsettings.cpp | 933 ++++++++++++++++++++++++++++++++ plugins/Db3x_mmap/src/init.cpp | 160 ++++++ plugins/Db3x_mmap/src/resource.h | 30 + plugins/Db3x_mmap/src/version.h | 5 + 14 files changed, 2821 insertions(+) create mode 100644 plugins/Db3x_mmap/src/commonheaders.h create mode 100644 plugins/Db3x_mmap/src/database.cpp create mode 100644 plugins/Db3x_mmap/src/database.h create mode 100644 plugins/Db3x_mmap/src/dbcache.cpp create mode 100644 plugins/Db3x_mmap/src/dbcontacts.cpp create mode 100644 plugins/Db3x_mmap/src/dbevents.cpp create mode 100644 plugins/Db3x_mmap/src/dbheaders.cpp create mode 100644 plugins/Db3x_mmap/src/dbintf.cpp create mode 100644 plugins/Db3x_mmap/src/dbintf.h create mode 100644 plugins/Db3x_mmap/src/dbmodulechain.cpp create mode 100644 plugins/Db3x_mmap/src/dbsettings.cpp create mode 100644 plugins/Db3x_mmap/src/init.cpp create mode 100644 plugins/Db3x_mmap/src/resource.h create mode 100644 plugins/Db3x_mmap/src/version.h (limited to 'plugins/Db3x_mmap/src') diff --git a/plugins/Db3x_mmap/src/commonheaders.h b/plugins/Db3x_mmap/src/commonheaders.h new file mode 100644 index 0000000000..ea615956e1 --- /dev/null +++ b/plugins/Db3x_mmap/src/commonheaders.h @@ -0,0 +1,62 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#define _CRT_SECURE_NO_WARNINGS +#define _WIN32_WINNT 0x0501 + +#include "m_stdhdr.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef __GNUC__ +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "database.h" +#include "dbintf.h" +#include "resource.h" +#include "version.h" + +extern LIST g_Dbs; + +#ifdef __GNUC__ +#define mir_i64(x) (x##LL) +#else +#define mir_i64(x) (x##i64) +#endif diff --git a/plugins/Db3x_mmap/src/database.cpp b/plugins/Db3x_mmap/src/database.cpp new file mode 100644 index 0000000000..1962805a35 --- /dev/null +++ b/plugins/Db3x_mmap/src/database.cpp @@ -0,0 +1,121 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +int InitModuleNames(void); +int InitCache(void); +int InitIni(void); +void UninitIni(void); + +DWORD CDdxMmap::CreateNewSpace(int bytes) +{ + DWORD ofsNew = m_dbHeader.ofsFileEnd; + m_dbHeader.ofsFileEnd += bytes; + DBWrite(0, &m_dbHeader, sizeof(m_dbHeader)); + log2("newspace %d@%08x", bytes, ofsNew); + return ofsNew; +} + +void CDdxMmap::DeleteSpace(DWORD ofs, int bytes) +{ + if (ofs+bytes == m_dbHeader.ofsFileEnd) { + log2("freespace %d@%08x",bytes,ofs); + m_dbHeader.ofsFileEnd = ofs; + } + else { + log2("deletespace %d@%08x",bytes,ofs); + m_dbHeader.slackSpace += bytes; + } + DBWrite(0, &m_dbHeader, sizeof(m_dbHeader)); + DBFill(ofs, bytes); +} + +DWORD CDdxMmap::ReallocSpace(DWORD ofs, int oldSize, int newSize) +{ + if (oldSize >= newSize) + return ofs; + + DWORD ofsNew; + if (ofs+oldSize == m_dbHeader.ofsFileEnd) { + ofsNew = ofs; + m_dbHeader.ofsFileEnd += newSize-oldSize; + DBWrite(0,&m_dbHeader,sizeof(m_dbHeader)); + log3("adding newspace %d@%08x+%d", newSize, ofsNew, oldSize); + } + else { + ofsNew = CreateNewSpace(newSize); + DBMoveChunk(ofsNew,ofs,oldSize); + DeleteSpace(ofs,oldSize); + } + return ofsNew; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static DWORD DatabaseCorrupted = 0; +static TCHAR *msg = NULL; +static DWORD dwErr = 0; + +void __cdecl dbpanic(void *arg) +{ + if (msg) + { + TCHAR err[256]; + + if (dwErr == ERROR_DISK_FULL) + msg = TranslateT("Disk is full. Miranda will now shutdown."); + + mir_sntprintf(err, SIZEOF(err), msg, TranslateT("Database failure. Miranda will now shutdown."), dwErr); + + MessageBox(0,err,TranslateT("Database Error"),MB_SETFOREGROUND|MB_TOPMOST|MB_APPLMODAL|MB_ICONWARNING|MB_OK); + } + else + MessageBox(0,TranslateT("Miranda has detected corruption in your database. This corruption maybe fixed by DBTool. Please download it from http://nightly.miranda.im/. Miranda will now shutdown."), + TranslateT("Database Panic"),MB_SETFOREGROUND|MB_TOPMOST|MB_APPLMODAL|MB_ICONWARNING|MB_OK); + TerminateProcess(GetCurrentProcess(),255); +} + +void CDdxMmap::DatabaseCorruption(TCHAR *text) +{ + int kill = 0; + + EnterCriticalSection(&m_csDbAccess); + if (DatabaseCorrupted == 0) { + DatabaseCorrupted++; + kill++; + msg = text; + dwErr = GetLastError(); + } else { + /* db is already corrupted, someone else is dealing with it, wait here + so that we don't do any more damage */ + LeaveCriticalSection(&m_csDbAccess); + Sleep(INFINITE); + return; + } + LeaveCriticalSection(&m_csDbAccess); + if (kill) { + _beginthread(dbpanic,0,NULL); + Sleep(INFINITE); + } +} diff --git a/plugins/Db3x_mmap/src/database.h b/plugins/Db3x_mmap/src/database.h new file mode 100644 index 0000000000..83c2fcaea9 --- /dev/null +++ b/plugins/Db3x_mmap/src/database.h @@ -0,0 +1,70 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + + +//all offsets are relative to the start of the file +//offsets are 0 if there is nothing in the chain or this is the last in the +//chain + +/* tree diagram + +DBHeader + |-->end of file (plain offset) + |-->first contact (DBContact) + | |-->next contact (DBContact) + | | \--> ... + | |-->first settings (DBContactSettings) + | | |-->next settings (DBContactSettings) + | | | \--> ... + | | \-->module name (DBModuleName) + | \-->first/last/firstunread event + |-->user contact (DBContact) + | |-->next contact = NULL + | |-->first settings as above + | \-->first/last/firstunread event as above + \-->first module name (DBModuleName) + \-->next module name (DBModuleName) + \--> ... +*/ + +//#define DBLOGGING + +#ifdef _DEBUG +//#define DBLOGGING +#endif +#ifdef DBLOGGING +void DBLog(const char *file,int line,const char *fmt,...); +#define logg() DBLog(__FILE__,__LINE__,"") +#define log0(s) DBLog(__FILE__,__LINE__,s) +#define log1(s,a) DBLog(__FILE__,__LINE__,s,a) +#define log2(s,a,b) DBLog(__FILE__,__LINE__,s,a,b) +#define log3(s,a,b,c) DBLog(__FILE__,__LINE__,s,a,b,c) +#define log4(s,a,b,c,d) DBLog(__FILE__,__LINE__,s,a,b,c,d) +#else +#define logg() +#define log0(s) +#define log1(s,a) +#define log2(s,a,b) +#define log3(s,a,b,c) +#define log4(s,a,b,c,d) +#endif diff --git a/plugins/Db3x_mmap/src/dbcache.cpp b/plugins/Db3x_mmap/src/dbcache.cpp new file mode 100644 index 0000000000..466aa7a00d --- /dev/null +++ b/plugins/Db3x_mmap/src/dbcache.cpp @@ -0,0 +1,175 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +void CDdxMmap::Map() +{ + m_hMap = CreateFileMapping(m_hDbFile, NULL, PAGE_READWRITE, 0, m_dwFileSize, NULL); + + if (m_hMap) + { + m_pDbCache = (PBYTE)MapViewOfFile(m_hMap, FILE_MAP_ALL_ACCESS/*FILE_MAP_WRITE*/, 0, 0 ,0); + if (!m_pDbCache) + DatabaseCorruption( _T("%s (MapViewOfFile failed. Code: %d)")); + } + else + DatabaseCorruption( _T("%s (CreateFileMapping failed. Code: %d)")); +} + +void CDdxMmap::ReMap(DWORD needed) +{ + KillTimer(NULL,m_flushBuffersTimerId); + + log3("remapping %d + %d (file end: %d)",m_dwFileSize,needed,m_dbHeader.ofsFileEnd); + + if (needed > m_ChunkSize) + { + if (needed + m_dwFileSize > m_dbHeader.ofsFileEnd + m_ChunkSize) + DatabaseCorruption( _T("%s (Too large increment)")); + else + { + DWORD x = m_dbHeader.ofsFileEnd/m_ChunkSize; + m_dwFileSize = (x+1)*m_ChunkSize; + } + } + else + m_dwFileSize += m_ChunkSize; + +// FlushViewOfFile(m_pDbCache, 0); + UnmapViewOfFile(m_pDbCache); + m_pDbCache = NULL; + CloseHandle(m_hMap); + + Map(); +} + +void CDdxMmap::DBMoveChunk(DWORD ofsDest,DWORD ofsSource,int bytes) +{ + int x = 0; + log3("move %d %08x->%08x",bytes,ofsSource,ofsDest); + if (ofsDest+bytes > m_dwFileSize) ReMap(ofsDest+bytes-m_dwFileSize); + if (ofsSource+bytes > m_dwFileSize) { + x = ofsSource+bytes-m_dwFileSize; + log0("buggy move!"); + _ASSERT(0); + } + if (x > 0) + ZeroMemory(m_pDbCache+ofsDest+bytes-x, x); + if (ofsSource < m_dwFileSize) + MoveMemory(m_pDbCache+ofsDest,m_pDbCache+ofsSource, bytes-x); + + logg(); +} + +//we are assumed to be in a mutex here +PBYTE CDdxMmap::DBRead(DWORD ofs,int bytesRequired,int *bytesAvail) +{ + // buggy read + if (ofs>= m_dwFileSize) { + log2("read from outside %d@%08x",bytesRequired,ofs); + if (bytesAvail != NULL) *bytesAvail = m_ChunkSize; + return m_pNull; + } + log3((ofs+bytesRequired > m_dwFileSize)?"read %d@%08x, only %d avaliable":"read %d@%08x",bytesRequired,ofs,m_dwFileSize-ofs); + if (bytesAvail != NULL) *bytesAvail = m_dwFileSize - ofs; + return m_pDbCache+ofs; +} + +//we are assumed to be in a mutex here +void CDdxMmap::DBWrite(DWORD ofs,PVOID pData,int bytes) +{ + log2("write %d@%08x",bytes,ofs); + if (ofs+bytes > m_dwFileSize) ReMap(ofs+bytes-m_dwFileSize); + MoveMemory(m_pDbCache+ofs,pData,bytes); + logg(); +} + +//we are assumed to be in a mutex here +void CDdxMmap::DBFill(DWORD ofs,int bytes) +{ + log2("zerofill %d@%08x",bytes,ofs); + if (ofs+bytes <= m_dwFileSize) + ZeroMemory(m_pDbCache+ofs,bytes); + logg(); +} + +static VOID CALLBACK DoBufferFlushTimerProc(HWND hwnd, UINT message, UINT_PTR idEvent, DWORD dwTime) +{ + for (int i=0; i < g_Dbs.getCount(); i++) { + CDdxMmap* db = g_Dbs[i]; + if (db->m_flushBuffersTimerId != idEvent) + continue; + + if (!db->m_pDbCache) + return; + + KillTimer(NULL, db->m_flushBuffersTimerId); + log0("tflush1"); + if (FlushViewOfFile(db->m_pDbCache, 0) == 0) { + if (db->m_flushFailTick == 0) + db->m_flushFailTick = GetTickCount(); + else if (GetTickCount() - db->m_flushFailTick > 5000) + db->DatabaseCorruption(NULL); + } + else db->m_flushFailTick = 0; + log0("tflush2"); + } +} + +void CDdxMmap::DBFlush(int setting) +{ + if (!setting) { + log0("nflush1"); + if (m_safetyMode && m_pDbCache) { + if (FlushViewOfFile(m_pDbCache, 0) == 0) { + if (m_flushFailTick == 0) + m_flushFailTick = GetTickCount(); + else if (GetTickCount() - m_flushFailTick > 5000) + DatabaseCorruption(NULL); + } + else + m_flushFailTick = 0; + } + log0("nflush2"); + return; + } + KillTimer(NULL, m_flushBuffersTimerId); + m_flushBuffersTimerId = SetTimer(NULL, m_flushBuffersTimerId, 50, DoBufferFlushTimerProc); +} + +int CDdxMmap::InitCache(void) +{ + m_dwFileSize = GetFileSize(m_hDbFile, NULL); + + // Align to chunk + DWORD x = m_dwFileSize % m_ChunkSize; + if (x) + m_dwFileSize += m_ChunkSize - x; + + Map(); + + // zero region for reads outside the file + m_pNull = (PBYTE)calloc(m_ChunkSize, 1); + return 0; +} diff --git a/plugins/Db3x_mmap/src/dbcontacts.cpp b/plugins/Db3x_mmap/src/dbcontacts.cpp new file mode 100644 index 0000000000..b8d4a843b8 --- /dev/null +++ b/plugins/Db3x_mmap/src/dbcontacts.cpp @@ -0,0 +1,244 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +DBCachedContactValueList* CDdxMmap::AddToCachedContactList(HANDLE hContact, int index) +{ + DBCachedContactValueList* VL = (DBCachedContactValueList*)HeapAlloc(m_hCacheHeap,HEAP_ZERO_MEMORY,sizeof(DBCachedContactValueList)); + VL->hContact = hContact; + if (index == -1) + m_lContacts.insert(VL); + else + m_lContacts.insert(VL,index); + return VL; +} + +#define proto_module "Protocol" +#define proto_setting "p" + +int CDdxMmap::CheckProto(HANDLE hContact, const char *proto) +{ + char protobuf[MAX_PATH] = {0}; + DBVARIANT dbv; + DBCONTACTGETSETTING sVal = {proto_module,proto_setting,&dbv}; + + dbv.type = DBVT_ASCIIZ; + dbv.pszVal = protobuf; + dbv.cchVal = sizeof(protobuf); + + if (GetContactSettingStatic(hContact, &sVal) != 0 || (dbv.type != DBVT_ASCIIZ)) + return 0; + + return !strcmp(protobuf,proto); +} + +STDMETHODIMP_(LONG) CDdxMmap::GetContactCount(void) +{ + mir_cslock lck(m_csDbAccess); + return m_dbHeader.contactCount; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::FindFirstContact(const char *szProto) +{ + mir_cslock lck(m_csDbAccess); + HANDLE ret = (HANDLE)m_dbHeader.ofsFirstContact; + if (szProto && !CheckProto(ret, szProto)) + ret = FindNextContact(ret, szProto); + return ret; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::FindNextContact(HANDLE hContact, const char *szProto) +{ + int index; + DBContact *dbc; + DBCachedContactValueList VLtemp, *VL = NULL; + VLtemp.hContact = hContact; + + mir_cslock lck(m_csDbAccess); + while (VLtemp.hContact) { + if (( index = m_lContacts.getIndex(&VLtemp)) != -1) { + VL = m_lContacts[index]; + if (VL->hNext != NULL) { + if (!szProto || CheckProto(VL->hNext, szProto)) + return VL->hNext; + + VLtemp.hContact = VL->hNext; + continue; + } } + + dbc = (DBContact*)DBRead(VLtemp.hContact,sizeof(DBContact),NULL); + if (dbc->signature != DBCONTACT_SIGNATURE) + break; + + if ( VL == NULL ) + VL = AddToCachedContactList(VLtemp.hContact,index); + + VL->hNext = (HANDLE)dbc->ofsNext; + if (VL->hNext != NULL && (!szProto || CheckProto(VL->hNext, szProto))) + return VL->hNext; + + VLtemp.hContact = VL->hNext; + } + + return NULL; +} + +STDMETHODIMP_(LONG) CDdxMmap::DeleteContact(HANDLE hContact) +{ + if (hContact == NULL) + return 1; + + mir_cslockfull lck(m_csDbAccess); + DBContact *dbc = (DBContact*)DBRead(hContact, sizeof(DBContact), NULL); + if (dbc->signature != DBCONTACT_SIGNATURE) + return 1; + + if (hContact == (HANDLE)m_dbHeader.ofsUser) { + log0("FATAL: del of user chain attempted."); + return 1; + } + + lck.unlock(); + log0("del contact"); + + // call notifier while outside mutex + NotifyEventHooks(hContactDeletedEvent, (WPARAM)hContact, 0); + + // get back in + lck.lock(); + + DBCachedContactValueList VLtemp; + VLtemp.hContact = hContact; + int index; + if ((index = m_lContacts.getIndex(&VLtemp)) != -1) { + DBCachedContactValueList *VL = m_lContacts[index]; + DBCachedContactValue* V = VL->first; + while ( V != NULL ) { + DBCachedContactValue* V1 = V->next; + FreeCachedVariant(&V->value); + HeapFree( m_hCacheHeap, 0, V ); + V = V1; + } + HeapFree( m_hCacheHeap, 0, VL ); + + if (VLtemp.hContact == m_hLastCachedContact) + m_hLastCachedContact = NULL; + m_lContacts.remove(index); + } + + dbc = (DBContact*)DBRead(hContact, sizeof(DBContact), NULL); + //delete settings chain + DWORD ofsThis = dbc->ofsFirstSettings; + DWORD ofsFirstEvent = dbc->ofsFirstEvent; + while (ofsThis) { + DBContactSettings *dbcs = (DBContactSettings*)DBRead(ofsThis,sizeof(DBContactSettings),NULL); + DWORD ofsNext = dbcs->ofsNext; + DeleteSpace(ofsThis,offsetof(DBContactSettings,blob)+dbcs->cbBlob); + ofsThis = ofsNext; + } + //delete event chain + ofsThis = ofsFirstEvent; + while (ofsThis) { + DBEvent *dbe = (DBEvent*)DBRead(ofsThis,sizeof(DBEvent),NULL); + DWORD ofsNext = dbe->ofsNext; + DeleteSpace(ofsThis,offsetof(DBEvent,blob)+dbe->cbBlob); + ofsThis = ofsNext; + } + //find previous contact in chain and change ofsNext + dbc = (DBContact*)DBRead(hContact, sizeof(DBContact), NULL); + if (m_dbHeader.ofsFirstContact == (DWORD)hContact) { + m_dbHeader.ofsFirstContact = dbc->ofsNext; + DBWrite(0,&m_dbHeader,sizeof(m_dbHeader)); + } + else { + DWORD ofsNext = dbc->ofsNext; + ofsThis = m_dbHeader.ofsFirstContact; + DBContact *dbcPrev = (DBContact*)DBRead(ofsThis,sizeof(DBContact),NULL); + while (dbcPrev->ofsNext != (DWORD)hContact) { + if (dbcPrev->ofsNext == 0) DatabaseCorruption(NULL); + ofsThis = dbcPrev->ofsNext; + dbcPrev = (DBContact*)DBRead(ofsThis,sizeof(DBContact),NULL); + } + dbcPrev->ofsNext = ofsNext; + DBWrite(ofsThis,dbcPrev,sizeof(DBContact)); + + DBCachedContactValueList VLtemp; + VLtemp.hContact = (HANDLE)ofsThis; + if ((index = m_lContacts.getIndex(&VLtemp)) != -1) { + DBCachedContactValueList *VL = m_lContacts[index]; + VL->hNext = (HANDLE)ofsNext; + } + } + + //delete contact + DeleteSpace((DWORD)hContact, sizeof(DBContact)); + //decrement contact count + m_dbHeader.contactCount--; + DBWrite(0,&m_dbHeader,sizeof(m_dbHeader)); + DBFlush(0); + return 0; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::AddContact() +{ + DWORD ofsNew; + log0("add contact"); + { + mir_cslock lck(m_csDbAccess); + ofsNew = CreateNewSpace(sizeof(DBContact)); + + DBContact dbc = { 0 }; + dbc.signature = DBCONTACT_SIGNATURE; + dbc.ofsNext = m_dbHeader.ofsFirstContact; + m_dbHeader.ofsFirstContact = ofsNew; + m_dbHeader.contactCount++; + DBWrite(ofsNew,&dbc,sizeof(DBContact)); + DBWrite(0,&m_dbHeader,sizeof(m_dbHeader)); + DBFlush(0); + + AddToCachedContactList((HANDLE)ofsNew, -1); + } + + NotifyEventHooks(hContactAddedEvent,(WPARAM)ofsNew,0); + return (HANDLE)ofsNew; +} + +STDMETHODIMP_(BOOL) CDdxMmap::IsDbContact(HANDLE hContact) +{ + mir_cslock lck(m_csDbAccess); + + DBCachedContactValueList VLtemp; + VLtemp.hContact = hContact; + int index = m_lContacts.getIndex(&VLtemp); + if (index != -1) + return TRUE; + + DBContact *dbc = (DBContact*)DBRead(hContact,sizeof(DBContact),NULL); + if (dbc->signature == DBCONTACT_SIGNATURE) { + AddToCachedContactList(hContact, index); + return TRUE; + } + + return FALSE; +} diff --git a/plugins/Db3x_mmap/src/dbevents.cpp b/plugins/Db3x_mmap/src/dbevents.cpp new file mode 100644 index 0000000000..917d1680b2 --- /dev/null +++ b/plugins/Db3x_mmap/src/dbevents.cpp @@ -0,0 +1,355 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +extern BOOL m_safetyMode; + +DWORD GetModuleNameOfs(const char *szName); +char *GetModuleNameByOfs(DWORD ofs); + +static HANDLE hEventDeletedEvent,hEventAddedEvent,hEventFilterAddedEvent; + +STDMETHODIMP_(LONG) CDdxMmap::GetEventCount(HANDLE hContact) +{ + mir_cslock lck(m_csDbAccess); + if (hContact == 0) + hContact = (HANDLE)m_dbHeader.ofsUser; + + DBContact *dbc = (DBContact*)DBRead(hContact,sizeof(DBContact),NULL); + return (dbc->signature != DBCONTACT_SIGNATURE) ? -1 : dbc->eventCount; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::AddEvent(HANDLE hContact, DBEVENTINFO *dbei) +{ + if (dbei == NULL || dbei->cbSize != sizeof(DBEVENTINFO)) return 0; + if (dbei->timestamp == 0) return 0; + if (NotifyEventHooks(hEventFilterAddedEvent, (WPARAM)hContact, (LPARAM)dbei)) + return 0; + + BOOL neednotify; + mir_cslockfull lck(m_csDbAccess); + + DWORD ofsContact = (hContact == 0) ? m_dbHeader.ofsUser : (DWORD)hContact; + DBContact dbc = *(DBContact*)DBRead(ofsContact,sizeof(DBContact),NULL); + if (dbc.signature != DBCONTACT_SIGNATURE) + return 0; + + DWORD ofsNew = CreateNewSpace(offsetof(DBEvent,blob) + dbei->cbBlob); + DWORD ofsModuleName = GetModuleNameOfs(dbei->szModule); + + DBEvent dbe; + dbe.signature = DBEVENT_SIGNATURE; + dbe.ofsModuleName = ofsModuleName; + dbe.timestamp = dbei->timestamp; + dbe.flags = dbei->flags; + dbe.eventType = dbei->eventType; + dbe.cbBlob = dbei->cbBlob; + //find where to put it - sort by timestamp + if (dbc.eventCount == 0) { + dbe.ofsPrev = (DWORD)hContact; + dbe.ofsNext = 0; + dbe.flags |= DBEF_FIRST; + dbc.ofsFirstEvent = dbc.ofsLastEvent = ofsNew; + } + else { + DBEvent *dbeTest = (DBEvent*)DBRead(dbc.ofsFirstEvent,sizeof(DBEvent),NULL); + // Should new event be placed before first event in chain? + if (dbei->timestamp < dbeTest->timestamp) { + dbe.ofsPrev = (DWORD)hContact; + dbe.ofsNext = dbc.ofsFirstEvent; + dbe.flags |= DBEF_FIRST; + dbc.ofsFirstEvent = ofsNew; + dbeTest = (DBEvent*)DBRead(dbe.ofsNext,sizeof(DBEvent),NULL); + dbeTest->flags &= ~DBEF_FIRST; + dbeTest->ofsPrev = ofsNew; + DBWrite(dbe.ofsNext,dbeTest,sizeof(DBEvent)); + } + else { + // Loop through the chain, starting at the end + DWORD ofsThis = dbc.ofsLastEvent; + dbeTest = (DBEvent*)DBRead(ofsThis, sizeof(DBEvent), NULL); + for (;;) { + // If the new event's timesstamp is equal to or greater than the + // current dbevent, it will be inserted after. If not, continue + // with the previous dbevent in chain. + if (dbe.timestamp >= dbeTest->timestamp) { + dbe.ofsPrev = ofsThis; + dbe.ofsNext = dbeTest->ofsNext; + dbeTest->ofsNext = ofsNew; + DBWrite(ofsThis, dbeTest, sizeof(DBEvent)); + if (dbe.ofsNext == 0) + dbc.ofsLastEvent = ofsNew; + else { + dbeTest = (DBEvent*)DBRead(dbe.ofsNext, sizeof(DBEvent), NULL); + dbeTest->ofsPrev = ofsNew; + DBWrite(dbe.ofsNext, dbeTest, sizeof(DBEvent)); + } + break; + } + ofsThis = dbeTest->ofsPrev; + dbeTest = (DBEvent*)DBRead(ofsThis, sizeof(DBEvent), NULL); + } + } + } + dbc.eventCount++; + + if (!(dbe.flags&(DBEF_READ|DBEF_SENT))) { + if (dbe.timestamppBlob,dbei->cbBlob); + DBFlush(0); + lck.unlock(); + + log1("add event @ %08x",ofsNew); + + // Notify only in safe mode or on really new events + if (neednotify) + NotifyEventHooks(hEventAddedEvent, (WPARAM)hContact, (LPARAM)ofsNew); + + return (HANDLE)ofsNew; +} + +STDMETHODIMP_(BOOL) CDdxMmap::DeleteEvent(HANDLE hContact, HANDLE hDbEvent) +{ + mir_cslockfull lck(m_csDbAccess); + + DWORD ofsContact = (hContact == 0) ? m_dbHeader.ofsUser : (DWORD)hContact; + DBContact dbc = *(DBContact*)DBRead(ofsContact, sizeof(DBContact), NULL); + DBEvent dbe = *(DBEvent*)DBRead(hDbEvent, sizeof(DBEvent), NULL); + if (dbc.signature != DBCONTACT_SIGNATURE || dbe.signature != DBEVENT_SIGNATURE) + return 1; + + lck.unlock(); + log1("delete event @ %08x",wParam); + + //call notifier while outside mutex + NotifyEventHooks(hEventDeletedEvent,(WPARAM)hContact, (LPARAM)hDbEvent); + + //get back in + lck.lock(); + dbc = *(DBContact*)DBRead(ofsContact,sizeof(DBContact),NULL); + dbe = *(DBEvent*)DBRead(hDbEvent,sizeof(DBEvent),NULL); + + //check if this was the first unread, if so, recalc the first unread + if (dbc.ofsFirstUnreadEvent == (DWORD)hDbEvent) { + DBEvent *dbeNext = &dbe; + for (;;) { + if (dbeNext->ofsNext == 0) { + dbc.ofsFirstUnreadEvent = 0; + dbc.timestampFirstUnread = 0; + break; + } + DWORD ofsThis = dbeNext->ofsNext; + dbeNext = (DBEvent*)DBRead(ofsThis,sizeof(DBEvent),NULL); + if ( !(dbeNext->flags & (DBEF_READ | DBEF_SENT))) { + dbc.ofsFirstUnreadEvent = ofsThis; + dbc.timestampFirstUnread = dbeNext->timestamp; + break; + } + } + } + + //get previous and next events in chain and change offsets + if (dbe.flags&DBEF_FIRST) { + if (dbe.ofsNext == 0) + dbc.ofsFirstEvent = dbc.ofsLastEvent = 0; + else { + DBEvent *dbeNext = (DBEvent*)DBRead(dbe.ofsNext,sizeof(DBEvent),NULL); + dbeNext->flags |= DBEF_FIRST; + dbeNext->ofsPrev = dbe.ofsPrev; + DBWrite(dbe.ofsNext,dbeNext,sizeof(DBEvent)); + dbc.ofsFirstEvent = dbe.ofsNext; + } + } + else { + if (dbe.ofsNext == 0) { + DBEvent *dbePrev = (DBEvent*)DBRead(dbe.ofsPrev,sizeof(DBEvent),NULL); + dbePrev->ofsNext = 0; + DBWrite(dbe.ofsPrev,dbePrev,sizeof(DBEvent)); + dbc.ofsLastEvent = dbe.ofsPrev; + } + else { + DBEvent *dbePrev = (DBEvent*)DBRead(dbe.ofsPrev,sizeof(DBEvent),NULL); + dbePrev->ofsNext = dbe.ofsNext; + DBWrite(dbe.ofsPrev,dbePrev,sizeof(DBEvent)); + + DBEvent *dbeNext = (DBEvent*)DBRead(dbe.ofsNext,sizeof(DBEvent),NULL); + dbeNext->ofsPrev = dbe.ofsPrev; + DBWrite(dbe.ofsNext,dbeNext,sizeof(DBEvent)); + } + } + //delete event + DeleteSpace((DWORD)hDbEvent, offsetof(DBEvent,blob)+dbe.cbBlob); + //decrement event count + dbc.eventCount--; + DBWrite(ofsContact,&dbc,sizeof(DBContact)); + DBFlush(0); + return 0; +} + +STDMETHODIMP_(LONG) CDdxMmap::GetBlobSize(HANDLE hDbEvent) +{ + mir_cslock lck(m_csDbAccess); + DBEvent *dbe = (DBEvent*)DBRead(hDbEvent, sizeof(DBEvent), NULL); + return (dbe->signature != DBEVENT_SIGNATURE) ? -1 : dbe->cbBlob; +} + +STDMETHODIMP_(BOOL) CDdxMmap::GetEvent(HANDLE hDbEvent, DBEVENTINFO *dbei) +{ + if (dbei == NULL || dbei->cbSize != sizeof(DBEVENTINFO)) return 1; + if (dbei->cbBlob > 0 && dbei->pBlob == NULL) { + dbei->cbBlob = 0; + return 1; + } + + mir_cslock lck(m_csDbAccess); + DBEvent *dbe = (DBEvent*)DBRead(hDbEvent,sizeof(DBEvent),NULL); + if (dbe->signature != DBEVENT_SIGNATURE) + return 1; + + dbei->szModule = GetModuleNameByOfs(dbe->ofsModuleName); + dbei->timestamp = dbe->timestamp; + dbei->flags = dbe->flags; + dbei->eventType = dbe->eventType; + int bytesToCopy = (dbei->cbBlob < dbe->cbBlob) ? dbei->cbBlob : dbe->cbBlob; + dbei->cbBlob = dbe->cbBlob; + if (bytesToCopy && dbei->pBlob) { + for (int i = 0;;i += MAXCACHEDREADSIZE) { + if (bytesToCopy-i <= MAXCACHEDREADSIZE) { + DecodeCopyMemory(dbei->pBlob+i, DBRead(DWORD(hDbEvent)+offsetof(DBEvent,blob)+i,bytesToCopy-i,NULL), bytesToCopy-i); + break; + } + DecodeCopyMemory(dbei->pBlob+i, DBRead(DWORD(hDbEvent)+offsetof(DBEvent,blob)+i, MAXCACHEDREADSIZE, NULL), MAXCACHEDREADSIZE); + } + } + return 0; +} + +STDMETHODIMP_(BOOL) CDdxMmap::MarkEventRead(HANDLE hContact, HANDLE hDbEvent) +{ + DBEvent *dbe; + DBContact dbc; + DWORD ofsThis; + + mir_cslock lck(m_csDbAccess); + if (hContact == 0) + hContact = (HANDLE)m_dbHeader.ofsUser; + dbc = *(DBContact*)DBRead(hContact, sizeof(DBContact), NULL); + dbe = (DBEvent*)DBRead(hDbEvent, sizeof(DBEvent), NULL); + if (dbe->signature != DBEVENT_SIGNATURE || dbc.signature != DBCONTACT_SIGNATURE) + return -1; + + if ((dbe->flags & DBEF_READ) || (dbe->flags & DBEF_SENT)) + return (INT_PTR)dbe->flags; + + log1("mark read @ %08x",wParam); + dbe->flags |= DBEF_READ; + DBWrite((DWORD)hDbEvent,dbe,sizeof(DBEvent)); + BOOL ret = dbe->flags; + if (dbc.ofsFirstUnreadEvent == (DWORD)hDbEvent) { + for (;;) { + if (dbe->ofsNext == 0) { + dbc.ofsFirstUnreadEvent = 0; + dbc.timestampFirstUnread = 0; + break; + } + ofsThis = dbe->ofsNext; + dbe = (DBEvent*)DBRead(ofsThis,sizeof(DBEvent),NULL); + if ( !(dbe->flags & (DBEF_READ | DBEF_SENT))) { + dbc.ofsFirstUnreadEvent = ofsThis; + dbc.timestampFirstUnread = dbe->timestamp; + break; + } + } + } + DBWrite((DWORD)hContact, &dbc, sizeof(DBContact)); + DBFlush(0); + return ret; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::GetEventContact(HANDLE hDbEvent) +{ + mir_cslock lck(m_csDbAccess); + DBEvent *dbe = (DBEvent*)DBRead(hDbEvent,sizeof(DBEvent),NULL); + if (dbe->signature != DBEVENT_SIGNATURE) + return (HANDLE)-1; + + while (!(dbe->flags & DBEF_FIRST)) + dbe = (DBEvent*)DBRead(dbe->ofsPrev,sizeof(DBEvent),NULL); + + return (HANDLE)dbe->ofsPrev; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::FindFirstEvent(HANDLE hContact) +{ + mir_cslock lck(m_csDbAccess); + if (hContact == 0) + hContact = (HANDLE)m_dbHeader.ofsUser; + + DBContact *dbc = (DBContact*)DBRead(hContact, sizeof(DBContact), NULL); + return (dbc->signature != DBCONTACT_SIGNATURE) ? 0 : (HANDLE)dbc->ofsFirstEvent; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::FindFirstUnreadEvent(HANDLE hContact) +{ + mir_cslock lck(m_csDbAccess); + if (hContact == 0) + hContact = (HANDLE)m_dbHeader.ofsUser; + + DBContact *dbc = (DBContact*)DBRead(hContact,sizeof(DBContact),NULL); + return (dbc->signature != DBCONTACT_SIGNATURE) ? 0 : (HANDLE)dbc->ofsFirstUnreadEvent; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::FindLastEvent(HANDLE hContact) +{ + mir_cslock lck(m_csDbAccess); + if (hContact == 0) + hContact = (HANDLE)m_dbHeader.ofsUser; + + DBContact *dbc = (DBContact*)DBRead(hContact,sizeof(DBContact),NULL); + return (dbc->signature != DBCONTACT_SIGNATURE) ? 0 : (HANDLE)dbc->ofsLastEvent; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::FindNextEvent(HANDLE hDbEvent) +{ + mir_cslock lck(m_csDbAccess); + DBEvent *dbe = (DBEvent*)DBRead(hDbEvent,sizeof(DBEvent),NULL); + return (dbe->signature != DBEVENT_SIGNATURE) ? 0 : (HANDLE)dbe->ofsNext; +} + +STDMETHODIMP_(HANDLE) CDdxMmap::FindPrevEvent(HANDLE hDbEvent) +{ + mir_cslock lck(m_csDbAccess); + DBEvent *dbe = (DBEvent*)DBRead(hDbEvent,sizeof(DBEvent),NULL); + if (dbe->signature != DBEVENT_SIGNATURE) return 0; + return (dbe->flags & DBEF_FIRST) ? 0 : (HANDLE)dbe->ofsPrev; +} diff --git a/plugins/Db3x_mmap/src/dbheaders.cpp b/plugins/Db3x_mmap/src/dbheaders.cpp new file mode 100644 index 0000000000..3d18a373fa --- /dev/null +++ b/plugins/Db3x_mmap/src/dbheaders.cpp @@ -0,0 +1,65 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +//the cache has not been loaded when these functions are used + +extern DBSignature dbSignature; + +int CDdxMmap::CreateDbHeaders() +{ + DBContact user; + DWORD bytesWritten; + + CopyMemory(m_dbHeader.signature, &dbSignature,sizeof(m_dbHeader.signature)); + m_dbHeader.version = DB_THIS_VERSION; + m_dbHeader.ofsFileEnd = sizeof(struct DBHeader); + m_dbHeader.slackSpace = 0; + m_dbHeader.contactCount = 0; + m_dbHeader.ofsFirstContact = 0; + m_dbHeader.ofsFirstModuleName = 0; + m_dbHeader.ofsUser = 0; + //create user + m_dbHeader.ofsUser = m_dbHeader.ofsFileEnd; + m_dbHeader.ofsFileEnd += sizeof(DBContact); + SetFilePointer(m_hDbFile,0,NULL,FILE_BEGIN); + WriteFile(m_hDbFile,&m_dbHeader,sizeof(m_dbHeader),&bytesWritten,NULL); + user.signature = DBCONTACT_SIGNATURE; + user.ofsNext = 0; + user.ofsFirstSettings = 0; + user.eventCount = 0; + user.ofsFirstEvent = user.ofsLastEvent = 0; + SetFilePointer(m_hDbFile,m_dbHeader.ofsUser,NULL,FILE_BEGIN); + WriteFile(m_hDbFile,&user,sizeof(DBContact),&bytesWritten,NULL); + FlushFileBuffers(m_hDbFile); + return 0; +} + +int CDdxMmap::CheckDbHeaders() +{ + if (memcmp(m_dbHeader.signature, &dbSignature, sizeof(m_dbHeader.signature))) return 1; + if (m_dbHeader.version != DB_THIS_VERSION) return 2; + if (m_dbHeader.ofsUser == 0) return 3; + return 0; +} diff --git a/plugins/Db3x_mmap/src/dbintf.cpp b/plugins/Db3x_mmap/src/dbintf.cpp new file mode 100644 index 0000000000..83ae3aa4ca --- /dev/null +++ b/plugins/Db3x_mmap/src/dbintf.cpp @@ -0,0 +1,174 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +DBSignature dbSignature = {"Miranda ICQ DB",0x1A}; + +static int stringCompare(const char* p1, const char* p2) +{ + return strcmp(p1+1, p2+1); +} + +static int stringCompare2(const char* p1, const char* p2) +{ + return strcmp(p1, p2); +} + +static int compareGlobals(const DBCachedGlobalValue* p1, const DBCachedGlobalValue* p2) +{ + return strcmp(p1->name, p2->name); +} + +static int ModCompare(const ModuleName *mn1, const ModuleName *mn2 ) +{ + return strcmp( mn1->name, mn2->name ); +} + +static int OfsCompare(const ModuleName *mn1, const ModuleName *mn2 ) +{ + return ( mn1->ofs - mn2->ofs ); +} + +CDdxMmap::CDdxMmap(const TCHAR* tszFileName) : + m_hDbFile(INVALID_HANDLE_VALUE), + m_safetyMode(TRUE), + m_lSettings(100, stringCompare), + m_lContacts(50, LIST::FTSortFunc(HandleKeySort)), + m_lGlobalSettings(50, compareGlobals), + m_lResidentSettings(50, stringCompare2), + m_lMods(50, ModCompare), + m_lOfs(50, OfsCompare) +{ + m_tszProfileName = mir_tstrdup(tszFileName); + + InitializeCriticalSection(&m_csDbAccess); + + SYSTEM_INFO sinf; + GetSystemInfo(&sinf); + m_ChunkSize = sinf.dwAllocationGranularity; + + m_codePage = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); + m_hCacheHeap = HeapCreate(0, 0, 0); + m_hModHeap = HeapCreate(0,0,0); +} + +CDdxMmap::~CDdxMmap() +{ + // destroy settings + HeapDestroy(m_hCacheHeap); + m_lContacts.destroy(); + m_lSettings.destroy(); + m_lGlobalSettings.destroy(); + m_lResidentSettings.destroy(); + + // destroy modules + HeapDestroy(m_hModHeap); + m_lMods.destroy(); + m_lOfs.destroy(); + + // destroy map + KillTimer(NULL,m_flushBuffersTimerId); + if (m_pDbCache) { + FlushViewOfFile(m_pDbCache, 0); + UnmapViewOfFile(m_pDbCache); + } + if (m_hMap) + CloseHandle(m_hMap); + if (m_pNull) + free(m_pNull); + + // update profile last modified time + DWORD bytesWritten; + SetFilePointer(m_hDbFile, 0, NULL, FILE_BEGIN); + WriteFile(m_hDbFile, &dbSignature, 1, &bytesWritten, NULL); + CloseHandle(m_hDbFile); + + DeleteCriticalSection(&m_csDbAccess); + + mir_free(m_tszProfileName); +} + +int CDdxMmap::Load(bool bSkipInit) +{ + log0("DB logging running"); + + DWORD dummy = 0; + m_hDbFile = CreateFile(m_tszProfileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL); + if ( m_hDbFile == INVALID_HANDLE_VALUE ) + return 1; + + if ( !ReadFile(m_hDbFile,&m_dbHeader,sizeof(m_dbHeader),&dummy,NULL)) { + CloseHandle(m_hDbFile); + return 1; + } + + if ( !bSkipInit) { + if (InitCache()) return 1; + if (InitModuleNames()) return 1; + + hContactDeletedEvent = CreateHookableEvent(ME_DB_CONTACT_DELETED); + hContactAddedEvent = CreateHookableEvent(ME_DB_CONTACT_ADDED); + hSettingChangeEvent = CreateHookableEvent(ME_DB_CONTACT_SETTINGCHANGED); + + hEventAddedEvent = CreateHookableEvent(ME_DB_EVENT_ADDED); + hEventDeletedEvent = CreateHookableEvent(ME_DB_EVENT_DELETED); + hEventFilterAddedEvent = CreateHookableEvent(ME_DB_EVENT_FILTER_ADD); + } + return 0; +} + +int CDdxMmap::Create() +{ + m_hDbFile = CreateFile(m_tszProfileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); + return ( m_hDbFile == INVALID_HANDLE_VALUE ); +} + +STDMETHODIMP_(void) CDdxMmap::SetCacheSafetyMode(BOOL bIsSet) +{ + { mir_cslock lck(m_csDbAccess); + m_safetyMode = bIsSet; + } + DBFlush(1); +} + +void CDdxMmap::EncodeCopyMemory(void *dst, void *src, size_t size) +{ + MoveMemory(dst, src, size); +} + +void CDdxMmap::DecodeCopyMemory(void *dst, void *src, size_t size) +{ + MoveMemory(dst, src, size); +} + +void CDdxMmap::EncodeDBWrite(DWORD ofs, void *src, int size) +{ + DBWrite(ofs, src, size); +} + +void CDdxMmap::DecodeDBWrite(DWORD ofs, void *src, int size) +{ + DBWrite(ofs, src, size); +} + diff --git a/plugins/Db3x_mmap/src/dbintf.h b/plugins/Db3x_mmap/src/dbintf.h new file mode 100644 index 0000000000..4dff427516 --- /dev/null +++ b/plugins/Db3x_mmap/src/dbintf.h @@ -0,0 +1,289 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include + +/* tree diagram + +DBHeader + |-->end of file (plain offset) + |-->first contact (DBContact) + | |-->next contact (DBContact) + | | \--> ... + | |-->first settings (DBContactSettings) + | | |-->next settings (DBContactSettings) + | | | \--> ... + | | \-->module name (DBModuleName) + | \-->first/last/firstunread event + |-->user contact (DBContact) + | |-->next contact = NULL + | |-->first settings as above + | \-->first/last/firstunread event as above + \-->first module name (DBModuleName) + \-->next module name (DBModuleName) + \--> ... +*/ + +#define DB_THIS_VERSION 0x00000700u +#define DB_SETTINGS_RESIZE_GRANULARITY 128 + +struct DBSignature { + char name[15]; + BYTE eof; +}; + +struct ModuleName +{ + char *name; + DWORD ofs; +}; + +#include +struct DBHeader { + BYTE signature[16]; // 'Miranda ICQ DB',0,26 + DWORD version; //as 4 bytes, ie 1.2.3.10 = 0x0102030a + //this version is 0x00000700 + DWORD ofsFileEnd; //offset of the end of the database - place to write + //new structures + DWORD slackSpace; //a counter of the number of bytes that have been + //wasted so far due to deleting structures and/or + //re-making them at the end. We should compact when + //this gets above a threshold + DWORD contactCount; //number of contacts in the chain,excluding the user + DWORD ofsFirstContact; //offset to first DBContact in the chain + DWORD ofsUser; //offset to DBContact representing the user + DWORD ofsFirstModuleName; //offset to first struct DBModuleName in the chain +}; + +#define DBCONTACT_SIGNATURE 0x43DECADEu + +struct DBContact +{ + DWORD signature; + DWORD ofsNext; //offset to the next contact in the chain. zero if + //this is the 'user' contact or the last contact + //in the chain + DWORD ofsFirstSettings; //offset to the first DBContactSettings in the + //chain for this contact. + DWORD eventCount; //number of events in the chain for this contact + DWORD ofsFirstEvent,ofsLastEvent; //offsets to the first and last DBEvent in + //the chain for this contact + DWORD ofsFirstUnreadEvent; //offset to the first (chronological) unread event + //in the chain, 0 if all are read + DWORD timestampFirstUnread; //timestamp of the event at ofsFirstUnreadEvent +}; + +#define DBMODULENAME_SIGNATURE 0x4DDECADEu +struct DBModuleName +{ + DWORD signature; + DWORD ofsNext; //offset to the next module name in the chain + BYTE cbName; //number of characters in this module name + char name[1]; //name, no nul terminator +}; + +#define DBCONTACTSETTINGS_SIGNATURE 0x53DECADEu +struct DBContactSettings +{ + DWORD signature; + DWORD ofsNext; //offset to the next contactsettings in the chain + DWORD ofsModuleName; //offset to the DBModuleName of the owner of these + //settings + DWORD cbBlob; //size of the blob in bytes. May be larger than the + //actual size for reducing the number of moves + //required using granularity in resizing + BYTE blob[1]; //the blob. a back-to-back sequence of DBSetting + //structs, the last has cbName = 0 +}; + +#define DBEVENT_SIGNATURE 0x45DECADEu +struct DBEvent +{ + DWORD signature; + DWORD ofsPrev,ofsNext; //offset to the previous and next events in the + //chain. Chain is sorted chronologically + DWORD ofsModuleName; //offset to a DBModuleName struct of the name of + //the owner of this event + DWORD timestamp; //seconds since 00:00:00 01/01/1970 + DWORD flags; //see m_database.h, db/event/add + WORD eventType; //module-defined event type + DWORD cbBlob; //number of bytes in the blob + BYTE blob[1]; //the blob. module-defined formatting +}; + +#include + +struct DBCachedGlobalValue +{ + char* name; + DBVARIANT value; +}; + +struct DBCachedContactValue +{ + char* name; + DBVARIANT value; + DBCachedContactValue* next; +}; + +struct DBCachedContactValueList +{ + HANDLE hContact; + HANDLE hNext; + DBCachedContactValue* first; + DBCachedContactValue* last; +}; + +#define MAXCACHEDREADSIZE 65536 + +struct CDdxMmap : public MIDatabase, public MZeroedObject +{ + CDdxMmap(const TCHAR* tszFileName); + ~CDdxMmap(); + + int Load(bool bSkipInit); + int Create(void); + int CreateDbHeaders(); + int CheckDbHeaders(); + void DatabaseCorruption(TCHAR *text); +protected: + STDMETHODIMP_(void) SetCacheSafetyMode(BOOL); + + STDMETHODIMP_(LONG) GetContactCount(void); + STDMETHODIMP_(HANDLE) FindFirstContact(const char* szProto = NULL); + STDMETHODIMP_(HANDLE) FindNextContact(HANDLE hContact, const char* szProto = NULL); + STDMETHODIMP_(LONG) DeleteContact(HANDLE hContact); + STDMETHODIMP_(HANDLE) AddContact(void); + STDMETHODIMP_(BOOL) IsDbContact(HANDLE hContact); + + STDMETHODIMP_(LONG) GetEventCount(HANDLE hContact); + STDMETHODIMP_(HANDLE) AddEvent(HANDLE hContact, DBEVENTINFO *dbe); + STDMETHODIMP_(BOOL) DeleteEvent(HANDLE hContact, HANDLE hDbEvent); + STDMETHODIMP_(LONG) GetBlobSize(HANDLE hDbEvent); + STDMETHODIMP_(BOOL) GetEvent(HANDLE hDbEvent, DBEVENTINFO *dbe); + STDMETHODIMP_(BOOL) MarkEventRead(HANDLE hContact, HANDLE hDbEvent); + STDMETHODIMP_(HANDLE) GetEventContact(HANDLE hDbEvent); + STDMETHODIMP_(HANDLE) FindFirstEvent(HANDLE hContact); + STDMETHODIMP_(HANDLE) FindFirstUnreadEvent(HANDLE hContact); + STDMETHODIMP_(HANDLE) FindLastEvent(HANDLE hContact); + STDMETHODIMP_(HANDLE) FindNextEvent(HANDLE hDbEvent); + STDMETHODIMP_(HANDLE) FindPrevEvent(HANDLE hDbEvent); + + STDMETHODIMP_(BOOL) EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam); + + STDMETHODIMP_(BOOL) GetContactSetting(HANDLE hContact, DBCONTACTGETSETTING *dbcgs); + STDMETHODIMP_(BOOL) GetContactSettingStr(HANDLE hContact, DBCONTACTGETSETTING *dbcgs); + STDMETHODIMP_(BOOL) GetContactSettingStatic(HANDLE hContact, DBCONTACTGETSETTING *dbcgs); + STDMETHODIMP_(BOOL) FreeVariant(DBVARIANT *dbv); + STDMETHODIMP_(BOOL) WriteContactSetting(HANDLE hContact, DBCONTACTWRITESETTING *dbcws); + STDMETHODIMP_(BOOL) DeleteContactSetting(HANDLE hContact, DBCONTACTGETSETTING *dbcgs); + STDMETHODIMP_(BOOL) EnumContactSettings(HANDLE hContact, DBCONTACTENUMSETTINGS* dbces); + STDMETHODIMP_(BOOL) SetSettingResident(BOOL bIsResident, const char *pszSettingName); + STDMETHODIMP_(BOOL) EnumResidentSettings(DBMODULEENUMPROC pFunc, void *pParam); + +protected: + virtual void EncodeCopyMemory(void *dst, void *src, size_t size); + virtual void DecodeCopyMemory(void *dst, void *src, size_t size); + virtual void EncodeDBWrite(DWORD ofs, void *src, int size); + virtual void DecodeDBWrite(DWORD ofs, void *src, int size); + +protected: + TCHAR* m_tszProfileName; + HANDLE m_hDbFile; + DBHeader m_dbHeader; + DWORD m_ChunkSize; + BOOL m_safetyMode; + + //////////////////////////////////////////////////////////////////////////// + // database stuff +public: + UINT_PTR m_flushBuffersTimerId; + DWORD m_flushFailTick; + PBYTE m_pDbCache; +protected: + PBYTE m_pNull; + HANDLE m_hMap; + DWORD m_dwFileSize; + + CRITICAL_SECTION m_csDbAccess; + + int CheckProto(HANDLE hContact, const char *proto); + DWORD CreateNewSpace(int bytes); + void DeleteSpace(DWORD ofs, int bytes); + DWORD ReallocSpace(DWORD ofs, int oldSize, int newSize); + + void Map(); + void ReMap(DWORD needed); + void DBMoveChunk(DWORD ofsDest, DWORD ofsSource, int bytes); + PBYTE DBRead(DWORD ofs, int bytesRequired, int *bytesAvail); + void DBWrite(DWORD ofs, PVOID pData, int bytes); + void DBFill(DWORD ofs, int bytes); + void DBFlush(int setting); + int InitCache(void); + + __forceinline PBYTE DBRead(HANDLE hContact, int bytesRequired, int *bytesAvail) + { return DBRead((DWORD)hContact, bytesRequired, bytesAvail); + } + + //////////////////////////////////////////////////////////////////////////// + // settings + + int m_codePage; + + HANDLE m_hCacheHeap; + HANDLE m_hLastCachedContact; + char* m_lastSetting; + DBCachedContactValueList *m_lastVL; + + LIST m_lContacts; + LIST m_lGlobalSettings; + LIST m_lSettings, m_lResidentSettings; + HANDLE hSettingChangeEvent, hContactDeletedEvent, hContactAddedEvent; + + DWORD GetSettingsGroupOfsByModuleNameOfs(DBContact *dbc,DWORD ofsModuleName); + char* InsertCachedSetting(const char* szName, size_t cbNameLen); + char* GetCachedSetting(const char *szModuleName,const char *szSettingName, int moduleNameLen, int settingNameLen); + void SetCachedVariant(DBVARIANT* s, DBVARIANT* d); + void FreeCachedVariant(DBVARIANT* V); + DBVARIANT* GetCachedValuePtr(HANDLE hContact, char* szSetting, int bAllocate); + int GetContactSettingWorker(HANDLE hContact,DBCONTACTGETSETTING *dbcgs,int isStatic); + + //////////////////////////////////////////////////////////////////////////// + // contacts + + DBCachedContactValueList* AddToCachedContactList(HANDLE hContact, int index); + + //////////////////////////////////////////////////////////////////////////// + // modules + + HANDLE m_hModHeap; + LIST m_lMods, m_lOfs; + HANDLE hEventAddedEvent, hEventDeletedEvent, hEventFilterAddedEvent; + ModuleName *m_lastmn; + + void AddToList(char *name, DWORD len, DWORD ofs); + DWORD FindExistingModuleNameOfs(const char *szName); + int InitModuleNames(void); + DWORD GetModuleNameOfs(const char *szName); + char *GetModuleNameByOfs(DWORD ofs); +}; diff --git a/plugins/Db3x_mmap/src/dbmodulechain.cpp b/plugins/Db3x_mmap/src/dbmodulechain.cpp new file mode 100644 index 0000000000..f8b25ef879 --- /dev/null +++ b/plugins/Db3x_mmap/src/dbmodulechain.cpp @@ -0,0 +1,138 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +void CDdxMmap::AddToList(char *name, DWORD len, DWORD ofs) +{ + ModuleName *mn = (ModuleName*)HeapAlloc(m_hModHeap,0,sizeof(ModuleName)); + mn->name = name; + mn->ofs = ofs; + + if (m_lMods.getIndex(mn) != -1) + DatabaseCorruption( _T("%s (Module Name not unique)")); + m_lMods.insert(mn); + + if (m_lOfs.getIndex(mn) != -1) + DatabaseCorruption( _T("%s (Module Offset not unique)")); + m_lOfs.insert(mn); +} + +int CDdxMmap::InitModuleNames(void) +{ + DWORD ofsThis = m_dbHeader.ofsFirstModuleName; + DBModuleName *dbmn = (struct DBModuleName*)DBRead(ofsThis,sizeof(struct DBModuleName),NULL); + while (ofsThis) { + if (dbmn->signature != DBMODULENAME_SIGNATURE) + DatabaseCorruption(NULL); + + int nameLen = dbmn->cbName; + + char *mod = (char*)HeapAlloc(m_hModHeap,0,nameLen+1); + CopyMemory(mod,DBRead(ofsThis + offsetof(struct DBModuleName,name),nameLen,NULL),nameLen); + mod[nameLen] = 0; + + AddToList(mod, nameLen, ofsThis); + + ofsThis = dbmn->ofsNext; + dbmn = (struct DBModuleName*)DBRead(ofsThis,sizeof(struct DBModuleName),NULL); + } + return 0; +} + +DWORD CDdxMmap::FindExistingModuleNameOfs(const char *szName) +{ + ModuleName mn = { (char*)szName, 0 }; + if (m_lastmn && !strcmp(mn.name, m_lastmn->name)) + return m_lastmn->ofs; + + int index = m_lMods.getIndex(&mn); + if (index != -1) { + ModuleName *pmn = m_lMods[index]; + m_lastmn = pmn; + return pmn->ofs; + } + + return 0; +} + +//will create the offset if it needs to +DWORD CDdxMmap::GetModuleNameOfs(const char *szName) +{ + struct DBModuleName dbmn; + int nameLen; + DWORD ofsNew,ofsExisting; + char *mod; + + ofsExisting = FindExistingModuleNameOfs(szName); + if (ofsExisting) return ofsExisting; + + nameLen = (int)strlen(szName); + + //need to create the module name + ofsNew = CreateNewSpace(nameLen+offsetof(struct DBModuleName,name)); + dbmn.signature = DBMODULENAME_SIGNATURE; + dbmn.cbName = nameLen; + dbmn.ofsNext = m_dbHeader.ofsFirstModuleName; + m_dbHeader.ofsFirstModuleName = ofsNew; + DBWrite(0,&m_dbHeader,sizeof(m_dbHeader)); + DBWrite(ofsNew,&dbmn,offsetof(struct DBModuleName,name)); + DBWrite(ofsNew+offsetof(struct DBModuleName,name),(PVOID)szName,nameLen); + DBFlush(0); + + //add to cache + mod = (char*)HeapAlloc(m_hModHeap,0,nameLen+1); + strcpy(mod,szName); + AddToList(mod, nameLen, ofsNew); + + //quit + return ofsNew; +} + +char* CDdxMmap::GetModuleNameByOfs(DWORD ofs) +{ + if (m_lastmn && m_lastmn->ofs == ofs) + return m_lastmn->name; + + ModuleName mn = {NULL, ofs}; + int index = m_lOfs.getIndex(&mn); + if (index != -1) { + ModuleName *pmn = m_lOfs[index]; + m_lastmn = pmn; + return pmn->name; + } + + DatabaseCorruption(NULL); + return NULL; +} + +STDMETHODIMP_(BOOL) CDdxMmap::EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) +{ + for (int i = 0; i < m_lMods.getCount(); i++) { + ModuleName *pmn = m_lMods[i]; + int ret = pFunc(pmn->name, pmn->ofs, (LPARAM)pParam); + if (ret) + return ret; + } + return 0; +} diff --git a/plugins/Db3x_mmap/src/dbsettings.cpp b/plugins/Db3x_mmap/src/dbsettings.cpp new file mode 100644 index 0000000000..49ac018047 --- /dev/null +++ b/plugins/Db3x_mmap/src/dbsettings.cpp @@ -0,0 +1,933 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +DWORD GetModuleNameOfs(const char *szName); +DBCachedContactValueList* AddToCachedContactList(HANDLE hContact, int index); + +int DBPreset_QuerySetting(const char *szModule, const char *szSetting, DBVARIANT *dbv, BOOL isStatic); + +DWORD CDdxMmap::GetSettingsGroupOfsByModuleNameOfs(DBContact *dbc,DWORD ofsModuleName) +{ + DWORD ofsThis = dbc->ofsFirstSettings; + while (ofsThis) { + DBContactSettings *dbcs = (DBContactSettings*)DBRead(ofsThis,sizeof(DBContactSettings),NULL); + if (dbcs->signature != DBCONTACTSETTINGS_SIGNATURE) DatabaseCorruption(NULL); + if (dbcs->ofsModuleName == ofsModuleName) + return ofsThis; + + ofsThis = dbcs->ofsNext; + } + return 0; +} + +DWORD __forceinline GetSettingValueLength(PBYTE pSetting) +{ + if (pSetting[0] & DBVTF_VARIABLELENGTH) + return 2+*(PWORD)(pSetting+1); + return pSetting[0]; +} + +char* CDdxMmap::InsertCachedSetting(const char* szName, size_t cbNameLen) +{ + char* newValue = (char*)HeapAlloc(m_hCacheHeap, 0, cbNameLen); + *newValue = 0; + strcpy(newValue+1, szName+1); + m_lSettings.insert(newValue); + return newValue; +} + +char* CDdxMmap::GetCachedSetting(const char *szModuleName,const char *szSettingName, int moduleNameLen, int settingNameLen) +{ + char szFullName[512]; + strcpy(szFullName+1,szModuleName); + szFullName[moduleNameLen+1] = '/'; + strcpy(szFullName+moduleNameLen+2,szSettingName); + + if (m_lastSetting && strcmp(szFullName+1, m_lastSetting) == 0) + return m_lastSetting; + + int index = m_lSettings.getIndex(szFullName); + if (index != -1) + m_lastSetting = m_lSettings[index]+1; + else + m_lastSetting = InsertCachedSetting( szFullName, settingNameLen+moduleNameLen+3)+1; + + return m_lastSetting; +} + +void CDdxMmap::SetCachedVariant( DBVARIANT* s /* new */, DBVARIANT* d /* cached */ ) +{ + char* szSave = ( d->type == DBVT_UTF8 || d->type == DBVT_ASCIIZ ) ? d->pszVal : NULL; + + memcpy( d, s, sizeof( DBVARIANT )); + if (( s->type == DBVT_UTF8 || s->type == DBVT_ASCIIZ ) && s->pszVal != NULL ) { + if ( szSave != NULL ) + d->pszVal = (char*)HeapReAlloc(m_hCacheHeap,0,szSave,strlen(s->pszVal)+1); + else + d->pszVal = (char*)HeapAlloc(m_hCacheHeap,0,strlen(s->pszVal)+1); + strcpy(d->pszVal,s->pszVal); + } + else if ( szSave != NULL ) + HeapFree(m_hCacheHeap,0,szSave); + +#ifdef DBLOGGING + switch( d->type ) { + case DBVT_BYTE: log1( "set cached byte: %d", d->bVal ); break; + case DBVT_WORD: log1( "set cached word: %d", d->wVal ); break; + case DBVT_DWORD: log1( "set cached dword: %d", d->dVal ); break; + case DBVT_UTF8: + case DBVT_ASCIIZ: log1( "set cached string: '%s'", d->pszVal ); break; + default: log1( "set cached crap: %d", d->type ); break; + } +#endif +} + +void CDdxMmap::FreeCachedVariant( DBVARIANT* V ) +{ + if (( V->type == DBVT_ASCIIZ || V->type == DBVT_UTF8 ) && V->pszVal != NULL ) + HeapFree(m_hCacheHeap,0,V->pszVal); +} + +DBVARIANT* CDdxMmap::GetCachedValuePtr( HANDLE hContact, char* szSetting, int bAllocate ) +{ + if ( hContact == 0 ) { + DBCachedGlobalValue Vtemp, *V; + Vtemp.name = szSetting; + int index = m_lGlobalSettings.getIndex(&Vtemp); + if (index != -1) { + V = m_lGlobalSettings[index]; + if ( bAllocate == -1 ) { + FreeCachedVariant( &V->value ); + m_lGlobalSettings.remove(index); + HeapFree(m_hCacheHeap,0,V); + return NULL; + } + } + else { + if ( bAllocate != 1 ) + return NULL; + + V = (DBCachedGlobalValue*)HeapAlloc(m_hCacheHeap,HEAP_ZERO_MEMORY,sizeof(DBCachedGlobalValue)); + V->name = szSetting; + m_lGlobalSettings.insert(V); + } + + return &V->value; + } + else { + DBCachedContactValue *V, *V1; + DBCachedContactValueList VLtemp,*VL; + + if (m_hLastCachedContact == hContact && m_lastVL) + VL = m_lastVL; + else { + VLtemp.hContact = hContact; + + int index = m_lContacts.getIndex(&VLtemp); + if (index == -1) { + if ( bAllocate != 1 ) + return NULL; + + VL = AddToCachedContactList(hContact, index); + } + else VL = m_lContacts[index]; + + m_lastVL = VL; + m_hLastCachedContact = hContact; + } + + for ( V = VL->first; V != NULL; V = V->next) + if (V->name == szSetting) + break; + + if ( V == NULL ) { + if ( bAllocate != 1 ) + return NULL; + + V = (DBCachedContactValue *)HeapAlloc(m_hCacheHeap, HEAP_ZERO_MEMORY, sizeof(DBCachedContactValue)); + if (VL->last) + VL->last->next = V; + else + VL->first = V; + VL->last = V; + V->name = szSetting; + } + else if ( bAllocate == -1 ) { + m_lastVL = NULL; + FreeCachedVariant(&V->value); + if ( VL->first == V ) { + VL->first = V->next; + if (VL->last == V) + VL->last = V->next; // NULL + } + else + for ( V1 = VL->first; V1 != NULL; V1 = V1->next ) + if ( V1->next == V ) { + V1->next = V->next; + if (VL->last == V) + VL->last = V1; + break; + } + HeapFree(m_hCacheHeap,0,V); + return NULL; + } + + return &V->value; +} } + +#define NeedBytes(n) if (bytesRemaining<(n)) pBlob = (PBYTE)DBRead(ofsBlobPtr,(n),&bytesRemaining) +#define MoveAlong(n) {int x = n; pBlob += (x); ofsBlobPtr += (x); bytesRemaining -= (x);} +#define VLT(n) ((n == DBVT_UTF8)?DBVT_ASCIIZ:n) + +int CDdxMmap::GetContactSettingWorker(HANDLE hContact,DBCONTACTGETSETTING *dbcgs,int isStatic) +{ + DBContact *dbc; + DWORD ofsModuleName,ofsContact,ofsSettingsGroup,ofsBlobPtr; + int settingNameLen,moduleNameLen; + int bytesRemaining; + PBYTE pBlob; + char* szCachedSettingName; + + if ((!dbcgs->szSetting) || (!dbcgs->szModule)) + return 1; + // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name + settingNameLen = (int)strlen(dbcgs->szSetting); + moduleNameLen = (int)strlen(dbcgs->szModule); + if ( settingNameLen > 0xFE ) + { + #ifdef _DEBUG + OutputDebugStringA("GetContactSettingWorker() got a > 255 setting name length. \n"); + #endif + return 1; + } + if ( moduleNameLen > 0xFE ) + { + #ifdef _DEBUG + OutputDebugStringA("GetContactSettingWorker() got a > 255 module name length. \n"); + #endif + return 1; + } + + mir_cslock lck(m_csDbAccess); + + log3("get [%08p] %s/%s",hContact,dbcgs->szModule,dbcgs->szSetting); + + szCachedSettingName = GetCachedSetting(dbcgs->szModule,dbcgs->szSetting,moduleNameLen,settingNameLen); + { + DBVARIANT* pCachedValue = GetCachedValuePtr( hContact, szCachedSettingName, 0 ); + if ( pCachedValue != NULL ) { + if ( pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8 ) { + int cbOrigLen = dbcgs->pValue->cchVal; + char* cbOrigPtr = dbcgs->pValue->pszVal; + memcpy( dbcgs->pValue, pCachedValue, sizeof( DBVARIANT )); + if ( isStatic ) { + int cbLen = 0; + if ( pCachedValue->pszVal != NULL ) + cbLen = (int)strlen( pCachedValue->pszVal ); + + cbOrigLen--; + dbcgs->pValue->pszVal = cbOrigPtr; + if (cbLenpValue->pszVal,pCachedValue->pszVal,cbOrigLen); + dbcgs->pValue->pszVal[cbOrigLen] = 0; + dbcgs->pValue->cchVal = cbLen; + } + else { + dbcgs->pValue->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal)+1); + strcpy(dbcgs->pValue->pszVal,pCachedValue->pszVal); + } + } + else + memcpy( dbcgs->pValue, pCachedValue, sizeof( DBVARIANT )); + + switch( dbcgs->pValue->type ) { + case DBVT_BYTE: log1( "get cached byte: %d", dbcgs->pValue->bVal ); break; + case DBVT_WORD: log1( "get cached word: %d", dbcgs->pValue->wVal ); break; + case DBVT_DWORD: log1( "get cached dword: %d", dbcgs->pValue->dVal ); break; + case DBVT_UTF8: + case DBVT_ASCIIZ: log1( "get cached string: '%s'", dbcgs->pValue->pszVal); break; + default: log1( "get cached crap: %d", dbcgs->pValue->type ); break; + } + + return ( pCachedValue->type == DBVT_DELETED ) ? 1 : 0; + } } + + ofsModuleName = GetModuleNameOfs(dbcgs->szModule); + if (hContact == NULL) ofsContact = m_dbHeader.ofsUser; + else ofsContact = (DWORD)hContact; + dbc = (DBContact*)DBRead(ofsContact,sizeof(DBContact),NULL); + if (dbc->signature != DBCONTACT_SIGNATURE) + return 1; + + ofsSettingsGroup = GetSettingsGroupOfsByModuleNameOfs(dbc,ofsModuleName); + if (ofsSettingsGroup) { + ofsBlobPtr = ofsSettingsGroup+offsetof(DBContactSettings,blob); + pBlob = DBRead(ofsBlobPtr,sizeof(DBContactSettings),&bytesRemaining); + while (pBlob[0]) { + NeedBytes(1+settingNameLen); + if (pBlob[0] == settingNameLen && !memcmp(pBlob+1,dbcgs->szSetting,settingNameLen)) { + MoveAlong(1+settingNameLen); + NeedBytes(5); + if (isStatic && pBlob[0]&DBVTF_VARIABLELENGTH && VLT(dbcgs->pValue->type) != VLT(pBlob[0])) + return 1; + + dbcgs->pValue->type = pBlob[0]; + switch(pBlob[0]) { + case DBVT_DELETED: /* this setting is deleted */ + dbcgs->pValue->type = DBVT_DELETED; + return 2; + + case DBVT_BYTE: dbcgs->pValue->bVal = pBlob[1]; break; + case DBVT_WORD: DecodeCopyMemory(&(dbcgs->pValue->wVal), (PWORD)(pBlob+1), 2); break; + case DBVT_DWORD: DecodeCopyMemory(&(dbcgs->pValue->dVal), (PDWORD)(pBlob+1), 4); break; + case DBVT_UTF8: + case DBVT_ASCIIZ: + NeedBytes(3+*(PWORD)(pBlob+1)); + if (isStatic) { + dbcgs->pValue->cchVal--; + if (*(PWORD)(pBlob+1)pValue->cchVal) dbcgs->pValue->cchVal = *(PWORD)(pBlob+1); + DecodeCopyMemory(dbcgs->pValue->pszVal,pBlob+3,dbcgs->pValue->cchVal); + dbcgs->pValue->pszVal[dbcgs->pValue->cchVal] = 0; + dbcgs->pValue->cchVal = *(PWORD)(pBlob+1); + } + else { + dbcgs->pValue->pszVal = (char*)mir_alloc(1+*(PWORD)(pBlob+1)); + DecodeCopyMemory(dbcgs->pValue->pszVal,pBlob+3,*(PWORD)(pBlob+1)); + dbcgs->pValue->pszVal[*(PWORD)(pBlob+1)] = 0; + } + break; + case DBVT_BLOB: + NeedBytes(3+*(PWORD)(pBlob+1)); + if (isStatic) { + if (*(PWORD)(pBlob+1)pValue->cpbVal) dbcgs->pValue->cpbVal = *(PWORD)(pBlob+1); + DecodeCopyMemory(dbcgs->pValue->pbVal,pBlob+3,dbcgs->pValue->cpbVal); + } + else { + dbcgs->pValue->pbVal = (BYTE *)mir_alloc(*(PWORD)(pBlob+1)); + DecodeCopyMemory(dbcgs->pValue->pbVal,pBlob+3,*(PWORD)(pBlob+1)); + } + dbcgs->pValue->cpbVal = *(PWORD)(pBlob+1); + break; + } + + /**** add to cache **********************/ + if ( dbcgs->pValue->type != DBVT_BLOB ) { + DBVARIANT* pCachedValue = GetCachedValuePtr( hContact, szCachedSettingName, 1 ); + if ( pCachedValue != NULL ) + SetCachedVariant(dbcgs->pValue,pCachedValue); + } + + logg(); + return 0; + } + NeedBytes(1); + MoveAlong(pBlob[0]+1); + NeedBytes(3); + MoveAlong(1+GetSettingValueLength(pBlob)); + NeedBytes(1); + } } + + #ifndef DB3X_EXPORTS + /**** nullbie: query info from preset **********************/ + if (!hContact && DBPreset_QuerySetting(dbcgs->szModule, dbcgs->szSetting, dbcgs->pValue, isStatic)) { + /**** add to cache **********************/ + if ( dbcgs->pValue->type != DBVT_BLOB ) { + DBVARIANT* pCachedValue = GetCachedValuePtr( hContact, szCachedSettingName, 1 ); + if ( pCachedValue != NULL ) + SetCachedVariant(dbcgs->pValue,pCachedValue); + } + return 0; + } + #endif + + /**** add missing setting to cache **********************/ + if ( dbcgs->pValue->type != DBVT_BLOB ) + { + DBVARIANT* pCachedValue = GetCachedValuePtr( hContact, szCachedSettingName, 1 ); + if ( pCachedValue != NULL ) + pCachedValue->type = DBVT_DELETED; + } + + logg(); + return 1; +} + +STDMETHODIMP_(BOOL) CDdxMmap::GetContactSetting(HANDLE hContact, DBCONTACTGETSETTING *dgs) +{ + dgs->pValue->type = 0; + if ( GetContactSettingWorker(hContact, dgs, 0 )) + return 1; + + if ( dgs->pValue->type == DBVT_UTF8 ) { + WCHAR* tmp = NULL; + char* p = NEWSTR_ALLOCA(dgs->pValue->pszVal); + if ( mir_utf8decode( p, &tmp ) != NULL ) { + BOOL bUsed = FALSE; + int result = WideCharToMultiByte( m_codePage, WC_NO_BEST_FIT_CHARS, tmp, -1, NULL, 0, NULL, &bUsed ); + + mir_free( dgs->pValue->pszVal ); + + if ( bUsed || result == 0 ) { + dgs->pValue->type = DBVT_WCHAR; + dgs->pValue->pwszVal = tmp; + } + else { + dgs->pValue->type = DBVT_ASCIIZ; + dgs->pValue->pszVal = (char *)mir_alloc(result); + WideCharToMultiByte( m_codePage, WC_NO_BEST_FIT_CHARS, tmp, -1, dgs->pValue->pszVal, result, NULL, NULL ); + mir_free( tmp ); + } + } + else { + dgs->pValue->type = DBVT_ASCIIZ; + mir_free( tmp ); + } } + + return 0; +} + +STDMETHODIMP_(BOOL) CDdxMmap::GetContactSettingStr(HANDLE hContact, DBCONTACTGETSETTING *dgs) +{ + int iSaveType = dgs->pValue->type; + + if ( GetContactSettingWorker(hContact, dgs, 0 )) + return 1; + + if ( iSaveType == 0 || iSaveType == dgs->pValue->type ) + return 0; + + if ( dgs->pValue->type != DBVT_ASCIIZ && dgs->pValue->type != DBVT_UTF8 ) + return 1; + + if ( iSaveType == DBVT_WCHAR ) { + if ( dgs->pValue->type != DBVT_UTF8 ) { + int len = MultiByteToWideChar( CP_ACP, 0, dgs->pValue->pszVal, -1, NULL, 0 ); + wchar_t* wszResult = ( wchar_t* )mir_alloc(( len+1 )*sizeof( wchar_t )); + if ( wszResult == NULL ) + return 1; + + MultiByteToWideChar( CP_ACP, 0, dgs->pValue->pszVal, -1, wszResult, len ); + wszResult[ len ] = 0; + mir_free( dgs->pValue->pszVal ); + dgs->pValue->pwszVal = wszResult; + } + else { + char* savePtr = NEWSTR_ALLOCA(dgs->pValue->pszVal); + mir_free( dgs->pValue->pszVal ); + if ( !mir_utf8decode( savePtr, &dgs->pValue->pwszVal )) + return 1; + } + } + else if ( iSaveType == DBVT_UTF8 ) { + char* tmpBuf = mir_utf8encode( dgs->pValue->pszVal ); + if ( tmpBuf == NULL ) + return 1; + + mir_free( dgs->pValue->pszVal ); + dgs->pValue->pszVal = tmpBuf; + } + else if ( iSaveType == DBVT_ASCIIZ ) + mir_utf8decode( dgs->pValue->pszVal, NULL ); + + dgs->pValue->type = iSaveType; + return 0; +} + +STDMETHODIMP_(BOOL) CDdxMmap::GetContactSettingStatic(HANDLE hContact, DBCONTACTGETSETTING *dgs) +{ + if ( GetContactSettingWorker(hContact, dgs, 1 )) + return 1; + + if ( dgs->pValue->type == DBVT_UTF8 ) { + mir_utf8decode( dgs->pValue->pszVal, NULL ); + dgs->pValue->type = DBVT_ASCIIZ; + } + + return 0; +} + +STDMETHODIMP_(BOOL) CDdxMmap::FreeVariant(DBVARIANT *dbv) +{ + if ( dbv == 0 ) return 1; + switch ( dbv->type ) { + case DBVT_ASCIIZ: + case DBVT_UTF8: + case DBVT_WCHAR: + { + if ( dbv->pszVal ) mir_free(dbv->pszVal); + dbv->pszVal = 0; + break; + } + case DBVT_BLOB: + { + if ( dbv->pbVal ) mir_free(dbv->pbVal); + dbv->pbVal = 0; + break; + } + } + dbv->type = 0; + return 0; +} + +STDMETHODIMP_(BOOL) CDdxMmap::SetSettingResident(BOOL bIsResident, const char *pszSettingName) +{ + size_t cbSettingNameLen = strlen(pszSettingName) + 2; + if (cbSettingNameLen < 512) { + char* szSetting; + char szTemp[512]; + strcpy( szTemp+1, pszSettingName); + + mir_cslock lck(m_csDbAccess); + int idx = m_lSettings.getIndex(szTemp); + if (idx == -1) + szSetting = InsertCachedSetting( szTemp, cbSettingNameLen); + else + szSetting = m_lSettings[idx]; + + *szSetting = (char)bIsResident; + + idx = m_lResidentSettings.getIndex(szSetting+1); + if (idx == -1) { + if (bIsResident) + m_lResidentSettings.insert(szSetting+1); + } + else if (!bIsResident) + m_lResidentSettings.remove(idx); + } + return 0; +} + +STDMETHODIMP_(BOOL) CDdxMmap::WriteContactSetting(HANDLE hContact, DBCONTACTWRITESETTING *dbcws) +{ + DBCONTACTWRITESETTING tmp; + DWORD ofsModuleName; + DBContactSettings dbcs; + PBYTE pBlob; + int settingNameLen = 0; + int moduleNameLen = 0; + int settingDataLen = 0; + int bytesRequired,bytesRemaining; + DWORD ofsContact,ofsSettingsGroup,ofsBlobPtr; + + if (dbcws == NULL || dbcws->szSetting == NULL || dbcws->szModule == NULL ) + return 1; + + // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name + settingNameLen = (int)strlen(dbcws->szSetting); + moduleNameLen = (int)strlen(dbcws->szModule); + if ( settingNameLen > 0xFE ) + { + #ifdef _DEBUG + OutputDebugStringA("WriteContactSetting() got a > 255 setting name length. \n"); + #endif + return 1; + } + if ( moduleNameLen > 0xFE ) + { + #ifdef _DEBUG + OutputDebugStringA("WriteContactSetting() got a > 255 module name length. \n"); + #endif + return 1; + } + + tmp = *dbcws; + + if (tmp.value.type == DBVT_WCHAR) { + if (tmp.value.pszVal != NULL) { + char* val = mir_utf8encodeW(tmp.value.pwszVal); + if ( val == NULL ) + return 1; + + tmp.value.pszVal = ( char* )alloca( strlen( val )+1 ); + strcpy( tmp.value.pszVal, val ); + mir_free(val); + tmp.value.type = DBVT_UTF8; + } + else return 1; + } + + if (tmp.value.type != DBVT_BYTE && tmp.value.type != DBVT_WORD && tmp.value.type != DBVT_DWORD && tmp.value.type != DBVT_ASCIIZ && tmp.value.type != DBVT_UTF8 && tmp.value.type != DBVT_BLOB) + return 1; + if ((!tmp.szModule) || (!tmp.szSetting) || ((tmp.value.type == DBVT_ASCIIZ || tmp.value.type == DBVT_UTF8 )&& tmp.value.pszVal == NULL) || (tmp.value.type == DBVT_BLOB && tmp.value.pbVal == NULL)) + return 1; + + // the db can not tolerate strings/blobs longer than 0xFFFF since the format writes 2 lengths + switch( tmp.value.type ) { + case DBVT_ASCIIZ: case DBVT_BLOB: case DBVT_UTF8: + { size_t len = ( tmp.value.type != DBVT_BLOB ) ? strlen(tmp.value.pszVal) : tmp.value.cpbVal; + if ( len >= 0xFFFF ) { + #ifdef _DEBUG + OutputDebugStringA("WriteContactSetting() writing huge string/blob, rejecting ( >= 0xFFFF ) \n"); + #endif + return 1; + } + } + } + + mir_cslockfull lck(m_csDbAccess); + + char* szCachedSettingName = GetCachedSetting(tmp.szModule, tmp.szSetting, moduleNameLen, settingNameLen); + if ( tmp.value.type != DBVT_BLOB ) { + DBVARIANT* pCachedValue = GetCachedValuePtr(hContact, szCachedSettingName, 1); + if ( pCachedValue != NULL ) { + BOOL bIsIdentical = FALSE; + if ( pCachedValue->type == tmp.value.type ) { + switch(tmp.value.type) { + case DBVT_BYTE: bIsIdentical = pCachedValue->bVal == tmp.value.bVal; break; + case DBVT_WORD: bIsIdentical = pCachedValue->wVal == tmp.value.wVal; break; + case DBVT_DWORD: bIsIdentical = pCachedValue->dVal == tmp.value.dVal; break; + case DBVT_UTF8: + case DBVT_ASCIIZ: bIsIdentical = strcmp( pCachedValue->pszVal, tmp.value.pszVal ) == 0; break; + } + if ( bIsIdentical ) + return 0; + } + SetCachedVariant(&tmp.value, pCachedValue); + } + if ( szCachedSettingName[-1] != 0 ) { + lck.unlock(); + NotifyEventHooks(hSettingChangeEvent, (WPARAM)hContact, (LPARAM)&tmp); + return 0; + } + } + else GetCachedValuePtr(hContact, szCachedSettingName, -1); + + ofsModuleName = GetModuleNameOfs(tmp.szModule); + if (hContact == 0) ofsContact = m_dbHeader.ofsUser; + else ofsContact = (DWORD)hContact; + + DBContact dbc = *(DBContact*)DBRead(ofsContact,sizeof(DBContact),NULL); + if (dbc.signature != DBCONTACT_SIGNATURE) + return 1; + + log0("write setting"); + //make sure the module group exists + ofsSettingsGroup = GetSettingsGroupOfsByModuleNameOfs(&dbc,ofsModuleName); + if (ofsSettingsGroup == 0) { //module group didn't exist - make it + if (tmp.value.type&DBVTF_VARIABLELENGTH) { + if (tmp.value.type == DBVT_ASCIIZ || tmp.value.type == DBVT_UTF8) bytesRequired = (int)strlen(tmp.value.pszVal)+2; + else if (tmp.value.type == DBVT_BLOB) bytesRequired = tmp.value.cpbVal+2; + } + else bytesRequired = tmp.value.type; + bytesRequired += 2+settingNameLen; + bytesRequired += (DB_SETTINGS_RESIZE_GRANULARITY-(bytesRequired%DB_SETTINGS_RESIZE_GRANULARITY))%DB_SETTINGS_RESIZE_GRANULARITY; + ofsSettingsGroup = CreateNewSpace(bytesRequired+offsetof(DBContactSettings,blob)); + dbcs.signature = DBCONTACTSETTINGS_SIGNATURE; + dbcs.ofsNext = dbc.ofsFirstSettings; + dbcs.ofsModuleName = ofsModuleName; + dbcs.cbBlob = bytesRequired; + dbcs.blob[0] = 0; + dbc.ofsFirstSettings = ofsSettingsGroup; + DBWrite(ofsContact,&dbc,sizeof(DBContact)); + DBWrite(ofsSettingsGroup,&dbcs,sizeof(DBContactSettings)); + ofsBlobPtr = ofsSettingsGroup+offsetof(DBContactSettings,blob); + pBlob = (PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); + } + else { + dbcs = *(DBContactSettings*)DBRead(ofsSettingsGroup,sizeof(DBContactSettings),&bytesRemaining); + //find if the setting exists + ofsBlobPtr = ofsSettingsGroup+offsetof(DBContactSettings,blob); + pBlob = (PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); + while (pBlob[0]) { + NeedBytes(settingNameLen+1); + if (pBlob[0] == settingNameLen && !memcmp(pBlob+1,tmp.szSetting,settingNameLen)) + break; + NeedBytes(1); + MoveAlong(pBlob[0]+1); + NeedBytes(3); + MoveAlong(1+GetSettingValueLength(pBlob)); + NeedBytes(1); + } + if (pBlob[0]) { //setting already existed, and up to end of name is in cache + MoveAlong(1+settingNameLen); + //if different type or variable length and length is different + NeedBytes(3); + if (pBlob[0] != tmp.value.type || ((pBlob[0] == DBVT_ASCIIZ || pBlob[0] == DBVT_UTF8) && *(PWORD)(pBlob+1) != strlen(tmp.value.pszVal)) || (pBlob[0] == DBVT_BLOB && *(PWORD)(pBlob+1) != tmp.value.cpbVal)) { + //bin it + int nameLen,valLen; + DWORD ofsSettingToCut; + NeedBytes(3); + nameLen = 1+settingNameLen; + valLen = 1+GetSettingValueLength(pBlob); + ofsSettingToCut = ofsBlobPtr-nameLen; + MoveAlong(valLen); + NeedBytes(1); + while (pBlob[0]) { + MoveAlong(pBlob[0]+1); + NeedBytes(3); + MoveAlong(1+GetSettingValueLength(pBlob)); + NeedBytes(1); + } + DBMoveChunk(ofsSettingToCut,ofsSettingToCut+nameLen+valLen,ofsBlobPtr+1-ofsSettingToCut); + ofsBlobPtr -= nameLen+valLen; + pBlob = (PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); + } + else { + //replace existing setting at pBlob + MoveAlong(1); //skip data type + switch(tmp.value.type) { + case DBVT_BYTE: DBWrite(ofsBlobPtr,&tmp.value.bVal,1); break; + case DBVT_WORD: EncodeDBWrite(ofsBlobPtr,&tmp.value.wVal,2); break; + case DBVT_DWORD: EncodeDBWrite(ofsBlobPtr,&tmp.value.dVal,4); break; + case DBVT_UTF8: + case DBVT_ASCIIZ: EncodeDBWrite(ofsBlobPtr+2,tmp.value.pszVal,(int)strlen(tmp.value.pszVal)); break; + case DBVT_BLOB: EncodeDBWrite(ofsBlobPtr+2,tmp.value.pbVal,tmp.value.cpbVal); break; + } + //quit + DBFlush(1); + lck.unlock(); + //notify + NotifyEventHooks(hSettingChangeEvent, (WPARAM)hContact, (LPARAM)&tmp); + return 0; + } + } + } + //cannot do a simple replace, add setting to end of list + //pBlob already points to end of list + //see if it fits + if (tmp.value.type&DBVTF_VARIABLELENGTH) { + if (tmp.value.type == DBVT_ASCIIZ || tmp.value.type == DBVT_UTF8) bytesRequired = (int)strlen(tmp.value.pszVal)+2; + else if (tmp.value.type == DBVT_BLOB) bytesRequired = tmp.value.cpbVal+2; + } + else bytesRequired = tmp.value.type; + bytesRequired += 2+settingNameLen; + bytesRequired += ofsBlobPtr+1-(ofsSettingsGroup+offsetof(DBContactSettings,blob)); + if ((DWORD)bytesRequired > dbcs.cbBlob) { + //doesn't fit: move entire group + DBContactSettings *dbcsPrev; + DWORD ofsDbcsPrev,ofsNew; + + bytesRequired += (DB_SETTINGS_RESIZE_GRANULARITY-(bytesRequired%DB_SETTINGS_RESIZE_GRANULARITY))%DB_SETTINGS_RESIZE_GRANULARITY; + //find previous group to change its offset + ofsDbcsPrev = dbc.ofsFirstSettings; + if (ofsDbcsPrev == ofsSettingsGroup) ofsDbcsPrev = 0; + else { + dbcsPrev = (DBContactSettings*)DBRead(ofsDbcsPrev,sizeof(DBContactSettings),NULL); + while (dbcsPrev->ofsNext != ofsSettingsGroup) { + if (dbcsPrev->ofsNext == 0) DatabaseCorruption(NULL); + ofsDbcsPrev = dbcsPrev->ofsNext; + dbcsPrev = (DBContactSettings*)DBRead(ofsDbcsPrev,sizeof(DBContactSettings),NULL); + } + } + + //create the new one + ofsNew = ReallocSpace(ofsSettingsGroup, dbcs.cbBlob+offsetof(DBContactSettings,blob), bytesRequired+offsetof(DBContactSettings,blob)); + + dbcs.cbBlob = bytesRequired; + + DBWrite(ofsNew,&dbcs,offsetof(DBContactSettings,blob)); + if (ofsDbcsPrev == 0) { + dbc.ofsFirstSettings = ofsNew; + DBWrite(ofsContact,&dbc,sizeof(DBContact)); + } + else { + dbcsPrev = (DBContactSettings*)DBRead(ofsDbcsPrev,sizeof(DBContactSettings),NULL); + dbcsPrev->ofsNext = ofsNew; + DBWrite(ofsDbcsPrev,dbcsPrev,offsetof(DBContactSettings,blob)); + } + ofsBlobPtr += ofsNew-ofsSettingsGroup; + ofsSettingsGroup = ofsNew; + pBlob = (PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); + } + //we now have a place to put it and enough space: make it + DBWrite(ofsBlobPtr,&settingNameLen,1); + DBWrite(ofsBlobPtr+1,(PVOID)tmp.szSetting,settingNameLen); + MoveAlong(1+settingNameLen); + DBWrite(ofsBlobPtr,&tmp.value.type,1); + MoveAlong(1); + switch(tmp.value.type) { + case DBVT_BYTE: DBWrite(ofsBlobPtr,&tmp.value.bVal,1); MoveAlong(1); break; + case DBVT_WORD: EncodeDBWrite(ofsBlobPtr,&tmp.value.wVal,2); MoveAlong(2); break; + case DBVT_DWORD: EncodeDBWrite(ofsBlobPtr,&tmp.value.dVal,4); MoveAlong(4); break; + case DBVT_UTF8: + case DBVT_ASCIIZ: + { int len = (int)strlen(tmp.value.pszVal); + DBWrite(ofsBlobPtr,&len,2); + EncodeDBWrite(ofsBlobPtr+2,tmp.value.pszVal,len); + MoveAlong(2+len); + } + break; + case DBVT_BLOB: + DBWrite(ofsBlobPtr,&tmp.value.cpbVal,2) ; + EncodeDBWrite(ofsBlobPtr+2,tmp.value.pbVal,tmp.value.cpbVal); + MoveAlong(2+tmp.value.cpbVal); + break; + } + + BYTE zero = 0; + DBWrite(ofsBlobPtr,&zero,1); + + //quit + DBFlush(1); + lck.unlock(); + + //notify + NotifyEventHooks(hSettingChangeEvent, (WPARAM)hContact, (LPARAM)&tmp ); + return 0; +} + +STDMETHODIMP_(BOOL) CDdxMmap::DeleteContactSetting(HANDLE hContact, DBCONTACTGETSETTING *dbcgs) +{ + DBContact *dbc; + DWORD ofsModuleName,ofsSettingsGroup,ofsBlobPtr; + PBYTE pBlob; + int settingNameLen,moduleNameLen,bytesRemaining; + char* szCachedSettingName; + WPARAM saveWparam = (WPARAM)hContact; + + if ( !dbcgs->szModule || !dbcgs->szSetting) + return 1; + + // the db format can't tolerate more than 255 bytes of space (incl. null) for settings+module name + settingNameLen = (int)strlen(dbcgs->szSetting); + moduleNameLen = (int)strlen(dbcgs->szModule); + if ( settingNameLen > 0xFE ) { + #ifdef _DEBUG + OutputDebugStringA("DeleteContactSetting() got a > 255 setting name length. \n"); + #endif + return 1; + } + if ( moduleNameLen > 0xFE ) { + #ifdef _DEBUG + OutputDebugStringA("DeleteContactSetting() got a > 255 module name length. \n"); + #endif + return 1; + } + + mir_cslockfull lck(m_csDbAccess); + ofsModuleName = GetModuleNameOfs(dbcgs->szModule); + if (hContact == 0) + hContact = (HANDLE)m_dbHeader.ofsUser; + + dbc = (DBContact*)DBRead(hContact,sizeof(DBContact),NULL); + if (dbc->signature != DBCONTACT_SIGNATURE) + return 1; + + //make sure the module group exists + ofsSettingsGroup = GetSettingsGroupOfsByModuleNameOfs(dbc,ofsModuleName); + if (ofsSettingsGroup == 0) + return 1; + + //find if the setting exists + ofsBlobPtr = ofsSettingsGroup+offsetof(DBContactSettings,blob); + pBlob = (PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); + while (pBlob[0]) { + NeedBytes(settingNameLen+1); + if (pBlob[0] == settingNameLen && !memcmp(pBlob+1,dbcgs->szSetting,settingNameLen)) + break; + NeedBytes(1); + MoveAlong(pBlob[0]+1); + NeedBytes(3); + MoveAlong(1+GetSettingValueLength(pBlob)); + NeedBytes(1); + } + if (!pBlob[0]) //setting didn't exist + return 1; + + //bin it + int nameLen,valLen; + DWORD ofsSettingToCut; + MoveAlong(1+settingNameLen); + NeedBytes(3); + nameLen = 1+settingNameLen; + valLen = 1+GetSettingValueLength(pBlob); + ofsSettingToCut = ofsBlobPtr-nameLen; + MoveAlong(valLen); + NeedBytes(1); + while (pBlob[0]) { + MoveAlong(pBlob[0]+1); + NeedBytes(3); + MoveAlong(1+GetSettingValueLength(pBlob)); + NeedBytes(1); + } + DBMoveChunk(ofsSettingToCut,ofsSettingToCut+nameLen+valLen,ofsBlobPtr+1-ofsSettingToCut); + + szCachedSettingName = GetCachedSetting(dbcgs->szModule,dbcgs->szSetting,moduleNameLen,settingNameLen); + GetCachedValuePtr((HANDLE)saveWparam, szCachedSettingName, -1 ); + + //quit + DBFlush(1); + lck.unlock(); + + //notify + DBCONTACTWRITESETTING dbcws = {0}; + dbcws.szModule = dbcgs->szModule; + dbcws.szSetting = dbcgs->szSetting; + dbcws.value.type = DBVT_DELETED; + NotifyEventHooks(hSettingChangeEvent,saveWparam,(LPARAM)&dbcws); + return 0; +} + +STDMETHODIMP_(BOOL) CDdxMmap::EnumContactSettings(HANDLE hContact, DBCONTACTENUMSETTINGS* dbces) +{ + DBContact *dbc; + DWORD ofsModuleName,ofsContact,ofsBlobPtr; + int bytesRemaining, result; + PBYTE pBlob; + char szSetting[256]; + + if (!dbces->szModule) + return -1; + + mir_cslock lck(m_csDbAccess); + + ofsModuleName = GetModuleNameOfs(dbces->szModule); + if (hContact == 0) ofsContact = m_dbHeader.ofsUser; + else ofsContact = (DWORD)hContact; + dbc = (DBContact*)DBRead(ofsContact,sizeof(DBContact),NULL); + if (dbc->signature != DBCONTACT_SIGNATURE) + return -1; + + dbces->ofsSettings = GetSettingsGroupOfsByModuleNameOfs(dbc,ofsModuleName); + if ( !dbces->ofsSettings) + return -1; + + ofsBlobPtr = dbces->ofsSettings+offsetof(DBContactSettings,blob); + pBlob = (PBYTE)DBRead(ofsBlobPtr,1,&bytesRemaining); + if (pBlob[0] == 0) + return -1; + + result = 0; + while (pBlob[0]) { + NeedBytes(1); + NeedBytes(1+pBlob[0]); + CopyMemory(szSetting,pBlob+1,pBlob[0]); szSetting[pBlob[0]] = 0; + result = (dbces->pfnEnumProc)(szSetting,dbces->lParam); + MoveAlong(1+pBlob[0]); + NeedBytes(3); + MoveAlong(1+GetSettingValueLength(pBlob)); + NeedBytes(1); + } + return result; +} + +STDMETHODIMP_(BOOL) CDdxMmap::EnumResidentSettings(DBMODULEENUMPROC pFunc, void *pParam) +{ + for(int i = 0; i < m_lResidentSettings.getCount(); i++) { + int ret = pFunc(m_lResidentSettings[i], 0, (LPARAM)pParam); + if (ret) return ret; + } + return 0; +} diff --git a/plugins/Db3x_mmap/src/init.cpp b/plugins/Db3x_mmap/src/init.cpp new file mode 100644 index 0000000000..b38fab0ce2 --- /dev/null +++ b/plugins/Db3x_mmap/src/init.cpp @@ -0,0 +1,160 @@ +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright 2012 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +#include "commonheaders.h" + +int hLangpack; + +static PLUGININFOEX pluginInfo = +{ + sizeof(PLUGININFOEX), + "Miranda NG mmap database driver", + __VERSION_DWORD, + "Provides Miranda database support: global settings, contacts, history, settings per contact.", + "Miranda-NG project", + "bio@msx.ru; ghazan@miranda.im", + "Copyright 2012 Miranda NG project", + "", + UNICODE_AWARE, + {0xf7a6b27c, 0x9d9c, 0x4a42, { 0xbe, 0x86, 0xa4, 0x48, 0xae, 0x10, 0x91, 0x61 }} //{F7A6B27C-9D9C-4a42-BE86-A448AE109161} +}; + +HINSTANCE g_hInst = NULL; + +LIST g_Dbs(1, (LIST::FTSortFunc)HandleKeySort); + +///////////////////////////////////////////////////////////////////////////////////////// + +// returns 0 if the profile is created, EMKPRF* +static int makeDatabase(const TCHAR *profile, int *error) +{ + CDdxMmap *tmp = new CDdxMmap(profile); + if (tmp->Create() == ERROR_SUCCESS) { + tmp->CreateDbHeaders(); + delete tmp; + return 0; + } + delete tmp; + if (error != NULL) *error = EMKPRF_CREATEFAILED; + return 1; +} + +// returns 0 if the given profile has a valid header +static int grokHeader(const TCHAR *profile, int *error) +{ + CDdxMmap *tmp = new CDdxMmap(profile); + if (tmp->Load(true) != ERROR_SUCCESS) { + delete tmp; + if (error != NULL) *error = EGROKPRF_CANTREAD; + return 1; + } + + int chk = tmp->CheckDbHeaders(); + delete tmp; + if ( chk == 0 ) { + // all the internal tests passed, hurrah + if (error != NULL) *error = 0; + return 0; + } + + // didn't pass at all, or some did. + switch ( chk ) { + case 1: + // "Miranda ICQ DB" wasn't present + if (error != NULL) *error = EGROKPRF_UNKHEADER; + break; + + case 2: + // header was present, but version information newer + if (error != NULL) *error = EGROKPRF_VERNEWER; + break; + + case 3: + // header/version OK, internal data missing + if (error != NULL) *error = EGROKPRF_DAMAGED; + break; + } + + return 1; +} + +// returns 0 if all the APIs are injected otherwise, 1 +static MIDatabase* LoadDatabase(const TCHAR *profile) +{ + // set the memory, lists & UTF8 manager + mir_getLP( &pluginInfo ); + + CDdxMmap* db = new CDdxMmap(profile); + if (db->Load(false) != ERROR_SUCCESS) { + delete db; + return NULL; + } + + g_Dbs.insert(db); + return db; +} + +static int UnloadDatabase(MIDatabase* db) +{ + g_Dbs.remove((CDdxMmap*)db); + delete (CDdxMmap*)db; + return 0; +} + +static DATABASELINK dblink = +{ + sizeof(DATABASELINK), + "db3x mmap driver", + _T("db3x mmap database support"), + makeDatabase, + grokHeader, + LoadDatabase, + UnloadDatabase +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion) +{ + return &pluginInfo; +} + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_DATABASE, MIID_LAST}; + +extern "C" __declspec(dllexport) int Load(void) +{ + RegisterDatabasePlugin(&dblink); + return 0; +} + +extern "C" __declspec(dllexport) int Unload(void) +{ + g_Dbs.destroy(); + return 0; +} + +BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD dwReason, LPVOID reserved) +{ + g_hInst = hInstDLL; + return TRUE; +} diff --git a/plugins/Db3x_mmap/src/resource.h b/plugins/Db3x_mmap/src/resource.h new file mode 100644 index 0000000000..96abbfff99 --- /dev/null +++ b/plugins/Db3x_mmap/src/resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by resource.rc +// +#define IDC_NOTOALL 3 +#define IDD_INSTALLINI 235 +#define IDD_WARNINICHANGE 236 +#define IDD_INIIMPORTDONE 237 +#define IDC_ININAME 1333 +#define IDC_VIEWINI 1334 +#define IDC_SECURITYINFO 1335 +#define IDC_SETTINGNAME 1336 +#define IDC_NEWVALUE 1337 +#define IDC_WARNNOMORE 1338 +#define IDC_DELETE 1339 +#define IDC_RECYCLE 1340 +#define IDC_NEWNAME 1341 +#define IDC_MOVE 1342 +#define IDC_LEAVE 1343 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 269 +#define _APS_NEXT_COMMAND_VALUE 40018 +#define _APS_NEXT_CONTROL_VALUE 1657 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/Db3x_mmap/src/version.h b/plugins/Db3x_mmap/src/version.h new file mode 100644 index 0000000000..2d10138fcf --- /dev/null +++ b/plugins/Db3x_mmap/src/version.h @@ -0,0 +1,5 @@ +#include "m_version.h" + +#define __FILEVERSION_STRING MIRANDA_VERSION_FILEVERSION +#define __VERSION_STRING MIRANDA_VERSION_STRING +#define __VERSION_DWORD MIRANDA_VERSION_DWORD -- cgit v1.2.3