summaryrefslogtreecommitdiff
path: root/plugins/Db3x_mmap/src
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Db3x_mmap/src')
-rw-r--r--plugins/Db3x_mmap/src/commonheaders.h62
-rw-r--r--plugins/Db3x_mmap/src/database.cpp121
-rw-r--r--plugins/Db3x_mmap/src/database.h70
-rw-r--r--plugins/Db3x_mmap/src/dbcache.cpp175
-rw-r--r--plugins/Db3x_mmap/src/dbcontacts.cpp244
-rw-r--r--plugins/Db3x_mmap/src/dbevents.cpp355
-rw-r--r--plugins/Db3x_mmap/src/dbheaders.cpp65
-rw-r--r--plugins/Db3x_mmap/src/dbintf.cpp174
-rw-r--r--plugins/Db3x_mmap/src/dbintf.h289
-rw-r--r--plugins/Db3x_mmap/src/dbmodulechain.cpp138
-rw-r--r--plugins/Db3x_mmap/src/dbsettings.cpp933
-rw-r--r--plugins/Db3x_mmap/src/init.cpp160
-rw-r--r--plugins/Db3x_mmap/src/resource.h30
-rw-r--r--plugins/Db3x_mmap/src/version.h5
14 files changed, 2821 insertions, 0 deletions
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 <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <stddef.h>
+#include <process.h>
+#include <io.h>
+#include <string.h>
+#include <direct.h>
+#ifndef __GNUC__
+#include <crtdbg.h>
+#endif
+
+#include <newpluginapi.h>
+#include <win2k.h>
+#include <m_system.h>
+#include <m_system_cpp.h>
+#include <m_database.h>
+#include <m_langpack.h>
+#include <m_utils.h>
+
+#include "database.h"
+#include "dbintf.h"
+#include "resource.h"
+#include "version.h"
+
+extern LIST<CDdxMmap> 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.timestamp<dbc.timestampFirstUnread || dbc.timestampFirstUnread == 0) {
+ dbc.timestampFirstUnread = dbe.timestamp;
+ dbc.ofsFirstUnreadEvent = ofsNew;
+ }
+ neednotify = TRUE;
+ }
+ else neednotify = m_safetyMode;
+
+ DBWrite(ofsContact,&dbc,sizeof(DBContact));
+ DBWrite(ofsNew,&dbe,offsetof(DBEvent,blob));
+ EncodeDBWrite(ofsNew+offsetof(DBEvent,blob),dbei->pBlob,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<DBCachedContactValueList>::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 <m_db_int.h>
+
+/* 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 <pshpack1.h>
+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 <poppack.h>
+
+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<DBCachedContactValueList> m_lContacts;
+ LIST<DBCachedGlobalValue> m_lGlobalSettings;
+ LIST<char> 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<ModuleName> 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 (cbLen<cbOrigLen) cbOrigLen = cbLen;
+ CopyMemory(dbcgs->pValue->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)<dbcgs->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)<dbcgs->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<CDdxMmap> g_Dbs(1, (LIST<CDdxMmap>::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