/* Basic History plugin Copyright (C) 2011-2012 Krzysztof Kral 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 version 2 of the License. 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, see . */ #include "StdAfx.h" #include "EventList.h" #include "Options.h" #include "ExportManager.h" #include extern int iconsNum; static mir_cs csEventList; std::wstring GetName(const std::wstring &path); HistoryEventList::HistoryEventList() : m_hWnd(nullptr), m_isWnd(false), m_hContact(0), m_deltaTime(0), m_isFlat(false), m_useImportedMessages(true) { memset(&m_dbei, 0, sizeof(DBEVENTINFO)); m_oldBlobSize = 0; } HistoryEventList::HistoryEventList(MCONTACT _hContact, int filter) : m_hWnd(nullptr), m_isWnd(false), m_hContact(_hContact), m_deltaTime(0), m_isFlat(false), m_useImportedMessages(true) { memset(&m_dbei, 0, sizeof(DBEVENTINFO)); m_oldBlobSize = 0; SetDefFilter(filter); } HistoryEventList::~HistoryEventList() { mir_free(m_dbei.pBlob); m_eventList.clear(); } bool HistoryEventList::CanShowHistory(DBEVENTINFO* dbei) { if (m_deltaTime != 0) { if (m_deltaTime > 0) { if (m_now - m_deltaTime < dbei->timestamp) return false; } else { if (m_now + m_deltaTime > dbei->timestamp) return false; } } if (m_hContact == NULL || m_defFilter == 1) return true; if (m_defFilter < 1) { switch (dbei->eventType) { case EVENTTYPE_MESSAGE: case EVENTTYPE_FILE: return true; default: DBEVENTTYPEDESCR *et = DbEvent_GetType(dbei->szModule, dbei->eventType); if (et && (et->flags & DETF_HISTORY)) return true; } return false; } if (m_filterMap.find(dbei->eventType) != m_filterMap.end()) { if (m_onlyInFilter) return !(dbei->flags & DBEF_SENT); if (m_onlyOutFilter) return (dbei->flags & DBEF_SENT) != 0; return true; } return false; } bool HistoryEventList::CanShowHistory(const IImport::ExternalMessage &message) { if (m_deltaTime != 0) { if (m_deltaTime > 0) { if (m_now - m_deltaTime < message.timestamp) return false; } else { if (m_now + m_deltaTime > message.timestamp) return false; } } if (m_hContact == NULL || m_defFilter == 1) return true; if (m_defFilter < 1) { switch (message.eventType) { case EVENTTYPE_MESSAGE: case EVENTTYPE_FILE: return true; } return false; } if (m_filterMap.find(message.eventType) != m_filterMap.end()) { if (m_onlyInFilter) return !(message.flags & DBEF_SENT); if (m_onlyOutFilter) return (message.flags & DBEF_SENT) != 0; return true; } return false; } void HistoryEventList::InitFilters() { m_filterMap.clear(); m_onlyInFilter = false; m_onlyOutFilter = false; if (m_defFilter >= 2) { m_defFilter = 0; for (int i = 0; i < (int)Options::instance->customFilters.size(); ++i) { if (m_filterName == Options::instance->customFilters[i].name) { m_defFilter = i + 2; if (Options::instance->customFilters[i].onlyIncomming && !Options::instance->customFilters[i].onlyOutgoing) m_onlyInFilter = true; else if (Options::instance->customFilters[i].onlyOutgoing && !Options::instance->customFilters[i].onlyIncomming) m_onlyOutFilter = true; for (std::vector::iterator it = Options::instance->customFilters[i].events.begin(); it != Options::instance->customFilters[i].events.end(); ++it) m_filterMap[*it] = true; break; } } } else m_filterName = L""; } void HistoryEventList::SetDefFilter(int filter) { m_defFilter = filter; if (filter >= 2 && filter - 2 < (int)Options::instance->customFilters.size()) m_filterName = Options::instance->customFilters[filter - 2].name; else if (filter == 1) m_filterName = TranslateT("All events"); else m_filterName = TranslateT("Default history events"); } int HistoryEventList::GetFilterNr() { return m_defFilter; } std::wstring HistoryEventList::GetFilterName() { return m_filterName; } void HistoryEventList::GetTempList(std::list& tempList, bool noFilter, bool noExt, MCONTACT hContact) { bool isWndLocal = m_isWnd; EventTempIndex ti; EventData data; EventIndex ei; ti.isExternal = false; ei.isExternal = false; DB::ECPTR pCursor(DB::Events(hContact)); while (MEVENT hDbEvent = pCursor.FetchNext()) { if (isWndLocal && !IsWindow(m_hWnd)) break; ei.hEvent = hDbEvent; if (GetEventData(ei, data)) { if (noFilter || CanShowHistory(&m_dbei)) { ti.hEvent = hDbEvent; ti.timestamp = data.timestamp; tempList.push_back(ti); } } } if (!noExt) { std::list::iterator itL = tempList.begin(); ti.isExternal = true; for (int i = 0; i < (int)m_importedMessages.size(); ++i) { if (noFilter || CanShowHistory(m_importedMessages[i])) { uint32_t ts = m_importedMessages[i].timestamp; while (itL != tempList.end() && itL->timestamp < ts)++itL; if (itL == tempList.end() || itL->timestamp > ts) { ti.exIdx = i; ti.timestamp = ts; tempList.insert(itL, ti); } } } } } void HistoryEventList::RefreshEventList() { InitNames(); InitFilters(); if (m_useImportedMessages) { std::vector messages; { mir_cslock lck(csEventList); std::map::iterator it = m_contactFileMap.find(m_hContact); if (it != m_contactFileMap.end()) { ExportManager imp(m_hWnd, m_hContact, 1); imp.SetAutoImport(it->second.file); if (!imp.Import(it->second.type, messages, nullptr)) messages.clear(); } } ImportMessages(messages); } std::list tempList; GetTempList(tempList, false, false, m_hContact); std::list revTempList; std::list& nrTempList = tempList; bool isNewOnTop = Options::instance->groupNewOnTop; if (isNewOnTop) { revTempList.insert(revTempList.begin(), tempList.rbegin(), tempList.rend()); nrTempList = revTempList; } m_eventList.clear(); m_eventList.push_back(std::deque()); uint32_t lastTime = MAXDWORD; uint32_t groupTime = Options::instance->groupTime * 60 * 60; int maxMess = Options::instance->groupMessagesNumber; int limitator = 0; EventIndex ei; for (std::list::iterator itL = nrTempList.begin(); itL != nrTempList.end(); ++itL) { uint32_t tm = isNewOnTop ? lastTime - itL->timestamp : itL->timestamp - lastTime; if (m_isFlat || tm < groupTime && limitator < maxMess) { lastTime = itL->timestamp; ei.isExternal = itL->isExternal; ei.hEvent = itL->hEvent; if (isNewOnTop) m_eventList.back().push_front(ei); else m_eventList.back().push_back(ei); ++limitator; } else { limitator = 0; lastTime = itL->timestamp; if (!m_eventList.back().empty()) { ei = m_eventList.back().front(); AddGroup(ei); m_eventList.push_back(std::deque()); } ei.isExternal = itL->isExternal; ei.hEvent = itL->hEvent; m_eventList.back().push_front(ei); } } if (!m_eventList.back().empty()) { ei = m_eventList.back().front(); AddGroup(ei); } } bool HistoryEventList::SearchInContact(MCONTACT hContact, wchar_t *strFind, ComparatorInterface* compFun) { InitFilters(); if (m_useImportedMessages) { std::vector messages; { mir_cslock lck(csEventList); std::map::iterator it = m_contactFileMap.find(hContact); if (it != m_contactFileMap.end()) { ExportManager imp(m_hWnd, hContact, 1); imp.SetAutoImport(it->second.file); if (!imp.Import(it->second.type, messages, nullptr)) messages.clear(); } } for (int i = 0; i < (int)m_importedMessages.size(); ++i) if (compFun->Compare((m_importedMessages[i].flags & DBEF_SENT) != 0, m_importedMessages[i].message, strFind)) return true; } std::list tempList; GetTempList(tempList, false, true, hContact); EventIndex ei; EventData ed; wchar_t str[MAXSELECTSTR + 8]; // for safety reason for (std::list::iterator itL = tempList.begin(); itL != tempList.end(); ++itL) { ei.isExternal = itL->isExternal; ei.hEvent = itL->hEvent; if (GetEventData(ei, ed)) { GetEventMessage(ei, str); if (compFun->Compare(ed.isMe, str, strFind)) return true; } } return false; } void HistoryEventList::InitNames() { wchar_t str[200]; if (m_hContact) { wcscpy_s(m_contactName, Clist_GetContactDisplayName(m_hContact)); mir_snwprintf(str, TranslateT("History for %s"), m_contactName); } else { wcscpy_s(m_contactName, TranslateT("System")); mir_snwprintf(str, TranslateT("History")); } if (m_isWnd) SetWindowText(m_hWnd, str); wcscpy_s(m_myName, GetMyName().c_str()); } void HistoryEventList::AddGroup(const EventIndex& ev) { EventData data; GetEventData(ev, data); wchar_t eventText[256]; int i; eventText[0] = 0; TimeZone_PrintTimeStamp(nullptr, data.timestamp, L"d t", eventText, 64, 0); std::wstring time = eventText; std::wstring user; if (data.isMe) user = m_myName; else user = m_contactName; GetEventMessage(ev, eventText, 256); for (i = 0; eventText[i] != 0 && eventText[i] != '\r' && eventText[i] != '\n'; ++i); eventText[i] = 0; if (i > Options::instance->groupMessageLen) { eventText[Options::instance->groupMessageLen - 3] = '.'; eventText[Options::instance->groupMessageLen - 2] = '.'; eventText[Options::instance->groupMessageLen - 1] = '.'; eventText[Options::instance->groupMessageLen] = 0; } int ico = 0; GetEventIcon(data.isMe, data.eventType, ico); AddGroup(data.isMe, time, user, eventText, ico); } std::wstring HistoryEventList::GetContactName() { if (m_hContact) return Clist_GetContactDisplayName(m_hContact); return TranslateT("System"); } std::wstring HistoryEventList::GetMyName() { ptrW name(Contact::GetInfo(CNF_DISPLAY, NULL, Proto_GetBaseAccountName(m_hContact))); return (name == NULL) ? TranslateT("Me") : name; } inline std::wstring GetProtocolName(MCONTACT hContact) { char* ac = Proto_GetBaseAccountName(hContact); std::wstring proto1; if (ac != nullptr) { PROTOACCOUNT* acnt = Proto_GetAccount(ac); if (acnt != nullptr && acnt->szModuleName != nullptr) { wchar_t* proto = mir_a2u(acnt->szProtoName); proto1 = proto; mir_free(proto); } } return proto1; } std::wstring HistoryEventList::GetProtocolName() { return ::GetProtocolName(m_hContact); } std::string HistoryEventList::GetBaseProtocol() { char* proto = Proto_GetBaseAccountName(m_hContact); return proto == nullptr ? "" : proto; } std::wstring HistoryEventList::GetMyId() { ptrW id(Contact::GetInfo(CNF_DISPLAYUID, NULL, Proto_GetBaseAccountName(m_hContact))); return (id == NULL) ? L"" : id; } inline std::wstring GetContactId(MCONTACT hContact) { ptrW id(Contact::GetInfo(CNF_DISPLAYUID, hContact)); return (id == NULL) ? L"" : id; } std::wstring HistoryEventList::GetContactId() { return ::GetContactId(m_hContact); } static void GetMessageDescription(DBEVENTINFO *dbei, wchar_t* buf, int cbBuf) { wchar_t *msg = DbEvent_GetTextW(dbei, CP_ACP); wcsncpy_s(buf, cbBuf, msg ? msg : TranslateT("Invalid message"), _TRUNCATE); buf[cbBuf - 1] = 0; mir_free(msg); } void HistoryEventList::GetObjectDescription(DBEVENTINFO *dbei, wchar_t* str, int cbStr) { GetMessageDescription(dbei, str, cbStr); } bool HistoryEventList::GetEventIcon(bool isMe, int eventType, int &id) { switch (eventType) { case EVENTTYPE_MESSAGE: id = isMe ? 1 : 0; return true; case EVENTTYPE_FILE: id = iconsNum; return true; case EVENTTYPE_AUTHREQUEST: id = iconsNum + 2; return true; default: id = isMe ? 1 : 0; return false; } } void HistoryEventList::ImportMessages(const std::vector& messages) { uint32_t lastTime = 0; m_importedMessages.clear(); for (int i = 0; i < (int)messages.size(); ++i) { if (messages[i].timestamp >= lastTime) { m_importedMessages.push_back(messages[i]); lastTime = messages[i].timestamp; } else { assert(FALSE); } } } void HistoryEventList::MargeMessages(const std::vector& messages) { ImportMessages(messages); std::list tempList; GetTempList(tempList, true, false, m_hContact); DBEVENTINFO dbei = {}; dbei.szModule = Proto_GetBaseAccountName(m_hContact); db_set_safety_mode(FALSE); for (std::list::iterator it = tempList.begin(); it != tempList.end(); ++it) { if (it->isExternal) { IImport::ExternalMessage& msg = m_importedMessages[it->exIdx]; dbei.flags |= DBEF_READ; dbei.timestamp = msg.timestamp; // For now I do not convert event data from string to blob, and event type must be message to handle it properly dbei.eventType = EVENTTYPE_MESSAGE; UINT cp = dbei.flags & DBEF_UTF ? CP_UTF8 : CP_ACP; dbei.cbBlob = WideCharToMultiByte(cp, 0, msg.message.c_str(), (int)msg.message.length() + 1, nullptr, 0, nullptr, nullptr); char* buf = new char[dbei.cbBlob]; dbei.cbBlob = WideCharToMultiByte(cp, 0, msg.message.c_str(), (int)msg.message.length() + 1, buf, dbei.cbBlob, nullptr, nullptr); dbei.pBlob = (uint8_t*)buf; db_event_add(m_hContact, &dbei); delete[] buf; } } db_set_safety_mode(TRUE); std::vector emessages; ImportMessages(emessages); } bool HistoryEventList::GetEventData(const EventIndex& ev, EventData& data) { if (!ev.isExternal) { uint32_t newBlobSize = db_event_getBlobSize(ev.hEvent); if (newBlobSize > m_oldBlobSize) { m_dbei.pBlob = (uint8_t*)mir_realloc(m_dbei.pBlob, newBlobSize); m_oldBlobSize = newBlobSize; } m_dbei.cbBlob = m_oldBlobSize; if (db_event_get(ev.hEvent, &m_dbei) == 0) { data.isMe = (m_dbei.flags & DBEF_SENT) != 0; data.eventType = m_dbei.eventType; data.timestamp = m_dbei.timestamp; return true; } } else if (ev.exIdx >= 0 && ev.exIdx < (int)m_importedMessages.size()) { IImport::ExternalMessage& em = m_importedMessages[ev.exIdx]; data.isMe = (em.flags & DBEF_SENT) != 0; data.eventType = em.eventType; data.timestamp = em.timestamp; return true; } return false; } void HistoryEventList::GetExtEventDBei(const EventIndex& ev) { IImport::ExternalMessage& em = m_importedMessages[ev.exIdx]; m_dbei.flags = em.flags | 0x800; m_dbei.eventType = em.eventType; m_dbei.timestamp = em.timestamp; } HICON HistoryEventList::GetEventCoreIcon(const EventIndex& ev) { if (ev.isExternal) return nullptr; HICON ico = DbEvent_GetIcon(&m_dbei, LR_SHARED); HICON icoMsg = Skin_LoadIcon(SKINICON_EVENT_MESSAGE); if (icoMsg == ico) return nullptr; return ico; } void HistoryEventList::RebuildGroup(int selected) { std::deque newGroup; for (size_t i = 0; i < m_eventList[selected].size(); ++i) { EventIndex& ev = m_eventList[selected][i]; if (!ev.isExternal) { // If event exist, we add it to new group if (db_event_getBlobSize(ev.hEvent) >= 0) newGroup.push_back(m_eventList[selected][i]); } else newGroup.push_back(m_eventList[selected][i]); } m_eventList[selected].clear(); m_eventList[selected].insert(m_eventList[selected].begin(), newGroup.begin(), newGroup.end()); } std::map HistoryEventList::m_contactFileMap; std::wstring HistoryEventList::m_contactFileDir; void HistoryEventList::AddImporter(MCONTACT hContact, IImport::ImportType type, const std::wstring& file) { mir_cslock lck(csEventList); wchar_t buf[32]; mir_snwprintf(buf, L"%016llx", (unsigned long long int)hContact); ImportDiscData data; data.file = m_contactFileDir + buf; data.type = type; CopyFile(file.c_str(), data.file.c_str(), FALSE); m_contactFileMap[hContact] = data; } void HistoryEventList::Init() { wchar_t temp[MAX_PATH]; temp[0] = 0; GetTempPath(MAX_PATH, temp); m_contactFileDir = temp; m_contactFileDir += L"BasicHistoryImportDir\\"; DeleteDirectoryTreeW(m_contactFileDir.c_str()); CreateDirectory(m_contactFileDir.c_str(), nullptr); } int HistoryEventList::GetContactMessageNumber(MCONTACT hContact) { int count = db_event_count(hContact); mir_cslock lck(csEventList); std::map::iterator it = m_contactFileMap.find(hContact); if (it != m_contactFileMap.end()) ++count; return count; } bool HistoryEventList::IsImportedHistory(MCONTACT hContact) { bool count = false; mir_cslock lck(csEventList); std::map::iterator it = m_contactFileMap.find(hContact); if (it != m_contactFileMap.end()) count = true; return count; } void HistoryEventList::DeleteImporter(MCONTACT hContact) { mir_cslock lck(csEventList); std::map::iterator it = m_contactFileMap.find(hContact); if (it != m_contactFileMap.end()) { DeleteFile(it->second.file.c_str()); m_contactFileMap.erase(it); } }