summaryrefslogtreecommitdiff
path: root/plugins/Dbx_mdbx/src/dbevents.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Dbx_mdbx/src/dbevents.cpp')
-rw-r--r--plugins/Dbx_mdbx/src/dbevents.cpp428
1 files changed, 428 insertions, 0 deletions
diff --git a/plugins/Dbx_mdbx/src/dbevents.cpp b/plugins/Dbx_mdbx/src/dbevents.cpp
new file mode 100644
index 0000000000..3ad663a5a9
--- /dev/null
+++ b/plugins/Dbx_mdbx/src/dbevents.cpp
@@ -0,0 +1,428 @@
+/*
+
+Miranda NG: the free IM client for Microsoft* Windows*
+
+Copyright (c) 2012-18 Miranda NG team (https://miranda-ng.org)
+all portions of this codebase are copyrighted to the people
+listed in contributors.txt.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "stdafx.h"
+
+STDMETHODIMP_(LONG) CDbxMDBX::GetEventCount(MCONTACT contactID)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(contactID);
+ return (cc == NULL) ? 0 : cc->dbc.dwEventCount;
+}
+
+STDMETHODIMP_(MEVENT) CDbxMDBX::AddEvent(MCONTACT contactID, DBEVENTINFO *dbei)
+{
+ if (dbei == NULL) return 0;
+ if (dbei->timestamp == 0) return 0;
+
+ DBEvent dbe;
+ dbe.contactID = contactID; // store native or subcontact's id
+ dbe.iModuleId = GetModuleID(dbei->szModule);
+
+ MCONTACT contactNotifyID = contactID;
+ DBCachedContact *cc, *ccSub = NULL;
+ if ((cc = m_cache->GetCachedContact(contactID)) == NULL)
+ return 0;
+
+ if (cc->IsSub()) {
+ ccSub = cc;
+ if ((cc = m_cache->GetCachedContact(cc->parentID)) == NULL)
+ return 0;
+
+ // set default sub to the event's source
+ if (!(dbei->flags & DBEF_SENT))
+ db_mc_setDefault(cc->contactID, contactID, false);
+ contactID = cc->contactID; // and add an event to a metahistory
+ if (db_mc_isEnabled())
+ contactNotifyID = contactID;
+ }
+
+ if (m_safetyMode)
+ if (NotifyEventHooks(hEventFilterAddedEvent, contactNotifyID, (LPARAM)dbei))
+ return NULL;
+
+ dbe.timestamp = dbei->timestamp;
+ dbe.flags = dbei->flags;
+ dbe.wEventType = dbei->eventType;
+ dbe.cbBlob = dbei->cbBlob;
+ BYTE *pBlob = dbei->pBlob;
+
+ mir_ptr<BYTE> pCryptBlob;
+ if (m_bEncrypted) {
+ size_t len;
+ BYTE *pResult = m_crypto->encodeBuffer(pBlob, dbe.cbBlob, &len);
+ if (pResult != NULL) {
+ pCryptBlob = pBlob = pResult;
+ dbe.cbBlob = (uint16_t)len;
+ dbe.flags |= DBEF_ENCRYPTED;
+ }
+ }
+
+
+ MEVENT dwEventId = InterlockedIncrement(&m_dwMaxEventId);
+
+ const auto Snapshot = [&]() { cc->Snapshot(); if (ccSub) ccSub->Snapshot(); };
+ const auto Revert = [&]() { cc->Revert(); if (ccSub) ccSub->Revert(); };
+
+ for (Snapshot();; Revert(), Remap()) {
+ txn_ptr txn(m_env);
+
+ MDBX_val key = { &dwEventId, sizeof(MEVENT) }, data = { NULL, sizeof(DBEvent) + dbe.cbBlob };
+ MDBX_CHECK(mdbx_put(txn, m_dbEvents, &key, &data, MDBX_RESERVE), 0);
+
+ DBEvent *pNewEvent = (DBEvent*)data.iov_base;
+ *pNewEvent = dbe;
+ memcpy(pNewEvent + 1, pBlob, dbe.cbBlob);
+
+ // add a sorting key
+ DBEventSortingKey key2 = { contactID, dwEventId, dbe.timestamp };
+ key.iov_len = sizeof(key2); key.iov_base = &key2;
+ data.iov_len = 1; data.iov_base = (char*)("");
+ MDBX_CHECK(mdbx_put(txn, m_dbEventsSort, &key, &data, 0), 0);
+
+ cc->Advance(dwEventId, dbe);
+ MDBX_val keyc = { &contactID, sizeof(MCONTACT) }, datac = { &cc->dbc, sizeof(DBContact) };
+ MDBX_CHECK(mdbx_put(txn, m_dbContacts, &keyc, &datac, 0), 0);
+
+ // insert an event into a sub's history too
+ if (ccSub != NULL) {
+ key2.hContact = ccSub->contactID;
+ MDBX_CHECK(mdbx_put(txn, m_dbEventsSort, &key, &data, 0), 0);
+
+ ccSub->Advance(dwEventId, dbe);
+ datac.iov_base = &ccSub->dbc;
+ keyc.iov_base = &ccSub->contactID;
+ MDBX_CHECK(mdbx_put(txn, m_dbContacts, &keyc, &datac, 0), 0);
+ }
+
+ if (txn.commit() == MDBX_SUCCESS)
+ break;
+ }
+
+ // Notify only in safe mode or on really new events
+ if (m_safetyMode)
+ NotifyEventHooks(hEventAddedEvent, contactNotifyID, dwEventId);
+
+ return dwEventId;
+}
+
+STDMETHODIMP_(BOOL) CDbxMDBX::DeleteEvent(MCONTACT contactID, MEVENT hDbEvent)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(contactID), *cc2 = nullptr;
+ if (cc == NULL || cc->dbc.dwEventCount == 0)
+ return 1;
+
+ DBEvent dbe;
+ {
+ txn_ptr_ro txn(m_txn);
+ MDBX_val key = { &hDbEvent, sizeof(MEVENT) }, data;
+ if (mdbx_get(txn, m_dbEvents, &key, &data) != MDBX_SUCCESS)
+ return 1;
+ dbe = *(DBEvent*)data.iov_base;
+ }
+
+ if (contactID != dbe.contactID) {
+ cc2 = m_cache->GetCachedContact(dbe.contactID);
+ }
+
+ const auto Snapshot = [&]() { cc->Snapshot(); if (cc2) cc2->Snapshot(); };
+ const auto Revert = [&]() { cc->Revert(); if (cc2) cc2->Revert(); };
+
+ for (Snapshot();; Revert(), Remap()) {
+ DBEventSortingKey key2 = { contactID, hDbEvent, dbe.timestamp };
+
+ txn_ptr txn(m_env);
+ MDBX_val key = { &key2, sizeof(key2) }, data;
+
+ MDBX_CHECK(mdbx_del(txn, m_dbEventsSort, &key, &data), 1)
+
+ {
+ key.iov_len = sizeof(MCONTACT); key.iov_base = &contactID;
+ cc->dbc.dwEventCount--;
+ if (cc->dbc.evFirstUnread == hDbEvent)
+ FindNextUnread(txn, cc, key2);
+
+ data.iov_len = sizeof(DBContact); data.iov_base = &cc->dbc;
+ MDBX_CHECK(mdbx_put(txn, m_dbContacts, &key, &data, 0), 1);
+ }
+
+ if (cc2) {
+ key2.hContact = dbe.contactID;
+ MDBX_CHECK(mdbx_del(txn, m_dbEventsSort, &key, &data), 1);
+
+ key.iov_len = sizeof(MCONTACT); key.iov_base = &contactID;
+ cc2->dbc.dwEventCount--;
+ if (cc2->dbc.evFirstUnread == hDbEvent)
+ FindNextUnread(txn, cc2, key2);
+
+ data.iov_len = sizeof(DBContact); data.iov_base = &cc2->dbc;
+ MDBX_CHECK(mdbx_put(txn, m_dbContacts, &key, &data, 0), 1);
+
+ }
+
+ // remove a event
+ key.iov_len = sizeof(MEVENT); key.iov_base = &hDbEvent;
+ MDBX_CHECK(mdbx_del(txn, m_dbEvents, &key, &data), 1);
+
+ if (txn.commit() == MDBX_SUCCESS)
+ break;
+ }
+
+ NotifyEventHooks(hEventDeletedEvent, contactID, hDbEvent);
+
+ return 0;
+}
+
+STDMETHODIMP_(LONG) CDbxMDBX::GetBlobSize(MEVENT hDbEvent)
+{
+ txn_ptr_ro txn(m_txn);
+
+ MDBX_val key = { &hDbEvent, sizeof(MEVENT) }, data;
+ if (mdbx_get(txn, m_dbEvents, &key, &data) != MDBX_SUCCESS)
+ return -1;
+ return ((const DBEvent*)data.iov_base)->cbBlob;
+}
+
+STDMETHODIMP_(BOOL) CDbxMDBX::GetEvent(MEVENT hDbEvent, DBEVENTINFO *dbei)
+{
+ if (dbei == NULL) return 1;
+ if (dbei->cbBlob > 0 && dbei->pBlob == NULL) {
+ dbei->cbBlob = 0;
+ return 1;
+ }
+
+ txn_ptr_ro txn(m_txn);
+
+ MDBX_val key = { &hDbEvent, sizeof(MEVENT) }, data;
+ if (mdbx_get(txn, m_dbEvents, &key, &data) != MDBX_SUCCESS)
+ return 1;
+
+ const DBEvent *dbe = (const DBEvent*)data.iov_base;
+
+ dbei->szModule = GetModuleName(dbe->iModuleId);
+ dbei->timestamp = dbe->timestamp;
+ dbei->flags = dbe->flags;
+ dbei->eventType = dbe->wEventType;
+ size_t bytesToCopy = min(dbei->cbBlob, dbe->cbBlob);
+ dbei->cbBlob = dbe->cbBlob;
+ if (bytesToCopy && dbei->pBlob) {
+ BYTE *pSrc = (BYTE*)data.iov_base + sizeof(DBEvent);
+ if (dbe->flags & DBEF_ENCRYPTED) {
+ dbei->flags &= ~DBEF_ENCRYPTED;
+ size_t len;
+ BYTE* pBlob = (BYTE*)m_crypto->decodeBuffer(pSrc, dbe->cbBlob, &len);
+ if (pBlob == NULL)
+ return 1;
+
+ memcpy(dbei->pBlob, pBlob, bytesToCopy);
+ if (bytesToCopy > len)
+ memset(dbei->pBlob + len, 0, bytesToCopy - len);
+ mir_free(pBlob);
+ }
+ else memcpy(dbei->pBlob, pSrc, bytesToCopy);
+ }
+ return 0;
+}
+
+void CDbxMDBX::FindNextUnread(const txn_ptr &txn, DBCachedContact *cc, DBEventSortingKey &key2)
+{
+ cursor_ptr cursor(txn, m_dbEventsSort);
+
+ MDBX_val key = { &key2, sizeof(key2) }, data;
+
+ for (int res = mdbx_cursor_get(cursor, &key, &data, MDBX_SET); res == MDBX_SUCCESS; res = mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT)) {
+ const DBEvent *dbe = (const DBEvent*)data.iov_base;
+ if (dbe->contactID != cc->contactID)
+ break;
+ if (!dbe->markedRead()) {
+ cc->dbc.evFirstUnread = key2.hEvent;
+ cc->dbc.tsFirstUnread = key2.ts;
+ return;
+ }
+ }
+
+ cc->dbc.evFirstUnread = cc->dbc.tsFirstUnread = 0;
+}
+
+STDMETHODIMP_(BOOL) CDbxMDBX::MarkEventRead(MCONTACT contactID, MEVENT hDbEvent)
+{
+ if (hDbEvent == 0) return -1;
+
+ DBCachedContact *cc = m_cache->GetCachedContact(contactID);
+ if (cc == NULL)
+ return -1;
+
+ uint32_t wRetVal = -1;
+
+ for (cc->Snapshot();; cc->Revert(), Remap()) {
+ txn_ptr txn(m_env);
+
+ MDBX_val key = { &hDbEvent, sizeof(MEVENT) }, data;
+ MDBX_CHECK(mdbx_get(txn, m_dbEvents, &key, &data), -1);
+
+ const DBEvent *cdbe = (const DBEvent*)data.iov_base;
+
+ if (cdbe->markedRead())
+ return cdbe->flags;
+
+ DBEventSortingKey keyVal = { contactID, hDbEvent, cdbe->timestamp };
+
+ MDBX_CHECK(mdbx_put(txn, m_dbEvents, &key, &data, MDBX_RESERVE), -1);
+
+ DBEvent *pNewEvent = (DBEvent*)data.iov_base;
+ *pNewEvent = *cdbe;
+
+ wRetVal = (pNewEvent->flags |= DBEF_READ);
+
+ FindNextUnread(txn, cc, keyVal);
+ key.iov_len = sizeof(MCONTACT); key.iov_base = &contactID;
+ data.iov_base = &cc->dbc; data.iov_len = sizeof(cc->dbc);
+ MDBX_CHECK(mdbx_put(txn, m_dbContacts, &key, &data, 0), -1);
+
+ if (txn.commit() == MDBX_SUCCESS)
+ break;
+ }
+
+ NotifyEventHooks(hEventMarkedRead, contactID, (LPARAM)hDbEvent);
+ return wRetVal;
+}
+
+STDMETHODIMP_(MCONTACT) CDbxMDBX::GetEventContact(MEVENT hDbEvent)
+{
+ if (hDbEvent == 0)
+ return INVALID_CONTACT_ID;
+
+ txn_ptr_ro txn(m_txn);
+
+ MDBX_val key = { &hDbEvent, sizeof(MEVENT) }, data;
+ if (mdbx_get(txn, m_dbEvents, &key, &data) != MDBX_SUCCESS)
+ return INVALID_CONTACT_ID;
+
+ return ((const DBEvent*)data.iov_base)->contactID;
+}
+
+thread_local uint64_t t_tsLast = 0;
+thread_local MEVENT t_evLast = 0;
+
+STDMETHODIMP_(MEVENT) CDbxMDBX::FindFirstEvent(MCONTACT contactID)
+{
+ DBEventSortingKey keyVal = { contactID, 0, 0 };
+ MDBX_val key = { &keyVal, sizeof(keyVal) }, data;
+
+ txn_ptr_ro txn(m_txn);
+
+ cursor_ptr_ro cursor(m_curEventsSort);
+ if (mdbx_cursor_get(cursor, &key, &data, MDBX_SET_RANGE) != MDBX_SUCCESS)
+ return t_evLast = 0;
+
+ const DBEventSortingKey *pKey = (const DBEventSortingKey*)key.iov_base;
+ t_tsLast = pKey->ts;
+ return t_evLast = (pKey->hContact == contactID) ? pKey->hEvent : 0;
+}
+
+STDMETHODIMP_(MEVENT) CDbxMDBX::FindFirstUnreadEvent(MCONTACT contactID)
+{
+ DBCachedContact *cc = m_cache->GetCachedContact(contactID);
+ return (cc == NULL) ? 0 : cc->dbc.evFirstUnread;
+}
+
+STDMETHODIMP_(MEVENT) CDbxMDBX::FindLastEvent(MCONTACT contactID)
+{
+ DBEventSortingKey keyVal = { contactID, 0xFFFFFFFF, 0xFFFFFFFFFFFFFFFF };
+ MDBX_val key = { &keyVal, sizeof(keyVal) }, data;
+
+ txn_ptr_ro txn(m_txn);
+ cursor_ptr_ro cursor(m_curEventsSort);
+
+ if (mdbx_cursor_get(cursor, &key, &data, MDBX_SET_RANGE) != MDBX_SUCCESS) {
+ if (mdbx_cursor_get(cursor, &key, &data, MDBX_LAST) != MDBX_SUCCESS)
+ return t_evLast = 0;
+ }
+ else {
+ if (mdbx_cursor_get(cursor, &key, &data, MDBX_PREV) != MDBX_SUCCESS)
+ return t_evLast = 0;
+ }
+
+ const DBEventSortingKey *pKey = (const DBEventSortingKey*)key.iov_base;
+ t_tsLast = pKey->ts;
+ return t_evLast = (pKey->hContact == contactID) ? pKey->hEvent : 0;
+}
+
+STDMETHODIMP_(MEVENT) CDbxMDBX::FindNextEvent(MCONTACT contactID, MEVENT hDbEvent)
+{
+ if (hDbEvent == 0)
+ return t_evLast = 0;
+
+ txn_ptr_ro txn(m_txn);
+
+ if (t_evLast != hDbEvent) {
+ MDBX_val key = { &hDbEvent, sizeof(MEVENT) }, data;
+ if (mdbx_get(txn, m_dbEvents, &key, &data) != MDBX_SUCCESS)
+ return 0;
+ t_tsLast = ((DBEvent*)data.iov_base)->timestamp;
+ }
+
+ DBEventSortingKey keyVal = { contactID, hDbEvent, t_tsLast };
+ MDBX_val key = { &keyVal, sizeof(keyVal) }, data;
+
+ cursor_ptr_ro cursor(m_curEventsSort);
+ if (mdbx_cursor_get(cursor, &key, &data, MDBX_SET) != MDBX_SUCCESS)
+ return t_evLast = 0;
+
+ if (mdbx_cursor_get(cursor, &key, &data, MDBX_NEXT) != MDBX_SUCCESS)
+ return t_evLast = 0;
+
+ const DBEventSortingKey *pKey = (const DBEventSortingKey*)key.iov_base;
+ t_tsLast = pKey->ts;
+ return t_evLast = (pKey->hContact == contactID) ? pKey->hEvent : 0;
+}
+
+STDMETHODIMP_(MEVENT) CDbxMDBX::FindPrevEvent(MCONTACT contactID, MEVENT hDbEvent)
+{
+ if (hDbEvent == 0)
+ return t_evLast = 0;
+
+ MDBX_val data;
+
+ txn_ptr_ro txn(m_txn);
+
+ if (t_evLast != hDbEvent) {
+ MDBX_val key = { &hDbEvent, sizeof(MEVENT) };
+ if (mdbx_get(txn, m_dbEvents, &key, &data) != MDBX_SUCCESS)
+ return 0;
+ t_tsLast = ((DBEvent*)data.iov_base)->timestamp;
+ }
+
+ DBEventSortingKey keyVal = { contactID, hDbEvent, t_tsLast };
+ MDBX_val key = { &keyVal, sizeof(keyVal) };
+
+ cursor_ptr_ro cursor(m_curEventsSort);
+ if (mdbx_cursor_get(cursor, &key, &data, MDBX_SET) != MDBX_SUCCESS)
+ return t_evLast = 0;
+
+ if (mdbx_cursor_get(cursor, &key, &data, MDBX_PREV) != MDBX_SUCCESS)
+ return t_evLast = 0;
+
+ const DBEventSortingKey *pKey = (const DBEventSortingKey*)key.iov_base;
+ t_tsLast = pKey->ts;
+ return t_evLast = (pKey->hContact == contactID) ? pKey->hEvent : 0;
+}