diff options
Diffstat (limited to 'plugins/dbrw/events.c')
| -rw-r--r-- | plugins/dbrw/events.c | 461 | 
1 files changed, 461 insertions, 0 deletions
| diff --git a/plugins/dbrw/events.c b/plugins/dbrw/events.c new file mode 100644 index 0000000000..8b494a10b1 --- /dev/null +++ b/plugins/dbrw/events.c @@ -0,0 +1,461 @@ +/*
 +dbRW
 +
 +Copyright (c) 2005-2009 Robert Rainwater
 +
 +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 "dbrw.h"
 +
 +static CRITICAL_SECTION csEventsDb;
 +static HANDLE hEventsThread = 0, hEventsEvent = 0, hHeap;
 +static SortedList sModuleNames;
 +
 +static int events_cmpModuleNames(void *p1, void *p2);
 +static unsigned __stdcall events_timerProcThread(void *arg);
 +
 +typedef struct {
 +	char *name;
 +	DWORD nameHash;
 +} DBCachedModuleName;
 +
 +enum {
 +	SQL_EVT_STMT_COUNT=0,
 +	SQL_EVT_STMT_ADD,
 +	SQL_EVT_STMT_DELETE,
 +	SQL_EVT_STMT_BLOBSIZE,
 +	SQL_EVT_STMT_BLOBSIZE_CACHE,
 +	SQL_EVT_STMT_GET,
 +	SQL_EVT_STMT_GET_CACHE,
 +	SQL_EVT_STMT_GETFLAGS,
 +	SQL_EVT_STMT_SETFLAGS,
 +	SQL_EVT_STMT_GETCONTACT,
 +	SQL_EVT_STMT_GETCONTACT_CACHE,
 +	SQL_EVT_STMT_FINDFIRST,
 +	SQL_EVT_STMT_FINDFIRSTUNREAD,
 +	SQL_EVT_STMT_FINDLAST,
 +	SQL_EVT_STMT_FINDNEXT,
 +	SQL_EVT_STMT_FINDPREV,   
 +	SQL_EVT_STMT_CREATETEMPTABLE,
 +	SQL_EVT_STMT_DROPTEMPTABLE,
 +    SQL_EVT_STMT_DELETECONTACT,
 +	SQL_EVT_STMT_NUM
 +};
 +static char *evt_stmts[SQL_EVT_STMT_NUM] = {
 +	"SELECT count(*) FROM dbrw_events where contactid = ?;",
 +	"INSERT INTO dbrw_events VALUES(NULL,?,?,?,?,?,?,?,?);",
 +	"DELETE FROM dbrw_events where id = ? AND contactid = ?;",
 +	"SELECT blobsize FROM dbrw_events where id = ? LIMIT 1;",
 +	"SELECT blobsize FROM temp_dbrw_events where id = ? LIMIT 1;",
 +	"SELECT * FROM dbrw_events where id = ? LIMIT 1;",
 +    "SELECT * FROM temp_dbrw_events where id = ? LIMIT 1;",
 +	"SELECT flags FROM dbrw_events where id = ? LIMIT 1;",
 +	"UPDATE dbrw_events SET flags = ? WHERE id = ?;",
 +	"SELECT contactid FROM dbrw_events where id = ? LIMIT 1;",
 +	"SELECT contactid FROM temp_dbrw_events where id = ? LIMIT 1;",
 +	"SELECT id FROM dbrw_events where contactid = ? ORDER by id;",
 +	"SELECT flags,id FROM dbrw_events where contactid = ? ORDER by id;",
 +	"SELECT id FROM dbrw_events where contactid = ? ORDER by id DESC;",
 +	"SELECT id FROM dbrw_events where contactid = ? AND id > ? ORDER by id LIMIT 1;",
 +	"SELECT id FROM dbrw_events where contactid = ? AND id < ? ORDER by id DESC LIMIT 1;",
 +    "create temp table temp_dbrw_events (id integer primary key,eventtime integer,flags integer,eventtype integer, blob any, blobsize integer, contactid integer,modulename varchar(255),inserttime integer);"
 +        "create temp trigger insert_new_temp_event1 after insert on dbrw_events begin replace into temp_dbrw_events values(new.id,new.eventtime,new.flags,new.eventtype,new.blob,new.blobsize,new.contactid,new.modulename,new.inserttime); end;"
 +        "create temp trigger insert_new_temp_event2 after update on dbrw_events begin replace into temp_dbrw_events values(new.id,new.eventtime,new.flags,new.eventtype,new.blob,new.blobsize,new.contactid,new.modulename,new.inserttime); end;"
 +        "create temp trigger delete_temp_event after delete on dbrw_events begin delete from temp_dbrw_events where id=old.id and contactid=old.id; end;",
 +    "drop trigger insert_new_temp_event1;"
 +        "drop trigger insert_new_temp_event2;"
 +        "drop trigger delete_temp_event;"
 +        "drop table temp_dbrw_events;",
 +	"DELETE FROM dbrw_events WHERE contactid = ?;"
 +};
 +static sqlite3_stmt *evt_stmts_prep[SQL_EVT_STMT_NUM] = {0};
 +
 +void events_init() {
 +	InitializeCriticalSection(&csEventsDb);
 +    hHeap = HeapCreate(0, 0, 0);
 +    ZeroMemory(&sModuleNames, sizeof(sModuleNames));
 +    sModuleNames.increment = 100;
 +	sModuleNames.sortFunc = events_cmpModuleNames;
 +	sql_prepare_add(evt_stmts, evt_stmts_prep, SQL_EVT_STMT_NUM);
 +    sql_exec(g_sqlite, "BEGIN TRANSACTION;");
 +    sql_exec(g_sqlite, "create temp table temp_dbrw_events (id integer primary key,eventtime integer,flags integer,eventtype integer, blob any, blobsize integer, contactid integer,modulename varchar(255),inserttime integer);"
 +        "create temp trigger insert_new_temp_event1 after insert on dbrw_events begin replace into temp_dbrw_events values(new.id,new.eventtime,new.flags,new.eventtype,new.blob,new.blobsize,new.contactid,new.modulename,new.inserttime); end;"
 +        "create temp trigger insert_new_temp_event2 after update on dbrw_events begin replace into temp_dbrw_events values(new.id,new.eventtime,new.flags,new.eventtype,new.blob,new.blobsize,new.contactid,new.modulename,new.inserttime); end;"
 +        "create temp trigger delete_temp_event after delete on dbrw_events begin delete from temp_dbrw_events where id=old.id and contactid=old.id; end;");
 +    sql_exec(g_sqlite, "COMMIT;");
 +    hEventsEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
 +    hEventsThread = (HANDLE)mir_forkthreadex(events_timerProcThread, 0, 0, 0);
 +}
 +
 +void events_destroy() {
 +    if (hEventsEvent) {
 +        SetEvent(hEventsEvent);
 +        WaitForSingleObjectEx(hEventsThread, INFINITE, FALSE);
 +        CloseHandle(hEventsThread);
 +    }
 +    HeapDestroy(hHeap);
 +    li.List_Destroy(&sModuleNames);
 +	DeleteCriticalSection(&csEventsDb);
 +}
 +
 +static int events_cmpModuleNames(void *p1, void *p2) {
 +	DBCachedModuleName *v1 = (DBCachedModuleName*)p1;
 +	DBCachedModuleName *v2 = (DBCachedModuleName*)p2;
 +	
 +	if (v1->nameHash!=v2->nameHash)
 +		return v1->nameHash-v2->nameHash;
 +	return strcmp(v1->name, v2->name);
 +}
 +
 +static char *events_moduleCacheAdd(char *szModule) {
 +    if (!szModule)
 +        return 0;
 +    {
 +        int idx = 0;
 +        size_t nameLen;
 +		DBCachedModuleName Vtemp, *V;
 +
 +		Vtemp.name = szModule;
 +		Vtemp.nameHash = utils_hashString(szModule);
 +		if (li.List_GetIndex(&sModuleNames, &Vtemp, &idx)) {
 +			V = (DBCachedModuleName*)sModuleNames.items[idx];
 +            return V->name;
 +        }
 +        V = (DBCachedModuleName*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(DBCachedModuleName));
 +        nameLen = strlen(szModule)+1;
 +        V->name = (char*)HeapAlloc(hHeap, 0, nameLen);
 +        mir_snprintf(V->name, nameLen, "%s", szModule);
 +        V->nameHash = utils_hashString(szModule);
 +        li.List_Insert(&sModuleNames, V, idx);
 +        return V->name;
 +    }
 +}
 +
 +static unsigned __stdcall events_timerProcThread(void *arg) {
 +    DWORD dwWait;
 +    
 +    for(;;) {
 +        dwWait = WaitForSingleObjectEx(hEventsEvent, DBRW_EVENTS_FLUSHCACHE, TRUE);
 +
 +        if (dwWait==WAIT_OBJECT_0) 
 +            break;
 +        else if(dwWait == WAIT_TIMEOUT) {
 +            EnterCriticalSection(&csEventsDb);
 +            sql_stmt_begin();
 +            sql_step(evt_stmts_prep[SQL_EVT_STMT_DROPTEMPTABLE]);
 +            sql_step(evt_stmts_prep[SQL_EVT_STMT_CREATETEMPTABLE]);
 +            sql_stmt_end();
 +            sql_reset(evt_stmts_prep[SQL_EVT_STMT_DROPTEMPTABLE]);
 +            sql_reset(evt_stmts_prep[SQL_EVT_STMT_CREATETEMPTABLE]);
 +            LeaveCriticalSection(&csEventsDb);
 +        }
 +        else if (dwWait == WAIT_IO_COMPLETION)
 +            if (Miranda_Terminated()) 
 +                break;
 +    }
 +    CloseHandle(hEventsEvent);
 +    hEventsEvent = NULL;
 +    return 0;
 +}
 +
 +INT_PTR events_getCount(WPARAM wParam, LPARAM lParam) {
 +	int rc = 0;
 +	
 +	EnterCriticalSection(&csEventsDb);
 +    if (!contacts_isContact(wParam, 0)) {
 +        LeaveCriticalSection(&csEventsDb);
 +        return -1;
 +    }  
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 1, (int)wParam);
 +	if (sql_step(evt_stmts_prep[SQL_EVT_STMT_COUNT])==SQLITE_ROW)
 +		rc = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_COUNT], 0);
 +	sql_reset(evt_stmts_prep[SQL_EVT_STMT_COUNT]);
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc;
 +}
 +
 +INT_PTR events_add(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hContact = (HANDLE)wParam;
 +	DBEVENTINFO *dbei = (DBEVENTINFO*)lParam;
 +	int rc = 0;
 +
 +	if (dbei==NULL||
 +			dbei->cbSize!=sizeof(DBEVENTINFO)||
 +			dbei->timestamp==0||
 +			NotifyEventHooks(hEventFilterAddedEvent, wParam, lParam)) {
 +		log1("Attempt to add invalid event for contact(%d)", (int)hContact);
 +		return 0;
 +	}
 +	EnterCriticalSection(&csEventsDb);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_ADD], 1, (int)dbei->timestamp);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_ADD], 2, (int)dbei->flags);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_ADD], 3, (int)dbei->eventType);
 +	sqlite3_bind_blob(evt_stmts_prep[SQL_EVT_STMT_ADD], 4, dbei->pBlob, (int)dbei->cbBlob, SQLITE_STATIC);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_ADD], 5, (int)dbei->cbBlob);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_ADD], 6, (int)hContact);
 +	sqlite3_bind_text(evt_stmts_prep[SQL_EVT_STMT_ADD], 7, dbei->szModule?dbei->szModule:NULL, -1, SQLITE_STATIC);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_ADD], 8, time(NULL));
 +	if (sql_step(evt_stmts_prep[SQL_EVT_STMT_ADD])==SQLITE_DONE) {
 +		rc = (int)sqlite3_last_insert_rowid(g_sqlite);
 +	}
 +	else {
 +		log2("Error adding event(#%d,%s)", sqlite3_errcode(g_sqlite), sqlite3_errmsg(g_sqlite));
 +	}
 +	sql_reset(evt_stmts_prep[SQL_EVT_STMT_ADD]);
 +	LeaveCriticalSection(&csEventsDb);
 +	if (rc) {
 +		NotifyEventHooks(hEventAddedEvent,wParam,(LPARAM)rc);
 +	}
 +	return rc;
 +}
 +
 +INT_PTR events_delete(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hContact = (HANDLE)wParam, hContactFind;
 +	HANDLE hDbEvent = (HANDLE)lParam;
 +	int rc = 1;
 +	
 +    hContactFind = (HANDLE)events_getContact((WPARAM)hDbEvent, 0);
 +    if ((int)hContactFind==-1||hContact!=hContactFind)
 +        return rc;
 +	NotifyEventHooks(hEventDeletedEvent, wParam, lParam);
 +	EnterCriticalSection(&csEventsDb);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_DELETE], 1, (int)hDbEvent);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_DELETE], 2, (int)hContact);
 +	if (sql_step(evt_stmts_prep[SQL_EVT_STMT_DELETE])==SQLITE_DONE) {
 +		log1("Deleted event(%d)", (int)hDbEvent);
 +		rc = 0;
 +	}
 +	sql_reset(evt_stmts_prep[SQL_EVT_STMT_DELETE]);
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc;
 +}
 +
 +static int events_getBlobSizeConditional(HANDLE hDbEvent, int cache) {
 +	int rc = -1;
 +    sqlite3_stmt* stmt;
 +    
 +    EnterCriticalSection(&csEventsDb);
 +    stmt = cache?evt_stmts_prep[SQL_EVT_STMT_BLOBSIZE_CACHE]:evt_stmts_prep[SQL_EVT_STMT_BLOBSIZE];
 +	sqlite3_bind_int(stmt, 1, (int)hDbEvent);
 +    if (sql_step(stmt)==SQLITE_ROW)
 +		rc = sqlite3_column_int(stmt, 0);
 +	sql_reset(stmt);
 +    LeaveCriticalSection(&csEventsDb);
 +    return rc;
 +}
 +
 +INT_PTR events_getBlobSize(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hDbEvent = (HANDLE)wParam;
 +	int rc = events_getBlobSizeConditional(hDbEvent, 1);
 +
 +	if (rc!=-1)
 +        return rc;
 +	return events_getBlobSizeConditional(hDbEvent, 0);;
 +}
 +
 +static int events_getConditional(HANDLE hDbEvent, DBEVENTINFO *dbei, int cache) {
 +	int rc = 1;
 +    sqlite3_stmt* stmt;
 +    
 +	if(dbei==NULL||dbei->cbSize!=sizeof(DBEVENTINFO)) 
 +		return 1;
 +	if(dbei->cbBlob>0&&dbei->pBlob==NULL) {
 +		dbei->cbBlob = 0;
 +		return 1;
 +	}
 +	EnterCriticalSection(&csEventsDb);
 +    stmt = cache?evt_stmts_prep[SQL_EVT_STMT_GET_CACHE]:evt_stmts_prep[SQL_EVT_STMT_GET];
 +	sqlite3_bind_int(stmt, 1, (int)hDbEvent);
 +	if (sql_step(stmt)==SQLITE_ROW) {
 +		unsigned copySize;
 +		const void *blob = sqlite3_column_blob(stmt, 4);
 +		const unsigned size = sqlite3_column_int(stmt, 5);
 +		
 +		dbei->timestamp = (DWORD)sqlite3_column_int(stmt, 1);
 +		dbei->flags = (DWORD)sqlite3_column_int(stmt, 2);
 +		dbei->eventType = (WORD)sqlite3_column_int(stmt, 3);
 +		dbei->szModule = events_moduleCacheAdd((char*)sqlite3_column_text(stmt, 7));
 +		copySize = size<dbei->cbBlob ? size : dbei->cbBlob;
 +		CopyMemory(dbei->pBlob, blob, copySize);
 +		dbei->cbBlob = copySize;
 +		rc = 0;
 +	}
 +	sql_reset(stmt);
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc; 
 +}
 +
 +INT_PTR events_get(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hDbEvent = (HANDLE)wParam;
 +	DBEVENTINFO *dbei = (DBEVENTINFO*)lParam;
 +    int rc = events_getConditional(hDbEvent, dbei, 1);
 +    
 +    if (!rc)
 +        return 0;
 +    return events_getConditional(hDbEvent, dbei, 0);
 +}
 +
 +INT_PTR events_markRead(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hDbEvent = (HANDLE)lParam;
 +	int rc = -1;
 +	
 +	EnterCriticalSection(&csEventsDb);
 +    sql_stmt_begin();
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_GETFLAGS], 1, (int)hDbEvent);
 +	if (sql_step(evt_stmts_prep[SQL_EVT_STMT_GETFLAGS])==SQLITE_ROW) {
 +		DWORD flags = (DWORD)sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_GETFLAGS], 0);
 +		
 +		sql_reset(evt_stmts_prep[SQL_EVT_STMT_GETFLAGS]);
 +        rc = flags;
 +        if (!(flags&DBEF_READ)) {
 +            flags|=DBEF_READ;
 +            sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_SETFLAGS], 1, (int)flags);
 +            sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_SETFLAGS], 2, (int)hDbEvent);
 +            if (sql_step(evt_stmts_prep[SQL_EVT_STMT_SETFLAGS])!=SQLITE_DONE) {
 +                rc = -1;   
 +            }
 +            sql_reset(evt_stmts_prep[SQL_EVT_STMT_SETFLAGS]);
 +        }
 +	}
 +	else sql_reset(evt_stmts_prep[SQL_EVT_STMT_GETFLAGS]);
 +    sql_stmt_end();
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc;
 +}
 +
 +static int events_getContactConditional(HANDLE hDbEvent, int cache) {
 +    int rc = -1;
 +    sqlite3_stmt* stmt;
 +    
 +    EnterCriticalSection(&csEventsDb);
 +    stmt = cache?evt_stmts_prep[SQL_EVT_STMT_GETCONTACT_CACHE]:evt_stmts_prep[SQL_EVT_STMT_GETCONTACT];
 +    sqlite3_bind_int(stmt, 1, (int)hDbEvent);
 +	if (sql_step(stmt)==SQLITE_ROW)
 +		rc = sqlite3_column_int(stmt, 0);
 +    sql_reset(stmt);
 +    LeaveCriticalSection(&csEventsDb);
 +    return rc;
 +}
 +
 +INT_PTR events_getContact(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hDbEvent = (HANDLE)wParam;
 +	int rc = events_getContactConditional(hDbEvent, 1);
 +    
 +    if (rc!=-1)
 +        return rc;
 +	return events_getContactConditional(hDbEvent, 0);
 +}
 +
 +INT_PTR events_findFirst(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hContact = (HANDLE)wParam;
 +	int rc = 0;
 +
 +	EnterCriticalSection(&csEventsDb);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 1, (int)hContact);
 +	if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST])==SQLITE_ROW) {
 +		rc = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST], 0);
 +	}
 +	sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRST]);
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc;
 +}
 +
 +INT_PTR events_findFirstUnread(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hContact = (HANDLE)wParam;
 +	int rc = 0;
 +	DWORD flags = 0;
 +
 +	EnterCriticalSection(&csEventsDb);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1, (int)hContact);
 +	while (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD])==SQLITE_ROW) {
 +		flags = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 0);
 +		if(!(flags&(DBEF_READ|DBEF_SENT))) {
 +			rc = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD], 1);
 +			sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]);
 +			LeaveCriticalSection(&csEventsDb);
 +			return rc;
 +		}
 +	}
 +	sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDFIRSTUNREAD]);
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc;
 +}
 +
 +INT_PTR events_findLast(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hContact = (HANDLE)wParam;
 +	int rc = 0;
 +
 +	EnterCriticalSection(&csEventsDb);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 1, (int)hContact);
 +	if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDLAST])==SQLITE_ROW) 
 +		rc = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDLAST], 0);
 +	sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDLAST]);
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc;
 +}
 +
 +INT_PTR events_findNext(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hDbEvent = (HANDLE)wParam;
 +    int hContact = -1, rc = 0;
 +	
 +	if (hDbEvent==NULL) {
 +		return 0;
 +	}
 +    hContact = events_getContact(wParam, 0);
 +    if (hContact==-1)
 +        return 0;
 +	EnterCriticalSection(&csEventsDb);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 1, hContact);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 2, (int)hDbEvent);
 +	if (sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT])==SQLITE_ROW)
 +		rc = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT], 0);
 +	sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDNEXT]);
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc;
 +}
 +
 +INT_PTR events_findPrev(WPARAM wParam, LPARAM lParam) {
 +	HANDLE hDbEvent = (HANDLE)wParam;
 +	int hContact = -1, rc = 0;
 +
 +	if (hDbEvent==NULL) {
 +		return 0;
 +	}
 +    hContact = events_getContact(wParam, 0);
 +    if (hContact==-1) {
 +        return 0;
 +    }
 +	EnterCriticalSection(&csEventsDb);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 1, hContact);
 +	sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 2, (int)hDbEvent);
 +    rc = sql_step(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]);
 +	if (rc==SQLITE_ROW) {
 +		rc = sqlite3_column_int(evt_stmts_prep[SQL_EVT_STMT_FINDPREV], 0);
 +	}
 +	else {
 +        rc = 0;
 +	}
 +	sql_reset(evt_stmts_prep[SQL_EVT_STMT_FINDPREV]);
 +	LeaveCriticalSection(&csEventsDb);
 +	return rc;
 +}
 +
 +void events_deleteContactData(HANDLE hContact) {
 +    EnterCriticalSection(&csEventsDb);
 +    sqlite3_bind_int(evt_stmts_prep[SQL_EVT_STMT_DELETECONTACT], 1, (int)hContact);
 +    sql_step(evt_stmts_prep[SQL_EVT_STMT_DELETECONTACT]);
 +    sql_reset(evt_stmts_prep[SQL_EVT_STMT_DELETECONTACT]);
 +    LeaveCriticalSection(&csEventsDb);
 +}
 | 
