#include "stdafx.h" #include "mirandacontact.h" #include "_consts.h" #include "utils.h" #include "main.h" /* * MirandaContact */ void MirandaContact::fetchSlot(int i) { ContactInfo& ci = m_CIs[i]; if (!ci.hEvent) { free(ci.ei.dbe.pBlob); m_CIs.erase(m_CIs.begin() + i); return; } EventInfo& ei = ci.ei; ei.hContact = ci.hContact; ei.dbe.cbBlob = db_event_getBlobSize(ci.hEvent); if (ei.dbe.cbBlob > ei.nAllocated) { ei.nAllocated = ei.dbe.cbBlob; ei.dbe.pBlob = reinterpret_cast(realloc(ei.dbe.pBlob, ei.dbe.cbBlob + 1)); } db_event_get(ci.hEvent, &ei.dbe); stripMetaID(ei.dbe); ci.hEvent = db_event_next(ci.hContact, ci.hEvent); } void MirandaContact::stripMetaID(DBEVENTINFO& dbe) { if (mir_strcmp(dbe.szModule,META_PROTO)==0) { char* pTextBegin = reinterpret_cast(dbe.pBlob); if (dbe.cbBlob >= 6 && !pTextBegin[dbe.cbBlob - 1]) { char* pIDEnd = pTextBegin + dbe.cbBlob - 1; char* pIDBegin = pIDEnd; char* pIDSep = nullptr; while (pIDBegin >= pTextBegin + 2 && *--pIDBegin) if (*pIDBegin == '*') pIDSep = pIDBegin; ++pIDBegin; if (pIDSep && pIDBegin < pIDSep && !*(pIDBegin - 1)) { dbe.cbBlob = pIDBegin - pTextBegin; } } } } MirandaContact::MirandaContact(const ext::string& strNick, const ext::string& strProtocol, const ext::string& strGroup, const SourceHandles& sources) : m_strNick(strNick), m_strProtocol(strProtocol), m_strGroup(strGroup), m_Sources(sources) { } MirandaContact::~MirandaContact() { endRead(); } void MirandaContact::merge(const MirandaContact& other) { if (m_strNick != other.m_strNick) m_strNick = TranslateT("(multiple)"); if (m_strProtocol != other.m_strProtocol) m_strProtocol = TranslateT("(multiple)"); if (m_strGroup != other.m_strGroup) m_strGroup = TranslateT("(multiple)"); citer_each_(SourceHandles, i, other.m_Sources) { m_Sources.push_back(*i); } } void MirandaContact::beginRead() { // clean up first endRead(); // allocate required data m_CIs.resize(m_Sources.size()); for (int j = m_Sources.size() - 1; j >= 0; --j) { ContactInfo& ci = m_CIs[j]; ci.hContact = m_Sources[j]; ci.hEvent = db_event_first(ci.hContact); ci.ei.dbe.pBlob = nullptr; ci.ei.nAllocated = 0; fetchSlot(j); } fillQueue(); } void MirandaContact::endRead() { #if defined(_DEBUG) if (m_CIs.size() + m_EIs.size() + m_SpareEIs.size() > 0) { ext::string strLog = ext::str(ext::format(L"Freeing | CIs and |+| EIs...\n") % m_CIs.size() % m_EIs.size() % m_SpareEIs.size()); OutputDebugString(strLog.c_str()); } #endif // _DEBUG citer_each_(std::vector, i, m_CIs) { free(i->ei.dbe.pBlob); } citer_each_(std::list, i, m_EIs) { free(i->dbe.pBlob); } citer_each_(std::list, i, m_SpareEIs) { free(i->dbe.pBlob); } m_CIs.clear(); m_EIs.clear(); m_SpareEIs.clear(); } void MirandaContact::readNext() { if (!m_EIs.empty()) { m_SpareEIs.push_back(m_EIs.front()); m_EIs.pop_front(); } fillQueue(); } /* * MirandaContactTolerantMerge */ void MirandaContactTolerantMerge::fillQueue() { // assume that items with +/- 30 seconds may be equal static const int timestampTol = 30; while (!m_CIs.empty() && (m_EIs.size() < 2 || (m_EIs.back().dbe.timestamp - m_EIs.front().dbe.timestamp) <= timestampTol)) { // find oldest next event in chains int nNext = 0; DWORD timestampFirst = m_CIs.front().ei.dbe.timestamp; for (int i = 1; i < m_CIs.size(); ++i) { if (m_CIs[i].ei.dbe.timestamp < timestampFirst) { timestampFirst = m_CIs[i].ei.dbe.timestamp; nNext = i; } } // insert the fetched at correct position or throw away if duplicate ContactInfo& ci = m_CIs[nNext]; std::list::iterator insPos = m_EIs.end(); bool bIsDuplicate = false; iter_each_(std::list, j, m_EIs) { EventInfo& j_ei = *j; int timestampDelta = j_ei.dbe.timestamp - ci.ei.dbe.timestamp; if (timestampDelta > 0) { insPos = j; } if (j_ei.hContact != ci.ei.hContact && timestampDelta >= -timestampTol && timestampDelta <= timestampTol && j_ei.dbe.eventType == ci.ei.dbe.eventType && (j_ei.dbe.flags & ~DBEF_READ) == (ci.ei.dbe.flags & ~DBEF_READ) && j_ei.dbe.cbBlob == ci.ei.dbe.cbBlob && memcmp(j_ei.dbe.pBlob, ci.ei.dbe.pBlob, j_ei.dbe.cbBlob) == 0) { bIsDuplicate = true; break; } } if (!bIsDuplicate) { m_EIs.insert(insPos, ci.ei); if (!m_SpareEIs.empty()) { ci.ei = m_SpareEIs.front(); m_SpareEIs.pop_front(); } else { ci.ei.dbe.pBlob = nullptr; ci.ei.nAllocated = 0; } } fetchSlot(nNext); } } /* * MirandaContactStrictMerge */ void MirandaContactStrictMerge::fillQueue() { // assume that items with +/- 30 seconds may be equal static const int timestampTol = 0; while (!m_CIs.empty() && (m_EIs.size() < 2 || (m_EIs.back().dbe.timestamp - m_EIs.front().dbe.timestamp) <= timestampTol)) { // find oldest next event in chains int nNext = 0; DWORD timestampFirst = m_CIs.front().ei.dbe.timestamp; for (int i = 1; i < m_CIs.size(); ++i) { if (m_CIs[i].ei.dbe.timestamp < timestampFirst) { timestampFirst = m_CIs[i].ei.dbe.timestamp; nNext = i; } } // insert the fetched at correct position or throw away if duplicate ContactInfo& ci = m_CIs[nNext]; std::list::iterator insPos = m_EIs.end(); bool bIsDuplicate = false; iter_each_(std::list, j, m_EIs) { EventInfo& j_ei = *j; int timestampDelta = j_ei.dbe.timestamp - ci.ei.dbe.timestamp; if (timestampDelta > 0) insPos = j; if (j_ei.hContact != ci.ei.hContact && timestampDelta >= -timestampTol && timestampDelta <= timestampTol && j_ei.dbe.eventType == ci.ei.dbe.eventType && (j_ei.dbe.flags & ~DBEF_READ) == (ci.ei.dbe.flags & ~DBEF_READ) && j_ei.dbe.cbBlob == ci.ei.dbe.cbBlob && memcmp(j_ei.dbe.pBlob, ci.ei.dbe.pBlob, j_ei.dbe.cbBlob) == 0) { bIsDuplicate = true; break; } } if (!bIsDuplicate) { m_EIs.insert(insPos, ci.ei); if (!m_SpareEIs.empty()) { ci.ei = m_SpareEIs.front(); m_SpareEIs.pop_front(); } else { ci.ei.dbe.pBlob = nullptr; ci.ei.nAllocated = 0; } } fetchSlot(nNext); } } /* * MirandaContactNoMerge */ void MirandaContactNoMerge::fillQueue() { while (!m_CIs.empty() && m_EIs.size() < 1) { // find oldest next event in chains int nNext = 0; DWORD timestampFirst = m_CIs.front().ei.dbe.timestamp; for (int i = 1; i < m_CIs.size(); ++i) { if (m_CIs[i].ei.dbe.timestamp < timestampFirst) { timestampFirst = m_CIs[i].ei.dbe.timestamp; nNext = i; } } // insert the fetched at correct position or throw away if duplicate ContactInfo& ci = m_CIs[nNext]; m_EIs.push_back(ci.ei); if (!m_SpareEIs.empty()) { ci.ei = m_SpareEIs.front(); m_SpareEIs.pop_front(); } else { ci.ei.dbe.pBlob = nullptr; ci.ei.nAllocated = 0; } fetchSlot(nNext); } } /* * MirandaContactFactory */ MirandaContact* MirandaContactFactory::makeMirandaContact(int MergeMode, const ext::string& strNick, const ext::string& strProtocol, const ext::string& strGroup, const MirandaContact::SourceHandles& sources) { switch (MergeMode) { case Settings::mmTolerantMerge: return new MirandaContactTolerantMerge(strNick, strProtocol, strGroup, sources); case Settings::mmStrictMerge: return new MirandaContactStrictMerge(strNick, strProtocol, strGroup, sources); case Settings::mmNoMerge: return new MirandaContactNoMerge(strNick, strProtocol, strGroup, sources); default: return nullptr; } }