From de40f3be3f08487937525c2ef096dad665dda61d Mon Sep 17 00:00:00 2001 From: dartraiden Date: Sat, 14 Jan 2023 01:30:59 +0300 Subject: Convert sources to CR+LF --- plugins/Dbx_sqlite/src/dbcheck.cpp | 220 +++--- plugins/Dbx_sqlite/src/dbcontacts.cpp | 226 +++--- plugins/Dbx_sqlite/src/dbevents.cpp | 1398 ++++++++++++++++----------------- plugins/Dbx_sqlite/src/dbintf.cpp | 482 ++++++------ plugins/Dbx_sqlite/src/dbintf.h | 376 ++++----- plugins/Dbx_sqlite/src/dbsettings.cpp | 450 +++++------ 6 files changed, 1576 insertions(+), 1576 deletions(-) (limited to 'plugins/Dbx_sqlite/src') diff --git a/plugins/Dbx_sqlite/src/dbcheck.cpp b/plugins/Dbx_sqlite/src/dbcheck.cpp index 658d48cce6..7019503eec 100644 --- a/plugins/Dbx_sqlite/src/dbcheck.cpp +++ b/plugins/Dbx_sqlite/src/dbcheck.cpp @@ -1,110 +1,110 @@ -#include "stdafx.h" - -///////////////////////////////////////////////////////////////////////////////////////// -// first try to find events with wrong contact ids - -int CDbxSQLite::CheckPhase1() -{ - sqlite3_stmt *pQuery; - int rc = sqlite3_prepare_v2(m_db, "SELECT id, contact_id FROM events WHERE contact_id <> 0 AND contact_id NOT IN (SELECT id FROM contacts)", -1, &pQuery, nullptr); - logError(rc, __FILE__, __LINE__); - if (rc) - return rc; - - while (sqlite3_step(pQuery) == SQLITE_ROW) { - MEVENT hDbEvent = sqlite3_column_int(pQuery, 0); - MCONTACT hContact = sqlite3_column_int(pQuery, 1); - cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned event with wrong contact ID %d, deleting"), hContact)); - DeleteEvent(hDbEvent); - } - - sqlite3_finalize(pQuery); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// then we're wiping orphaned records from Events_srt which have no parent record in Events - -int CDbxSQLite::CheckPhase2() -{ - sqlite3_stmt *pQuery; - int rc = sqlite3_prepare_v2(m_db, "SELECT id, contact_id, timestamp FROM events_srt WHERE id NOT IN (SELECT id FROM events)", -1, &pQuery, nullptr); - logError(rc, __FILE__, __LINE__); - if (rc) - return rc; - - while (sqlite3_step(pQuery) == SQLITE_ROW) { - MEVENT hDbEvent = sqlite3_column_int(pQuery, 0); - MCONTACT hContact = sqlite3_column_int(pQuery, 1); - uint32_t ts = sqlite3_column_int(pQuery, 2); - - DeleteEventSrt(hDbEvent, hContact, ts); - cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong event ID %d:%08X, deleting"), hContact, hDbEvent)); - } - - sqlite3_finalize(pQuery); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// now try to find orphans in backward direction: from Events_srt to Events - -int CDbxSQLite::CheckPhase3() -{ - sqlite3_stmt *pQuery; - int rc = sqlite3_prepare_v2(m_db, "SELECT id, contact_id FROM events WHERE id NOT IN (SELECT id FROM events_srt)", -1, &pQuery, nullptr); - logError(rc, __FILE__, __LINE__); - if (rc) - return rc; - - while (sqlite3_step(pQuery) == SQLITE_ROW) { - MEVENT hDbEvent = sqlite3_column_int(pQuery, 0); - MCONTACT hContact = sqlite3_column_int(pQuery, 1); - - DeleteEventMain(hDbEvent); - cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned event with wrong event ID %d:%08X, deleting"), hContact, hDbEvent)); - } - - sqlite3_finalize(pQuery); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// remove settings with wrong contact ids - -int CDbxSQLite::CheckPhase4() -{ - sqlite3_stmt *pQuery; - int rc = sqlite3_prepare_v2(m_db, "SELECT contact_id,module,setting FROM settings WHERE contact_id <> 0 AND contact_id NOT IN (SELECT id FROM contacts)", -1, &pQuery, nullptr); - logError(rc, __FILE__, __LINE__); - if (rc) - return rc; - - while (sqlite3_step(pQuery) == SQLITE_ROW) { - MCONTACT hContact = sqlite3_column_int(pQuery, 0); - auto *szModule = (const char *)sqlite3_column_text(pQuery, 1); - auto *szSetting = (const char *)sqlite3_column_text(pQuery, 2); - - cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned setting [%S:%S] with wrong contact ID %d, deleting"), szModule, szSetting, hContact)); - DeleteContactSettingWorker(hContact, szModule, szSetting); - } - - sqlite3_finalize(pQuery); - return 0; -} - -/////////////////////////////////////////////////////////////////////////////// -// MIDatabaseChecker - -int CDbxSQLite::CheckDb(int phase) -{ - switch (phase) { - case 0: return CheckPhase1(); - case 1: return CheckPhase2(); - case 2: return CheckPhase3(); - case 3: return CheckPhase4(); - } - - DBFlush(true); - return ERROR_OUT_OF_PAPER; -} +#include "stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// +// first try to find events with wrong contact ids + +int CDbxSQLite::CheckPhase1() +{ + sqlite3_stmt *pQuery; + int rc = sqlite3_prepare_v2(m_db, "SELECT id, contact_id FROM events WHERE contact_id <> 0 AND contact_id NOT IN (SELECT id FROM contacts)", -1, &pQuery, nullptr); + logError(rc, __FILE__, __LINE__); + if (rc) + return rc; + + while (sqlite3_step(pQuery) == SQLITE_ROW) { + MEVENT hDbEvent = sqlite3_column_int(pQuery, 0); + MCONTACT hContact = sqlite3_column_int(pQuery, 1); + cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned event with wrong contact ID %d, deleting"), hContact)); + DeleteEvent(hDbEvent); + } + + sqlite3_finalize(pQuery); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// then we're wiping orphaned records from Events_srt which have no parent record in Events + +int CDbxSQLite::CheckPhase2() +{ + sqlite3_stmt *pQuery; + int rc = sqlite3_prepare_v2(m_db, "SELECT id, contact_id, timestamp FROM events_srt WHERE id NOT IN (SELECT id FROM events)", -1, &pQuery, nullptr); + logError(rc, __FILE__, __LINE__); + if (rc) + return rc; + + while (sqlite3_step(pQuery) == SQLITE_ROW) { + MEVENT hDbEvent = sqlite3_column_int(pQuery, 0); + MCONTACT hContact = sqlite3_column_int(pQuery, 1); + uint32_t ts = sqlite3_column_int(pQuery, 2); + + DeleteEventSrt(hDbEvent, hContact, ts); + cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned sorting event with wrong event ID %d:%08X, deleting"), hContact, hDbEvent)); + } + + sqlite3_finalize(pQuery); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// now try to find orphans in backward direction: from Events_srt to Events + +int CDbxSQLite::CheckPhase3() +{ + sqlite3_stmt *pQuery; + int rc = sqlite3_prepare_v2(m_db, "SELECT id, contact_id FROM events WHERE id NOT IN (SELECT id FROM events_srt)", -1, &pQuery, nullptr); + logError(rc, __FILE__, __LINE__); + if (rc) + return rc; + + while (sqlite3_step(pQuery) == SQLITE_ROW) { + MEVENT hDbEvent = sqlite3_column_int(pQuery, 0); + MCONTACT hContact = sqlite3_column_int(pQuery, 1); + + DeleteEventMain(hDbEvent); + cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned event with wrong event ID %d:%08X, deleting"), hContact, hDbEvent)); + } + + sqlite3_finalize(pQuery); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// remove settings with wrong contact ids + +int CDbxSQLite::CheckPhase4() +{ + sqlite3_stmt *pQuery; + int rc = sqlite3_prepare_v2(m_db, "SELECT contact_id,module,setting FROM settings WHERE contact_id <> 0 AND contact_id NOT IN (SELECT id FROM contacts)", -1, &pQuery, nullptr); + logError(rc, __FILE__, __LINE__); + if (rc) + return rc; + + while (sqlite3_step(pQuery) == SQLITE_ROW) { + MCONTACT hContact = sqlite3_column_int(pQuery, 0); + auto *szModule = (const char *)sqlite3_column_text(pQuery, 1); + auto *szSetting = (const char *)sqlite3_column_text(pQuery, 2); + + cb->pfnAddLogMessage(STATUS_ERROR, CMStringW(FORMAT, TranslateT("Orphaned setting [%S:%S] with wrong contact ID %d, deleting"), szModule, szSetting, hContact)); + DeleteContactSettingWorker(hContact, szModule, szSetting); + } + + sqlite3_finalize(pQuery); + return 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// MIDatabaseChecker + +int CDbxSQLite::CheckDb(int phase) +{ + switch (phase) { + case 0: return CheckPhase1(); + case 1: return CheckPhase2(); + case 2: return CheckPhase3(); + case 3: return CheckPhase4(); + } + + DBFlush(true); + return ERROR_OUT_OF_PAPER; +} diff --git a/plugins/Dbx_sqlite/src/dbcontacts.cpp b/plugins/Dbx_sqlite/src/dbcontacts.cpp index 1cd6229da3..f66e5840e8 100644 --- a/plugins/Dbx_sqlite/src/dbcontacts.cpp +++ b/plugins/Dbx_sqlite/src/dbcontacts.cpp @@ -1,113 +1,113 @@ -#include "stdafx.h" - -void CDbxSQLite::InitContacts() -{ - sqlite3_stmt *stmt = nullptr; - sqlite3_prepare_v2(m_db, "SELECT contacts.id, COUNT(es.id) FROM contacts LEFT JOIN events_srt es ON es.contact_id = contacts.id GROUP BY contacts.id;", -1, &stmt, nullptr); - int rc = 0; - while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { - MCONTACT hContact = sqlite3_column_int64(stmt, 0); - DBCachedContact *cc = (hContact) ? m_cache->AddContactToCache(hContact) : &m_system; - cc->m_count = sqlite3_column_int64(stmt, 1); - } - logError(rc, __FILE__, __LINE__); - sqlite3_finalize(stmt); -} - -int CDbxSQLite::GetContactCount() -{ - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("SELECT COUNT(1) FROM contacts LIMIT 1;", qCntCount); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - int count = sqlite3_column_int(stmt, 0); - sqlite3_reset(stmt); - return count; -} - -MCONTACT CDbxSQLite::AddContact() -{ - MCONTACT hContact = INVALID_CONTACT_ID; - { - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("INSERT INTO contacts VALUES (null);", qCntAdd); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - if (rc != SQLITE_DONE) - return INVALID_CONTACT_ID; - hContact = sqlite3_last_insert_rowid(m_db); - DBFlush(); - } - - DBCachedContact *cc = m_cache->AddContactToCache(hContact); - if (cc == nullptr) - return INVALID_CONTACT_ID; - - NotifyEventHooks(g_hevContactAdded, hContact); - return hContact; -} - -int CDbxSQLite::DeleteContact(MCONTACT hContact) -{ - // global contact cannot be removed - if (hContact == 0) - return 1; - - DBCachedContact *cc = m_cache->GetCachedContact(hContact); - if (cc == nullptr) - return 1; - - NotifyEventHooks(g_hevContactDeleted, hContact); - - mir_cslockfull lock(m_csDbAccess); - - sqlite3_stmt *stmt = InitQuery("DELETE FROM events WHERE contact_id = ?;", qCntDelEvents); - sqlite3_bind_int64(stmt, 1, hContact); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - if (rc != SQLITE_DONE) - return 1; - - stmt = InitQuery("DELETE FROM events_srt WHERE contact_id = ?;", qCntDelEventSrt); - sqlite3_bind_int64(stmt, 1, hContact); - rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - if (rc != SQLITE_DONE) - return 1; - - stmt = InitQuery("DELETE FROM settings WHERE contact_id = ?;", qCntDelSettings); - sqlite3_bind_int64(stmt, 1, hContact); - rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - if (rc != SQLITE_DONE) - return 1; - - stmt = InitQuery("DELETE FROM contacts WHERE id = ?;", qCntDel); - sqlite3_bind_int64(stmt, 1, hContact); - rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - if (rc != SQLITE_DONE) - return 1; - - m_cache->FreeCachedContact(hContact); - lock.unlock(); - - DBFlush(); - return 0; -} - -BOOL CDbxSQLite::IsDbContact(MCONTACT hContact) -{ - DBCachedContact *cc = m_cache->GetCachedContact(hContact); - return (cc != nullptr); -} - -int CDbxSQLite::GetContactSize(void) -{ - return sizeof(DBCachedContact); -} +#include "stdafx.h" + +void CDbxSQLite::InitContacts() +{ + sqlite3_stmt *stmt = nullptr; + sqlite3_prepare_v2(m_db, "SELECT contacts.id, COUNT(es.id) FROM contacts LEFT JOIN events_srt es ON es.contact_id = contacts.id GROUP BY contacts.id;", -1, &stmt, nullptr); + int rc = 0; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + MCONTACT hContact = sqlite3_column_int64(stmt, 0); + DBCachedContact *cc = (hContact) ? m_cache->AddContactToCache(hContact) : &m_system; + cc->m_count = sqlite3_column_int64(stmt, 1); + } + logError(rc, __FILE__, __LINE__); + sqlite3_finalize(stmt); +} + +int CDbxSQLite::GetContactCount() +{ + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("SELECT COUNT(1) FROM contacts LIMIT 1;", qCntCount); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + int count = sqlite3_column_int(stmt, 0); + sqlite3_reset(stmt); + return count; +} + +MCONTACT CDbxSQLite::AddContact() +{ + MCONTACT hContact = INVALID_CONTACT_ID; + { + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("INSERT INTO contacts VALUES (null);", qCntAdd); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return INVALID_CONTACT_ID; + hContact = sqlite3_last_insert_rowid(m_db); + DBFlush(); + } + + DBCachedContact *cc = m_cache->AddContactToCache(hContact); + if (cc == nullptr) + return INVALID_CONTACT_ID; + + NotifyEventHooks(g_hevContactAdded, hContact); + return hContact; +} + +int CDbxSQLite::DeleteContact(MCONTACT hContact) +{ + // global contact cannot be removed + if (hContact == 0) + return 1; + + DBCachedContact *cc = m_cache->GetCachedContact(hContact); + if (cc == nullptr) + return 1; + + NotifyEventHooks(g_hevContactDeleted, hContact); + + mir_cslockfull lock(m_csDbAccess); + + sqlite3_stmt *stmt = InitQuery("DELETE FROM events WHERE contact_id = ?;", qCntDelEvents); + sqlite3_bind_int64(stmt, 1, hContact); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return 1; + + stmt = InitQuery("DELETE FROM events_srt WHERE contact_id = ?;", qCntDelEventSrt); + sqlite3_bind_int64(stmt, 1, hContact); + rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return 1; + + stmt = InitQuery("DELETE FROM settings WHERE contact_id = ?;", qCntDelSettings); + sqlite3_bind_int64(stmt, 1, hContact); + rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return 1; + + stmt = InitQuery("DELETE FROM contacts WHERE id = ?;", qCntDel); + sqlite3_bind_int64(stmt, 1, hContact); + rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return 1; + + m_cache->FreeCachedContact(hContact); + lock.unlock(); + + DBFlush(); + return 0; +} + +BOOL CDbxSQLite::IsDbContact(MCONTACT hContact) +{ + DBCachedContact *cc = m_cache->GetCachedContact(hContact); + return (cc != nullptr); +} + +int CDbxSQLite::GetContactSize(void) +{ + return sizeof(DBCachedContact); +} diff --git a/plugins/Dbx_sqlite/src/dbevents.cpp b/plugins/Dbx_sqlite/src/dbevents.cpp index f4a4618c2e..c4af7cf487 100644 --- a/plugins/Dbx_sqlite/src/dbevents.cpp +++ b/plugins/Dbx_sqlite/src/dbevents.cpp @@ -1,699 +1,699 @@ -#include "stdafx.h" - -//TODO: hide it inside cursor class -static const char normal_order_query[] = "SELECT id FROM events_srt WHERE contact_id = ? ORDER BY timestamp, id;"; -static const char normal_order_pos_query[] = "SELECT id FROM events_srt WHERE contact_id = ? AND id >= ? ORDER BY timestamp, id;"; - -static const char reverse_order_query[] = "SELECT id FROM events_srt WHERE contact_id = ? ORDER BY timestamp desc, id DESC;"; -static const char reverse_order_pos_query[] = "SELECT id FROM events_srt WHERE contact_id = ? AND id <= ? ORDER BY timestamp desc, id DESC;"; - -static const char add_event_sort_query[] = "INSERT INTO events_srt(id, contact_id, timestamp) VALUES (?, ?, ?);"; - -void CDbxSQLite::InitEvents() -{ - sqlite3_stmt *stmt = nullptr; - sqlite3_prepare_v2(m_db, "SELECT DISTINCT module FROM events;", -1, &stmt, nullptr); - int rc = 0; - while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { - const char *module = (char*)sqlite3_column_text(stmt, 0); - if (mir_strlen(module) > 0) - m_modules.insert(mir_strdup(module)); - } - logError(rc, __FILE__, __LINE__); - sqlite3_finalize(stmt); -} - -void CDbxSQLite::UninitEvents() -{ - for (auto &module : m_modules) - mir_free(module); - m_modules.destroy(); -} - -int CDbxSQLite::GetEventCount(MCONTACT hContact) -{ - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return 0; - - if (cc->HasCount()) - return cc->m_count; - - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("SELECT COUNT(1) FROM events_srt WHERE contact_id = ? LIMIT 1;", qEvCount); - sqlite3_bind_int64(stmt, 1, hContact); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - cc->m_count = (rc != SQLITE_ROW) ? 0 : sqlite3_column_int64(stmt, 0); - sqlite3_reset(stmt); - return cc->m_count; -} - -MEVENT CDbxSQLite::AddEvent(MCONTACT hContact, const DBEVENTINFO *dbei) -{ - if (dbei == nullptr) - return 0; - - if (dbei->timestamp == 0) - return 0; - - MCONTACT hNotifyContact = hContact; - DBCachedContact *cc, *ccSub = nullptr; - if (hContact != 0) { - if ((cc = m_cache->GetCachedContact(hContact)) == nullptr) - return 0; - - if (cc->IsSub()) { - ccSub = cc; - if ((cc = m_cache->GetCachedContact(cc->parentID)) == nullptr) - return 0; - - // set default sub to the event's source - if (!(dbei->flags & DBEF_SENT)) - db_mc_setDefault(cc->contactID, hContact, false); - if (db_mc_isEnabled()) - hNotifyContact = cc->contactID; // and add an event to a metahistory - } - } - else cc = &m_system; - - if (cc == nullptr) - return 0; - - if (m_safetyMode) - if (NotifyEventHooks(g_hevEventFiltered, hNotifyContact, (LPARAM)dbei)) - return 0; - - DBEVENTINFO tmp = *dbei; - const char *szEventId; - if (tmp.szId != nullptr) { - tmp.flags |= DBEF_HAS_ID; - szEventId = tmp.szId; - } - else szEventId = ""; - - mir_ptr pCryptBlob; - if (m_bEncrypted) { - size_t len; - uint8_t *pResult = m_crypto->encodeBuffer(tmp.pBlob, tmp.cbBlob, &len); - if (pResult != nullptr) { - pCryptBlob = tmp.pBlob = pResult; - tmp.cbBlob = (uint16_t)len; - tmp.flags |= DBEF_ENCRYPTED; - } - } - - mir_cslockfull lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("INSERT INTO events(contact_id, module, timestamp, type, flags, data, server_id, is_read) VALUES (?, ?, ?, ?, ?, ?, ?, ?);", qEvAdd); - sqlite3_bind_int64(stmt, 1, hContact); - sqlite3_bind_text(stmt, 2, tmp.szModule, (int)mir_strlen(tmp.szModule), nullptr); - sqlite3_bind_int64(stmt, 3, tmp.timestamp); - sqlite3_bind_int(stmt, 4, tmp.eventType); - sqlite3_bind_int64(stmt, 5, tmp.flags); - sqlite3_bind_blob(stmt, 6, tmp.pBlob, tmp.cbBlob, nullptr); - sqlite3_bind_text(stmt, 7, szEventId, (int)mir_strlen(szEventId), nullptr); - sqlite3_bind_int(stmt, 8, tmp.markedRead()); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - - MEVENT hDbEvent = sqlite3_last_insert_rowid(m_db); - - stmt = InitQuery(add_event_sort_query, qEvAddSrt); - sqlite3_bind_int64(stmt, 1, hDbEvent); - sqlite3_bind_int64(stmt, 2, cc->contactID); - sqlite3_bind_int64(stmt, 3, tmp.timestamp); - rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - - if (ccSub != nullptr) { - stmt = InitQuery(add_event_sort_query, qEvAddSrt); - sqlite3_bind_int64(stmt, 1, hDbEvent); - sqlite3_bind_int64(stmt, 2, ccSub->contactID); - sqlite3_bind_int64(stmt, 3, tmp.timestamp); - rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); //is this necessary ? - } - - char *module = m_modules.find((char *)tmp.szModule); - if (module == nullptr) - m_modules.insert(mir_strdup(tmp.szModule)); - - lock.unlock(); - - DBFlush(); - if (m_safetyMode && !(tmp.flags & DBEF_TEMPORARY)) - NotifyEventHooks(g_hevEventAdded, hNotifyContact, (LPARAM)hDbEvent); - - return hDbEvent; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CDbxSQLite::DeleteEventMain(MEVENT hDbEvent) -{ - auto *stmt = InitQuery("DELETE FROM events WHERE id = ?;", qEvDel); - sqlite3_bind_int64(stmt, 1, hDbEvent); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - return rc; -} - -int CDbxSQLite::DeleteEventSrt(MEVENT hDbEvent, MCONTACT hContact, uint32_t ts) -{ - auto *stmt = InitQuery("DELETE FROM events_srt WHERE id = ? AND contact_id = ? AND timestamp = ?;", qEvDelSrt); - sqlite3_bind_int64(stmt, 1, hDbEvent); - sqlite3_bind_int64(stmt, 2, hContact); - sqlite3_bind_int64(stmt, 3, ts); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - return rc; -} - -BOOL CDbxSQLite::DeleteEvent(MEVENT hDbEvent) -{ - if (hDbEvent == 0) - return 1; - - MEVENT hContact; - uint32_t ts; - { - sqlite3_stmt *stmt = InitQuery("SELECT contact_id, timestamp FROM events WHERE id = ? LIMIT 1;", qEvGetContact2); - sqlite3_bind_int64(stmt, 1, hDbEvent); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - sqlite3_reset(stmt); - return 2; - } - hContact = sqlite3_column_int64(stmt, 0); - ts = sqlite3_column_int64(stmt, 1); - sqlite3_reset(stmt); - } - - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return 1; - - mir_cslockfull lock(m_csDbAccess); - int rc = DeleteEventMain(hDbEvent); - if (rc != SQLITE_DONE) - return 1; - - rc = DeleteEventSrt(hDbEvent, hContact, ts); - if (rc != SQLITE_DONE) - return 1; - - lock.unlock(); - - DBFlush(); - NotifyEventHooks(g_hevEventDeleted, hContact, hDbEvent); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL CDbxSQLite::EditEvent(MCONTACT hContact, MEVENT hDbEvent, const DBEVENTINFO *dbei) -{ - if (dbei == nullptr) - return 1; - - if (dbei->timestamp == 0) - return 1; - - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return 1; - - DBEVENTINFO tmp = *dbei; - mir_ptr pCryptBlob; - if (m_bEncrypted) { - size_t len; - uint8_t *pResult = m_crypto->encodeBuffer(tmp.pBlob, tmp.cbBlob, &len); - if (pResult != nullptr) { - pCryptBlob = tmp.pBlob = pResult; - tmp.cbBlob = (uint16_t)len; - tmp.flags |= DBEF_ENCRYPTED; - } - } - - mir_cslockfull lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("UPDATE events SET module = ?, timestamp = ?, type = ?, flags = ?, data = ?, is_read = ? WHERE id = ?;", qEvEdit); - sqlite3_bind_text(stmt, 1, tmp.szModule, (int)mir_strlen(tmp.szModule), nullptr); - sqlite3_bind_int64(stmt, 2, tmp.timestamp); - sqlite3_bind_int(stmt, 3, tmp.eventType); - sqlite3_bind_int64(stmt, 4, tmp.flags); - sqlite3_bind_blob(stmt, 5, tmp.pBlob, tmp.cbBlob, nullptr); - sqlite3_bind_int(stmt, 6, tmp.markedRead()); - sqlite3_bind_int64(stmt, 7, hDbEvent); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - - char *module = m_modules.find((char *)tmp.szModule); - if (module == nullptr) - m_modules.insert(mir_strdup(tmp.szModule)); - - lock.unlock(); - - DBFlush(); - NotifyEventHooks(g_hevEventEdited, hContact, (LPARAM)hDbEvent); - return 0; -} - -int CDbxSQLite::GetBlobSize(MEVENT hDbEvent) -{ - if (hDbEvent == 0) - return -1; - - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("SELECT LENGTH(data) FROM events WHERE id = ? LIMIT 1;", qEvBlobSize); - sqlite3_bind_int(stmt, 1, hDbEvent); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - sqlite3_reset(stmt); - return -1; - } - - LONG res = sqlite3_column_int64(stmt, 0); - sqlite3_reset(stmt); - return res; -} - -static char g_szId[100]; - -BOOL CDbxSQLite::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei) -{ - if (hDbEvent == 0) - return 1; - - if (dbei == nullptr) - return 1; - - if (dbei->cbBlob > 0 && dbei->pBlob == nullptr) { - dbei->cbBlob = 0; - return 1; - } - - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("SELECT module, timestamp, type, flags, server_id, length(data), data FROM events WHERE id = ? LIMIT 1;", qEvGet); - sqlite3_bind_int64(stmt, 1, hDbEvent); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - sqlite3_reset(stmt); - return 1; - } - - char *module = (char *)sqlite3_column_text(stmt, 0); - dbei->szModule = m_modules.find(module); - if (dbei->szModule == nullptr) - return 1; - - dbei->timestamp = sqlite3_column_int64(stmt, 1); - dbei->eventType = sqlite3_column_int(stmt, 2); - dbei->flags = sqlite3_column_int64(stmt, 3); - - char *pszId = (char *)sqlite3_column_text(stmt, 4); - if (mir_strlen(pszId)) { - mir_strncpy(g_szId, pszId, sizeof(g_szId)); - dbei->szId = g_szId; - } - else dbei->szId = nullptr; - - int32_t cbBlob = sqlite3_column_int64(stmt, 5); - size_t bytesToCopy = cbBlob; - if (dbei->cbBlob == -1) - dbei->pBlob = (uint8_t*)mir_calloc(cbBlob + 2); - else if (dbei->cbBlob < cbBlob) - bytesToCopy = dbei->cbBlob; - - dbei->cbBlob = cbBlob; - if (bytesToCopy && dbei->pBlob) { - uint8_t *data = (uint8_t *)sqlite3_column_blob(stmt, 6); - - if (dbei->flags & DBEF_ENCRYPTED) { - dbei->flags &= ~DBEF_ENCRYPTED; - size_t len; - uint8_t* pBlob = (uint8_t*)m_crypto->decodeBuffer(data, cbBlob, &len); - if (pBlob == nullptr) - return 1; - - memcpy(dbei->pBlob, pBlob, bytesToCopy); - if (bytesToCopy > len) - memset(dbei->pBlob + len, 0, bytesToCopy - len); - - mir_free(pBlob); - } - else memcpy(dbei->pBlob, data, bytesToCopy); - } - sqlite3_reset(stmt); - return 0; -} - -BOOL CDbxSQLite::MarkEventRead(MCONTACT hContact, MEVENT hDbEvent) -{ - if (hDbEvent == 0) - return -1; - - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return -1; - - int rows; - { - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("UPDATE events SET flags = flags | 4, is_read = 1 WHERE id = ? AND is_read = 0;", qEvSetFlags); - sqlite3_bind_int64(stmt, 1, hDbEvent); - int rc = sqlite3_step(stmt); - rows = sqlite3_changes(m_db); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - if (rc != SQLITE_DONE) - return -1; - } - - DBFlush(); - NotifyEventHooks(g_hevMarkedRead, hContact, (LPARAM)hDbEvent); - return (rows == 0 ? 0 : DBEF_READ); -} - -MCONTACT CDbxSQLite::GetEventContact(MEVENT hDbEvent) -{ - if (hDbEvent == 0) - return INVALID_CONTACT_ID; - - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("SELECT contact_id FROM events WHERE id = ? LIMIT 1;", qEvGetContact); - sqlite3_bind_int64(stmt, 1, hDbEvent); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - sqlite3_reset(stmt); - return INVALID_CONTACT_ID; - } - MCONTACT hContact = sqlite3_column_int64(stmt, 0); - sqlite3_reset(stmt); - return hContact; -} - -MEVENT CDbxSQLite::FindFirstUnreadEvent(MCONTACT hContact) -{ - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return 0; - - mir_cslock lock(m_csDbAccess); - - if (cc->IsMeta()) { - if (cc->nSubs == 0) - return 0; - - CMStringA query("SELECT id FROM events WHERE is_read = 0 AND contact_id IN ("); - for (int k = 0; k < cc->nSubs; k++) - query.AppendFormat("%lu, ", cc->pSubs[k]); - query.Delete(query.GetLength() - 2, 2); - query.Append(") ORDER BY timestamp LIMIT 1;"); - - sqlite3_stmt *stmt; - sqlite3_prepare_v2(m_db, query, -1, &stmt, nullptr); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - sqlite3_finalize(stmt); - return 0; - } - - MEVENT ret = sqlite3_column_int64(stmt, 0); - sqlite3_finalize(stmt); - return ret; - } - - sqlite3_stmt *stmt = InitQuery("SELECT id FROM events WHERE contact_id = ? AND is_read = 0 ORDER BY timestamp LIMIT 1;", qEvFindUnread); - sqlite3_bind_int64(stmt, 1, hContact); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - sqlite3_reset(stmt); - return 0; - } - - MEVENT ret = sqlite3_column_int64(stmt, 0); - sqlite3_reset(stmt); - return ret; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// First/next event - -MEVENT CDbxSQLite::FindFirstEvent(MCONTACT hContact) -{ - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return 0; - - mir_cslock lock(m_csDbAccess); - - if (fwd.cur) - sqlite3_reset(fwd.cur); - - fwd.hContact = hContact; - fwd.cur = InitQuery(normal_order_query, qEvFindFirst); - sqlite3_bind_int64(fwd.cur, 1, hContact); - - int rc = sqlite3_step(fwd.cur); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - fwd.clear(); - return 0; - } - return fwd.hEvent = sqlite3_column_int64(fwd.cur, 0); -} - -MEVENT CDbxSQLite::FindNextEvent(MCONTACT hContact, MEVENT hDbEvent) -{ - if (hDbEvent == 0) - return 0; - - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return 0; - - if (hContact != fwd.hContact || hDbEvent != fwd.hEvent) { - if (fwd.cur) - sqlite3_reset(fwd.cur); - - fwd.hContact = hContact; - fwd.cur = InitQuery("SELECT id FROM events_srt WHERE contact_id = ? AND id > ? ORDER BY timestamp, id;", qEvFindNext); - sqlite3_bind_int64(fwd.cur, 1, hContact); - sqlite3_bind_int64(fwd.cur, 2, hDbEvent); - } - - int rc = sqlite3_step(fwd.cur); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - fwd.clear(); - return 0; - } - - return fwd.hEvent = sqlite3_column_int64(fwd.cur, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Last/prev event - -MEVENT CDbxSQLite::FindLastEvent(MCONTACT hContact) -{ - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return 0; - - mir_cslock lock(m_csDbAccess); - - if (back.cur) - sqlite3_reset(back.cur); - - back.hContact = hContact; - back.cur = InitQuery(reverse_order_query, qEvFindLast); - sqlite3_bind_int64(back.cur, 1, hContact); - int rc = sqlite3_step(back.cur); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - back.clear(); - return 0; - } - - return back.hEvent = sqlite3_column_int64(back.cur, 0); -} - -MEVENT CDbxSQLite::FindPrevEvent(MCONTACT hContact, MEVENT hDbEvent) -{ - if (hDbEvent == 0) - return 0; - - DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; - if (cc == nullptr) - return 0; - - if (hContact != back.hContact || hDbEvent != back.hEvent) { - if (back.cur) - sqlite3_reset(back.cur); - - back.hContact = hContact; - back.cur = InitQuery("SELECT id FROM events_srt WHERE contact_id = ? AND id < ? ORDER BY timestamp desc, id DESC;", qEvFindPrev); - sqlite3_bind_int64(back.cur, 1, hContact); - sqlite3_bind_int64(back.cur, 2, hDbEvent); - } - - int rc = sqlite3_step(back.cur); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - back.clear(); - return 0; - } - - return back.hEvent = sqlite3_column_int64(back.cur, 0); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Metacontacts - -BOOL CDbxSQLite::MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) -{ - //TODO: test this - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("SELECT id, timestamp FROM events WHERE contact_id = ?;", qEvMetaMerge); - sqlite3_bind_int64(stmt, 1, ccSub->contactID); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - while (rc == SQLITE_ROW) { - sqlite3_stmt *stmt2 = InitQuery(add_event_sort_query, qEvAddSrt); - sqlite3_bind_int64(stmt2, 1, sqlite3_column_int64(stmt, 0)); - sqlite3_bind_int64(stmt2, 2, ccMeta->contactID); - sqlite3_bind_int64(stmt2, 3, sqlite3_column_int64(stmt, 1)); - int rc2 = sqlite3_step(stmt2); - logError(rc2, __FILE__, __LINE__); - sqlite3_reset(stmt2); - rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - } - - sqlite3_reset(stmt); - DBFlush(); - return TRUE; -} - -BOOL CDbxSQLite::MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *) -{ - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("DELETE FROM events_srt WHERE contact_id = ?;", qEvMetaSplit); - sqlite3_bind_int64(stmt, 1, ccMeta->contactID); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - if (rc != SQLITE_DONE) - return 1; - - DBFlush(); - return TRUE; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Server ids - -MEVENT CDbxSQLite::GetEventById(LPCSTR szModule, LPCSTR szId) -{ - if (szModule == nullptr || szId == nullptr) - return 0; - - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("SELECT id, timestamp FROM events WHERE module = ? AND server_id = ? LIMIT 1;", qEvGetById); - sqlite3_bind_text(stmt, 1, szModule, (int)mir_strlen(szModule), nullptr); - sqlite3_bind_text(stmt, 2, szId, (int)mir_strlen(szId), nullptr); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - sqlite3_reset(stmt); - return 0; - } - MEVENT hDbEvent = sqlite3_column_int64(stmt, 0); - sqlite3_reset(stmt); - return hDbEvent; -} - -int CDbxSQLite::UpdateEventId(MEVENT hDbEvent, LPCSTR szId) -{ - if (hDbEvent == 0 || mir_strlen(szId) == 0) - return 1; - - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("UPDATE events SET server_id = ? WHERE id = ?;", qEvUpdateId); - sqlite3_bind_text(stmt, 1, szId, (int)mir_strlen(szId), nullptr); - sqlite3_bind_int64(stmt, 2, hDbEvent); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - int rows = sqlite3_changes(m_db); - sqlite3_reset(stmt); - return (rows == 0) ? 2 : 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Event cursors - -STDMETHODIMP_(DB::EventCursor *) CDbxSQLite::EventCursor(MCONTACT hContact, MEVENT hDbEvent) -{ - return new CDbxSQLiteEventCursor(hContact, m_db, hDbEvent); -} - -STDMETHODIMP_(DB::EventCursor *) CDbxSQLite::EventCursorRev(MCONTACT hContact, MEVENT hDbEvent) -{ - return new CDbxSQLiteEventCursor(hContact, m_db, hDbEvent, true); -} - -CDbxSQLiteEventCursor::CDbxSQLiteEventCursor(MCONTACT _1, sqlite3 *_db, MEVENT hDbEvent, bool reverse) : - EventCursor(_1), m_db(_db) -{ - if (reverse) { - if (!hDbEvent) - sqlite3_prepare_v2(m_db, reverse_order_query, -1, &cursor, nullptr); - else - sqlite3_prepare_v2(m_db, reverse_order_pos_query, -1, &cursor, nullptr); - } - else { - if (!hDbEvent) - sqlite3_prepare_v2(m_db, normal_order_query, -1, &cursor, nullptr); - else - sqlite3_prepare_v2(m_db, normal_order_pos_query, -1, &cursor, nullptr); - } - sqlite3_bind_int64(cursor, 1, hContact); - if (hDbEvent) - sqlite3_bind_int64(cursor, 2, hDbEvent); -} - -CDbxSQLiteEventCursor::~CDbxSQLiteEventCursor() -{ - if (cursor) - sqlite3_reset(cursor); -} - -MEVENT CDbxSQLiteEventCursor::FetchNext() -{ - if (!cursor) - return 0; - - int rc = sqlite3_step(cursor); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_ROW) { - //empty response - //reset sql cursor - sqlite3_reset(cursor); - cursor = nullptr; - return 0; - } - return sqlite3_column_int64(cursor, 0); -} +#include "stdafx.h" + +//TODO: hide it inside cursor class +static const char normal_order_query[] = "SELECT id FROM events_srt WHERE contact_id = ? ORDER BY timestamp, id;"; +static const char normal_order_pos_query[] = "SELECT id FROM events_srt WHERE contact_id = ? AND id >= ? ORDER BY timestamp, id;"; + +static const char reverse_order_query[] = "SELECT id FROM events_srt WHERE contact_id = ? ORDER BY timestamp desc, id DESC;"; +static const char reverse_order_pos_query[] = "SELECT id FROM events_srt WHERE contact_id = ? AND id <= ? ORDER BY timestamp desc, id DESC;"; + +static const char add_event_sort_query[] = "INSERT INTO events_srt(id, contact_id, timestamp) VALUES (?, ?, ?);"; + +void CDbxSQLite::InitEvents() +{ + sqlite3_stmt *stmt = nullptr; + sqlite3_prepare_v2(m_db, "SELECT DISTINCT module FROM events;", -1, &stmt, nullptr); + int rc = 0; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + const char *module = (char*)sqlite3_column_text(stmt, 0); + if (mir_strlen(module) > 0) + m_modules.insert(mir_strdup(module)); + } + logError(rc, __FILE__, __LINE__); + sqlite3_finalize(stmt); +} + +void CDbxSQLite::UninitEvents() +{ + for (auto &module : m_modules) + mir_free(module); + m_modules.destroy(); +} + +int CDbxSQLite::GetEventCount(MCONTACT hContact) +{ + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return 0; + + if (cc->HasCount()) + return cc->m_count; + + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("SELECT COUNT(1) FROM events_srt WHERE contact_id = ? LIMIT 1;", qEvCount); + sqlite3_bind_int64(stmt, 1, hContact); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + cc->m_count = (rc != SQLITE_ROW) ? 0 : sqlite3_column_int64(stmt, 0); + sqlite3_reset(stmt); + return cc->m_count; +} + +MEVENT CDbxSQLite::AddEvent(MCONTACT hContact, const DBEVENTINFO *dbei) +{ + if (dbei == nullptr) + return 0; + + if (dbei->timestamp == 0) + return 0; + + MCONTACT hNotifyContact = hContact; + DBCachedContact *cc, *ccSub = nullptr; + if (hContact != 0) { + if ((cc = m_cache->GetCachedContact(hContact)) == nullptr) + return 0; + + if (cc->IsSub()) { + ccSub = cc; + if ((cc = m_cache->GetCachedContact(cc->parentID)) == nullptr) + return 0; + + // set default sub to the event's source + if (!(dbei->flags & DBEF_SENT)) + db_mc_setDefault(cc->contactID, hContact, false); + if (db_mc_isEnabled()) + hNotifyContact = cc->contactID; // and add an event to a metahistory + } + } + else cc = &m_system; + + if (cc == nullptr) + return 0; + + if (m_safetyMode) + if (NotifyEventHooks(g_hevEventFiltered, hNotifyContact, (LPARAM)dbei)) + return 0; + + DBEVENTINFO tmp = *dbei; + const char *szEventId; + if (tmp.szId != nullptr) { + tmp.flags |= DBEF_HAS_ID; + szEventId = tmp.szId; + } + else szEventId = ""; + + mir_ptr pCryptBlob; + if (m_bEncrypted) { + size_t len; + uint8_t *pResult = m_crypto->encodeBuffer(tmp.pBlob, tmp.cbBlob, &len); + if (pResult != nullptr) { + pCryptBlob = tmp.pBlob = pResult; + tmp.cbBlob = (uint16_t)len; + tmp.flags |= DBEF_ENCRYPTED; + } + } + + mir_cslockfull lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("INSERT INTO events(contact_id, module, timestamp, type, flags, data, server_id, is_read) VALUES (?, ?, ?, ?, ?, ?, ?, ?);", qEvAdd); + sqlite3_bind_int64(stmt, 1, hContact); + sqlite3_bind_text(stmt, 2, tmp.szModule, (int)mir_strlen(tmp.szModule), nullptr); + sqlite3_bind_int64(stmt, 3, tmp.timestamp); + sqlite3_bind_int(stmt, 4, tmp.eventType); + sqlite3_bind_int64(stmt, 5, tmp.flags); + sqlite3_bind_blob(stmt, 6, tmp.pBlob, tmp.cbBlob, nullptr); + sqlite3_bind_text(stmt, 7, szEventId, (int)mir_strlen(szEventId), nullptr); + sqlite3_bind_int(stmt, 8, tmp.markedRead()); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + + MEVENT hDbEvent = sqlite3_last_insert_rowid(m_db); + + stmt = InitQuery(add_event_sort_query, qEvAddSrt); + sqlite3_bind_int64(stmt, 1, hDbEvent); + sqlite3_bind_int64(stmt, 2, cc->contactID); + sqlite3_bind_int64(stmt, 3, tmp.timestamp); + rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + + if (ccSub != nullptr) { + stmt = InitQuery(add_event_sort_query, qEvAddSrt); + sqlite3_bind_int64(stmt, 1, hDbEvent); + sqlite3_bind_int64(stmt, 2, ccSub->contactID); + sqlite3_bind_int64(stmt, 3, tmp.timestamp); + rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); //is this necessary ? + } + + char *module = m_modules.find((char *)tmp.szModule); + if (module == nullptr) + m_modules.insert(mir_strdup(tmp.szModule)); + + lock.unlock(); + + DBFlush(); + if (m_safetyMode && !(tmp.flags & DBEF_TEMPORARY)) + NotifyEventHooks(g_hevEventAdded, hNotifyContact, (LPARAM)hDbEvent); + + return hDbEvent; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CDbxSQLite::DeleteEventMain(MEVENT hDbEvent) +{ + auto *stmt = InitQuery("DELETE FROM events WHERE id = ?;", qEvDel); + sqlite3_bind_int64(stmt, 1, hDbEvent); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + return rc; +} + +int CDbxSQLite::DeleteEventSrt(MEVENT hDbEvent, MCONTACT hContact, uint32_t ts) +{ + auto *stmt = InitQuery("DELETE FROM events_srt WHERE id = ? AND contact_id = ? AND timestamp = ?;", qEvDelSrt); + sqlite3_bind_int64(stmt, 1, hDbEvent); + sqlite3_bind_int64(stmt, 2, hContact); + sqlite3_bind_int64(stmt, 3, ts); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + return rc; +} + +BOOL CDbxSQLite::DeleteEvent(MEVENT hDbEvent) +{ + if (hDbEvent == 0) + return 1; + + MEVENT hContact; + uint32_t ts; + { + sqlite3_stmt *stmt = InitQuery("SELECT contact_id, timestamp FROM events WHERE id = ? LIMIT 1;", qEvGetContact2); + sqlite3_bind_int64(stmt, 1, hDbEvent); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + sqlite3_reset(stmt); + return 2; + } + hContact = sqlite3_column_int64(stmt, 0); + ts = sqlite3_column_int64(stmt, 1); + sqlite3_reset(stmt); + } + + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return 1; + + mir_cslockfull lock(m_csDbAccess); + int rc = DeleteEventMain(hDbEvent); + if (rc != SQLITE_DONE) + return 1; + + rc = DeleteEventSrt(hDbEvent, hContact, ts); + if (rc != SQLITE_DONE) + return 1; + + lock.unlock(); + + DBFlush(); + NotifyEventHooks(g_hevEventDeleted, hContact, hDbEvent); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +BOOL CDbxSQLite::EditEvent(MCONTACT hContact, MEVENT hDbEvent, const DBEVENTINFO *dbei) +{ + if (dbei == nullptr) + return 1; + + if (dbei->timestamp == 0) + return 1; + + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return 1; + + DBEVENTINFO tmp = *dbei; + mir_ptr pCryptBlob; + if (m_bEncrypted) { + size_t len; + uint8_t *pResult = m_crypto->encodeBuffer(tmp.pBlob, tmp.cbBlob, &len); + if (pResult != nullptr) { + pCryptBlob = tmp.pBlob = pResult; + tmp.cbBlob = (uint16_t)len; + tmp.flags |= DBEF_ENCRYPTED; + } + } + + mir_cslockfull lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("UPDATE events SET module = ?, timestamp = ?, type = ?, flags = ?, data = ?, is_read = ? WHERE id = ?;", qEvEdit); + sqlite3_bind_text(stmt, 1, tmp.szModule, (int)mir_strlen(tmp.szModule), nullptr); + sqlite3_bind_int64(stmt, 2, tmp.timestamp); + sqlite3_bind_int(stmt, 3, tmp.eventType); + sqlite3_bind_int64(stmt, 4, tmp.flags); + sqlite3_bind_blob(stmt, 5, tmp.pBlob, tmp.cbBlob, nullptr); + sqlite3_bind_int(stmt, 6, tmp.markedRead()); + sqlite3_bind_int64(stmt, 7, hDbEvent); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + + char *module = m_modules.find((char *)tmp.szModule); + if (module == nullptr) + m_modules.insert(mir_strdup(tmp.szModule)); + + lock.unlock(); + + DBFlush(); + NotifyEventHooks(g_hevEventEdited, hContact, (LPARAM)hDbEvent); + return 0; +} + +int CDbxSQLite::GetBlobSize(MEVENT hDbEvent) +{ + if (hDbEvent == 0) + return -1; + + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("SELECT LENGTH(data) FROM events WHERE id = ? LIMIT 1;", qEvBlobSize); + sqlite3_bind_int(stmt, 1, hDbEvent); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + sqlite3_reset(stmt); + return -1; + } + + LONG res = sqlite3_column_int64(stmt, 0); + sqlite3_reset(stmt); + return res; +} + +static char g_szId[100]; + +BOOL CDbxSQLite::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei) +{ + if (hDbEvent == 0) + return 1; + + if (dbei == nullptr) + return 1; + + if (dbei->cbBlob > 0 && dbei->pBlob == nullptr) { + dbei->cbBlob = 0; + return 1; + } + + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("SELECT module, timestamp, type, flags, server_id, length(data), data FROM events WHERE id = ? LIMIT 1;", qEvGet); + sqlite3_bind_int64(stmt, 1, hDbEvent); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + sqlite3_reset(stmt); + return 1; + } + + char *module = (char *)sqlite3_column_text(stmt, 0); + dbei->szModule = m_modules.find(module); + if (dbei->szModule == nullptr) + return 1; + + dbei->timestamp = sqlite3_column_int64(stmt, 1); + dbei->eventType = sqlite3_column_int(stmt, 2); + dbei->flags = sqlite3_column_int64(stmt, 3); + + char *pszId = (char *)sqlite3_column_text(stmt, 4); + if (mir_strlen(pszId)) { + mir_strncpy(g_szId, pszId, sizeof(g_szId)); + dbei->szId = g_szId; + } + else dbei->szId = nullptr; + + int32_t cbBlob = sqlite3_column_int64(stmt, 5); + size_t bytesToCopy = cbBlob; + if (dbei->cbBlob == -1) + dbei->pBlob = (uint8_t*)mir_calloc(cbBlob + 2); + else if (dbei->cbBlob < cbBlob) + bytesToCopy = dbei->cbBlob; + + dbei->cbBlob = cbBlob; + if (bytesToCopy && dbei->pBlob) { + uint8_t *data = (uint8_t *)sqlite3_column_blob(stmt, 6); + + if (dbei->flags & DBEF_ENCRYPTED) { + dbei->flags &= ~DBEF_ENCRYPTED; + size_t len; + uint8_t* pBlob = (uint8_t*)m_crypto->decodeBuffer(data, cbBlob, &len); + if (pBlob == nullptr) + return 1; + + memcpy(dbei->pBlob, pBlob, bytesToCopy); + if (bytesToCopy > len) + memset(dbei->pBlob + len, 0, bytesToCopy - len); + + mir_free(pBlob); + } + else memcpy(dbei->pBlob, data, bytesToCopy); + } + sqlite3_reset(stmt); + return 0; +} + +BOOL CDbxSQLite::MarkEventRead(MCONTACT hContact, MEVENT hDbEvent) +{ + if (hDbEvent == 0) + return -1; + + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return -1; + + int rows; + { + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("UPDATE events SET flags = flags | 4, is_read = 1 WHERE id = ? AND is_read = 0;", qEvSetFlags); + sqlite3_bind_int64(stmt, 1, hDbEvent); + int rc = sqlite3_step(stmt); + rows = sqlite3_changes(m_db); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return -1; + } + + DBFlush(); + NotifyEventHooks(g_hevMarkedRead, hContact, (LPARAM)hDbEvent); + return (rows == 0 ? 0 : DBEF_READ); +} + +MCONTACT CDbxSQLite::GetEventContact(MEVENT hDbEvent) +{ + if (hDbEvent == 0) + return INVALID_CONTACT_ID; + + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("SELECT contact_id FROM events WHERE id = ? LIMIT 1;", qEvGetContact); + sqlite3_bind_int64(stmt, 1, hDbEvent); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + sqlite3_reset(stmt); + return INVALID_CONTACT_ID; + } + MCONTACT hContact = sqlite3_column_int64(stmt, 0); + sqlite3_reset(stmt); + return hContact; +} + +MEVENT CDbxSQLite::FindFirstUnreadEvent(MCONTACT hContact) +{ + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return 0; + + mir_cslock lock(m_csDbAccess); + + if (cc->IsMeta()) { + if (cc->nSubs == 0) + return 0; + + CMStringA query("SELECT id FROM events WHERE is_read = 0 AND contact_id IN ("); + for (int k = 0; k < cc->nSubs; k++) + query.AppendFormat("%lu, ", cc->pSubs[k]); + query.Delete(query.GetLength() - 2, 2); + query.Append(") ORDER BY timestamp LIMIT 1;"); + + sqlite3_stmt *stmt; + sqlite3_prepare_v2(m_db, query, -1, &stmt, nullptr); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + sqlite3_finalize(stmt); + return 0; + } + + MEVENT ret = sqlite3_column_int64(stmt, 0); + sqlite3_finalize(stmt); + return ret; + } + + sqlite3_stmt *stmt = InitQuery("SELECT id FROM events WHERE contact_id = ? AND is_read = 0 ORDER BY timestamp LIMIT 1;", qEvFindUnread); + sqlite3_bind_int64(stmt, 1, hContact); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + sqlite3_reset(stmt); + return 0; + } + + MEVENT ret = sqlite3_column_int64(stmt, 0); + sqlite3_reset(stmt); + return ret; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// First/next event + +MEVENT CDbxSQLite::FindFirstEvent(MCONTACT hContact) +{ + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return 0; + + mir_cslock lock(m_csDbAccess); + + if (fwd.cur) + sqlite3_reset(fwd.cur); + + fwd.hContact = hContact; + fwd.cur = InitQuery(normal_order_query, qEvFindFirst); + sqlite3_bind_int64(fwd.cur, 1, hContact); + + int rc = sqlite3_step(fwd.cur); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + fwd.clear(); + return 0; + } + return fwd.hEvent = sqlite3_column_int64(fwd.cur, 0); +} + +MEVENT CDbxSQLite::FindNextEvent(MCONTACT hContact, MEVENT hDbEvent) +{ + if (hDbEvent == 0) + return 0; + + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return 0; + + if (hContact != fwd.hContact || hDbEvent != fwd.hEvent) { + if (fwd.cur) + sqlite3_reset(fwd.cur); + + fwd.hContact = hContact; + fwd.cur = InitQuery("SELECT id FROM events_srt WHERE contact_id = ? AND id > ? ORDER BY timestamp, id;", qEvFindNext); + sqlite3_bind_int64(fwd.cur, 1, hContact); + sqlite3_bind_int64(fwd.cur, 2, hDbEvent); + } + + int rc = sqlite3_step(fwd.cur); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + fwd.clear(); + return 0; + } + + return fwd.hEvent = sqlite3_column_int64(fwd.cur, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Last/prev event + +MEVENT CDbxSQLite::FindLastEvent(MCONTACT hContact) +{ + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return 0; + + mir_cslock lock(m_csDbAccess); + + if (back.cur) + sqlite3_reset(back.cur); + + back.hContact = hContact; + back.cur = InitQuery(reverse_order_query, qEvFindLast); + sqlite3_bind_int64(back.cur, 1, hContact); + int rc = sqlite3_step(back.cur); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + back.clear(); + return 0; + } + + return back.hEvent = sqlite3_column_int64(back.cur, 0); +} + +MEVENT CDbxSQLite::FindPrevEvent(MCONTACT hContact, MEVENT hDbEvent) +{ + if (hDbEvent == 0) + return 0; + + DBCachedContact *cc = (hContact) ? m_cache->GetCachedContact(hContact) : &m_system; + if (cc == nullptr) + return 0; + + if (hContact != back.hContact || hDbEvent != back.hEvent) { + if (back.cur) + sqlite3_reset(back.cur); + + back.hContact = hContact; + back.cur = InitQuery("SELECT id FROM events_srt WHERE contact_id = ? AND id < ? ORDER BY timestamp desc, id DESC;", qEvFindPrev); + sqlite3_bind_int64(back.cur, 1, hContact); + sqlite3_bind_int64(back.cur, 2, hDbEvent); + } + + int rc = sqlite3_step(back.cur); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + back.clear(); + return 0; + } + + return back.hEvent = sqlite3_column_int64(back.cur, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Metacontacts + +BOOL CDbxSQLite::MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) +{ + //TODO: test this + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("SELECT id, timestamp FROM events WHERE contact_id = ?;", qEvMetaMerge); + sqlite3_bind_int64(stmt, 1, ccSub->contactID); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + while (rc == SQLITE_ROW) { + sqlite3_stmt *stmt2 = InitQuery(add_event_sort_query, qEvAddSrt); + sqlite3_bind_int64(stmt2, 1, sqlite3_column_int64(stmt, 0)); + sqlite3_bind_int64(stmt2, 2, ccMeta->contactID); + sqlite3_bind_int64(stmt2, 3, sqlite3_column_int64(stmt, 1)); + int rc2 = sqlite3_step(stmt2); + logError(rc2, __FILE__, __LINE__); + sqlite3_reset(stmt2); + rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + } + + sqlite3_reset(stmt); + DBFlush(); + return TRUE; +} + +BOOL CDbxSQLite::MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *) +{ + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("DELETE FROM events_srt WHERE contact_id = ?;", qEvMetaSplit); + sqlite3_bind_int64(stmt, 1, ccMeta->contactID); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return 1; + + DBFlush(); + return TRUE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Server ids + +MEVENT CDbxSQLite::GetEventById(LPCSTR szModule, LPCSTR szId) +{ + if (szModule == nullptr || szId == nullptr) + return 0; + + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("SELECT id, timestamp FROM events WHERE module = ? AND server_id = ? LIMIT 1;", qEvGetById); + sqlite3_bind_text(stmt, 1, szModule, (int)mir_strlen(szModule), nullptr); + sqlite3_bind_text(stmt, 2, szId, (int)mir_strlen(szId), nullptr); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + sqlite3_reset(stmt); + return 0; + } + MEVENT hDbEvent = sqlite3_column_int64(stmt, 0); + sqlite3_reset(stmt); + return hDbEvent; +} + +int CDbxSQLite::UpdateEventId(MEVENT hDbEvent, LPCSTR szId) +{ + if (hDbEvent == 0 || mir_strlen(szId) == 0) + return 1; + + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("UPDATE events SET server_id = ? WHERE id = ?;", qEvUpdateId); + sqlite3_bind_text(stmt, 1, szId, (int)mir_strlen(szId), nullptr); + sqlite3_bind_int64(stmt, 2, hDbEvent); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + int rows = sqlite3_changes(m_db); + sqlite3_reset(stmt); + return (rows == 0) ? 2 : 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Event cursors + +STDMETHODIMP_(DB::EventCursor *) CDbxSQLite::EventCursor(MCONTACT hContact, MEVENT hDbEvent) +{ + return new CDbxSQLiteEventCursor(hContact, m_db, hDbEvent); +} + +STDMETHODIMP_(DB::EventCursor *) CDbxSQLite::EventCursorRev(MCONTACT hContact, MEVENT hDbEvent) +{ + return new CDbxSQLiteEventCursor(hContact, m_db, hDbEvent, true); +} + +CDbxSQLiteEventCursor::CDbxSQLiteEventCursor(MCONTACT _1, sqlite3 *_db, MEVENT hDbEvent, bool reverse) : + EventCursor(_1), m_db(_db) +{ + if (reverse) { + if (!hDbEvent) + sqlite3_prepare_v2(m_db, reverse_order_query, -1, &cursor, nullptr); + else + sqlite3_prepare_v2(m_db, reverse_order_pos_query, -1, &cursor, nullptr); + } + else { + if (!hDbEvent) + sqlite3_prepare_v2(m_db, normal_order_query, -1, &cursor, nullptr); + else + sqlite3_prepare_v2(m_db, normal_order_pos_query, -1, &cursor, nullptr); + } + sqlite3_bind_int64(cursor, 1, hContact); + if (hDbEvent) + sqlite3_bind_int64(cursor, 2, hDbEvent); +} + +CDbxSQLiteEventCursor::~CDbxSQLiteEventCursor() +{ + if (cursor) + sqlite3_reset(cursor); +} + +MEVENT CDbxSQLiteEventCursor::FetchNext() +{ + if (!cursor) + return 0; + + int rc = sqlite3_step(cursor); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_ROW) { + //empty response + //reset sql cursor + sqlite3_reset(cursor); + cursor = nullptr; + return 0; + } + return sqlite3_column_int64(cursor, 0); +} diff --git a/plugins/Dbx_sqlite/src/dbintf.cpp b/plugins/Dbx_sqlite/src/dbintf.cpp index a34551b617..035a651ccf 100644 --- a/plugins/Dbx_sqlite/src/dbintf.cpp +++ b/plugins/Dbx_sqlite/src/dbintf.cpp @@ -1,241 +1,241 @@ -#include "stdafx.h" - -CDbxSQLite::CDbxSQLite(const wchar_t *pwszFileName, bool bReadOnly, bool bShared) : - m_impl(*this), - m_wszFileName(mir_wstrdup(pwszFileName)), - m_safetyMode(true), - m_bReadOnly(bReadOnly), - m_bShared(bShared), - m_modules(1, strcmp) -{ -} - -CDbxSQLite::~CDbxSQLite() -{ - if (m_bTranStarted) { - int rc = sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - } - - UninitEvents(); - - if (m_db) { - int rc = sqlite3_close(m_db); - logError(rc, __FILE__, __LINE__); - - m_db = nullptr; - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CDbxSQLite::Create() -{ - ptrA path(mir_utf8encodeW(m_wszFileName)); - int rc = sqlite3_open_v2(path, &m_db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_EXCLUSIVE, nullptr); - logError(rc, __FILE__, __LINE__); - if (rc != SQLITE_OK) { - logError(rc, __FILE__, __LINE__); - return 1; - } - - rc = sqlite3_exec(m_db, "CREATE TABLE contacts (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE TABLE crypto (id INTEGER NOT NULL PRIMARY KEY, data NOT NULL);", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE TABLE events (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, contact_id INTEGER NOT NULL, module TEXT NOT NULL," - "timestamp INTEGER NOT NULL, type INTEGER NOT NULL, flags INTEGER NOT NULL, data BLOB, server_id TEXT, is_read INTEGER NOT NULL DEFAULT 0);", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_contactid_timestamp ON events(contact_id, timestamp);", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_module_serverid ON events(module, server_id);", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_isread ON events(contact_id, is_read, timestamp);", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE TABLE events_srt (id INTEGER NOT NULL, contact_id INTEGER NOT NULL, timestamp INTEGER, PRIMARY KEY(contact_id, timestamp, id));", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE TABLE settings (contact_id INTEGER NOT NULL, module TEXT NOT NULL, setting TEXT NOT NULL, type INTEGER NOT NULL, value NOT NULL," - "PRIMARY KEY(contact_id, module, setting)) WITHOUT ROWID;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE INDEX idx_settings_module ON settings(module);", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CDbxSQLite::Check() -{ - FILE *hFile = _wfopen(m_wszFileName, L"rb"); - if (hFile == INVALID_HANDLE_VALUE) - return EGROKPRF_CANTREAD; - - char header[16] = {}; - size_t size = sizeof(header); - - if (fread(header, sizeof(char), size, hFile) != size) { - fclose(hFile); - return EGROKPRF_CANTREAD; - } - - fclose(hFile); - - if (memcmp(header, SQLITE_HEADER_STR, mir_strlen(SQLITE_HEADER_STR)) != 0) - return EGROKPRF_UNKHEADER; - - sqlite3 *database = nullptr; - ptrA path(mir_utf8encodeW(m_wszFileName)); - int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_READONLY | SQLITE_OPEN_EXCLUSIVE, nullptr); - if (rc != SQLITE_OK) { - logError(rc, __FILE__, __LINE__); - return EGROKPRF_DAMAGED; - } - - sqlite3_close(database); - - return EGROKPRF_NOERROR; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CDbxSQLite::Load() -{ - if (!LockName(m_wszFileName)) - return EGROKPRF_CANTREAD; - - ptrA path(mir_utf8encodeW(m_wszFileName)); - int flags = 0; - if (!m_bShared) - flags |= SQLITE_OPEN_EXCLUSIVE; - if (m_bReadOnly) - flags |= SQLITE_OPEN_READONLY; - else - flags |= SQLITE_OPEN_READWRITE; - - int rc = sqlite3_open_v2(path, &m_db, flags, nullptr); - if (rc != SQLITE_OK) { - logError(rc, __FILE__, __LINE__); - return EGROKPRF_CANTREAD; - } - - rc = sqlite3_exec(m_db, "pragma locking_mode = EXCLUSIVE;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - rc = sqlite3_exec(m_db, "pragma synchronous = NORMAL;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - rc = sqlite3_exec(m_db, "pragma foreign_keys = OFF;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - rc = sqlite3_exec(m_db, "pragma journal_mode = OFF;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - if (rc == SQLITE_BUSY) { - sqlite3_close(m_db); - return EGROKPRF_CANTREAD; - } - - InitContacts(); - InitEncryption(); - InitSettings(); - InitEvents(); - - if (InitCrypt()) - return EGROKPRF_CANTREAD; - - m_bTranStarted = true; - rc = sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - return EGROKPRF_NOERROR; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL CDbxSQLite::Backup(LPCWSTR profile) -{ - sqlite3 *database = nullptr; - ptrA path(mir_utf8encodeW(profile)); - int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_EXCLUSIVE, nullptr); - if (rc != SQLITE_OK) { - logError(rc, __FILE__, __LINE__); - return rc; - } - - mir_cslock lock(m_csDbAccess); - - sqlite3_backup *backup = sqlite3_backup_init(database, "main", m_db, "main"); - if (backup == nullptr) { - sqlite3_close(database); - DeleteFileW(profile); - return ERROR_BACKUP_CONTROLLER; - } - - rc = sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - logError(sqlite3_backup_step(backup, -1), __FILE__, __LINE__); - logError(sqlite3_backup_finish(backup), __FILE__, __LINE__); - sqlite3_close(database); - - rc = sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - return 0; -} - -BOOL CDbxSQLite::Compact() -{ - mir_cslock lck(m_csDbAccess); - int rc = sqlite3_exec(m_db, "pragma optimize;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "vacuum;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - return 0; -} - -void CDbxSQLite::DBFlush(bool bForce) -{ - if (bForce) { - mir_cslock lck(m_csDbAccess); - - int rc = sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - } - else if (m_safetyMode) - m_impl.m_timer.Start(50); -} - -BOOL CDbxSQLite::Flush() -{ - DBFlush(true); - sqlite3_db_cacheflush(m_db); - return ERROR_SUCCESS; -} - -BOOL CDbxSQLite::IsRelational() -{ - return TRUE; -} - -void CDbxSQLite::SetCacheSafetyMode(BOOL value) -{ - // hack to increase import speed - if (!value) - sqlite3_exec(m_db, "pragma synchronous = OFF;", nullptr, nullptr, nullptr); - else - sqlite3_exec(m_db, "pragma synchronous = NORMAL;", nullptr, nullptr, nullptr); - m_safetyMode = value != FALSE; -} +#include "stdafx.h" + +CDbxSQLite::CDbxSQLite(const wchar_t *pwszFileName, bool bReadOnly, bool bShared) : + m_impl(*this), + m_wszFileName(mir_wstrdup(pwszFileName)), + m_safetyMode(true), + m_bReadOnly(bReadOnly), + m_bShared(bShared), + m_modules(1, strcmp) +{ +} + +CDbxSQLite::~CDbxSQLite() +{ + if (m_bTranStarted) { + int rc = sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + } + + UninitEvents(); + + if (m_db) { + int rc = sqlite3_close(m_db); + logError(rc, __FILE__, __LINE__); + + m_db = nullptr; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CDbxSQLite::Create() +{ + ptrA path(mir_utf8encodeW(m_wszFileName)); + int rc = sqlite3_open_v2(path, &m_db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_EXCLUSIVE, nullptr); + logError(rc, __FILE__, __LINE__); + if (rc != SQLITE_OK) { + logError(rc, __FILE__, __LINE__); + return 1; + } + + rc = sqlite3_exec(m_db, "CREATE TABLE contacts (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT);", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE TABLE crypto (id INTEGER NOT NULL PRIMARY KEY, data NOT NULL);", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE TABLE events (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, contact_id INTEGER NOT NULL, module TEXT NOT NULL," + "timestamp INTEGER NOT NULL, type INTEGER NOT NULL, flags INTEGER NOT NULL, data BLOB, server_id TEXT, is_read INTEGER NOT NULL DEFAULT 0);", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_contactid_timestamp ON events(contact_id, timestamp);", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_module_serverid ON events(module, server_id);", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_isread ON events(contact_id, is_read, timestamp);", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE TABLE events_srt (id INTEGER NOT NULL, contact_id INTEGER NOT NULL, timestamp INTEGER, PRIMARY KEY(contact_id, timestamp, id));", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE TABLE settings (contact_id INTEGER NOT NULL, module TEXT NOT NULL, setting TEXT NOT NULL, type INTEGER NOT NULL, value NOT NULL," + "PRIMARY KEY(contact_id, module, setting)) WITHOUT ROWID;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE INDEX idx_settings_module ON settings(module);", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CDbxSQLite::Check() +{ + FILE *hFile = _wfopen(m_wszFileName, L"rb"); + if (hFile == INVALID_HANDLE_VALUE) + return EGROKPRF_CANTREAD; + + char header[16] = {}; + size_t size = sizeof(header); + + if (fread(header, sizeof(char), size, hFile) != size) { + fclose(hFile); + return EGROKPRF_CANTREAD; + } + + fclose(hFile); + + if (memcmp(header, SQLITE_HEADER_STR, mir_strlen(SQLITE_HEADER_STR)) != 0) + return EGROKPRF_UNKHEADER; + + sqlite3 *database = nullptr; + ptrA path(mir_utf8encodeW(m_wszFileName)); + int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_READONLY | SQLITE_OPEN_EXCLUSIVE, nullptr); + if (rc != SQLITE_OK) { + logError(rc, __FILE__, __LINE__); + return EGROKPRF_DAMAGED; + } + + sqlite3_close(database); + + return EGROKPRF_NOERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CDbxSQLite::Load() +{ + if (!LockName(m_wszFileName)) + return EGROKPRF_CANTREAD; + + ptrA path(mir_utf8encodeW(m_wszFileName)); + int flags = 0; + if (!m_bShared) + flags |= SQLITE_OPEN_EXCLUSIVE; + if (m_bReadOnly) + flags |= SQLITE_OPEN_READONLY; + else + flags |= SQLITE_OPEN_READWRITE; + + int rc = sqlite3_open_v2(path, &m_db, flags, nullptr); + if (rc != SQLITE_OK) { + logError(rc, __FILE__, __LINE__); + return EGROKPRF_CANTREAD; + } + + rc = sqlite3_exec(m_db, "pragma locking_mode = EXCLUSIVE;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + rc = sqlite3_exec(m_db, "pragma synchronous = NORMAL;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + rc = sqlite3_exec(m_db, "pragma foreign_keys = OFF;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + rc = sqlite3_exec(m_db, "pragma journal_mode = OFF;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + if (rc == SQLITE_BUSY) { + sqlite3_close(m_db); + return EGROKPRF_CANTREAD; + } + + InitContacts(); + InitEncryption(); + InitSettings(); + InitEvents(); + + if (InitCrypt()) + return EGROKPRF_CANTREAD; + + m_bTranStarted = true; + rc = sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + return EGROKPRF_NOERROR; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +BOOL CDbxSQLite::Backup(LPCWSTR profile) +{ + sqlite3 *database = nullptr; + ptrA path(mir_utf8encodeW(profile)); + int rc = sqlite3_open_v2(path, &database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_EXCLUSIVE, nullptr); + if (rc != SQLITE_OK) { + logError(rc, __FILE__, __LINE__); + return rc; + } + + mir_cslock lock(m_csDbAccess); + + sqlite3_backup *backup = sqlite3_backup_init(database, "main", m_db, "main"); + if (backup == nullptr) { + sqlite3_close(database); + DeleteFileW(profile); + return ERROR_BACKUP_CONTROLLER; + } + + rc = sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + logError(sqlite3_backup_step(backup, -1), __FILE__, __LINE__); + logError(sqlite3_backup_finish(backup), __FILE__, __LINE__); + sqlite3_close(database); + + rc = sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + return 0; +} + +BOOL CDbxSQLite::Compact() +{ + mir_cslock lck(m_csDbAccess); + int rc = sqlite3_exec(m_db, "pragma optimize;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "vacuum;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + return 0; +} + +void CDbxSQLite::DBFlush(bool bForce) +{ + if (bForce) { + mir_cslock lck(m_csDbAccess); + + int rc = sqlite3_exec(m_db, "commit;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "begin transaction;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + } + else if (m_safetyMode) + m_impl.m_timer.Start(50); +} + +BOOL CDbxSQLite::Flush() +{ + DBFlush(true); + sqlite3_db_cacheflush(m_db); + return ERROR_SUCCESS; +} + +BOOL CDbxSQLite::IsRelational() +{ + return TRUE; +} + +void CDbxSQLite::SetCacheSafetyMode(BOOL value) +{ + // hack to increase import speed + if (!value) + sqlite3_exec(m_db, "pragma synchronous = OFF;", nullptr, nullptr, nullptr); + else + sqlite3_exec(m_db, "pragma synchronous = NORMAL;", nullptr, nullptr, nullptr); + m_safetyMode = value != FALSE; +} diff --git a/plugins/Dbx_sqlite/src/dbintf.h b/plugins/Dbx_sqlite/src/dbintf.h index e7a88591c5..35404505cb 100644 --- a/plugins/Dbx_sqlite/src/dbintf.h +++ b/plugins/Dbx_sqlite/src/dbintf.h @@ -1,188 +1,188 @@ -#pragma once - -#define OWN_CACHED_CONTACT - -#include - -struct CQuery -{ - ~CQuery(); - - sqlite3_stmt *pQuery = nullptr; -}; - -struct DBCachedContact : public DBCachedContactBase -{ - int32_t m_count; - - DBCachedContact() : - m_count(-1) - {} - - __forceinline bool HasCount() const { - return m_count > -1; - } -}; - -struct CDbxSQLiteEventCursor : public DB::EventCursor -{ - CDbxSQLiteEventCursor(MCONTACT _1, sqlite3* m_db, MEVENT hDbEvent, bool reverse = false); - ~CDbxSQLiteEventCursor() override; - MEVENT FetchNext() override; - -private: - sqlite3 *m_db; - sqlite3_stmt *cursor; -}; - -class CDbxSQLite : public MDatabaseCommon, public MIDatabaseChecker, public MZeroedObject -{ - ptrW m_wszFileName; - sqlite3 *m_db = nullptr; - - struct { - sqlite3_stmt *cur; - MCONTACT hContact; - MEVENT hEvent; - - void clear() { - if (cur) - sqlite3_reset(cur); - memset(this, 0, sizeof(*this)); - } - } - fwd, back; - - DBCachedContact m_system; - - struct Impl { - CDbxSQLite &pro; - - CTimer m_timer; - void OnTimer(CTimer *pTimer) - { - pTimer->Stop(); - pro.DBFlush(true); - } - - Impl(CDbxSQLite &_p) : - pro(_p), - m_timer(Miranda_GetSystemWindow(), UINT_PTR(this)) - { - m_timer.OnEvent = Callback(this, &Impl::OnTimer); - } - } m_impl; - - bool m_safetyMode, m_bReadOnly, m_bShared, m_bTranStarted; - - // contacts - void InitContacts(); - CQuery qCntCount, qCntAdd, qCntDel, qCntDelSettings, qCntDelEvents, qCntDelEventSrt; - - // encryption - void InitEncryption(); - CQuery qCryptGetMode, qCryptSetMode, qCryptGetProvider, qCryptSetProvider, qCryptGetKey, qCryptSetKey, qCryptEnc1, qCryptEnc2; - - // events - LIST m_modules; - void InitEvents(); - void UninitEvents(); - CQuery qEvCount, qEvAdd, qEvDel, qEvEdit, qEvBlobSize, qEvGet, qEvGetFlags, qEvSetFlags, qEvGetContact, qEvGetContact2; - CQuery qEvFindFirst, qEvFindNext, qEvFindLast, qEvFindPrev, qEvFindUnread, qEvAddSrt, qEvDelSrt, qEvMetaSplit, qEvMetaMerge; - CQuery qEvGetById, qEvUpdateId; - int DeleteEventMain(MEVENT); - int DeleteEventSrt(MEVENT, MCONTACT, uint32_t); - - // settings - void InitSettings(); - CQuery qSettModules, qSettWrite, qSettDel, qSettEnum, qSettChanges; - int DeleteContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting); - - void DBFlush(bool bForce = false); - sqlite3_stmt* InitQuery(const char *szQuery, CQuery &stmt); - -public: - CDbxSQLite(const wchar_t *pwszFileName, bool bReadOnly, bool bShared); - ~CDbxSQLite(); - - int Create(); - int Check(); - int Load(); - - STDMETHODIMP_(BOOL) IsRelational(void) override; - STDMETHODIMP_(void) SetCacheSafetyMode(BOOL) override; - - STDMETHODIMP_(int) GetContactCount(void) override; - STDMETHODIMP_(int) DeleteContact(MCONTACT contactID) override; - STDMETHODIMP_(MCONTACT) AddContact(void) override; - STDMETHODIMP_(BOOL) IsDbContact(MCONTACT contactID) override; - STDMETHODIMP_(int) GetContactSize(void) override; - - STDMETHODIMP_(int) GetEventCount(MCONTACT contactID) override; - STDMETHODIMP_(MEVENT) AddEvent(MCONTACT contactID, const DBEVENTINFO *dbe) override; - STDMETHODIMP_(BOOL) DeleteEvent(MEVENT hDbEvent) override; - STDMETHODIMP_(BOOL) EditEvent(MCONTACT contactID, MEVENT hDbEvent, const DBEVENTINFO *dbe) override; - STDMETHODIMP_(int) GetBlobSize(MEVENT hDbEvent) override; - STDMETHODIMP_(BOOL) GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbe) override; - STDMETHODIMP_(BOOL) MarkEventRead(MCONTACT contactID, MEVENT hDbEvent) override; - STDMETHODIMP_(MCONTACT) GetEventContact(MEVENT hDbEvent) override; - STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT contactID) override; - STDMETHODIMP_(MEVENT) FindFirstUnreadEvent(MCONTACT contactID) override; - STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT contactID) override; - STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT contactID, MEVENT hDbEvent) override; - STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent) override; - - STDMETHODIMP_(MEVENT) GetEventById(LPCSTR szModule, LPCSTR szId) override; - STDMETHODIMP_(int) UpdateEventId(MEVENT hDbEvent, LPCSTR szId) override; - - STDMETHODIMP_(BOOL) EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) override; - - STDMETHODIMP_(BOOL) ReadCryptoKey(MBinBuffer&) override; - STDMETHODIMP_(BOOL) StoreCryptoKey() override; - - STDMETHODIMP_(CRYPTO_PROVIDER*) ReadProvider() override; - STDMETHODIMP_(BOOL) StoreProvider(CRYPTO_PROVIDER*) override; - - STDMETHODIMP_(BOOL) EnableEncryption(BOOL) override; - STDMETHODIMP_(BOOL) ReadEncryption() override; - - STDMETHODIMP_(BOOL) WriteContactSettingWorker(MCONTACT contactID, DBCONTACTWRITESETTING &dbcws) override; - STDMETHODIMP_(BOOL) DeleteContactSetting(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting) override; - STDMETHODIMP_(BOOL) EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) override; - - STDMETHODIMP_(BOOL) MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override; - STDMETHODIMP_(BOOL) MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override; - - STDMETHODIMP_(BOOL) Compact() override; - STDMETHODIMP_(BOOL) Backup(LPCWSTR) override; - STDMETHODIMP_(BOOL) Flush() override; - - STDMETHODIMP_(DATABASELINK*) GetDriver() override; - - STDMETHODIMP_(DB::EventCursor*) EventCursor(MCONTACT hContact, MEVENT hDbEvent) override; - STDMETHODIMP_(DB::EventCursor*) EventCursorRev(MCONTACT hContact, MEVENT hDbEvent) override; - - //////////////////////////////////////////////////////////////////////////////////////// - // database checker interface implementation - -protected: - STDMETHODIMP_(MIDatabaseChecker *) GetChecker() override - { return this; - } - - STDMETHODIMP_(BOOL) Start(DBCHeckCallback *callback) override - { - cb = callback; - return ERROR_SUCCESS; - } - - STDMETHODIMP_(BOOL) CheckDb(int phase) override; - STDMETHODIMP_(VOID) Destroy() override - {} - - DBCHeckCallback *cb; - int CheckPhase1(void); - int CheckPhase2(void); - int CheckPhase3(void); - int CheckPhase4(void); -}; +#pragma once + +#define OWN_CACHED_CONTACT + +#include + +struct CQuery +{ + ~CQuery(); + + sqlite3_stmt *pQuery = nullptr; +}; + +struct DBCachedContact : public DBCachedContactBase +{ + int32_t m_count; + + DBCachedContact() : + m_count(-1) + {} + + __forceinline bool HasCount() const { + return m_count > -1; + } +}; + +struct CDbxSQLiteEventCursor : public DB::EventCursor +{ + CDbxSQLiteEventCursor(MCONTACT _1, sqlite3* m_db, MEVENT hDbEvent, bool reverse = false); + ~CDbxSQLiteEventCursor() override; + MEVENT FetchNext() override; + +private: + sqlite3 *m_db; + sqlite3_stmt *cursor; +}; + +class CDbxSQLite : public MDatabaseCommon, public MIDatabaseChecker, public MZeroedObject +{ + ptrW m_wszFileName; + sqlite3 *m_db = nullptr; + + struct { + sqlite3_stmt *cur; + MCONTACT hContact; + MEVENT hEvent; + + void clear() { + if (cur) + sqlite3_reset(cur); + memset(this, 0, sizeof(*this)); + } + } + fwd, back; + + DBCachedContact m_system; + + struct Impl { + CDbxSQLite &pro; + + CTimer m_timer; + void OnTimer(CTimer *pTimer) + { + pTimer->Stop(); + pro.DBFlush(true); + } + + Impl(CDbxSQLite &_p) : + pro(_p), + m_timer(Miranda_GetSystemWindow(), UINT_PTR(this)) + { + m_timer.OnEvent = Callback(this, &Impl::OnTimer); + } + } m_impl; + + bool m_safetyMode, m_bReadOnly, m_bShared, m_bTranStarted; + + // contacts + void InitContacts(); + CQuery qCntCount, qCntAdd, qCntDel, qCntDelSettings, qCntDelEvents, qCntDelEventSrt; + + // encryption + void InitEncryption(); + CQuery qCryptGetMode, qCryptSetMode, qCryptGetProvider, qCryptSetProvider, qCryptGetKey, qCryptSetKey, qCryptEnc1, qCryptEnc2; + + // events + LIST m_modules; + void InitEvents(); + void UninitEvents(); + CQuery qEvCount, qEvAdd, qEvDel, qEvEdit, qEvBlobSize, qEvGet, qEvGetFlags, qEvSetFlags, qEvGetContact, qEvGetContact2; + CQuery qEvFindFirst, qEvFindNext, qEvFindLast, qEvFindPrev, qEvFindUnread, qEvAddSrt, qEvDelSrt, qEvMetaSplit, qEvMetaMerge; + CQuery qEvGetById, qEvUpdateId; + int DeleteEventMain(MEVENT); + int DeleteEventSrt(MEVENT, MCONTACT, uint32_t); + + // settings + void InitSettings(); + CQuery qSettModules, qSettWrite, qSettDel, qSettEnum, qSettChanges; + int DeleteContactSettingWorker(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting); + + void DBFlush(bool bForce = false); + sqlite3_stmt* InitQuery(const char *szQuery, CQuery &stmt); + +public: + CDbxSQLite(const wchar_t *pwszFileName, bool bReadOnly, bool bShared); + ~CDbxSQLite(); + + int Create(); + int Check(); + int Load(); + + STDMETHODIMP_(BOOL) IsRelational(void) override; + STDMETHODIMP_(void) SetCacheSafetyMode(BOOL) override; + + STDMETHODIMP_(int) GetContactCount(void) override; + STDMETHODIMP_(int) DeleteContact(MCONTACT contactID) override; + STDMETHODIMP_(MCONTACT) AddContact(void) override; + STDMETHODIMP_(BOOL) IsDbContact(MCONTACT contactID) override; + STDMETHODIMP_(int) GetContactSize(void) override; + + STDMETHODIMP_(int) GetEventCount(MCONTACT contactID) override; + STDMETHODIMP_(MEVENT) AddEvent(MCONTACT contactID, const DBEVENTINFO *dbe) override; + STDMETHODIMP_(BOOL) DeleteEvent(MEVENT hDbEvent) override; + STDMETHODIMP_(BOOL) EditEvent(MCONTACT contactID, MEVENT hDbEvent, const DBEVENTINFO *dbe) override; + STDMETHODIMP_(int) GetBlobSize(MEVENT hDbEvent) override; + STDMETHODIMP_(BOOL) GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbe) override; + STDMETHODIMP_(BOOL) MarkEventRead(MCONTACT contactID, MEVENT hDbEvent) override; + STDMETHODIMP_(MCONTACT) GetEventContact(MEVENT hDbEvent) override; + STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT contactID) override; + STDMETHODIMP_(MEVENT) FindFirstUnreadEvent(MCONTACT contactID) override; + STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT contactID) override; + STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT contactID, MEVENT hDbEvent) override; + STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent) override; + + STDMETHODIMP_(MEVENT) GetEventById(LPCSTR szModule, LPCSTR szId) override; + STDMETHODIMP_(int) UpdateEventId(MEVENT hDbEvent, LPCSTR szId) override; + + STDMETHODIMP_(BOOL) EnumModuleNames(DBMODULEENUMPROC pFunc, void *pParam) override; + + STDMETHODIMP_(BOOL) ReadCryptoKey(MBinBuffer&) override; + STDMETHODIMP_(BOOL) StoreCryptoKey() override; + + STDMETHODIMP_(CRYPTO_PROVIDER*) ReadProvider() override; + STDMETHODIMP_(BOOL) StoreProvider(CRYPTO_PROVIDER*) override; + + STDMETHODIMP_(BOOL) EnableEncryption(BOOL) override; + STDMETHODIMP_(BOOL) ReadEncryption() override; + + STDMETHODIMP_(BOOL) WriteContactSettingWorker(MCONTACT contactID, DBCONTACTWRITESETTING &dbcws) override; + STDMETHODIMP_(BOOL) DeleteContactSetting(MCONTACT contactID, LPCSTR szModule, LPCSTR szSetting) override; + STDMETHODIMP_(BOOL) EnumContactSettings(MCONTACT hContact, DBSETTINGENUMPROC pfnEnumProc, const char *szModule, void *param) override; + + STDMETHODIMP_(BOOL) MetaMergeHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override; + STDMETHODIMP_(BOOL) MetaSplitHistory(DBCachedContact *ccMeta, DBCachedContact *ccSub) override; + + STDMETHODIMP_(BOOL) Compact() override; + STDMETHODIMP_(BOOL) Backup(LPCWSTR) override; + STDMETHODIMP_(BOOL) Flush() override; + + STDMETHODIMP_(DATABASELINK*) GetDriver() override; + + STDMETHODIMP_(DB::EventCursor*) EventCursor(MCONTACT hContact, MEVENT hDbEvent) override; + STDMETHODIMP_(DB::EventCursor*) EventCursorRev(MCONTACT hContact, MEVENT hDbEvent) override; + + //////////////////////////////////////////////////////////////////////////////////////// + // database checker interface implementation + +protected: + STDMETHODIMP_(MIDatabaseChecker *) GetChecker() override + { return this; + } + + STDMETHODIMP_(BOOL) Start(DBCHeckCallback *callback) override + { + cb = callback; + return ERROR_SUCCESS; + } + + STDMETHODIMP_(BOOL) CheckDb(int phase) override; + STDMETHODIMP_(VOID) Destroy() override + {} + + DBCHeckCallback *cb; + int CheckPhase1(void); + int CheckPhase2(void); + int CheckPhase3(void); + int CheckPhase4(void); +}; diff --git a/plugins/Dbx_sqlite/src/dbsettings.cpp b/plugins/Dbx_sqlite/src/dbsettings.cpp index 2fe4d3c18e..c4bd196cb4 100644 --- a/plugins/Dbx_sqlite/src/dbsettings.cpp +++ b/plugins/Dbx_sqlite/src/dbsettings.cpp @@ -1,225 +1,225 @@ -#include "stdafx.h" - -void CDbxSQLite::InitSettings() -{ - sqlite3_stmt *stmt = nullptr; - sqlite3_prepare_v2(m_db, "SELECT type, value, contact_id, module, setting FROM settings;", -1, &stmt, nullptr); - while (sqlite3_step(stmt) == SQLITE_ROW) { - MCONTACT hContact = sqlite3_column_int64(stmt, 2); - auto *szModule = (const char *)sqlite3_column_text(stmt, 3); - auto *szSetting = (const char *)sqlite3_column_text(stmt, 4); - - size_t settingNameLen = strlen(szSetting); - size_t moduleNameLen = strlen(szModule); - - char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen); - - DBVARIANT *dbv = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 1); - if (dbv == nullptr) // garbage! a setting for removed/non-existent contact - continue; - - dbv->type = (int)sqlite3_column_int(stmt, 0); - switch (dbv->type) { - 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_ASCIIZ: - case DBVT_UTF8: - dbv->cchVal = sqlite3_column_bytes(stmt, 1); - { - const char *value = (const char *)sqlite3_column_text(stmt, 1); - dbv->pszVal = (char *)mir_alloc(dbv->cchVal + 1); - memcpy(dbv->pszVal, value, dbv->cchVal); - dbv->pszVal[dbv->cchVal] = 0; - } - break; - - case DBVT_ENCRYPTED: - case DBVT_BLOB: - dbv->cpbVal = sqlite3_column_bytes(stmt, 1); - { - const char *data = (const char *)sqlite3_column_blob(stmt, 1); - dbv->pbVal = (uint8_t *)mir_alloc(dbv->cpbVal + 1); - memcpy(dbv->pbVal, data, dbv->cpbVal); - dbv->pbVal[dbv->cpbVal] = 0; - } - break; - } - } - sqlite3_finalize(stmt); - - FillContactSettings(); - - DBVARIANT dbv; dbv.type = DBVT_BYTE; - if (GetContactSetting(0, "Compatibility", "Sqlite", &dbv)) - dbv.bVal = 0; - - if (dbv.bVal < 1) { - int rc = sqlite3_exec(m_db, "ALTER TABLE events ADD COLUMN is_read INTEGER NOT NULL DEFAULT 0;", 0, 0, 0); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_isread ON events(contact_id, is_read, timestamp);", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - rc = sqlite3_exec(m_db, "UPDATE events SET is_read=1 WHERE (flags & 6) <> 0;", nullptr, nullptr, nullptr); - logError(rc, __FILE__, __LINE__); - - dbv.type = DBVT_BYTE; - dbv.dVal = 1; - WriteContactSetting(0, "Compatibility", "Sqlite", &dbv); - } -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *param) -{ - LIST modules(100); - { - sqlite3_stmt *stmt = InitQuery("SELECT DISTINCT module FROM settings;", qSettModules); - int rc = 0; - while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { - const char *value = (const char *)sqlite3_column_text(stmt, 0); - modules.insert(mir_strdup(value)); - } - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - } - - int result = -1; - for (auto &module : modules) { - result = pFunc(module, param); - mir_free(module); - } - return result; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -BOOL CDbxSQLite::WriteContactSettingWorker(MCONTACT hContact, DBCONTACTWRITESETTING &dbcws) -{ - sqlite3_stmt *stmt = InitQuery("REPLACE INTO settings(contact_id, module, setting, type, value) VALUES (?, ?, ?, ?, ?);", qSettWrite); - sqlite3_bind_int64(stmt, 1, hContact); - sqlite3_bind_text(stmt, 2, dbcws.szModule, (int)mir_strlen(dbcws.szModule), nullptr); - sqlite3_bind_text(stmt, 3, dbcws.szSetting, (int)mir_strlen(dbcws.szSetting), nullptr); - sqlite3_bind_int(stmt, 4, dbcws.value.type); - switch (dbcws.value.type) { - case DBVT_BYTE: - sqlite3_bind_int(stmt, 5, dbcws.value.bVal); - break; - case DBVT_WORD: - sqlite3_bind_int(stmt, 5, dbcws.value.wVal); - break; - case DBVT_DWORD: - sqlite3_bind_int64(stmt, 5, dbcws.value.dVal); - break; - case DBVT_ASCIIZ: - case DBVT_UTF8: - sqlite3_bind_text(stmt, 5, dbcws.value.pszVal, dbcws.value.cchVal, nullptr); - break; - case DBVT_ENCRYPTED: - case DBVT_BLOB: - sqlite3_bind_blob(stmt, 5, dbcws.value.pbVal, dbcws.value.cpbVal, nullptr); - break; - } - - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - if (rc != SQLITE_DONE) - return 1; - - DBFlush(); - return 0; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -int CDbxSQLite::DeleteContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting) -{ - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("DELETE FROM settings WHERE contact_id = ? AND module = ? AND setting = ?;", qSettDel); - sqlite3_bind_int64(stmt, 1, hContact); - sqlite3_bind_text(stmt, 2, szModule, (int)mir_strlen(szModule), nullptr); - sqlite3_bind_text(stmt, 3, szSetting, (int)mir_strlen(szSetting), nullptr); - int rc = sqlite3_step(stmt); - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - return rc; -} - -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; - } - - // if a setting isn't found in cache, then return an error - we don't cache misses anymore - char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting)); - if (m_cache->GetCachedValuePtr(hContact, szCachedSettingName, -1) == nullptr) - return 1; - - if (szCachedSettingName[-1] == 0) { // it's not a resident variable - DeleteContactSettingWorker(hContact, szModule, szSetting); - DBFlush(); - } - - // notify - DBCONTACTWRITESETTING dbcws = { 0 }; - dbcws.szModule = szModule; - dbcws.szSetting = szSetting; - dbcws.value.type = DBVT_DELETED; - NotifyEventHooks(g_hevSettingChanged, 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 settings(100); - { - mir_cslock lock(m_csDbAccess); - sqlite3_stmt *stmt = InitQuery("SELECT setting FROM settings WHERE contact_id = ? AND module = ?;", qSettEnum); - sqlite3_bind_int64(stmt, 1, hContact); - sqlite3_bind_text(stmt, 2, szModule, (int)mir_strlen(szModule), nullptr); - int rc = 0; - while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { - const char *value = (const char *)sqlite3_column_text(stmt, 0); - settings.insert(mir_strdup(value)); - } - logError(rc, __FILE__, __LINE__); - sqlite3_reset(stmt); - } - - int result = -1; - for (auto &setting : settings) { - result = pfnEnumProc(setting, param); - mir_free(setting); - } - return result; -} +#include "stdafx.h" + +void CDbxSQLite::InitSettings() +{ + sqlite3_stmt *stmt = nullptr; + sqlite3_prepare_v2(m_db, "SELECT type, value, contact_id, module, setting FROM settings;", -1, &stmt, nullptr); + while (sqlite3_step(stmt) == SQLITE_ROW) { + MCONTACT hContact = sqlite3_column_int64(stmt, 2); + auto *szModule = (const char *)sqlite3_column_text(stmt, 3); + auto *szSetting = (const char *)sqlite3_column_text(stmt, 4); + + size_t settingNameLen = strlen(szSetting); + size_t moduleNameLen = strlen(szModule); + + char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen); + + DBVARIANT *dbv = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 1); + if (dbv == nullptr) // garbage! a setting for removed/non-existent contact + continue; + + dbv->type = (int)sqlite3_column_int(stmt, 0); + switch (dbv->type) { + 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_ASCIIZ: + case DBVT_UTF8: + dbv->cchVal = sqlite3_column_bytes(stmt, 1); + { + const char *value = (const char *)sqlite3_column_text(stmt, 1); + dbv->pszVal = (char *)mir_alloc(dbv->cchVal + 1); + memcpy(dbv->pszVal, value, dbv->cchVal); + dbv->pszVal[dbv->cchVal] = 0; + } + break; + + case DBVT_ENCRYPTED: + case DBVT_BLOB: + dbv->cpbVal = sqlite3_column_bytes(stmt, 1); + { + const char *data = (const char *)sqlite3_column_blob(stmt, 1); + dbv->pbVal = (uint8_t *)mir_alloc(dbv->cpbVal + 1); + memcpy(dbv->pbVal, data, dbv->cpbVal); + dbv->pbVal[dbv->cpbVal] = 0; + } + break; + } + } + sqlite3_finalize(stmt); + + FillContactSettings(); + + DBVARIANT dbv; dbv.type = DBVT_BYTE; + if (GetContactSetting(0, "Compatibility", "Sqlite", &dbv)) + dbv.bVal = 0; + + if (dbv.bVal < 1) { + int rc = sqlite3_exec(m_db, "ALTER TABLE events ADD COLUMN is_read INTEGER NOT NULL DEFAULT 0;", 0, 0, 0); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "CREATE INDEX idx_events_isread ON events(contact_id, is_read, timestamp);", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + rc = sqlite3_exec(m_db, "UPDATE events SET is_read=1 WHERE (flags & 6) <> 0;", nullptr, nullptr, nullptr); + logError(rc, __FILE__, __LINE__); + + dbv.type = DBVT_BYTE; + dbv.dVal = 1; + WriteContactSetting(0, "Compatibility", "Sqlite", &dbv); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +BOOL CDbxSQLite::EnumModuleNames(DBMODULEENUMPROC pFunc, void *param) +{ + LIST modules(100); + { + sqlite3_stmt *stmt = InitQuery("SELECT DISTINCT module FROM settings;", qSettModules); + int rc = 0; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + const char *value = (const char *)sqlite3_column_text(stmt, 0); + modules.insert(mir_strdup(value)); + } + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + } + + int result = -1; + for (auto &module : modules) { + result = pFunc(module, param); + mir_free(module); + } + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +BOOL CDbxSQLite::WriteContactSettingWorker(MCONTACT hContact, DBCONTACTWRITESETTING &dbcws) +{ + sqlite3_stmt *stmt = InitQuery("REPLACE INTO settings(contact_id, module, setting, type, value) VALUES (?, ?, ?, ?, ?);", qSettWrite); + sqlite3_bind_int64(stmt, 1, hContact); + sqlite3_bind_text(stmt, 2, dbcws.szModule, (int)mir_strlen(dbcws.szModule), nullptr); + sqlite3_bind_text(stmt, 3, dbcws.szSetting, (int)mir_strlen(dbcws.szSetting), nullptr); + sqlite3_bind_int(stmt, 4, dbcws.value.type); + switch (dbcws.value.type) { + case DBVT_BYTE: + sqlite3_bind_int(stmt, 5, dbcws.value.bVal); + break; + case DBVT_WORD: + sqlite3_bind_int(stmt, 5, dbcws.value.wVal); + break; + case DBVT_DWORD: + sqlite3_bind_int64(stmt, 5, dbcws.value.dVal); + break; + case DBVT_ASCIIZ: + case DBVT_UTF8: + sqlite3_bind_text(stmt, 5, dbcws.value.pszVal, dbcws.value.cchVal, nullptr); + break; + case DBVT_ENCRYPTED: + case DBVT_BLOB: + sqlite3_bind_blob(stmt, 5, dbcws.value.pbVal, dbcws.value.cpbVal, nullptr); + break; + } + + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + if (rc != SQLITE_DONE) + return 1; + + DBFlush(); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CDbxSQLite::DeleteContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting) +{ + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("DELETE FROM settings WHERE contact_id = ? AND module = ? AND setting = ?;", qSettDel); + sqlite3_bind_int64(stmt, 1, hContact); + sqlite3_bind_text(stmt, 2, szModule, (int)mir_strlen(szModule), nullptr); + sqlite3_bind_text(stmt, 3, szSetting, (int)mir_strlen(szSetting), nullptr); + int rc = sqlite3_step(stmt); + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + return rc; +} + +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; + } + + // if a setting isn't found in cache, then return an error - we don't cache misses anymore + char *szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, mir_strlen(szModule), mir_strlen(szSetting)); + if (m_cache->GetCachedValuePtr(hContact, szCachedSettingName, -1) == nullptr) + return 1; + + if (szCachedSettingName[-1] == 0) { // it's not a resident variable + DeleteContactSettingWorker(hContact, szModule, szSetting); + DBFlush(); + } + + // notify + DBCONTACTWRITESETTING dbcws = { 0 }; + dbcws.szModule = szModule; + dbcws.szSetting = szSetting; + dbcws.value.type = DBVT_DELETED; + NotifyEventHooks(g_hevSettingChanged, 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 settings(100); + { + mir_cslock lock(m_csDbAccess); + sqlite3_stmt *stmt = InitQuery("SELECT setting FROM settings WHERE contact_id = ? AND module = ?;", qSettEnum); + sqlite3_bind_int64(stmt, 1, hContact); + sqlite3_bind_text(stmt, 2, szModule, (int)mir_strlen(szModule), nullptr); + int rc = 0; + while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) { + const char *value = (const char *)sqlite3_column_text(stmt, 0); + settings.insert(mir_strdup(value)); + } + logError(rc, __FILE__, __LINE__); + sqlite3_reset(stmt); + } + + int result = -1; + for (auto &setting : settings) { + result = pfnEnumProc(setting, param); + mir_free(setting); + } + return result; +} -- cgit v1.2.3