diff options
Diffstat (limited to 'plugins/Import/src/patterns.cpp')
| -rw-r--r-- | plugins/Import/src/patterns.cpp | 1464 |
1 files changed, 732 insertions, 732 deletions
diff --git a/plugins/Import/src/patterns.cpp b/plugins/Import/src/patterns.cpp index 094bf1f33c..9dadce018c 100644 --- a/plugins/Import/src/patterns.cpp +++ b/plugins/Import/src/patterns.cpp @@ -1,732 +1,732 @@ -/* - -Import plugin for Miranda NG - -Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org) - -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" - -#include <memory> -#include <vector> - -void CMPlugin::LoadPatterns() -{ - wchar_t wszPath[MAX_PATH], wszFullPath[MAX_PATH]; - GetModuleFileNameW(m_hInst, wszPath, _countof(wszPath)); - if (auto *p = wcsrchr(wszPath, '\\')) - *p = 0; - - mir_snwprintf(wszFullPath, L"%s\\Import\\*.ini", wszPath); - - WIN32_FIND_DATAW fd; - HANDLE hFind = FindFirstFileW(wszFullPath, &fd); - if (hFind == INVALID_HANDLE_VALUE) - return; - - do { - mir_snwprintf(wszFullPath, L"%s\\Import\\%s", wszPath, fd.cFileName); - LoadPattern(wszFullPath); - } - while (FindNextFileW(hFind, &fd) != 0); -} - -void CMPlugin::LoadPattern(const wchar_t *pwszFileName) -{ - // [General] section - wchar_t buf[1024]; - if (!GetPrivateProfileStringW(L"General", L"Name", L"", buf, _countof(buf), pwszFileName)) - return; - - std::unique_ptr<CImportPattern> pNew(new CImportPattern()); - pNew->wszName = buf; - pNew->iType = GetPrivateProfileIntW(L"General", L"Type", 1, pwszFileName); - - if (GetPrivateProfileStringW(L"General", L"DefaultExtension", L"", buf, _countof(buf), pwszFileName)) - pNew->wszExt = buf; - - if (pNew->iType == 1) { - if (GetPrivateProfileStringW(L"General", L"Charset", L"", buf, _countof(buf), pwszFileName)) { - if (!wcsicmp(buf, L"ANSI")) - pNew->iCodePage = GetPrivateProfileIntW(L"General", L"Codepage", CP_ACP, pwszFileName); - else if (!wcsicmp(buf, L"UCS2")) - pNew->iCodePage = 1200; - } - else return; - } - - pNew->iUseHeader = GetPrivateProfileIntW(L"General", L"UseHeader", 0, pwszFileName); - pNew->iUsePreMsg = GetPrivateProfileIntW(L"General", L"UsePreMsg", 0, pwszFileName); - pNew->iUseFilename = GetPrivateProfileIntW(L"General", L"UseFileName", 0, pwszFileName); - - // [Message] section - int erroffset; - const char *err; - if (pNew->iType == 1) { - if (GetPrivateProfileStringW(L"Message", L"Pattern", L"", buf, _countof(buf), pwszFileName)) { - if ((pNew->regMessage.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr) - return; - pNew->regMessage.extra = pcre16_study(pNew->regMessage.pattern, 0, &err); - } - else return; - - if (GetPrivateProfileStringW(L"Message", L"In", L"", buf, _countof(buf), pwszFileName)) - pNew->wszIncoming = buf; - if (GetPrivateProfileStringW(L"Message", L"Out", L"", buf, _countof(buf), pwszFileName)) - pNew->wszOutgoing = buf; - - pNew->iDirection = GetPrivateProfileIntW(L"Message", L"Direction", 0, pwszFileName); - pNew->iDay = GetPrivateProfileIntW(L"Message", L"Day", 0, pwszFileName); - pNew->iMonth = GetPrivateProfileIntW(L"Message", L"Month", 0, pwszFileName); - pNew->iYear = GetPrivateProfileIntW(L"Message", L"Year", 0, pwszFileName); - pNew->iHours = GetPrivateProfileIntW(L"Message", L"Hours", 0, pwszFileName); - pNew->iMinutes = GetPrivateProfileIntW(L"Message", L"Minutes", 0, pwszFileName); - pNew->iSeconds = GetPrivateProfileIntW(L"Message", L"Seconds", 0, pwszFileName); - } - - if (pNew->iUseHeader) { - if (GetPrivateProfileStringW(L"Header", L"Pattern", L"", buf, _countof(buf), pwszFileName)) { - if ((pNew->regHeader.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr) - return; - pNew->regHeader.extra = pcre16_study(pNew->regMessage.pattern, 0, &err); - } - else return; - - pNew->iHdrIncoming = GetPrivateProfileIntW(L"Header", L"In", 0, pwszFileName); - pNew->iHdrOutgoing = GetPrivateProfileIntW(L"Header", L"Out", 0, pwszFileName); - pNew->iHdrInNick = GetPrivateProfileIntW(L"Header", L"InNick", 0, pwszFileName); - pNew->iHdrOutNick = GetPrivateProfileIntW(L"Header", L"OutNick", 0, pwszFileName); - pNew->iHdrInUID = GetPrivateProfileIntW(L"Header", L"InUID", 0, pwszFileName); - pNew->iHdrOutUID = GetPrivateProfileIntW(L"Header", L"OutUID", 0, pwszFileName); - } - - if (pNew->iUsePreMsg) { - pNew->preRN = GetPrivateProfileIntW(L"PreMessage", L"PreRN", -1, pwszFileName); - pNew->preSP = GetPrivateProfileIntW(L"PreMessage", L"PreSP", 0, pwszFileName); - pNew->afterRN = GetPrivateProfileIntW(L"PreMessage", L"AfterRN", -1, pwszFileName); - pNew->afterSP = GetPrivateProfileIntW(L"PreMessage", L"AfterSP", 0, pwszFileName); - } - - if (pNew->iUseFilename) { - if (!GetPrivateProfileStringW(L"FileName", L"Pattern", L"", buf, _countof(buf), pwszFileName)) - return; - if ((pNew->regFilename.pattern = pcre16_compile(buf, 0, &err, &erroffset, nullptr)) == nullptr) - return; - pNew->regFilename.extra = pcre16_study(pNew->regFilename.pattern, 0, &err); - - pNew->iInNick = GetPrivateProfileIntW(L"FileName", L"InNick", 0, pwszFileName); - pNew->iInUID = GetPrivateProfileIntW(L"FileName", L"InUID", 0, pwszFileName); - pNew->iOutNick = GetPrivateProfileIntW(L"FileName", L"OutNick", 0, pwszFileName); - pNew->iOutUID = GetPrivateProfileIntW(L"FileName", L"OutUID", 0, pwszFileName); - } - - m_patterns.insert(pNew.release()); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// pattern-based database driver - -class CDbxPattern : public MDatabaseReadonly, public MZeroedObject -{ - typedef MDatabaseReadonly CSuper; - - CMStringW m_buf, m_folder; - MCONTACT m_hCurrContact = INVALID_CONTACT_ID; - HANDLE m_hFile = INVALID_HANDLE_VALUE, m_hMap = 0; - const uint8_t *m_pFile = 0; - int m_iFileVersion = 0, m_iMsgHeaderSize = 0; - - std::vector<uint32_t> m_events; - std::vector<CMStringW> m_files; - - bool CheckContact(MCONTACT hContact) - { - if (hContact != m_hCurrContact) { - m_hCurrContact = hContact; - if (!Load(m_files[hContact - 1])) - return false; - } - return true; - } - - //////////////////////////////////////////////////////////////////////////////////////// - // QHF file format - - bool LoadBinaryFile(const uint8_t *pFile, uint32_t iSize) - { - if (memicmp(pFile, "QHF", 3)) { - AddMessage(LPGENW("Invalid file header")); - return false; - } - - m_iFileVersion = pFile[3]; - - uint32_t fsz = RLInteger(&pFile[4]); pFile += 0x2C; - uint32_t UIDLen = RLWord(pFile); pFile += 2; - char *UIDStr = (char*)_alloca(UIDLen + 2); - if (m_iFileVersion <= 2) - strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen); - else - strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen + 1); - pFile += UIDLen; - - uint32_t NickLen = RLWord(pFile); pFile += 2; - char *NickStr = (char*)_alloca(NickLen + 2); - if (m_iFileVersion <= 2) - strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen); - else - strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen + 1); - pFile += NickLen; - - DBCONTACTWRITESETTING dbcws = {}; - dbcws.szModule = "Pattern"; - dbcws.value.type = DBVT_UTF8; - - dbcws.szSetting = "ID"; - dbcws.value.pszVal = UIDStr; - WriteContactSetting(m_hCurrContact, &dbcws); - - dbcws.szSetting = "Nick"; - dbcws.value.pszVal = NickStr; - WriteContactSetting(m_hCurrContact, &dbcws); - - uint32_t iHeaderSize = 0x30 + NickLen + UIDLen; - if (fsz != iSize - iHeaderSize) - fsz = iSize - iHeaderSize; - - m_iMsgHeaderSize = (m_iFileVersion >= 3) ? 0x23 : 0x21; - for (uint32_t i = 0; i < fsz; i += m_iMsgHeaderSize) { - m_events.push_back(i + iHeaderSize); - i += RLWord(&pFile[i + m_iMsgHeaderSize - 2]); - } - - return true; - } - - //////////////////////////////////////////////////////////////////////////////////////// - // Text file format, parsed by regexps - - bool LoadTextFile(const uint8_t *pFile) - { - auto *pPattern = g_pBatch->m_pPattern; - - switch (pPattern->iCodePage) { - case CP_UTF8: - m_buf = mir_utf8decodeW((char*)pFile); - break; - case 1200: - m_buf = mir_wstrdup((wchar_t*)pFile); - break; - default: - m_buf = mir_a2u_cp((char*)pFile, pPattern->iCodePage); - break; - } - - - // smth went wrong or empty file - if (m_buf.IsEmpty()) - return false; - - int iOffset = 0; - if (m_buf[0] == 0xFEFF) - m_buf.Delete(0); - - if (pPattern->iUseHeader) { - int offsets[99]; - int nMatch = pcre16_exec(pPattern->regHeader.pattern, pPattern->regHeader.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets)); - if (nMatch <= 0) { - AddMessage(LPGENW("Cannot parse file header, skipping file")); - return false; - } - - const wchar_t **substrings; - if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) { - if (pPattern->iUseHeader & 1) { - pPattern->wszIncoming = substrings[pPattern->iHdrIncoming]; - pPattern->wszOutgoing = substrings[pPattern->iHdrOutgoing]; - } - - if (pPattern->iUseHeader & 2) { - DBCONTACTWRITESETTING dbcws = {}; - dbcws.szModule = "Pattern"; - dbcws.value.type = DBVT_WCHAR; - - if (pPattern->iInUID && substrings[pPattern->iHdrInUID]) { - dbcws.szSetting = "ID"; - dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInUID]; - WriteContactSetting(m_hCurrContact, &dbcws); - } - - if (pPattern->iInNick && substrings[pPattern->iHdrInNick]) { - dbcws.szSetting = "Nick"; - dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInNick]; - WriteContactSetting(m_hCurrContact, &dbcws); - } - } - } - - iOffset = offsets[1]; - } - - while (true) { - int offsets[99]; - int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets)); - if (nMatch <= 0) - break; - - m_events.push_back(offsets[0]); - iOffset = offsets[1]; - } - return true; - } - -public: - CDbxPattern() - {} - - ~CDbxPattern() - { - Close(); - } - - void Close() - { - if (m_pFile != nullptr) - ::UnmapViewOfFile(m_pFile); - - if (m_hMap != nullptr) - ::CloseHandle(m_hMap); - - if (m_hFile != INVALID_HANDLE_VALUE) - ::CloseHandle(m_hFile); - } - - bool Load(const wchar_t *pwszFileName) - { - m_buf.Empty(); - m_events.clear(); - Close(); - - AddMessage(LPGENW("Loading file '%s'..."), pwszFileName); - - m_hFile = ::CreateFileW(pwszFileName, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, 0); - if (m_hFile == INVALID_HANDLE_VALUE) { - AddMessage(LPGENW("Failed to open file <%s> for import: %d"), pwszFileName, GetLastError()); - return false; - } - - uint32_t cbLen = ::GetFileSize(m_hFile, 0); - m_hMap = ::CreateFileMappingW(m_hFile, nullptr, PAGE_READONLY, 0, 0, L"ImportMapfile"); - if (m_hMap == nullptr) { - AddMessage(LPGENW("Failed to mmap file <%s> for import: %d"), pwszFileName, GetLastError()); - return false; - } - - m_pFile = (const uint8_t*)::MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 0); - if (m_pFile == nullptr) { - AddMessage(LPGENW("Failed to map view of file <%s> for import: %d"), pwszFileName, GetLastError()); - return false; - } - - if (g_pBatch->m_pPattern->iType == 1) // text file - return LoadTextFile(m_pFile); - return LoadBinaryFile(m_pFile, cbLen); - } - - int Open(const wchar_t *profile) - { - CMStringW wszBaseFolder(profile); - auto *pPattern = g_pBatch->m_pPattern; - - // create a mask for loading multiple data files for a folder - uint32_t dwAttr = GetFileAttributesW(profile); - if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) { - wszBaseFolder = profile; - m_folder.AppendFormat(L"%s\\*.%s", profile, pPattern->wszExt.c_str()); - } - else { - int i = wszBaseFolder.ReverseFind('\\'); - if (i != -1) - wszBaseFolder = wszBaseFolder.Left(i); - m_folder = profile; - } - - int hContact = 1; - WIN32_FIND_DATA fd; - HANDLE hFind = FindFirstFile(m_folder, &fd); - if (hFind != INVALID_HANDLE_VALUE) { - do { - // find all subfolders except "." and ".." - if (!mir_wstrcmp(fd.cFileName, L".") || !mir_wstrcmp(fd.cFileName, L"..")) - continue; - - CMStringW wszFullName(wszBaseFolder + L"\\" + fd.cFileName); - m_files.push_back(wszFullName); - - auto *cc = m_cache->AddContactToCache(hContact); - cc->szProto = "Pattern"; - - // we try to restore user id from the file name - if (pPattern->iUseFilename) { - int offsets[100]; - int nMatch = pcre16_exec(pPattern->regFilename.pattern, pPattern->regFilename.extra, wszFullName, wszFullName.GetLength(), 0, 0, offsets, _countof(offsets)); - if (nMatch > 0) { - const wchar_t **substrings; - if (pcre16_get_substring_list(wszFullName, offsets, nMatch, &substrings) >= 0) { - DBCONTACTWRITESETTING dbcws = {}; - dbcws.szModule = cc->szProto; - dbcws.value.type = DBVT_WCHAR; - - if (pPattern->iInUID && substrings[pPattern->iInUID]) { - dbcws.szSetting = "ID"; - dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInUID]; - WriteContactSetting(hContact, &dbcws); - } - - if (pPattern->iInNick && substrings[pPattern->iInNick]) { - dbcws.szSetting = "Nick"; - dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInNick]; - WriteContactSetting(hContact, &dbcws); - } - - pcre16_free_substring_list(substrings); - } - } - } - hContact++; - } - while (FindNextFile(hFind, &fd)); - - FindClose(hFind); - } - - if (m_files.empty()) - return EGROKPRF_CANTREAD; - - return EGROKPRF_NOERROR; - } - - // patterns file always stores history for the single contact only - STDMETHODIMP_(int) GetBlobSize(MEVENT idx) override - { - if (m_events.size() == 0 || idx < 1 || idx > m_events.size()) - return 0; - - if (g_pBatch->m_pPattern->iType == 1) { - int iStart = m_events[idx-1], iEnd = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx]; - CMStringW msg = m_buf.Mid(iStart, iEnd - iStart); - return (LONG)mir_strlen(ptrA(mir_utf8encodeW(msg))) + 1; - } - - if (m_pFile == nullptr) - return 0; - - const uint8_t *pMsg = m_pFile + m_events[idx-1]; - return RLWord(&pMsg[m_iMsgHeaderSize - 2]) + 1; - } - - STDMETHODIMP_(int) GetContactCount(void) override - { - return (int)m_files.size(); - } - - STDMETHODIMP_(MCONTACT) FindFirstContact(const char *szProto) override - { - MCONTACT ret = CSuper::FindFirstContact(szProto); - if (ret != 0) - if (!CheckContact(ret)) - return 0; - return ret; - } - - STDMETHODIMP_(MCONTACT) FindNextContact(MCONTACT contactID, const char *szProto) override - { - MCONTACT ret = CSuper::FindNextContact(contactID, szProto); - if (ret != 0) - if (!CheckContact(ret)) - return 0; - return ret; - } - - STDMETHODIMP_(int) GetEventCount(MCONTACT) override - { - return (int)m_events.size(); - } - - STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT* dbv, int isStatic) - { - if (szSetting == nullptr || szModule == nullptr) - return 1; - - DBCachedContact *cc = nullptr; - if (hContact) { - cc = m_cache->GetCachedContact(hContact); - if (cc == nullptr) - return 1; - } - - size_t settingNameLen = strlen(szSetting); - size_t moduleNameLen = strlen(szModule); - char* szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen); - - DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 0); - if (pCachedValue != nullptr) { - if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) { - int cbOrigLen = dbv->cchVal; - char *cbOrigPtr = dbv->pszVal; - memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); - if (isStatic) { - int cbLen = 0; - if (pCachedValue->pszVal != nullptr) - cbLen = (int)strlen(pCachedValue->pszVal); - - cbOrigLen--; - dbv->pszVal = cbOrigPtr; - if (cbLen < cbOrigLen) - cbOrigLen = cbLen; - memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen); - dbv->pszVal[cbOrigLen] = 0; - dbv->cchVal = cbLen; - } - else { - dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1); - strcpy(dbv->pszVal, pCachedValue->pszVal); - } - } - else memcpy(dbv, pCachedValue, sizeof(DBVARIANT)); - - return (pCachedValue->type == DBVT_DELETED) ? 1 : 0; - } - - return 1; - } - - STDMETHODIMP_(BOOL) WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws) - { - if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr) - return 1; - - if (hContact) { - DBCachedContact* cc = m_cache->GetCachedContact(hContact); - if (cc == nullptr) - return 1; - } - - DBCONTACTWRITESETTING dbcwWork = *dbcws; - if (dbcwWork.value.type == DBVT_WCHAR) { - T2Utf value(dbcwWork.value.pwszVal); - dbcwWork.value.pszVal = NEWSTR_ALLOCA(value); - dbcwWork.value.type = DBVT_UTF8; - dbcwWork.value.cchVal = (uint16_t)strlen(dbcwWork.value.pszVal); - } - - char* cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting)); - DBVARIANT* cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1); - if (cachedValue != nullptr) - m_cache->SetCachedVariant(&dbcwWork.value, cachedValue); - - return 0; - } - - //////////////////////////////////////////////////////////////////////////////////////// - - static int str2int(const wchar_t* str) - { - if (str == nullptr || *str == 0) - return 0; - - return _wtoi(str); - } - - int getBinaryEvent(MEVENT idx, DBEVENTINFO *dbei) - { - if (m_pFile == nullptr) - return 1; - - const uint8_t *pMsg = m_pFile + m_events[idx-1]; - - dbei->eventType = EVENTTYPE_MESSAGE; - dbei->flags = DBEF_READ | DBEF_UTF; - if (pMsg[0x1A] != 0) - dbei->flags |= DBEF_SENT; - dbei->timestamp = RLInteger(&pMsg[0x12]); - dbei->timestamp -= TimeZone_ToLocal(dbei->timestamp) - dbei->timestamp; // deduct time zone offset from timestamp - dbei->cbBlob = RLWord(&pMsg[m_iMsgHeaderSize - 2]); - dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob + 1); - memcpy(dbei->pBlob, pMsg + m_iMsgHeaderSize, dbei->cbBlob); - if (m_iFileVersion != 1) - for (int i = 0; i < dbei->cbBlob; i++) { - dbei->pBlob[i] += i+1; - dbei->pBlob[i] = 255 - dbei->pBlob[i]; - } - - dbei->pBlob[dbei->cbBlob] = 0; - return 0; - } - - int getTextEvent(MEVENT idx, DBEVENTINFO *dbei) - { - auto *pPattern = g_pBatch->m_pPattern; - - int offsets[99]; - int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), m_events[idx-1], PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets)); - if (nMatch <= 0) - return 1; - - dbei->eventType = EVENTTYPE_MESSAGE; - dbei->flags = DBEF_READ | DBEF_UTF; - - int h1 = offsets[1], h2 = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx]; - int prn = -1, arn = -1; - if (pPattern->iUsePreMsg) - prn = pPattern->preRN, arn = pPattern->afterRN; - - if (prn != 0) { - int i = 0; - while (m_buf[h1] == '\r' && m_buf[h1 + 1] == '\n' && i < prn) - h1 += 2, i++; - } - - if (arn != 0) { - int i = 0; - while (m_buf[h2 - 2] == '\r' && m_buf[h2 - 1] == '\n' && i < arn) - h2 -= 2, i++; - } - - if (dbei->cbBlob) { - CMStringW wszBody = m_buf.Mid(h1, h2 - h1).Trim(); - if (!wszBody.IsEmpty()) { - ptrA tmp(mir_utf8encodeW(wszBody)); - int copySize = min(dbei->cbBlob - 1, (int)mir_strlen(tmp)); - memcpy(dbei->pBlob, tmp, copySize); - dbei->pBlob[copySize] = 0; - dbei->cbBlob = copySize; - } - else dbei->cbBlob = 0; - } - - const wchar_t **substrings; - if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) { - struct tm st = {}; - st.tm_year = str2int(substrings[pPattern->iYear]); - if (st.tm_year > 1900) - st.tm_year -= 1900; - st.tm_mon = str2int(substrings[pPattern->iMonth]) - 1; - st.tm_mday = str2int(substrings[pPattern->iDay]); - st.tm_hour = str2int(substrings[pPattern->iHours]); - st.tm_min = str2int(substrings[pPattern->iMinutes]); - st.tm_sec = (pPattern->iSeconds) ? str2int(substrings[pPattern->iSeconds]) : 0; - dbei->timestamp = mktime(&st); - - if (pPattern->iDirection) - if (pPattern->wszOutgoing == substrings[pPattern->iDirection]) - dbei->flags |= DBEF_SENT; - - - pcre16_free_substring_list(substrings); - } - return 0; - } - - STDMETHODIMP_(BOOL) GetEvent(MEVENT idx, DBEVENTINFO *dbei) override - { - if (dbei == nullptr || m_events.size() == 0 || idx < 1 || idx > m_events.size()) - return 1; - - if (g_pBatch->m_pPattern->iType == 1) - return getTextEvent(idx, dbei); - - return getBinaryEvent(idx, dbei); - } - - STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT hContact) override - { - // no system history - if (hContact == 0) - return 0; - - if (!CheckContact(hContact)) - return 0; - - return m_events.size() > 0 ? 1 : 0; - } - - STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT idx) override - { - if (idx >= m_events.size()) - return 0; - - return idx + 1; - } - - STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT hContact) override - { - // no system history - if (hContact == 0) - return 0; - - if (!CheckContact(hContact)) - return 0; - - return m_events.size() > 0 ? (MEVENT)m_events.size() : 0; - } - - STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT idx) override - { - return (idx >= 1) ? idx-1 : 0; - } - - STDMETHODIMP_(DATABASELINK *) GetDriver(); -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// database link functions - -static int pattern_makeDatabase(const wchar_t*) -{ - return 1; -} - -static int pattern_grokHeader(const wchar_t *profile) -{ - return CDbxPattern().Open(profile); -} - -static MDatabaseCommon* pattern_load(const wchar_t *profile, BOOL) -{ - std::unique_ptr<CDbxPattern> db(new CDbxPattern()); - if (db->Open(profile)) - return nullptr; - - return db.release(); -} - -DATABASELINK g_patternDbLink = -{ - 0, - "pattern", - L"Pattern-based file driver", - pattern_makeDatabase, - pattern_grokHeader, - pattern_load -}; - -STDMETHODIMP_(DATABASELINK *) CDbxPattern::GetDriver() -{ - return &g_patternDbLink; -} +/*
+
+Import plugin for Miranda NG
+
+Copyright (C) 2012-23 Miranda NG team (https://miranda-ng.org)
+
+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"
+
+#include <memory>
+#include <vector>
+
+void CMPlugin::LoadPatterns()
+{
+ wchar_t wszPath[MAX_PATH], wszFullPath[MAX_PATH];
+ GetModuleFileNameW(m_hInst, wszPath, _countof(wszPath));
+ if (auto *p = wcsrchr(wszPath, '\\'))
+ *p = 0;
+
+ mir_snwprintf(wszFullPath, L"%s\\Import\\*.ini", wszPath);
+
+ WIN32_FIND_DATAW fd;
+ HANDLE hFind = FindFirstFileW(wszFullPath, &fd);
+ if (hFind == INVALID_HANDLE_VALUE)
+ return;
+
+ do {
+ mir_snwprintf(wszFullPath, L"%s\\Import\\%s", wszPath, fd.cFileName);
+ LoadPattern(wszFullPath);
+ }
+ while (FindNextFileW(hFind, &fd) != 0);
+}
+
+void CMPlugin::LoadPattern(const wchar_t *pwszFileName)
+{
+ // [General] section
+ wchar_t buf[1024];
+ if (!GetPrivateProfileStringW(L"General", L"Name", L"", buf, _countof(buf), pwszFileName))
+ return;
+
+ std::unique_ptr<CImportPattern> pNew(new CImportPattern());
+ pNew->wszName = buf;
+ pNew->iType = GetPrivateProfileIntW(L"General", L"Type", 1, pwszFileName);
+
+ if (GetPrivateProfileStringW(L"General", L"DefaultExtension", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszExt = buf;
+
+ if (pNew->iType == 1) {
+ if (GetPrivateProfileStringW(L"General", L"Charset", L"", buf, _countof(buf), pwszFileName)) {
+ if (!wcsicmp(buf, L"ANSI"))
+ pNew->iCodePage = GetPrivateProfileIntW(L"General", L"Codepage", CP_ACP, pwszFileName);
+ else if (!wcsicmp(buf, L"UCS2"))
+ pNew->iCodePage = 1200;
+ }
+ else return;
+ }
+
+ pNew->iUseHeader = GetPrivateProfileIntW(L"General", L"UseHeader", 0, pwszFileName);
+ pNew->iUsePreMsg = GetPrivateProfileIntW(L"General", L"UsePreMsg", 0, pwszFileName);
+ pNew->iUseFilename = GetPrivateProfileIntW(L"General", L"UseFileName", 0, pwszFileName);
+
+ // [Message] section
+ int erroffset;
+ const char *err;
+ if (pNew->iType == 1) {
+ if (GetPrivateProfileStringW(L"Message", L"Pattern", L"", buf, _countof(buf), pwszFileName)) {
+ if ((pNew->regMessage.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regMessage.extra = pcre16_study(pNew->regMessage.pattern, 0, &err);
+ }
+ else return;
+
+ if (GetPrivateProfileStringW(L"Message", L"In", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszIncoming = buf;
+ if (GetPrivateProfileStringW(L"Message", L"Out", L"", buf, _countof(buf), pwszFileName))
+ pNew->wszOutgoing = buf;
+
+ pNew->iDirection = GetPrivateProfileIntW(L"Message", L"Direction", 0, pwszFileName);
+ pNew->iDay = GetPrivateProfileIntW(L"Message", L"Day", 0, pwszFileName);
+ pNew->iMonth = GetPrivateProfileIntW(L"Message", L"Month", 0, pwszFileName);
+ pNew->iYear = GetPrivateProfileIntW(L"Message", L"Year", 0, pwszFileName);
+ pNew->iHours = GetPrivateProfileIntW(L"Message", L"Hours", 0, pwszFileName);
+ pNew->iMinutes = GetPrivateProfileIntW(L"Message", L"Minutes", 0, pwszFileName);
+ pNew->iSeconds = GetPrivateProfileIntW(L"Message", L"Seconds", 0, pwszFileName);
+ }
+
+ if (pNew->iUseHeader) {
+ if (GetPrivateProfileStringW(L"Header", L"Pattern", L"", buf, _countof(buf), pwszFileName)) {
+ if ((pNew->regHeader.pattern = pcre16_compile(buf, PCRE_MULTILINE, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regHeader.extra = pcre16_study(pNew->regMessage.pattern, 0, &err);
+ }
+ else return;
+
+ pNew->iHdrIncoming = GetPrivateProfileIntW(L"Header", L"In", 0, pwszFileName);
+ pNew->iHdrOutgoing = GetPrivateProfileIntW(L"Header", L"Out", 0, pwszFileName);
+ pNew->iHdrInNick = GetPrivateProfileIntW(L"Header", L"InNick", 0, pwszFileName);
+ pNew->iHdrOutNick = GetPrivateProfileIntW(L"Header", L"OutNick", 0, pwszFileName);
+ pNew->iHdrInUID = GetPrivateProfileIntW(L"Header", L"InUID", 0, pwszFileName);
+ pNew->iHdrOutUID = GetPrivateProfileIntW(L"Header", L"OutUID", 0, pwszFileName);
+ }
+
+ if (pNew->iUsePreMsg) {
+ pNew->preRN = GetPrivateProfileIntW(L"PreMessage", L"PreRN", -1, pwszFileName);
+ pNew->preSP = GetPrivateProfileIntW(L"PreMessage", L"PreSP", 0, pwszFileName);
+ pNew->afterRN = GetPrivateProfileIntW(L"PreMessage", L"AfterRN", -1, pwszFileName);
+ pNew->afterSP = GetPrivateProfileIntW(L"PreMessage", L"AfterSP", 0, pwszFileName);
+ }
+
+ if (pNew->iUseFilename) {
+ if (!GetPrivateProfileStringW(L"FileName", L"Pattern", L"", buf, _countof(buf), pwszFileName))
+ return;
+ if ((pNew->regFilename.pattern = pcre16_compile(buf, 0, &err, &erroffset, nullptr)) == nullptr)
+ return;
+ pNew->regFilename.extra = pcre16_study(pNew->regFilename.pattern, 0, &err);
+
+ pNew->iInNick = GetPrivateProfileIntW(L"FileName", L"InNick", 0, pwszFileName);
+ pNew->iInUID = GetPrivateProfileIntW(L"FileName", L"InUID", 0, pwszFileName);
+ pNew->iOutNick = GetPrivateProfileIntW(L"FileName", L"OutNick", 0, pwszFileName);
+ pNew->iOutUID = GetPrivateProfileIntW(L"FileName", L"OutUID", 0, pwszFileName);
+ }
+
+ m_patterns.insert(pNew.release());
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// pattern-based database driver
+
+class CDbxPattern : public MDatabaseReadonly, public MZeroedObject
+{
+ typedef MDatabaseReadonly CSuper;
+
+ CMStringW m_buf, m_folder;
+ MCONTACT m_hCurrContact = INVALID_CONTACT_ID;
+ HANDLE m_hFile = INVALID_HANDLE_VALUE, m_hMap = 0;
+ const uint8_t *m_pFile = 0;
+ int m_iFileVersion = 0, m_iMsgHeaderSize = 0;
+
+ std::vector<uint32_t> m_events;
+ std::vector<CMStringW> m_files;
+
+ bool CheckContact(MCONTACT hContact)
+ {
+ if (hContact != m_hCurrContact) {
+ m_hCurrContact = hContact;
+ if (!Load(m_files[hContact - 1]))
+ return false;
+ }
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // QHF file format
+
+ bool LoadBinaryFile(const uint8_t *pFile, uint32_t iSize)
+ {
+ if (memicmp(pFile, "QHF", 3)) {
+ AddMessage(LPGENW("Invalid file header"));
+ return false;
+ }
+
+ m_iFileVersion = pFile[3];
+
+ uint32_t fsz = RLInteger(&pFile[4]); pFile += 0x2C;
+ uint32_t UIDLen = RLWord(pFile); pFile += 2;
+ char *UIDStr = (char*)_alloca(UIDLen + 2);
+ if (m_iFileVersion <= 2)
+ strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen);
+ else
+ strncpy_s(UIDStr, UIDLen + 2, (char *)pFile, UIDLen + 1);
+ pFile += UIDLen;
+
+ uint32_t NickLen = RLWord(pFile); pFile += 2;
+ char *NickStr = (char*)_alloca(NickLen + 2);
+ if (m_iFileVersion <= 2)
+ strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen);
+ else
+ strncpy_s(NickStr, NickLen + 2, (char*)pFile, NickLen + 1);
+ pFile += NickLen;
+
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = "Pattern";
+ dbcws.value.type = DBVT_UTF8;
+
+ dbcws.szSetting = "ID";
+ dbcws.value.pszVal = UIDStr;
+ WriteContactSetting(m_hCurrContact, &dbcws);
+
+ dbcws.szSetting = "Nick";
+ dbcws.value.pszVal = NickStr;
+ WriteContactSetting(m_hCurrContact, &dbcws);
+
+ uint32_t iHeaderSize = 0x30 + NickLen + UIDLen;
+ if (fsz != iSize - iHeaderSize)
+ fsz = iSize - iHeaderSize;
+
+ m_iMsgHeaderSize = (m_iFileVersion >= 3) ? 0x23 : 0x21;
+ for (uint32_t i = 0; i < fsz; i += m_iMsgHeaderSize) {
+ m_events.push_back(i + iHeaderSize);
+ i += RLWord(&pFile[i + m_iMsgHeaderSize - 2]);
+ }
+
+ return true;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Text file format, parsed by regexps
+
+ bool LoadTextFile(const uint8_t *pFile)
+ {
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ switch (pPattern->iCodePage) {
+ case CP_UTF8:
+ m_buf = mir_utf8decodeW((char*)pFile);
+ break;
+ case 1200:
+ m_buf = mir_wstrdup((wchar_t*)pFile);
+ break;
+ default:
+ m_buf = mir_a2u_cp((char*)pFile, pPattern->iCodePage);
+ break;
+ }
+
+
+ // smth went wrong or empty file
+ if (m_buf.IsEmpty())
+ return false;
+
+ int iOffset = 0;
+ if (m_buf[0] == 0xFEFF)
+ m_buf.Delete(0);
+
+ if (pPattern->iUseHeader) {
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regHeader.pattern, pPattern->regHeader.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0) {
+ AddMessage(LPGENW("Cannot parse file header, skipping file"));
+ return false;
+ }
+
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) {
+ if (pPattern->iUseHeader & 1) {
+ pPattern->wszIncoming = substrings[pPattern->iHdrIncoming];
+ pPattern->wszOutgoing = substrings[pPattern->iHdrOutgoing];
+ }
+
+ if (pPattern->iUseHeader & 2) {
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = "Pattern";
+ dbcws.value.type = DBVT_WCHAR;
+
+ if (pPattern->iInUID && substrings[pPattern->iHdrInUID]) {
+ dbcws.szSetting = "ID";
+ dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInUID];
+ WriteContactSetting(m_hCurrContact, &dbcws);
+ }
+
+ if (pPattern->iInNick && substrings[pPattern->iHdrInNick]) {
+ dbcws.szSetting = "Nick";
+ dbcws.value.pwszVal = (wchar_t *)substrings[pPattern->iHdrInNick];
+ WriteContactSetting(m_hCurrContact, &dbcws);
+ }
+ }
+ }
+
+ iOffset = offsets[1];
+ }
+
+ while (true) {
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), iOffset, PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0)
+ break;
+
+ m_events.push_back(offsets[0]);
+ iOffset = offsets[1];
+ }
+ return true;
+ }
+
+public:
+ CDbxPattern()
+ {}
+
+ ~CDbxPattern()
+ {
+ Close();
+ }
+
+ void Close()
+ {
+ if (m_pFile != nullptr)
+ ::UnmapViewOfFile(m_pFile);
+
+ if (m_hMap != nullptr)
+ ::CloseHandle(m_hMap);
+
+ if (m_hFile != INVALID_HANDLE_VALUE)
+ ::CloseHandle(m_hFile);
+ }
+
+ bool Load(const wchar_t *pwszFileName)
+ {
+ m_buf.Empty();
+ m_events.clear();
+ Close();
+
+ AddMessage(LPGENW("Loading file '%s'..."), pwszFileName);
+
+ m_hFile = ::CreateFileW(pwszFileName, GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, 0);
+ if (m_hFile == INVALID_HANDLE_VALUE) {
+ AddMessage(LPGENW("Failed to open file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ uint32_t cbLen = ::GetFileSize(m_hFile, 0);
+ m_hMap = ::CreateFileMappingW(m_hFile, nullptr, PAGE_READONLY, 0, 0, L"ImportMapfile");
+ if (m_hMap == nullptr) {
+ AddMessage(LPGENW("Failed to mmap file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ m_pFile = (const uint8_t*)::MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 0);
+ if (m_pFile == nullptr) {
+ AddMessage(LPGENW("Failed to map view of file <%s> for import: %d"), pwszFileName, GetLastError());
+ return false;
+ }
+
+ if (g_pBatch->m_pPattern->iType == 1) // text file
+ return LoadTextFile(m_pFile);
+ return LoadBinaryFile(m_pFile, cbLen);
+ }
+
+ int Open(const wchar_t *profile)
+ {
+ CMStringW wszBaseFolder(profile);
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ // create a mask for loading multiple data files for a folder
+ uint32_t dwAttr = GetFileAttributesW(profile);
+ if (dwAttr & FILE_ATTRIBUTE_DIRECTORY) {
+ wszBaseFolder = profile;
+ m_folder.AppendFormat(L"%s\\*.%s", profile, pPattern->wszExt.c_str());
+ }
+ else {
+ int i = wszBaseFolder.ReverseFind('\\');
+ if (i != -1)
+ wszBaseFolder = wszBaseFolder.Left(i);
+ m_folder = profile;
+ }
+
+ int hContact = 1;
+ WIN32_FIND_DATA fd;
+ HANDLE hFind = FindFirstFile(m_folder, &fd);
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ // find all subfolders except "." and ".."
+ if (!mir_wstrcmp(fd.cFileName, L".") || !mir_wstrcmp(fd.cFileName, L".."))
+ continue;
+
+ CMStringW wszFullName(wszBaseFolder + L"\\" + fd.cFileName);
+ m_files.push_back(wszFullName);
+
+ auto *cc = m_cache->AddContactToCache(hContact);
+ cc->szProto = "Pattern";
+
+ // we try to restore user id from the file name
+ if (pPattern->iUseFilename) {
+ int offsets[100];
+ int nMatch = pcre16_exec(pPattern->regFilename.pattern, pPattern->regFilename.extra, wszFullName, wszFullName.GetLength(), 0, 0, offsets, _countof(offsets));
+ if (nMatch > 0) {
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(wszFullName, offsets, nMatch, &substrings) >= 0) {
+ DBCONTACTWRITESETTING dbcws = {};
+ dbcws.szModule = cc->szProto;
+ dbcws.value.type = DBVT_WCHAR;
+
+ if (pPattern->iInUID && substrings[pPattern->iInUID]) {
+ dbcws.szSetting = "ID";
+ dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInUID];
+ WriteContactSetting(hContact, &dbcws);
+ }
+
+ if (pPattern->iInNick && substrings[pPattern->iInNick]) {
+ dbcws.szSetting = "Nick";
+ dbcws.value.pwszVal = (wchar_t*)substrings[pPattern->iInNick];
+ WriteContactSetting(hContact, &dbcws);
+ }
+
+ pcre16_free_substring_list(substrings);
+ }
+ }
+ }
+ hContact++;
+ }
+ while (FindNextFile(hFind, &fd));
+
+ FindClose(hFind);
+ }
+
+ if (m_files.empty())
+ return EGROKPRF_CANTREAD;
+
+ return EGROKPRF_NOERROR;
+ }
+
+ // patterns file always stores history for the single contact only
+ STDMETHODIMP_(int) GetBlobSize(MEVENT idx) override
+ {
+ if (m_events.size() == 0 || idx < 1 || idx > m_events.size())
+ return 0;
+
+ if (g_pBatch->m_pPattern->iType == 1) {
+ int iStart = m_events[idx-1], iEnd = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx];
+ CMStringW msg = m_buf.Mid(iStart, iEnd - iStart);
+ return (LONG)mir_strlen(ptrA(mir_utf8encodeW(msg))) + 1;
+ }
+
+ if (m_pFile == nullptr)
+ return 0;
+
+ const uint8_t *pMsg = m_pFile + m_events[idx-1];
+ return RLWord(&pMsg[m_iMsgHeaderSize - 2]) + 1;
+ }
+
+ STDMETHODIMP_(int) GetContactCount(void) override
+ {
+ return (int)m_files.size();
+ }
+
+ STDMETHODIMP_(MCONTACT) FindFirstContact(const char *szProto) override
+ {
+ MCONTACT ret = CSuper::FindFirstContact(szProto);
+ if (ret != 0)
+ if (!CheckContact(ret))
+ return 0;
+ return ret;
+ }
+
+ STDMETHODIMP_(MCONTACT) FindNextContact(MCONTACT contactID, const char *szProto) override
+ {
+ MCONTACT ret = CSuper::FindNextContact(contactID, szProto);
+ if (ret != 0)
+ if (!CheckContact(ret))
+ return 0;
+ return ret;
+ }
+
+ STDMETHODIMP_(int) GetEventCount(MCONTACT) override
+ {
+ return (int)m_events.size();
+ }
+
+ STDMETHODIMP_(BOOL) GetContactSettingWorker(MCONTACT hContact, LPCSTR szModule, LPCSTR szSetting, DBVARIANT* dbv, int isStatic)
+ {
+ if (szSetting == nullptr || szModule == nullptr)
+ return 1;
+
+ DBCachedContact *cc = nullptr;
+ if (hContact) {
+ cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ size_t settingNameLen = strlen(szSetting);
+ size_t moduleNameLen = strlen(szModule);
+ char* szCachedSettingName = m_cache->GetCachedSetting(szModule, szSetting, moduleNameLen, settingNameLen);
+
+ DBVARIANT *pCachedValue = m_cache->GetCachedValuePtr(hContact, szCachedSettingName, 0);
+ if (pCachedValue != nullptr) {
+ if (pCachedValue->type == DBVT_ASCIIZ || pCachedValue->type == DBVT_UTF8) {
+ int cbOrigLen = dbv->cchVal;
+ char *cbOrigPtr = dbv->pszVal;
+ memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+ if (isStatic) {
+ int cbLen = 0;
+ if (pCachedValue->pszVal != nullptr)
+ cbLen = (int)strlen(pCachedValue->pszVal);
+
+ cbOrigLen--;
+ dbv->pszVal = cbOrigPtr;
+ if (cbLen < cbOrigLen)
+ cbOrigLen = cbLen;
+ memcpy(dbv->pszVal, pCachedValue->pszVal, cbOrigLen);
+ dbv->pszVal[cbOrigLen] = 0;
+ dbv->cchVal = cbLen;
+ }
+ else {
+ dbv->pszVal = (char*)mir_alloc(strlen(pCachedValue->pszVal) + 1);
+ strcpy(dbv->pszVal, pCachedValue->pszVal);
+ }
+ }
+ else memcpy(dbv, pCachedValue, sizeof(DBVARIANT));
+
+ return (pCachedValue->type == DBVT_DELETED) ? 1 : 0;
+ }
+
+ return 1;
+ }
+
+ STDMETHODIMP_(BOOL) WriteContactSetting(MCONTACT hContact, DBCONTACTWRITESETTING *dbcws)
+ {
+ if (dbcws == nullptr || dbcws->szSetting == nullptr || dbcws->szModule == nullptr)
+ return 1;
+
+ if (hContact) {
+ DBCachedContact* cc = m_cache->GetCachedContact(hContact);
+ if (cc == nullptr)
+ return 1;
+ }
+
+ DBCONTACTWRITESETTING dbcwWork = *dbcws;
+ if (dbcwWork.value.type == DBVT_WCHAR) {
+ T2Utf value(dbcwWork.value.pwszVal);
+ dbcwWork.value.pszVal = NEWSTR_ALLOCA(value);
+ dbcwWork.value.type = DBVT_UTF8;
+ dbcwWork.value.cchVal = (uint16_t)strlen(dbcwWork.value.pszVal);
+ }
+
+ char* cachedSettingName = m_cache->GetCachedSetting(dbcwWork.szModule, dbcwWork.szSetting, mir_strlen(dbcwWork.szModule), mir_strlen(dbcwWork.szSetting));
+ DBVARIANT* cachedValue = m_cache->GetCachedValuePtr(hContact, cachedSettingName, 1);
+ if (cachedValue != nullptr)
+ m_cache->SetCachedVariant(&dbcwWork.value, cachedValue);
+
+ return 0;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ static int str2int(const wchar_t* str)
+ {
+ if (str == nullptr || *str == 0)
+ return 0;
+
+ return _wtoi(str);
+ }
+
+ int getBinaryEvent(MEVENT idx, DBEVENTINFO *dbei)
+ {
+ if (m_pFile == nullptr)
+ return 1;
+
+ const uint8_t *pMsg = m_pFile + m_events[idx-1];
+
+ dbei->eventType = EVENTTYPE_MESSAGE;
+ dbei->flags = DBEF_READ | DBEF_UTF;
+ if (pMsg[0x1A] != 0)
+ dbei->flags |= DBEF_SENT;
+ dbei->timestamp = RLInteger(&pMsg[0x12]);
+ dbei->timestamp -= TimeZone_ToLocal(dbei->timestamp) - dbei->timestamp; // deduct time zone offset from timestamp
+ dbei->cbBlob = RLWord(&pMsg[m_iMsgHeaderSize - 2]);
+ dbei->pBlob = (uint8_t*)mir_alloc(dbei->cbBlob + 1);
+ memcpy(dbei->pBlob, pMsg + m_iMsgHeaderSize, dbei->cbBlob);
+ if (m_iFileVersion != 1)
+ for (int i = 0; i < dbei->cbBlob; i++) {
+ dbei->pBlob[i] += i+1;
+ dbei->pBlob[i] = 255 - dbei->pBlob[i];
+ }
+
+ dbei->pBlob[dbei->cbBlob] = 0;
+ return 0;
+ }
+
+ int getTextEvent(MEVENT idx, DBEVENTINFO *dbei)
+ {
+ auto *pPattern = g_pBatch->m_pPattern;
+
+ int offsets[99];
+ int nMatch = pcre16_exec(pPattern->regMessage.pattern, pPattern->regMessage.extra, m_buf, m_buf.GetLength(), m_events[idx-1], PCRE_NEWLINE_ANYCRLF, offsets, _countof(offsets));
+ if (nMatch <= 0)
+ return 1;
+
+ dbei->eventType = EVENTTYPE_MESSAGE;
+ dbei->flags = DBEF_READ | DBEF_UTF;
+
+ int h1 = offsets[1], h2 = (idx == m_events.size()) ? m_buf.GetLength() : m_events[idx];
+ int prn = -1, arn = -1;
+ if (pPattern->iUsePreMsg)
+ prn = pPattern->preRN, arn = pPattern->afterRN;
+
+ if (prn != 0) {
+ int i = 0;
+ while (m_buf[h1] == '\r' && m_buf[h1 + 1] == '\n' && i < prn)
+ h1 += 2, i++;
+ }
+
+ if (arn != 0) {
+ int i = 0;
+ while (m_buf[h2 - 2] == '\r' && m_buf[h2 - 1] == '\n' && i < arn)
+ h2 -= 2, i++;
+ }
+
+ if (dbei->cbBlob) {
+ CMStringW wszBody = m_buf.Mid(h1, h2 - h1).Trim();
+ if (!wszBody.IsEmpty()) {
+ ptrA tmp(mir_utf8encodeW(wszBody));
+ int copySize = min(dbei->cbBlob - 1, (int)mir_strlen(tmp));
+ memcpy(dbei->pBlob, tmp, copySize);
+ dbei->pBlob[copySize] = 0;
+ dbei->cbBlob = copySize;
+ }
+ else dbei->cbBlob = 0;
+ }
+
+ const wchar_t **substrings;
+ if (pcre16_get_substring_list(m_buf, offsets, nMatch, &substrings) >= 0) {
+ struct tm st = {};
+ st.tm_year = str2int(substrings[pPattern->iYear]);
+ if (st.tm_year > 1900)
+ st.tm_year -= 1900;
+ st.tm_mon = str2int(substrings[pPattern->iMonth]) - 1;
+ st.tm_mday = str2int(substrings[pPattern->iDay]);
+ st.tm_hour = str2int(substrings[pPattern->iHours]);
+ st.tm_min = str2int(substrings[pPattern->iMinutes]);
+ st.tm_sec = (pPattern->iSeconds) ? str2int(substrings[pPattern->iSeconds]) : 0;
+ dbei->timestamp = mktime(&st);
+
+ if (pPattern->iDirection)
+ if (pPattern->wszOutgoing == substrings[pPattern->iDirection])
+ dbei->flags |= DBEF_SENT;
+
+
+ pcre16_free_substring_list(substrings);
+ }
+ return 0;
+ }
+
+ STDMETHODIMP_(BOOL) GetEvent(MEVENT idx, DBEVENTINFO *dbei) override
+ {
+ if (dbei == nullptr || m_events.size() == 0 || idx < 1 || idx > m_events.size())
+ return 1;
+
+ if (g_pBatch->m_pPattern->iType == 1)
+ return getTextEvent(idx, dbei);
+
+ return getBinaryEvent(idx, dbei);
+ }
+
+ STDMETHODIMP_(MEVENT) FindFirstEvent(MCONTACT hContact) override
+ {
+ // no system history
+ if (hContact == 0)
+ return 0;
+
+ if (!CheckContact(hContact))
+ return 0;
+
+ return m_events.size() > 0 ? 1 : 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindNextEvent(MCONTACT, MEVENT idx) override
+ {
+ if (idx >= m_events.size())
+ return 0;
+
+ return idx + 1;
+ }
+
+ STDMETHODIMP_(MEVENT) FindLastEvent(MCONTACT hContact) override
+ {
+ // no system history
+ if (hContact == 0)
+ return 0;
+
+ if (!CheckContact(hContact))
+ return 0;
+
+ return m_events.size() > 0 ? (MEVENT)m_events.size() : 0;
+ }
+
+ STDMETHODIMP_(MEVENT) FindPrevEvent(MCONTACT, MEVENT idx) override
+ {
+ return (idx >= 1) ? idx-1 : 0;
+ }
+
+ STDMETHODIMP_(DATABASELINK *) GetDriver();
+};
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// database link functions
+
+static int pattern_makeDatabase(const wchar_t*)
+{
+ return 1;
+}
+
+static int pattern_grokHeader(const wchar_t *profile)
+{
+ return CDbxPattern().Open(profile);
+}
+
+static MDatabaseCommon* pattern_load(const wchar_t *profile, BOOL)
+{
+ std::unique_ptr<CDbxPattern> db(new CDbxPattern());
+ if (db->Open(profile))
+ return nullptr;
+
+ return db.release();
+}
+
+DATABASELINK g_patternDbLink =
+{
+ 0,
+ "pattern",
+ L"Pattern-based file driver",
+ pattern_makeDatabase,
+ pattern_grokHeader,
+ pattern_load
+};
+
+STDMETHODIMP_(DATABASELINK *) CDbxPattern::GetDriver()
+{
+ return &g_patternDbLink;
+}
|
