summaryrefslogtreecommitdiff
path: root/plugins/Db3x_mmap/src/dbtool/eventchain.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Db3x_mmap/src/dbtool/eventchain.cpp')
-rw-r--r--plugins/Db3x_mmap/src/dbtool/eventchain.cpp363
1 files changed, 363 insertions, 0 deletions
diff --git a/plugins/Db3x_mmap/src/dbtool/eventchain.cpp b/plugins/Db3x_mmap/src/dbtool/eventchain.cpp
new file mode 100644
index 0000000000..1e0076371d
--- /dev/null
+++ b/plugins/Db3x_mmap/src/dbtool/eventchain.cpp
@@ -0,0 +1,363 @@
+/*
+Miranda Database Tool
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright 2000-2011 Miranda ICQ/IM 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"
+
+static BOOL backLookup;
+static DWORD ofsThisEvent,ofsPrevEvent;
+static DWORD ofsDestPrevEvent;
+static DWORD eventCount;
+static DWORD lastTimestamp;
+static DWORD ofsFirstUnread,timestampFirstUnread;
+static DWORD memsize = 0;
+static DBEvent* memblock = NULL;
+static DBEvent* dbePrevEvent = NULL;
+
+static void ConvertOldEvent(DBEvent*& dbei)
+{
+ int msglen = (int)strlen((char*)dbei->blob) + 1, msglenW = 0;
+ if (msglen != (int) dbei->cbBlob) {
+ int i, count = ((dbei->cbBlob - msglen) / sizeof(WCHAR));
+ WCHAR* p = (WCHAR*)&dbei->blob[ msglen ];
+ for ( i = 0; i < count; i++) {
+ if (p[i] == 0) {
+ msglenW = i;
+ break;
+ } } }
+ else {
+ if (!is_utf8_string((char*)dbei->blob))
+ dbei->flags &= ~DBEF_UTF;
+ }
+
+ if (msglenW > 0 && msglenW <= msglen) {
+ char* utf8str = Utf8EncodeW((WCHAR*)&dbei->blob[ msglen ]);
+ dbei->cbBlob = (DWORD)strlen(utf8str)+1;
+ dbei->flags |= DBEF_UTF;
+ if (offsetof(DBEvent,blob)+dbei->cbBlob > memsize) {
+ memsize = offsetof(DBEvent,blob)+dbei->cbBlob;
+ memblock = (DBEvent*)realloc(memblock, memsize);
+ dbei = memblock;
+ }
+ memcpy(&dbei->blob, utf8str, dbei->cbBlob);
+ free(utf8str);
+} }
+
+static void WriteOfsNextToPrevious(DWORD ofsPrev,DBContact *dbc,DWORD ofsNext)
+{
+ if (ofsPrev)
+ WriteSegment(ofsPrev+offsetof(DBEvent,ofsNext),&ofsNext,sizeof(DWORD));
+ else
+ dbc->ofsFirstEvent = ofsNext;
+}
+
+static void FinishUp(DWORD ofsLast,DBContact *dbc)
+{
+ WriteOfsNextToPrevious(ofsLast,dbc,0);
+ if (eventCount != dbc->eventCount)
+ AddToStatus(STATUS_WARNING,TranslateT("Event count marked wrongly: correcting"));
+ dbc->eventCount = eventCount;
+ dbc->ofsLastEvent = ofsLast;
+ if (opts.bMarkRead) {
+ dbc->ofsFirstUnreadEvent = 0;
+ dbc->timestampFirstUnread = 0;
+ }
+ else {
+ dbc->ofsFirstUnreadEvent = ofsFirstUnread;
+ dbc->timestampFirstUnread = timestampFirstUnread;
+ }
+ if (memsize && memblock) {
+ free(memblock);
+ memsize = 0;
+ memblock = NULL;
+ }
+}
+
+static DWORD WriteEvent(DBEvent *dbe)
+{
+ DWORD ofs = WriteSegment(WSOFS_END, dbe, offsetof(DBEvent,blob)+dbe->cbBlob);
+ if (ofs == WS_ERROR) {
+ free(memblock);
+ memblock = NULL;
+ memsize = 0;
+ return 0;
+ }
+ return ofs;
+}
+
+int WorkEventChain(DWORD ofsContact,DBContact *dbc,int firstTime)
+{
+ DBEvent *dbeNew,dbeOld;
+ DBEvent *dbePrev = NULL;
+ DWORD ofsDestThis;
+ int isUnread = 0;
+
+ if (firstTime) {
+ dbePrevEvent = NULL;
+ ofsPrevEvent = 0;
+ ofsDestPrevEvent = 0;
+ ofsThisEvent = dbc->ofsFirstEvent;
+ eventCount = 0;
+ backLookup = 0;
+ lastTimestamp = 0;
+ ofsFirstUnread = timestampFirstUnread = 0;
+ if (opts.bEraseHistory) {
+ dbc->eventCount = 0;
+ dbc->ofsFirstEvent = 0;
+ dbc->ofsLastEvent = 0;
+ dbc->ofsFirstUnreadEvent = 0;
+ dbc->timestampFirstUnread = 0;
+ return ERROR_NO_MORE_ITEMS;
+ } }
+
+ if (ofsThisEvent == 0) {
+ FinishUp(ofsDestPrevEvent,dbc);
+ return ERROR_NO_MORE_ITEMS;
+ }
+ if (!SignatureValid(ofsThisEvent,DBEVENT_SIGNATURE))
+ {
+ DWORD ofsNew = 0;
+ DWORD ofsTmp = dbc->ofsLastEvent;
+
+ if (!backLookup && ofsTmp) {
+ backLookup = 1;
+ while(SignatureValid(ofsTmp,DBEVENT_SIGNATURE))
+ {
+ if (PeekSegment(ofsTmp,&dbeOld,sizeof(dbeOld)) != ERROR_SUCCESS)
+ break;
+ ofsNew = ofsTmp;
+ ofsTmp = dbeOld.ofsPrev;
+ }
+ }
+ if (ofsNew) {
+ AddToStatus(STATUS_WARNING,TranslateT("Event chain corrupted, trying to recover..."));
+ ofsThisEvent = ofsNew;
+ } else {
+ AddToStatus(STATUS_ERROR,TranslateT("Event chain corrupted, further entries ignored"));
+ FinishUp(ofsDestPrevEvent,dbc);
+ return ERROR_NO_MORE_ITEMS;
+ }
+ }
+
+ if (PeekSegment(ofsThisEvent,&dbeOld,sizeof(dbeOld)) != ERROR_SUCCESS) {
+ FinishUp(ofsDestPrevEvent,dbc);
+ return ERROR_NO_MORE_ITEMS;
+ }
+
+ if (firstTime) {
+ if (!(dbeOld.flags&DBEF_FIRST)) {
+ AddToStatus(STATUS_WARNING,TranslateT("First event not marked as such: correcting"));
+ dbeOld.flags|=DBEF_FIRST;
+ }
+ dbeOld.ofsPrev = ofsContact;
+ lastTimestamp = dbeOld.timestamp;
+ }
+ else if (dbeOld.flags&DBEF_FIRST) {
+ AddToStatus(STATUS_WARNING,TranslateT("Event marked as first which is not: correcting"));
+ dbeOld.flags&=~DBEF_FIRST;
+ }
+
+ if (dbeOld.flags&~(DBEF_FIRST|DBEF_READ|DBEF_SENT|DBEF_RTL|DBEF_UTF)) {
+ AddToStatus(STATUS_WARNING,TranslateT("Extra flags found in event: removing"));
+ dbeOld.flags&=(DBEF_FIRST|DBEF_READ|DBEF_SENT|DBEF_RTL|DBEF_UTF);
+ }
+
+ if (!(dbeOld.flags&(DBEF_READ|DBEF_SENT))) {
+ if (opts.bMarkRead) dbeOld.flags|=DBEF_READ;
+ else if (ofsFirstUnread == 0) {
+ if (dbc->ofsFirstUnreadEvent != ofsThisEvent || dbc->timestampFirstUnread != dbeOld.timestamp)
+ AddToStatus(STATUS_WARNING,TranslateT("First unread event marked wrong: fixing"));
+ isUnread = 1;
+ } }
+
+ if (dbeOld.cbBlob>1024*1024 || dbeOld.cbBlob == 0) {
+ AddToStatus(STATUS_ERROR,TranslateT("Infeasibly large event blob: skipping"));
+ ofsThisEvent = dbeOld.ofsNext;
+ return ERROR_SUCCESS;
+ }
+
+ if (dbePrevEvent && dbeOld.timestamp == lastTimestamp) {
+ int len = offsetof(DBEvent,blob)+dbePrevEvent->cbBlob;
+ dbePrev = (DBEvent*)malloc(len);
+ memcpy(dbePrev, dbePrevEvent, len);
+ }
+
+ if (offsetof(DBEvent,blob)+dbeOld.cbBlob > memsize) {
+ memsize = offsetof(DBEvent,blob)+dbeOld.cbBlob;
+ memblock = (DBEvent*)realloc(memblock, memsize);
+ }
+ dbeNew = memblock;
+
+ if (ReadSegment(ofsThisEvent,dbeNew,offsetof(DBEvent,blob)+dbeOld.cbBlob) != ERROR_SUCCESS) {
+ FinishUp(ofsDestPrevEvent,dbc);
+ return ERROR_NO_MORE_ITEMS;
+ }
+
+ if ((dbeNew->ofsModuleName = ConvertModuleNameOfs(dbeOld.ofsModuleName)) == 0) {
+ ofsThisEvent = dbeOld.ofsNext;
+ return ERROR_SUCCESS;
+ }
+
+ if (!firstTime && dbeOld.ofsPrev != ofsPrevEvent)
+ AddToStatus(STATUS_WARNING,TranslateT("Event not backlinked correctly: fixing"));
+
+ dbeNew->flags = dbeOld.flags;
+ dbeNew->ofsPrev = ofsDestPrevEvent;
+ dbeNew->ofsNext = 0;
+
+ if (dbeOld.eventType == EVENTTYPE_MESSAGE && opts.bConvertUtf)
+ ConvertOldEvent(dbeNew);
+
+ if (dbePrev)
+ {
+ if (dbePrev->cbBlob == dbeNew->cbBlob &&
+ dbePrev->ofsModuleName == dbeNew->ofsModuleName &&
+ dbePrev->eventType == dbeNew->eventType &&
+ (dbePrev->flags & DBEF_SENT) == (dbeNew->flags & DBEF_SENT) &&
+ !memcmp(dbePrev->blob, dbeNew->blob, dbeNew->cbBlob)
+ ) {
+ AddToStatus(STATUS_WARNING,TranslateT("Duplicate event was found: skipping"));
+ if (dbc->eventCount)
+ dbc->eventCount--;
+ free(dbePrev);
+ // ofsDestPrevEvent is still the same!
+ ofsPrevEvent = ofsThisEvent;
+ ofsThisEvent = dbeOld.ofsNext;
+ return ERROR_SUCCESS;
+ }
+ free(dbePrev);
+ }
+ else if (!firstTime && dbeNew->timestamp < lastTimestamp)
+ {
+ DWORD found = 0;
+ DBEvent dbeTmp;
+ DWORD ofsTmp;
+
+ if (opts.bCheckOnly)
+ {
+ if (!opts.bAggressive)
+ {
+ ofsTmp = dbeOld.ofsPrev;
+ while(PeekSegment(ofsTmp,&dbeTmp,sizeof(dbeTmp)) == ERROR_SUCCESS)
+ {
+ if (dbeTmp.ofsPrev == ofsContact) {
+ found = 1;
+ break;
+ }
+ if (dbeTmp.timestamp < dbeNew->timestamp) {
+ found = 2;
+ break;
+ }
+ ofsTmp = dbeTmp.ofsPrev;
+ }
+ }
+ AddToStatus(STATUS_WARNING,TranslateT("Event position in chain is not correct"));
+ }
+ else
+ {
+ ofsTmp = ofsDestPrevEvent;
+ while(ReadWrittenSegment(ofsTmp,&dbeTmp,sizeof(dbeTmp)) == ERROR_SUCCESS)
+ {
+ if (dbeTmp.ofsPrev == ofsContact) {
+ found = 1;
+ break;
+ }
+ if (dbeTmp.timestamp < dbeNew->timestamp) {
+ found = 2;
+ break;
+ }
+ ofsTmp = dbeTmp.ofsPrev;
+ }
+ if (found)
+ AddToStatus(STATUS_WARNING,TranslateT("Event position in chain is not correct: fixing"));
+ else
+ AddToStatus(STATUS_WARNING,TranslateT("Event position in chain is not correct: unable to fix"));
+ }
+
+ // insert before FIRST
+ if (found == 1 && !opts.bCheckOnly) {
+ dbeNew->flags|=DBEF_FIRST;
+ dbeNew->ofsPrev = ofsContact;
+ dbeNew->ofsNext = dbc->ofsFirstEvent;
+
+ ofsDestThis = WriteEvent(dbeNew);
+ if (!ofsDestThis)
+ return ERROR_HANDLE_DISK_FULL;
+
+ if (isUnread && timestampFirstUnread >= dbeNew->timestamp) {
+ ofsFirstUnread = ofsDestThis;
+ timestampFirstUnread = dbeNew->timestamp;
+ }
+ // fix first event
+ WriteOfsNextToPrevious(0,dbc,ofsDestThis);
+ // fix next event
+ WriteSegment(dbeNew->ofsNext+offsetof(DBEvent,ofsPrev),&ofsDestThis,sizeof(DWORD));
+ dbeTmp.flags &=~DBEF_FIRST;
+ WriteSegment(dbeNew->ofsNext+offsetof(DBEvent,flags),&dbeTmp.flags,sizeof(DWORD));
+ }
+ else if (found == 2 && !opts.bCheckOnly) {
+
+ dbeNew->ofsPrev = ofsTmp;
+ dbeNew->ofsNext = dbeTmp.ofsNext;
+
+ ofsDestThis = WriteEvent(dbeNew);
+ if (!ofsDestThis)
+ return ERROR_HANDLE_DISK_FULL;
+
+ if (isUnread && timestampFirstUnread >= dbeNew->timestamp) {
+ ofsFirstUnread = ofsDestThis;
+ timestampFirstUnread = dbeNew->timestamp;
+ }
+ // fix previous event
+ WriteOfsNextToPrevious(dbeNew->ofsPrev,dbc,ofsDestThis);
+ // fix next event
+ WriteSegment(dbeNew->ofsNext+offsetof(DBEvent,ofsPrev),&ofsDestThis,sizeof(DWORD));
+ }
+
+ if (found) {
+ eventCount++;
+ // ofsDestPrevEvent is still the same!
+ ofsPrevEvent = ofsThisEvent;
+ ofsThisEvent = dbeOld.ofsNext;
+ return ERROR_SUCCESS;
+ }
+ }
+
+ lastTimestamp = dbeNew->timestamp;
+ dbePrevEvent = dbeNew;
+
+ ofsDestThis = WriteEvent(dbeNew);
+ if (!ofsDestThis)
+ return ERROR_HANDLE_DISK_FULL;
+
+ if (isUnread) {
+ ofsFirstUnread = ofsDestThis;
+ timestampFirstUnread = dbeOld.timestamp;
+ }
+
+ eventCount++;
+ WriteOfsNextToPrevious(ofsDestPrevEvent,dbc,ofsDestThis);
+
+ ofsDestPrevEvent = ofsDestThis;
+ ofsPrevEvent = ofsThisEvent;
+ ofsThisEvent = dbeOld.ofsNext;
+ return ERROR_SUCCESS;
+}