diff options
Diffstat (limited to 'plugins/Dbx_sqlite')
-rw-r--r-- | plugins/Dbx_sqlite/src/dbcheck.cpp | 220 | ||||
-rw-r--r-- | plugins/Dbx_sqlite/src/dbcontacts.cpp | 226 | ||||
-rw-r--r-- | plugins/Dbx_sqlite/src/dbevents.cpp | 1398 | ||||
-rw-r--r-- | plugins/Dbx_sqlite/src/dbintf.cpp | 482 | ||||
-rw-r--r-- | plugins/Dbx_sqlite/src/dbintf.h | 376 | ||||
-rw-r--r-- | plugins/Dbx_sqlite/src/dbsettings.cpp | 450 |
6 files changed, 1576 insertions, 1576 deletions
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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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 <m_db_int.h> - -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<char> 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 <m_db_int.h>
+
+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<char> 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<char> 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<char> 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<char> 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<char> 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;
+}
|