summaryrefslogtreecommitdiff
path: root/plugins/Dbx_sqlite/src/dbsettings.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Dbx_sqlite/src/dbsettings.cpp')
-rw-r--r--plugins/Dbx_sqlite/src/dbsettings.cpp368
1 files changed, 368 insertions, 0 deletions
diff --git a/plugins/Dbx_sqlite/src/dbsettings.cpp b/plugins/Dbx_sqlite/src/dbsettings.cpp
new file mode 100644
index 0000000000..121f2c73fc
--- /dev/null
+++ b/plugins/Dbx_sqlite/src/dbsettings.cpp
@@ -0,0 +1,368 @@
+#include "stdafx.h"
+
+enum {
+ SQL_SET_STMT_ENUM = 0,
+ SQL_SET_STMT_GET,
+ SQL_SET_STMT_REPLACE,
+ SQL_SET_STMT_DELETE,
+ SQL_SET_STMT_ENUMMODULE,
+ SQL_SET_STMT_NUM
+};
+
+static char *settings_stmts[SQL_SET_STMT_NUM] = {
+ "select distinct module from settings;",
+ "select type, value from settings where contactid = ? and module = ? and setting = ? limit 1;",
+ "replace into settings(contactid, module, setting, type, value) values (?, ?, ?, ?, ?);",
+ "delete from settings where contactid = ? and module = ? and setting = ?;",
+ "select setting from settings where contactid = ? and module = ?;"
+};
+
+static sqlite3_stmt *settings_stmts_prep[SQL_SET_STMT_NUM] = { 0 };
+
+void CDbxSQLite::InitSettings()
+{
+ for (size_t i = 0; i < SQL_SET_STMT_NUM; i++)
+ sqlite3_prepare_v3(m_db, settings_stmts[i], -1, SQLITE_PREPARE_PERSISTENT, &settings_stmts_prep[i], nullptr);
+ /*DBVARIANT dbv = {};
+ sqlite3_stmt *stmt = nullptr;
+ sqlite3_prepare_v2(m_db, "select contactid, module, setting, type, value from settings", -1, &stmt, nullptr);
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ MCONTACT hContact = sqlite3_column_int64(stmt, 0);
+ const char *module = (const char*)sqlite3_column_text(stmt, 1);
+ const char *setting = (const char*)sqlite3_column_text(stmt, 2);
+ dbv.type = sqlite3_column_int(stmt, 3);
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ dbv.bVal = sqlite3_column_int(stmt, 4);
+ break;
+ case DBVT_WORD:
+ dbv.wVal = sqlite3_column_int(stmt, 4);
+ break;
+ case DBVT_DWORD:
+ dbv.dVal = sqlite3_column_int64(stmt, 4);
+ break;
+ case DBVT_ASCIIZ:
+ {
+ char *utf8 = mir_strdup((const char*)sqlite3_column_text(stmt, 4));
+ dbv.pszVal = mir_utf8decode(utf8, nullptr);
+ break;
+ }
+ case DBVT_UTF8:
+ dbv.pszVal = mir_strdup((const char*)sqlite3_column_text(stmt, 4));
+ break;
+ case DBVT_BLOB:
+ continue;
+ }
+ char *cachedSettingName = m_cache->GetCachedSetting(module, setting, mir_strlen(module), mir_strlen(setting));
+ DBVARIANT *cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ m_cache->SetCachedVariant(&dbv, cachedValue);
+ if(dbv.type == DBVT_ASCIIZ || dbv.type == DBVT_UTF8)
+ mir_free(dbv.pszVal);
+ }
+ sqlite3_finalize(stmt);*/
+}
+
+void CDbxSQLite::UninitSettings()
+{
+ for (size_t i = 0; i < SQL_SET_STMT_NUM; i++)
+ sqlite3_finalize(settings_stmts_prep[i]);
+}
+
+BOOL CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *param)
+{
+ LIST<char> modules(100);
+ {
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_ENUM];
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ const char *value = (const char*)sqlite3_column_text(stmt, 0);
+ modules.insert(mir_strdup(value));
+ }
+ sqlite3_reset(stmt);
+ }
+
+ int result = -1;
+ for (auto &module : modules) {
+ result = pFunc(module, param);
+ mir_free(module);
+ }
+ return result;
+}
+
+BOOL CDbxSQLite::GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT *dbv, int isStatic)
+{
+ if (szSetting == nullptr || szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ mir_cslock lock(m_csDbAccess);
+
+ char *cachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting));
+ DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 0);
+ if (pCachedValue != nullptr) {
+ if (pCachedValue->type == DBVT_UTF8) {
+ int cbOrigLen = dbv->cchVal;
+ char *cbOrigPtr = dbv->pszVal;
+ memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+ if (isStatic) {
+ int cbLen = 0;
+ if (pCachedValue->pszVal != nullptr)
+ cbLen = (int)mir_strlen(pCachedValue->pszVal);
+
+ cbOrigLen--;
+ dbv->pszVal = cbOrigPtr;
+ if (cbLen < cbOrigLen)
+ cbOrigLen = cbLen;
+ memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
+ dbv->pszVal[cbOrigLen] = 0;
+ dbv->cchVal = cbLen;
+ }
+ else {
+ dbv->pszVal = (char*)mir_alloc(mir_strlen(pCachedValue->pszVal) + 1);
+ mir_strcpy(dbv->pszVal, pCachedValue->pszVal);
+ }
+ }
+ else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+
+ return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
+ }
+
+ // never look db for the resident variable
+ if (cachedSettingName[-1] != 0)
+ return 1;
+
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_GET];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr);
+ sqlite3_bind_text(stmt, 3, szSetting, mir_strlen(szSetting), nullptr);
+ int rc = sqlite3_step(stmt);
+ if (rc != SQLITE_ROW) {
+ sqlite3_reset(stmt);
+ return 1;
+ }
+ dbv->type = (int)sqlite3_column_int(stmt, 0);
+ switch (dbv->type) {
+ case DBVT_DELETED:
+ dbv->type = DBVT_DELETED;
+ sqlite3_reset(stmt);
+ return 2;
+ case DBVT_BYTE:
+ dbv->bVal = sqlite3_column_int(stmt, 1);
+ break;
+ case DBVT_WORD:
+ dbv->wVal = sqlite3_column_int(stmt, 1);
+ break;
+ case DBVT_DWORD:
+ dbv->dVal = sqlite3_column_int64(stmt, 1);
+ break;
+ case DBVT_UTF8:
+ {
+ dbv->cchVal = sqlite3_column_bytes(stmt, 1);
+ const char *value = (const char*)sqlite3_column_text(stmt, 1);
+ if (!isStatic)
+ dbv->pszVal = (char*)mir_alloc(dbv->cchVal + 1);
+ memcpy(dbv->pszVal, value, dbv->cchVal);
+ dbv->pszVal[dbv->cchVal] = 0;
+ break;
+ }
+ case DBVT_BLOB:
+ {
+ dbv->cpbVal = sqlite3_column_bytes(stmt, 1);
+ const char *data = (const char*)sqlite3_column_blob(stmt, 1);
+ if (!isStatic)
+ dbv->pbVal = (BYTE*)mir_alloc(dbv->cpbVal + 1);
+ memcpy(dbv->pbVal, data, dbv->cpbVal);
+ break;
+ }
+ }
+ sqlite3_reset(stmt);
+
+ // add to cache
+ if (dbv->type != DBVT_BLOB) {
+ pCachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ if (pCachedValue != nullptr)
+ m_cache->SetCachedVariant(dbv, pCachedValue);
+ }
+
+ return 0;
+}
+
+BOOL CDbxSQLite::WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws)
+{
+ if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ DBCONTACTWRITESETTING dbcwNotif = *dbcws;
+ // we work only with utf-8 inside
+ switch (dbcwNotif.value.type) {
+ case DBVT_UTF8:
+ dbcwNotif.value.pszVal = mir_strdup(dbcws->value.pszVal);
+ break;
+ case DBVT_ASCIIZ:
+ {
+ ptrA value(mir_utf8encode(dbcws->value.pszVal));
+ dbcwNotif.value.pszVal = NEWSTR_ALLOCA(value);
+ dbcwNotif.value.type = DBVT_UTF8;
+ break;
+ }
+ case DBVT_WCHAR:
+ {
+ T2Utf value(dbcwNotif.value.pwszVal);
+ dbcwNotif.value.pszVal = NEWSTR_ALLOCA(value);
+ dbcwNotif.value.type = DBVT_UTF8;
+ break;
+ }
+ }
+ DBCONTACTWRITESETTING dbcwWork = dbcwNotif;
+ if (dbcwWork.value.type == DBVT_UTF8)
+ dbcwWork.value.cchVal = (WORD)strlen(dbcwWork.value.pszVal);
+
+ mir_cslockfull lock(m_csDbAccess);
+
+ char *cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting));
+ bool isResident = cachedSettingName[-1] != 0;
+
+ // we don't cache blobs
+ if (dbcwWork.value.type != DBVT_BLOB) {
+ DBVARIANT *cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ if (cachedValue != nullptr) {
+ bool isIdentical = false;
+ if (cachedValue->type == dbcwWork.value.type) {
+ switch (dbcwWork.value.type) {
+ case DBVT_BYTE:
+ isIdentical = cachedValue->bVal == dbcwWork.value.bVal;
+ break;
+ case DBVT_WORD:
+ isIdentical = cachedValue->wVal == dbcwWork.value.wVal;
+ break;
+ case DBVT_DWORD:
+ isIdentical = cachedValue->dVal == dbcwWork.value.dVal;
+ break;
+ case DBVT_UTF8:
+ isIdentical = mir_strcmp(cachedValue->pszVal, dbcwWork.value.pszVal) == 0;
+ break;
+ }
+ if (isIdentical)
+ return 0;
+ }
+ m_cache->SetCachedVariant(&dbcwWork.value, cachedValue);
+ }
+ if (isResident) {
+ lock.unlock();
+ NotifyEventHooks(hSettingChangeEvent, hContact, (LPARAM)&dbcwWork);
+ return 0;
+ }
+ }
+ else m_cache->GetCachedValuePtr(hContact, cachedSettingName, -1);
+
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_REPLACE];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, dbcwWork.szModule, mir_strlen(dbcwWork.szModule), nullptr);
+ sqlite3_bind_text(stmt, 3, dbcwWork.szSetting, mir_strlen(dbcwWork.szSetting), nullptr);
+ sqlite3_bind_int(stmt, 4, dbcwWork.value.type);
+ switch (dbcwWork.value.type) {
+ case DBVT_BYTE:
+ sqlite3_bind_int(stmt, 5, dbcwWork.value.bVal);
+ break;
+ case DBVT_WORD:
+ sqlite3_bind_int(stmt, 5, dbcwWork.value.wVal);
+ break;
+ case DBVT_DWORD:
+ sqlite3_bind_int64(stmt, 5, dbcwWork.value.dVal);
+ break;
+ case DBVT_UTF8:
+ sqlite3_bind_text(stmt, 5, dbcwWork.value.pszVal, dbcwWork.value.cchVal, nullptr);
+ break;
+ case DBVT_BLOB:
+ sqlite3_bind_blob(stmt, 5, dbcwWork.value.pbVal, dbcwWork.value.cpbVal, nullptr);
+ break;
+ }
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return 1;
+
+ lock.unlock();
+
+ NotifyEventHooks(hSettingChangeEvent, hContact, (LPARAM)&dbcwNotif);
+
+ return 0;
+}
+
+BOOL CDbxSQLite::DeleteContactSetting(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting)
+{
+ if (szSetting == nullptr || szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting));
+ if (szCachedSettingName[-1] == 0) // it's not a resident variable
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_DELETE];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr);
+ sqlite3_bind_text(stmt, 3, szSetting, mir_strlen(szSetting), nullptr);
+ int rc = sqlite3_step(stmt);
+ sqlite3_reset(stmt);
+ if (rc != SQLITE_DONE)
+ return 1;
+ }
+ m_cache->GetCachedValuePtr(hContact, szCachedSettingName, -1);
+
+ // notify
+ DBCONTACTWRITESETTING dbcws = { };
+ dbcws.szModule = szModule;
+ dbcws.szSetting = szSetting;
+ dbcws.value.type = DBVT_DELETED;
+ NotifyEventHooks(hSettingChangeEvent, hContact, (LPARAM)&dbcws);
+
+ return 0;
+}
+
+BOOL CDbxSQLite::EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param)
+{
+ if (szModule == nullptr)
+ return -1;
+
+ if (hContact) {
+ DBCachedContact *cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return -1;
+ }
+
+ LIST<char> settings(100);
+ {
+ mir_cslock lock(m_csDbAccess);
+ sqlite3_stmt *stmt = settings_stmts_prep[SQL_SET_STMT_ENUMMODULE];
+ sqlite3_bind_int64(stmt, 1, hContact);
+ sqlite3_bind_text(stmt, 2, szModule, mir_strlen(szModule), nullptr);
+ while (sqlite3_step(stmt) == SQLITE_ROW) {
+ const char *value = (const char*)sqlite3_column_text(stmt, 0);
+ settings.insert(mir_strdup(value));
+ }
+ sqlite3_reset(stmt);
+ }
+
+ int result = -1;
+ for (auto &setting : settings) {
+ result = pfnEnumProc(setting, param);
+ mir_free(setting);
+ }
+ return result;
+}