diff options
Diffstat (limited to 'plugins/Dbrw/settings.c')
-rw-r--r-- | plugins/Dbrw/settings.c | 908 |
1 files changed, 908 insertions, 0 deletions
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; idx<sGlobalSettings.realCount; idx++) {
+ V = (DBCachedGlobalValue*)sGlobalSettings.items[idx];
+ if (V->update) {
+ 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; idx<sContactSettings.realCount; idx++) {
+ VL = (DBCachedContactValueList*)sContactSettings.items[idx];
+ for (VI=VL->first; 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 (cbLen<cbOrigLen)
+ cbOrigLen = cbLen;
+ memcpy(dbcgs->pValue->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;
+}
|