From 48540940b6c28bb4378abfeb500ec45a625b37b6 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Tue, 15 May 2012 10:38:20 +0000 Subject: initial commit git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/dbrw/settings.c | 908 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 908 insertions(+) create mode 100644 plugins/dbrw/settings.c (limited to 'plugins/dbrw/settings.c') diff --git a/plugins/dbrw/settings.c b/plugins/dbrw/settings.c new file mode 100644 index 0000000000..9944127411 --- /dev/null +++ b/plugins/dbrw/settings.c @@ -0,0 +1,908 @@ +/* +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" + +CRITICAL_SECTION csSettingsDb; +static HANDLE hHeap = 0, hSettingsThread = 0, hSettingsEvent = 0; +static SortedList sSettingNames, sContactSettings, sGlobalSettings, sResidentSettings; +static int mirCp = CP_ACP; + +static int settings_cmpSettingNames(void *p1, void *p2); +static int settings_cmpGlobalSettings(void* p1, void* p2); +static int settings_cmpContactSettings(void *p1, void *p2); +static int settings_cmpResidentSettings(void *p1, void *p2); +static char *settings_getCachedSettingName(const char *szModuleName, const char *szSettingName); +static void settings_setCachedValueUpdateStatus(HANDLE hContact, char *szSetting, int update); +static void settings_freeCachedVariant(DBVARIANT* V); +static DBVARIANT *settings_getCachedValue(HANDLE hContact, char *szSetting, int bAllocate); +static int settings_getContactSettingWorker(HANDLE hContact, DBCONTACTGETSETTING *dbcgs, int isStatic); +static void settings_writeToDB(HANDLE hContact, const char *szModule, const char *szSetting, DBVARIANT *value); +static void settings_writeUpdatedSettings(); +static unsigned __stdcall settings_threadProc(void *arg); +static int settings_isResident(char *szSetting); + +typedef struct { + char *name; + DWORD nameHash; + DBVARIANT value; + int update; +} DBCachedGlobalValue; + +typedef struct DBCachedContactValue_tag { + char *name; + DWORD nameHash; + DBVARIANT value; + int update; + struct DBCachedContactValue_tag *next; +} DBCachedContactValue; + +typedef struct { + HANDLE hContact; + HANDLE hNext; + DBCachedContactValue *first; +} DBCachedContactValueList; + +typedef struct { + char *name; + DWORD nameHash; + char *module; + DWORD moduleHash; +} DBCachedResidentSettingValue; + +enum { + SQL_SET_STMT_REPLACE=0, + SQL_SET_STMT_DELETE, + SQL_SET_STMT_READ, + SQL_SET_STMT_ENUM, + SQL_SET_STMT_ENUMMODULES, + SQL_SET_STMT_SETTINGCHECK, + SQL_SET_STMT_DELETECONTACT, + SQL_SET_STMT_NUM +}; +char *settings_stmts[SQL_SET_STMT_NUM] = { + "REPLACE INTO dbrw_settings VALUES(?,?,?,?,?);", + "DELETE FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ?;", + "SELECT type,val FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ? LIMIT 1;", + "SELECT setting from dbrw_settings where id = ? AND module = ? ORDER by setting;", + "SELECT DISTINCT module from dbrw_settings;", + "SELECT count(*) FROM dbrw_settings WHERE setting = ? AND module = ? AND id = ?;", + "DELETE FROM dbrw_settings WHERE id = ?;" +}; +static sqlite3_stmt *settings_stmts_prep[SQL_SET_STMT_NUM] = {0}; + +void settings_init() { + InitializeCriticalSection(&csSettingsDb); + hHeap = HeapCreate(0, 0, 0); + ZeroMemory(&sSettingNames, sizeof(sSettingNames)); + ZeroMemory(&sContactSettings, sizeof(sContactSettings)); + ZeroMemory(&sGlobalSettings, sizeof(sGlobalSettings)); + ZeroMemory(&sResidentSettings, sizeof(sResidentSettings)); + sSettingNames.increment = 100; + sSettingNames.sortFunc = settings_cmpSettingNames; + sContactSettings.increment = 100; + sContactSettings.sortFunc = settings_cmpContactSettings; + sGlobalSettings.increment = 100; + sGlobalSettings.sortFunc = settings_cmpGlobalSettings; + sResidentSettings.increment = 100; + sResidentSettings.sortFunc = settings_cmpResidentSettings; + sql_prepare_add(settings_stmts, settings_stmts_prep, SQL_SET_STMT_NUM); + mirCp = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); + hSettingsEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + hSettingsThread = (HANDLE)mir_forkthreadex(settings_threadProc, 0, 0, 0); +} + +void settings_destroy() { + if (hSettingsEvent) { + SetEvent(hSettingsEvent); + WaitForSingleObjectEx(hSettingsThread, INFINITE, FALSE); + CloseHandle(hSettingsThread); + } + settings_writeUpdatedSettings(); + HeapDestroy(hHeap); + li.List_Destroy(&sSettingNames); + li.List_Destroy(&sContactSettings); + li.List_Destroy(&sGlobalSettings); + li.List_Destroy(&sResidentSettings); + DeleteCriticalSection(&csSettingsDb); +} + +static int settings_cmpSettingNames(void *p1, void *p2) { + return strcmp((char*)p1, (char*)p2); +} + +static int settings_cmpGlobalSettings(void *p1, void *p2) { + DBCachedGlobalValue *v1 = (DBCachedGlobalValue*)p1; + DBCachedGlobalValue *v2 = (DBCachedGlobalValue*)p2; + + if (v1->nameHash!=v2->nameHash) + return v1->nameHash-v2->nameHash; + return strcmp(v1->name, v2->name); +} + +static int settings_cmpContactSettings(void *p1, void *p2) { + if (*(long*)p1==*(long*)p2) + return 0; + return *(long*)p1-*(long*)p2; +} + +static int settings_cmpResidentSettings(void *p1, void *p2) { + DBCachedResidentSettingValue *v1 = (DBCachedResidentSettingValue*)p1; + DBCachedResidentSettingValue *v2 = (DBCachedResidentSettingValue*)p2; + + if (v1->nameHash!=v2->nameHash) + return v1->nameHash-v2->nameHash; + return strcmp(v1->name, v2->name); +} + +static char *settings_getCachedSettingName(const char *szModuleName, const char *szSettingName) { + size_t nameLen = strlen(szModuleName)+strlen(szSettingName)+2; + int idx; + char *szFullName = (char*)alloca(nameLen), *ret; + + mir_snprintf(szFullName, nameLen, "%s/%s", szModuleName, szSettingName); + if (li.List_GetIndex(&sSettingNames, szFullName, &idx)) + return (char*)sSettingNames.items[idx]; + ret = (char*)HeapAlloc(hHeap, 0, nameLen); + mir_snprintf(ret, nameLen, "%s", szFullName); + li.List_Insert(&sSettingNames, ret, idx); + return ret; +} + +static void settings_setCachedValueUpdateStatus(HANDLE hContact, char *szSetting, int update) { + int idx; + + if (hContact==0) { + DBCachedGlobalValue Vtemp, *V; + + Vtemp.name = szSetting; + Vtemp.nameHash = utils_hashString(szSetting); + if (li.List_GetIndex(&sGlobalSettings, &Vtemp, &idx)) { + V = (DBCachedGlobalValue*)sGlobalSettings.items[idx]; + if (settings_isResident(szSetting)) { + V->update = 0; + } + else { + V->update = update; + } + } + return; + } + else { + DBCachedContactValue *V; + DBCachedContactValueList VLtemp,*VL; + DWORD hash = utils_hashString(szSetting); + + VLtemp.hContact = hContact; + if (li.List_GetIndex(&sContactSettings, &VLtemp, &idx)) { + VL = (DBCachedContactValueList*)sContactSettings.items[idx]; + } + else return; + for (V = VL->first; V!=NULL; V=V->next) + if ((hash==V->nameHash)&&(strcmp(V->name, szSetting)==0)) + break; + if (V) { + if (settings_isResident(szSetting)) { + V->update = 0; + } + else { + V->update = update; + } + } + } +} + +static void settings_freeCachedVariant(DBVARIANT* V) { + if ((V->type==DBVT_ASCIIZ||V->type==DBVT_UTF8)&&V->pszVal!=NULL) + HeapFree(hHeap, 0, V->pszVal); +} + +static void settings_setCachedVariant(DBVARIANT *VNew, DBVARIANT *VCached) { + char *szSave = (VCached->type==DBVT_UTF8||VCached->type==DBVT_ASCIIZ) ? VCached->pszVal : NULL; + + memcpy(VCached, VNew, sizeof(DBVARIANT)); + if ((VNew->type==DBVT_UTF8||VNew->type==DBVT_ASCIIZ )&&VNew->pszVal!=NULL) { + if (szSave!=NULL) + VCached->pszVal = (char*)HeapReAlloc(hHeap, 0, szSave, strlen(VNew->pszVal)+1); + else + VCached->pszVal = (char*)HeapAlloc(hHeap, 0, strlen(VNew->pszVal)+1); + strcpy(VCached->pszVal, VNew->pszVal); + } +} + +static DBVARIANT *settings_getCachedValue(HANDLE hContact, char *szSetting, int bAllocate) { + int idx; + + if (hContact==0) { + DBCachedGlobalValue Vtemp, *V; + + Vtemp.name = szSetting; + Vtemp.nameHash = utils_hashString(szSetting); + if (li.List_GetIndex(&sGlobalSettings, &Vtemp, &idx)) { + V = (DBCachedGlobalValue*)sGlobalSettings.items[idx]; + if (bAllocate==-1) { + settings_freeCachedVariant(&V->value); + li.List_Remove(&sGlobalSettings, idx); + HeapFree(hHeap, 0, V); + return NULL; + } + } + else { + if (bAllocate!=1) + return NULL; + V = (DBCachedGlobalValue*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(DBCachedGlobalValue)); + V->name = szSetting; + V->nameHash = utils_hashString(szSetting); + V->update = 0; + li.List_Insert(&sGlobalSettings, V, idx); + } + return &V->value; + } + else { + DBCachedContactValue *V, *V1; + DBCachedContactValueList VLtemp,*VL; + DWORD hash = utils_hashString(szSetting); + + VLtemp.hContact = hContact; + if (li.List_GetIndex(&sContactSettings, &VLtemp, &idx)) { + VL = (DBCachedContactValueList*)sContactSettings.items[idx]; + } + else { + if (bAllocate==-1) + return NULL; + VL = (DBCachedContactValueList*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(DBCachedContactValueList)); + VL->hContact = hContact; + li.List_Insert(&sContactSettings, VL, idx); + } + for (V = VL->first; V!=NULL; V=V->next) + if ((hash==V->nameHash)&&(strcmp(V->name, szSetting)==0)) + break; + if (V==NULL) { + if (bAllocate!=1) + return NULL; + V = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(DBCachedContactValue)); + V->next = VL->first; + VL->first = V; + V->name = szSetting; + V->nameHash = hash; + V->update = 0; + } + else if (bAllocate==-1) { + settings_freeCachedVariant(&V->value); + if (VL->first==V) + VL->first = V->next; + for (V1=VL->first; V1!=NULL; V1=V1->next) { + if (V1->next==V) { + V1->next = V->next; + break; + } + } + HeapFree(hHeap, 0, V); + return NULL; + } + return &V->value; + } +} + +static void settings_writeToDB(HANDLE hContact, const char *szModule, const char *szSetting, DBVARIANT *value) { + // TODO: Check the parameters + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_REPLACE], 1, (int)hContact); + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_REPLACE], 2, szModule, -1, SQLITE_STATIC); + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_REPLACE], 3, szSetting, -1, SQLITE_STATIC); + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_REPLACE], 4, (int)value->type); + switch (value->type) { + case DBVT_BYTE: + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_REPLACE], 5, (int)value->bVal); + break; + case DBVT_WORD: + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_REPLACE], 5, (int)value->wVal); + break; + case DBVT_DWORD: + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_REPLACE], 5, (int)value->dVal); + break; + case DBVT_UTF8: + case DBVT_ASCIIZ: + if (value->pszVal) + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_REPLACE], 5, value->pszVal, -1, SQLITE_STATIC); + break; + case DBVT_BLOB: + if (value->pbVal) { + sqlite3_bind_blob(settings_stmts_prep[SQL_SET_STMT_REPLACE], 5, value->pbVal, value->cpbVal, SQLITE_STATIC); + } + break; + default: + sql_reset(settings_stmts_prep[SQL_SET_STMT_REPLACE]); + return; + } + if (sql_step(settings_stmts_prep[SQL_SET_STMT_REPLACE])!=SQLITE_DONE) { + log2("Error writing: %s/%s", szModule, szSetting); + } + sql_reset(settings_stmts_prep[SQL_SET_STMT_REPLACE]); +} + +static void settings_writeUpdatedSettings() { + int idx, dbWrite = 0; + DBCachedGlobalValue *V; + DBCachedContactValueList *VL; + DBCachedContactValue *VI; + char *szTok, *szTokTmp1, *szTokTmp2; + + EnterCriticalSection(&csSettingsDb); + for (idx=0; idxupdate) { + szTok = dbrw_alloc(strlen(V->name)+1); + strcpy(szTok, V->name); + szTokTmp1 = strtok(szTok, "/"); + if (szTokTmp1) { + szTokTmp2 = szTok+strlen(szTokTmp1)+1; + if (szTokTmp2) { + if (!dbWrite) { + sql_stmt_begin(); + dbWrite = 1; + } + settings_writeToDB(0, szTokTmp1, szTokTmp2, &V->value); + } + } + dbrw_free(szTok); + V->update = 0; + } + } + for (idx=0; idxfirst; VI!=NULL; VI=VI->next) { + if (VI->update) { + szTok = dbrw_alloc(strlen(VI->name)+1); + strcpy(szTok, VI->name); + szTokTmp1 = strtok(szTok, "/"); + if (szTokTmp1) { + szTokTmp2 = szTok+strlen(szTokTmp1)+1; + if (szTokTmp2) { + if (!dbWrite) { + sql_stmt_begin(); + dbWrite = 1; + } + settings_writeToDB(VL->hContact, szTokTmp1, szTokTmp2, &VI->value); + } + } + dbrw_free(szTok); + VI->update = 0; + } + } + } + if (dbWrite) + sql_stmt_end(); + LeaveCriticalSection(&csSettingsDb); +} + +static unsigned __stdcall settings_threadProc(void *arg) { + DWORD dwWait; + + for(;;) { + dwWait = WaitForSingleObjectEx(hSettingsEvent, DBRW_SETTINGS_FLUSHCACHE, TRUE); + + if (dwWait==WAIT_OBJECT_0) + break; + else if(dwWait == WAIT_TIMEOUT) { + settings_writeUpdatedSettings(); + } + else if (dwWait == WAIT_IO_COMPLETION) + if (Miranda_Terminated()) + break; + } + CloseHandle(hSettingsEvent); + hSettingsEvent = NULL; + return 0; +} + +static int settings_getContactSettingWorker(HANDLE hContact, DBCONTACTGETSETTING *dbcgs, int isStatic) { + char* szCachedSettingName; + + if (!dbcgs->szSetting||!dbcgs->szModule) + return 1; + + szCachedSettingName = settings_getCachedSettingName(dbcgs->szModule, dbcgs->szSetting); + { + DBVARIANT *pCachedValue = settings_getCachedValue(hContact, szCachedSettingName, 0); + + if (pCachedValue!=NULL) { + if (pCachedValue->type==DBVT_ASCIIZ||pCachedValue->type==DBVT_UTF8) { + size_t cbOrigLen = dbcgs->pValue->cchVal; + char *cbOrigPtr = dbcgs->pValue->pszVal; + + memcpy(dbcgs->pValue, pCachedValue, sizeof(DBVARIANT)); + if (isStatic) { + size_t cbLen = 0; + + if (pCachedValue->pszVal!=NULL) + cbLen = strlen(pCachedValue->pszVal); + cbOrigLen--; + dbcgs->pValue->pszVal = cbOrigPtr; + if (cbLenpValue->pszVal, pCachedValue->pszVal, cbOrigLen); + dbcgs->pValue->pszVal[cbOrigLen] = 0; + dbcgs->pValue->cchVal = (WORD)cbLen; + } + else { + dbcgs->pValue->pszVal = (char*)dbrw_alloc(strlen(pCachedValue->pszVal)+1); + strcpy(dbcgs->pValue->pszVal, pCachedValue->pszVal); + } + } + else { + memcpy(dbcgs->pValue, pCachedValue, sizeof(DBVARIANT)); + } + return (pCachedValue->type==DBVT_DELETED) ? 1 : 0; + } + } + if (settings_isResident(szCachedSettingName)) { + return 1; + } + // Read from db + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_READ], 1, dbcgs->szSetting, -1, SQLITE_STATIC); + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_READ], 2, dbcgs->szModule, -1, SQLITE_STATIC); + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_READ], 3, (int)hContact); + if (sql_step(settings_stmts_prep[SQL_SET_STMT_READ])!=SQLITE_ROW) { + if (dbcgs->pValue->type!=DBVT_BLOB) { + DBVARIANT* pCachedValue = settings_getCachedValue(hContact, szCachedSettingName, 1); + + if (pCachedValue!=NULL) + pCachedValue->type = DBVT_DELETED; + } + sql_reset(settings_stmts_prep[SQL_SET_STMT_READ]); + return 1; + } + dbcgs->pValue->type = (int)sqlite3_column_int(settings_stmts_prep[SQL_SET_STMT_READ], 0); + switch(dbcgs->pValue->type) { + case DBVT_BYTE: + dbcgs->pValue->bVal = (BYTE)sqlite3_column_int(settings_stmts_prep[SQL_SET_STMT_READ], 1); + break; + case DBVT_WORD: + dbcgs->pValue->wVal = (WORD)sqlite3_column_int(settings_stmts_prep[SQL_SET_STMT_READ], 1); + break; + case DBVT_DWORD: + dbcgs->pValue->dVal = (DWORD)sqlite3_column_int(settings_stmts_prep[SQL_SET_STMT_READ], 1); + break; + case DBVT_UTF8: + case DBVT_ASCIIZ: + { + const char *p = sqlite3_column_text(settings_stmts_prep[SQL_SET_STMT_READ], 1); + + if (p!=NULL) { + size_t len = strlen(p) + 1; + size_t copylen = isStatic ? (len < dbcgs->pValue->cchVal ? len : dbcgs->pValue->cchVal) : len; + if (!isStatic) + dbcgs->pValue->pszVal = dbrw_alloc(len); + memmove(dbcgs->pValue->pszVal, p, copylen); + } + else { + dbcgs->pValue->pszVal = 0; + } + break; + } + case DBVT_BLOB: + { + size_t len = sqlite3_column_bytes(settings_stmts_prep[SQL_SET_STMT_READ], 1); + + if (len) { + size_t copylen = isStatic ? ( len < dbcgs->pValue->cpbVal ? len : dbcgs->pValue->cpbVal ) : len; + if (!isStatic) + dbcgs->pValue->pbVal=dbrw_alloc(copylen); + CopyMemory(dbcgs->pValue->pbVal, sqlite3_column_blob(settings_stmts_prep[SQL_SET_STMT_READ], 1), copylen); + dbcgs->pValue->cpbVal = (WORD)copylen; + } + else { + dbcgs->pValue = 0; + } + } + } + sql_reset(settings_stmts_prep[SQL_SET_STMT_READ]); + // Insert in cache + if ( dbcgs->pValue->type!=DBVT_BLOB) { + DBVARIANT *pCachedValue = settings_getCachedValue(hContact, szCachedSettingName, 1); + if (pCachedValue!=NULL) + settings_setCachedVariant(dbcgs->pValue, pCachedValue); + } + return 0; +} + +INT_PTR setting_getSetting(WPARAM wParam, LPARAM lParam) { + DBCONTACTGETSETTING* dgs = (DBCONTACTGETSETTING*)lParam; + + EnterCriticalSection(&csSettingsDb); + dgs->pValue->type = 0; + if (settings_getContactSettingWorker((HANDLE)wParam, dgs, 0)) { + LeaveCriticalSection(&csSettingsDb); + return 1; + } + if (dgs->pValue->type==DBVT_UTF8 ) { + WCHAR* tmp = mir_utf8decodeW(dgs->pValue->pszVal); + if (tmp) { + BOOL bUsed = FALSE; + int result = WideCharToMultiByte(CP_ACP, 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 = mir_alloc(result); + WideCharToMultiByte(mirCp, 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); + } + } + LeaveCriticalSection(&csSettingsDb); + return 0; +} + +INT_PTR setting_getSettingStr(WPARAM wParam, LPARAM lParam) { + DBCONTACTGETSETTING* dgs = (DBCONTACTGETSETTING*)lParam; + int iSaveType = dgs->pValue->type; + + EnterCriticalSection(&csSettingsDb); + if (settings_getContactSettingWorker((HANDLE)wParam, dgs, 0)) { + LeaveCriticalSection(&csSettingsDb); + return 1; + } + if (iSaveType==0||iSaveType==dgs->pValue->type) { + LeaveCriticalSection(&csSettingsDb); + return 0; + } + if (dgs->pValue->type!=DBVT_ASCIIZ&&dgs->pValue->type!=DBVT_UTF8) { + LeaveCriticalSection(&csSettingsDb); + return 0; + } + 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*)dbrw_alloc((len+1)*sizeof(wchar_t)); + + if (wszResult==NULL) { + LeaveCriticalSection(&csSettingsDb); + return 1; + } + MultiByteToWideChar(CP_ACP, 0, dgs->pValue->pszVal, -1, wszResult, len); + wszResult[len] = 0; + dbrw_free(dgs->pValue->pszVal); + dgs->pValue->pwszVal = wszResult; + } + else { + char *savePtr = dgs->pValue->pszVal; + + mir_utf8decode(dgs->pValue->pszVal, &dgs->pValue->pwszVal); + dbrw_free(savePtr); + } + } + else if (iSaveType==DBVT_UTF8) { + char *tmpBuf = mir_utf8encode(dgs->pValue->pszVal); + + if (tmpBuf==NULL) { + LeaveCriticalSection(&csSettingsDb); + return 1; + } + dbrw_free(dgs->pValue->pszVal); + dgs->pValue->pszVal = tmpBuf; + } + else if (iSaveType==DBVT_ASCIIZ) + mir_utf8decode(dgs->pValue->pszVal, NULL); + dgs->pValue->type = iSaveType; + LeaveCriticalSection(&csSettingsDb); + return 0; +} + +INT_PTR setting_getSettingStatic(WPARAM wParam, LPARAM lParam) { + DBCONTACTGETSETTING* dgs = (DBCONTACTGETSETTING*)lParam; + + EnterCriticalSection(&csSettingsDb); + if (settings_getContactSettingWorker((HANDLE)wParam, dgs, 1)) { + LeaveCriticalSection(&csSettingsDb); + return 1; + } + if (dgs->pValue->type==DBVT_UTF8 ) { + mir_utf8decode(dgs->pValue->pszVal, NULL); + dgs->pValue->type = DBVT_ASCIIZ; + } + LeaveCriticalSection(&csSettingsDb); + return 0; +} + +INT_PTR setting_freeVariant(WPARAM wParam, LPARAM lParam) { + DBVARIANT *dbv = (DBVARIANT*)lParam; + if (dbv==0) + return 1; + switch (dbv->type) { + case DBVT_ASCIIZ: + case DBVT_UTF8: + case DBVT_WCHAR: + { + if (dbv->pszVal) + dbrw_free(dbv->pszVal); + dbv->pszVal = 0; + break; + } + case DBVT_BLOB: + { + if (dbv->pbVal) + dbrw_free(dbv->pbVal); + dbv->pbVal = 0; + break; + } + } + dbv->type = 0; + return 0; +} + +INT_PTR setting_writeSetting(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + DBCONTACTWRITESETTING *dbcws = (DBCONTACTWRITESETTING*)lParam; + + if (dbcws==NULL) + return 1; + if (dbcws->value.type==DBVT_WCHAR) { + if (dbcws->value.pszVal!=NULL) { + char *val = mir_utf8encodeW(dbcws->value.pwszVal); + if (val== NULL) + return 1; + dbcws->value.pszVal = (char*)alloca(strlen(val)+1); + strcpy(dbcws->value.pszVal, val); + dbrw_free(val); + dbcws->value.type = DBVT_UTF8; + } + else return 1; + } + if (dbcws->value.type!=DBVT_BYTE&& + dbcws->value.type!=DBVT_WORD&& + dbcws->value.type!=DBVT_DWORD&& + dbcws->value.type!=DBVT_ASCIIZ&& + dbcws->value.type!=DBVT_UTF8&& + dbcws->value.type!=DBVT_BLOB) + return 1; + if ((!dbcws->szModule)||(!dbcws->szSetting)|| + ((dbcws->value.type==DBVT_ASCIIZ||dbcws->value.type==DBVT_UTF8)&&dbcws->value.pszVal == NULL)|| + (dbcws->value.type==DBVT_BLOB&&dbcws->value.pbVal==NULL)) + return 1; + EnterCriticalSection(&csSettingsDb); + { + char *szCachedSettingName = settings_getCachedSettingName(dbcws->szModule, dbcws->szSetting); + if (dbcws->value.type!=DBVT_BLOB) { + DBVARIANT *pCachedValue = settings_getCachedValue(hContact, szCachedSettingName, 1); + + if (pCachedValue!=NULL) { + BOOL isIdentical = FALSE; + + if (pCachedValue->type==dbcws->value.type) { + switch(pCachedValue->type) { + case DBVT_BYTE: + isIdentical = pCachedValue->bVal==dbcws->value.bVal; + break; + case DBVT_WORD: + isIdentical = pCachedValue->wVal==dbcws->value.wVal; + break; + case DBVT_DWORD: + isIdentical = pCachedValue->dVal==dbcws->value.dVal; + break; + case DBVT_UTF8: + case DBVT_ASCIIZ: + isIdentical = strcmp(pCachedValue->pszVal, dbcws->value.pszVal)==0; + break; + } + if (isIdentical) { + LeaveCriticalSection(&csSettingsDb); + return 0; + } + } + settings_setCachedVariant(&dbcws->value, pCachedValue); + // set key to write on timer update + settings_setCachedValueUpdateStatus(hContact, szCachedSettingName, 1); + } + } + else settings_getCachedValue(hContact, szCachedSettingName, -1); + } + // Only write blobs to the db immediately (do we want to cache blobs?) + if (dbcws->value.type==DBVT_BLOB) { + settings_writeToDB(hContact, dbcws->szModule, dbcws->szSetting, &dbcws->value); + } + LeaveCriticalSection(&csSettingsDb); + NotifyEventHooks(hSettingChangeEvent, wParam, lParam); + return 0; +} + +INT_PTR setting_deleteSetting(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + DBCONTACTGETSETTING *dbcgs = (DBCONTACTGETSETTING*)lParam; + + if (!dbcgs->szModule||!dbcgs->szSetting) + return 1; + EnterCriticalSection(&csSettingsDb); + { + int rc = 0; + char *szCachedSettingName = settings_getCachedSettingName(dbcgs->szModule, dbcgs->szSetting); + + // remove setting from cache + if (szCachedSettingName) + settings_getCachedValue(hContact, szCachedSettingName, -1); + + // check if exists + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_SETTINGCHECK], 1, dbcgs->szSetting, -1, SQLITE_STATIC); + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_SETTINGCHECK], 2, dbcgs->szModule, -1, SQLITE_STATIC); + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_SETTINGCHECK], 3, (int)hContact); + if (sql_step(settings_stmts_prep[SQL_SET_STMT_SETTINGCHECK])==SQLITE_ROW) + rc = sqlite3_column_int(settings_stmts_prep[SQL_SET_STMT_SETTINGCHECK], 0); + if (rc==0) { // should really check not 1 but lets be nice + sql_reset(settings_stmts_prep[SQL_SET_STMT_SETTINGCHECK]); + LeaveCriticalSection(&csSettingsDb); + return 1; + } + sql_reset(settings_stmts_prep[SQL_SET_STMT_SETTINGCHECK]); + + // Delete from db + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_DELETE], 1, dbcgs->szSetting, -1, SQLITE_STATIC); + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_DELETE], 2, dbcgs->szModule, -1, SQLITE_STATIC); + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_DELETE], 3, (int)hContact); + if (sql_step(settings_stmts_prep[SQL_SET_STMT_DELETE])!=SQLITE_DONE) { + sql_reset(settings_stmts_prep[SQL_SET_STMT_DELETE]); + LeaveCriticalSection(&csSettingsDb); + return 1; + } + sql_reset(settings_stmts_prep[SQL_SET_STMT_DELETE]); + } + LeaveCriticalSection(&csSettingsDb); + { + DBCONTACTWRITESETTING dbcws; + dbcws.szModule = dbcgs->szModule; + dbcws.szSetting = dbcgs->szSetting; + dbcws.value.type = DBVT_DELETED; + NotifyEventHooks(hSettingChangeEvent, wParam, (LPARAM)&dbcws); + } + return 0; +} + +INT_PTR setting_enumSettings(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + DBCONTACTENUMSETTINGS *dbces = (DBCONTACTENUMSETTINGS*)lParam; + int rc = -1; + + if (!dbces->szModule) + return -1; + settings_writeUpdatedSettings(); + EnterCriticalSection(&csSettingsDb); + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_ENUM], 1, (int)hContact); + sqlite3_bind_text(settings_stmts_prep[SQL_SET_STMT_ENUM], 2, dbces->szModule, -1, SQLITE_STATIC); + while (sql_step(settings_stmts_prep[SQL_SET_STMT_ENUM])==SQLITE_ROW) { + const char *sczSetting = sqlite3_column_text(settings_stmts_prep[SQL_SET_STMT_ENUM], 0); + if (sczSetting) { + char * szCachedSetting = settings_getCachedSettingName(dbces->szModule, sczSetting); + if (szCachedSetting&&!settings_isResident(szCachedSetting)) { + rc = (dbces->pfnEnumProc)(sczSetting,dbces->lParam); + } + } + } + sql_reset(settings_stmts_prep[SQL_SET_STMT_ENUM]); + LeaveCriticalSection(&csSettingsDb); + return rc; +} + +INT_PTR setting_modulesEnum(WPARAM wParam, LPARAM lParam) { + DBMODULEENUMPROC proc = (DBMODULEENUMPROC)lParam; + LPARAM lParamReal = (LPARAM)wParam; + int rc = 0; + int offset = 1; + const char *szModule; + + /* Flush db to disk */ + settings_writeUpdatedSettings(); + /* End flush */ + EnterCriticalSection(&csSettingsDb); + while (sql_step(settings_stmts_prep[SQL_SET_STMT_ENUMMODULES])==SQLITE_ROW && !rc) { + szModule = sqlite3_column_text(settings_stmts_prep[SQL_SET_STMT_ENUMMODULES], 0); + LeaveCriticalSection(&csSettingsDb); + rc = (proc)(szModule, (DWORD)offset++, lParamReal); + EnterCriticalSection(&csSettingsDb); + } + sql_reset(settings_stmts_prep[SQL_SET_STMT_ENUMMODULES]); + LeaveCriticalSection(&csSettingsDb); + return rc; +} + +// Assume critical section +void settings_deleteContactData(HANDLE hContact) { + int idx; + DBCachedContactValueList VLtemp,*VL; + DBCachedContactValue *V; + + EnterCriticalSection(&csSettingsDb); + VLtemp.hContact = hContact; + if (li.List_GetIndex(&sContactSettings, &VLtemp, &idx)) { + VL = (DBCachedContactValueList*)sContactSettings.items[idx]; + + V = VL->first; + while (V) { + settings_freeCachedVariant(&V->value); + V->value.type = DBVT_DELETED; + V = V->next; + } + } + sqlite3_bind_int(settings_stmts_prep[SQL_SET_STMT_DELETECONTACT], 1, (int)hContact); + sql_step(settings_stmts_prep[SQL_SET_STMT_DELETECONTACT]); + sql_reset(settings_stmts_prep[SQL_SET_STMT_DELETECONTACT]); + LeaveCriticalSection(&csSettingsDb); +} + +INT_PTR settings_setResident(WPARAM wParam, LPARAM lParam) { + EnterCriticalSection(&csSettingsDb); + { + DBCachedResidentSettingValue Vtemp, *V; + int resident = (int)wParam, idx; + char *szSetting = (char*)lParam; + + if (!szSetting) { + LeaveCriticalSection(&csSettingsDb); + return 0; + } + Vtemp.name = szSetting; + Vtemp.nameHash = utils_hashString(szSetting); + if (li.List_GetIndex(&sResidentSettings, &Vtemp, &idx)) { + if (!resident) { + V = (DBCachedResidentSettingValue*)sResidentSettings.items[idx]; + li.List_Remove(&sResidentSettings, idx); + HeapFree(hHeap, 0, V->name); + HeapFree(hHeap, 0, V->module); + HeapFree(hHeap, 0, V); + } + LeaveCriticalSection(&csSettingsDb); + return 0; + } + else { + size_t nameLen = strlen(szSetting)+1; + + V = (DBCachedResidentSettingValue*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(DBCachedResidentSettingValue)); + V->name = (char*)HeapAlloc(hHeap, 0, nameLen); + mir_snprintf(V->name, nameLen, "%s", szSetting); + V->nameHash = utils_hashString(szSetting); + V->module = (char*)HeapAlloc(hHeap, 0, nameLen); + mir_snprintf(V->module, nameLen, "%s", szSetting); + V->module = strtok(V->module, "/"); + V->moduleHash = utils_hashString(V->module); + li.List_Insert(&sResidentSettings, V, idx); + } + LeaveCriticalSection(&csSettingsDb); + } + return 0; +} + +// Assume critical section +static int settings_isResident(char *szSetting) { + DBCachedResidentSettingValue Vtemp; + int idx; + + Vtemp.name = szSetting; + Vtemp.nameHash = utils_hashString(szSetting); + if (li.List_GetIndex(&sResidentSettings, &Vtemp, &idx)) { + return 1; + } + return 0; +} -- cgit v1.2.3