123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 |
- /*
- * Copyright 2005 - 2016 Zarafa and its licensors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- #include <new>
- #include <kopano/platform.h>
- #include <kopano/ECRestriction.h>
- #include <kopano/memory.hpp>
- #include "ECMemTablePublic.h"
- #include "Mem.h"
- #include <kopano/ECGuid.h>
- #include <edkguid.h>
- #include <kopano/Util.h>
- #include "ClientUtil.h"
- #include <edkmdb.h>
- #include <kopano/mapiext.h>
- #include "ECMsgStorePublic.h"
- #include "favoritesutil.h"
- #include <mapiutil.h>
- using namespace KCHL;
- //FIXME: add the classname "ECMemTablePublic"
- ECMemTablePublic::ECMemTablePublic(ECMAPIFolderPublic *lpECParentFolder,
- const SPropTagArray *lpsPropTags, ULONG ulRowPropTag) :
- ECMemTable(lpsPropTags, ulRowPropTag),
- m_lpECParentFolder(lpECParentFolder)
- {
- if (m_lpECParentFolder)
- m_lpECParentFolder->AddRef();
- }
- ECMemTablePublic::~ECMemTablePublic(void)
- {
- if (m_lpShortcutTable)
- m_lpShortcutTable->Release();
- if (m_lpShortCutAdviseSink)
- m_lpShortCutAdviseSink->Release();
- for (auto &p : m_mapRelation) {
- if (p.second.ulAdviseConnectionId > 0)
- m_lpECParentFolder->GetMsgStore()->Unadvise(p.second.ulAdviseConnectionId);
- FreeRelation(&p.second);
- }
- if (m_lpECParentFolder)
- m_lpECParentFolder->Release();
- }
- HRESULT ECMemTablePublic::Create(ECMAPIFolderPublic *lpECParentFolder, ECMemTablePublic **lppECMemTable)
- {
- static constexpr const SizedSPropTagArray(12, sPropsHierarchyColumns) =
- {12, {PR_ENTRYID, PR_DISPLAY_NAME, PR_CONTENT_COUNT,
- PR_CONTENT_UNREAD, PR_STORE_ENTRYID, PR_STORE_RECORD_KEY,
- PR_STORE_SUPPORT_MASK, PR_INSTANCE_KEY, PR_RECORD_KEY,
- PR_ACCESS, PR_ACCESS_LEVEL, PR_CONTAINER_CLASS}};
- auto lpMemTable = new(std::nothrow) ECMemTablePublic(lpECParentFolder,
- sPropsHierarchyColumns, PR_ROWID);
- if (lpMemTable == nullptr)
- return MAPI_E_NOT_ENOUGH_MEMORY;
- auto ret = lpMemTable->QueryInterface(IID_ECMemTablePublic,
- reinterpret_cast<void **>(lppECMemTable));
- if (ret != hrSuccess)
- delete lpMemTable;
- return ret;
- }
- HRESULT ECMemTablePublic::QueryInterface(REFIID refiid, void **lppInterface)
- {
- REGISTER_INTERFACE2(ECMemTable, this);
- REGISTER_INTERFACE2(ECMemTablePublic, this);
- return MAPI_E_INTERFACE_NOT_SUPPORTED;
- }
- /*
- Advise function to check if there something is changed in the shortcut folder of you private store.
-
- This is used to build the favorits tree
- */
- static LONG __stdcall AdviseShortCutCallback(void *lpContext, ULONG cNotif,
- LPNOTIFICATION lpNotif)
- {
- if (lpContext == NULL) {
- return S_OK;
- }
- HRESULT hr = hrSuccess;
- auto lpMemTablePublic = static_cast<ECMemTablePublic *>(lpContext);
- lpMemTablePublic->AddRef(); // Besure we have the object
- for (ULONG i = 0; i < cNotif; ++i) {
- if(lpNotif[i].ulEventType != fnevTableModified)
- {
- assert(false);
- continue;
- }
- // NOTE: ignore errors at all.
- switch (lpNotif[i].info.tab.ulTableEvent)
- {
- case TABLE_ROW_ADDED:
- case TABLE_ROW_MODIFIED:
- lpMemTablePublic->ModifyRow(&lpNotif[i].info.tab.propIndex.Value.bin, &lpNotif[i].info.tab.row);
- break;
- case TABLE_ROW_DELETED:
- lpMemTablePublic->DelRow(&lpNotif[i].info.tab.propIndex.Value.bin);
- break;
- case TABLE_CHANGED:
- lpMemTablePublic->HrClear();
- hr = lpMemTablePublic->m_lpShortcutTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
- if (hr != hrSuccess)
- continue; // Next notification
- while(true)
- {
- rowset_ptr lpRows;
- hr = lpMemTablePublic->m_lpShortcutTable->QueryRows(1, 0, &~lpRows);
- if (hr != hrSuccess)
- break; // Next notification
- if (lpRows->cRows == 0)
- break;
- lpMemTablePublic->ModifyRow(&lpRows->aRow[0].lpProps[SC_INSTANCE_KEY].Value.bin, &lpRows->aRow[0]);
- }
- break;
- default:
- break;
- }
- }
- lpMemTablePublic->Release();
- return S_OK;
- }
- static LONG __stdcall AdviseFolderCallback(void *lpContext, ULONG cNotif,
- LPNOTIFICATION lpNotif)
- {
- if (lpContext == NULL)
- return S_OK;
- auto lpMemTablePublic = static_cast<ECMemTablePublic *>(lpContext);
- ULONG ulResult;
- SBinary sInstanceKey;
- lpMemTablePublic->AddRef(); // Besure we have the object
- for (ULONG i = 0; i < cNotif; ++i) {
- switch (lpNotif[i].ulEventType)
- {
- case fnevObjectModified:
- case fnevObjectDeleted:
- for (const auto &p : lpMemTablePublic->m_mapRelation)
- if (lpMemTablePublic->m_lpECParentFolder->GetMsgStore()->CompareEntryIDs(p.second.cbEntryID, p.second.lpEntryID, lpNotif[i].info.obj.cbEntryID, lpNotif[i].info.obj.lpEntryID, 0, &ulResult) == hrSuccess && ulResult == TRUE)
- {
- sInstanceKey.cb = p.first.size();
- sInstanceKey.lpb = reinterpret_cast<BYTE *>(const_cast<char *>(p.first.c_str()));
- switch (lpNotif[i].ulEventType)
- {
- case fnevObjectModified:
- lpMemTablePublic->ModifyRow(&sInstanceKey, NULL);
- TRACE_MAPI(TRACE_ENTRY, "AdviseFolderCallback", "fnevObjectModified fnevObjectModified");
- break;
- case fnevObjectDeleted:
- TRACE_MAPI(TRACE_ENTRY, "AdviseFolderCallback", "fnevObjectDeleted fnevObjectDeleted");
- lpMemTablePublic->DelRow(&sInstanceKey);
- break;
- }
- break;
- }
- break;
- //TODO: Move (Unknown what to update)
- }
- }
-
- lpMemTablePublic->Release();
- return S_OK;
- }
- HRESULT ECMemTablePublic::Init(ULONG ulFlags)
- {
- object_ptr<IMAPIFolder> lpShortcutFolder;
- object_ptr<IMAPITable> lpShortcutTable;
- memory_ptr<SPropValue> lpPropTmp;
- ULONG ulConnection;
- m_ulFlags = ulFlags;
- // Get the messages to build a folder list
- if (((ECMsgStorePublic *)m_lpECParentFolder->GetMsgStore())->GetDefaultShortcutFolder(&~lpShortcutFolder) == hrSuccess) {
- HRESULT hr = lpShortcutFolder->GetContentsTable(ulFlags | MAPI_DEFERRED_ERRORS, &~lpShortcutTable);
- if(hr != hrSuccess)
- return hr;
- hr = lpShortcutTable->SetColumns(GetShortCutTagArray(), MAPI_DEFERRED_ERRORS);
- if(hr != hrSuccess)
- return hr;
- // build restriction
- if (HrGetOneProp(&m_lpECParentFolder->m_xMAPIFolder, PR_SOURCE_KEY, &~lpPropTmp) != hrSuccess)
- {
- hr = ECNotRestriction(ECExistRestriction(PR_FAV_PARENT_SOURCE_KEY)).RestrictTable(lpShortcutTable, MAPI_DEFERRED_ERRORS);
- }else {
- hr = HrGetOneProp(&m_lpECParentFolder->m_xMAPIFolder, PR_SOURCE_KEY, &~lpPropTmp);
- if (hr != hrSuccess)
- return hr;
- hr = ECPropertyRestriction(RELOP_EQ, PR_FAV_PARENT_SOURCE_KEY, lpPropTmp, ECRestriction::Cheap).RestrictTable(lpShortcutTable, MAPI_DEFERRED_ERRORS);
- }
- if (hr != hrSuccess)
- return hr;
-
- // No advise needed because the client disable notifications
- // If you remove this check the webaccess favorites doesn't work.
- if(! (m_lpECParentFolder->GetMsgStore()->m_ulProfileFlags & EC_PROFILE_FLAGS_NO_NOTIFICATIONS) )
- {
- hr = HrAllocAdviseSink(AdviseShortCutCallback, this, &m_lpShortCutAdviseSink);
- if (hr != hrSuccess)
- return hr;
- // NOTE: the advise will destruct at release time
- hr = lpShortcutTable->Advise(fnevTableModified, m_lpShortCutAdviseSink, &ulConnection);
- if (hr != hrSuccess)
- return hr;
- }
- while(true)
- {
- rowset_ptr lpRows;
- hr = lpShortcutTable->QueryRows(1, 0, &~lpRows);
- if (hr != hrSuccess)
- return hr;
- if (lpRows->cRows == 0)
- break;
- ModifyRow(&lpRows->aRow[0].lpProps[SC_INSTANCE_KEY].Value.bin, &lpRows->aRow[0]);
- }
- hr = lpShortcutTable->QueryInterface(IID_IMAPITable, (void **)&m_lpShortcutTable);
- if (hr != hrSuccess)
- return hr;
- }
- return hrSuccess;
- }
- /*
- lpInstanceKey Instance key of the item
- lpsRow is a property array from the shortcuts
- */
- HRESULT ECMemTablePublic::ModifyRow(SBinary* lpInstanceKey, LPSRow lpsRow)
- {
- HRESULT hr = hrSuccess;
- memory_ptr<SPropValue> lpProps, lpPropsFolder;
- ULONG cProps = 0;
- SPropValue sKeyProp;
- object_ptr<IMAPIFolder> lpFolderReal;
- ULONG ulPropsFolder;
- ULONG ulObjType;
- ULONG cbEntryID = 0;
- ULONG cbFolderID = 0;
- LPENTRYID lpEntryID = NULL; //Do not free this
- memory_ptr<ENTRYID> lpFolderID, lpRecordKeyID;
- std::string strInstanceKey;
- ECMAPFolderRelation::const_iterator iterRel;
- ECKeyTable::UpdateType ulUpdateType;
- ULONG ulRowId;
- ULONG ulConnection = 0;
- object_ptr<IMAPIAdviseSink> lpFolderAdviseSink;
- rowset_ptr lpsRowsInternal;
- SPropValue sPropTmp;
- t_sRelation sRelFolder = {0};
- static constexpr const SizedSPropTagArray(11, sPropsFolderReal) =
- {11, { PR_ACCESS, PR_ACCESS_LEVEL, PR_STORE_ENTRYID,
- PR_STORE_RECORD_KEY, PR_STORE_SUPPORT_MASK, PR_ACCESS_LEVEL,
- PR_CONTENT_COUNT, PR_CONTENT_UNREAD, PR_CONTAINER_CLASS,
- PR_ENTRYID}};
- if (lpInstanceKey == NULL) {
- assert(false);
- hr = MAPI_E_INVALID_PARAMETER;
- goto exit;
- }
- strInstanceKey.assign((char*)lpInstanceKey->lpb, lpInstanceKey->cb);
- iterRel = m_mapRelation.find(strInstanceKey);
- if (iterRel != m_mapRelation.cend()) {
- sRelFolder = iterRel->second;
- ulRowId = sRelFolder.ulRowID;
- ulUpdateType = ECKeyTable::TABLE_ROW_MODIFY;
- cbEntryID = sRelFolder.cbEntryID;
- lpEntryID = sRelFolder.lpEntryID;
- } else {
- ulRowId = m_ulRowId;
- ulUpdateType = ECKeyTable::TABLE_ROW_ADD;
- if (lpsRow == NULL || lpsRow->lpProps[SC_FAV_PUBLIC_SOURCE_KEY].ulPropTag != PR_FAV_PUBLIC_SOURCE_KEY) {
- assert(false);
- hr = MAPI_E_INVALID_PARAMETER;
- goto exit;
- }
- hr = ((ECMsgStorePublic*)m_lpECParentFolder->GetMsgStore())->EntryIDFromSourceKey(lpsRow->lpProps[SC_FAV_PUBLIC_SOURCE_KEY].Value.bin.cb, lpsRow->lpProps[SC_FAV_PUBLIC_SOURCE_KEY].Value.bin.lpb, 0, NULL, &cbFolderID, &~lpFolderID);
- if (hr != hrSuccess)
- goto exit;
- cbEntryID = cbFolderID;
- lpEntryID = lpFolderID;
- }
- cProps = 0;
- hr = MAPIAllocateBuffer(sizeof(SPropValue) * 20, &~lpProps);
- if(hr != hrSuccess)
- goto exit;
- // Default table rows
- lpProps[cProps].ulPropTag = PR_ROWID;
- lpProps[cProps++].Value.ul = ulRowId;
- hr = MAPIAllocateBuffer(cbEntryID, &~lpRecordKeyID);
- if (hr != hrSuccess)
- goto exit;
- memcpy(lpRecordKeyID, lpEntryID, cbEntryID);
- lpRecordKeyID->abFlags[3] = KOPANO_FAVORITE;
- lpProps[cProps].ulPropTag = PR_RECORD_KEY;
- lpProps[cProps].Value.bin.cb = cbEntryID;
- lpProps[cProps].Value.bin.lpb = reinterpret_cast<BYTE *>(lpRecordKeyID.get());
- ++cProps;
- // Set this folder as parent
- if (ECGenericProp::DefaultGetProp(PR_ENTRYID, m_lpECParentFolder->GetMsgStore(), 0, &lpProps[cProps], m_lpECParentFolder, lpProps) == hrSuccess) {
- lpProps[cProps].ulPropTag = PR_PARENT_ENTRYID;
- ((LPENTRYID)lpProps[cProps].Value.bin.lpb)->abFlags[3] = KOPANO_FAVORITE;
- ++cProps;
- }
- lpProps[cProps].ulPropTag = PR_DISPLAY_TYPE;
- lpProps[cProps++].Value.ul = DT_FOLDER_LINK;
- //FIXME: check if there are subfolders. Do a restriction on the shortcut folder with this folder sourcekey as parent source
- lpProps[cProps].ulPropTag = PR_SUBFOLDERS;
- lpProps[cProps++].Value.b = TRUE;
- // Properties from the real folder
- if (ulUpdateType == ECKeyTable::TABLE_ROW_ADD) {
- hr = m_lpECParentFolder->OpenEntry(cbEntryID, lpEntryID, &IID_IMAPIFolder, MAPI_BEST_ACCESS, &ulObjType, &~lpFolderReal);
- if(hr != hrSuccess)
- goto exit;
-
- // No advise needed because the client disable notifications
- // If you remove this check the webaccess favorites doesn't work.
- if(! (m_lpECParentFolder->GetMsgStore()->m_ulProfileFlags & EC_PROFILE_FLAGS_NO_NOTIFICATIONS) )
- {
- hr = HrAllocAdviseSink(AdviseFolderCallback, this, &~lpFolderAdviseSink);
- if (hr != hrSuccess)
- goto exit;
- hr = m_lpECParentFolder->GetMsgStore()->Advise(cbEntryID, lpEntryID, fnevObjectModified|fnevObjectCreated|fnevObjectMoved|fnevObjectDeleted, lpFolderAdviseSink, &ulConnection);
- if (hr != hrSuccess)
- goto exit;
- }
- }else {
- if (sRelFolder.lpFolder)
- hr = sRelFolder.lpFolder->QueryInterface(IID_IMAPIFolder, &~lpFolderReal);
- else
- hr = MAPI_E_CALL_FAILED;
- if(hr != hrSuccess)
- goto exit;
- // Get shortcut folder information
- if(lpsRow == NULL)
- {
- sPropTmp.ulPropTag = PR_INSTANCE_KEY;
- sPropTmp.Value.bin = *lpInstanceKey;
- hr = ECPropertyRestriction(RELOP_EQ, PR_INSTANCE_KEY, &sPropTmp, ECRestriction::Cheap)
- .FindRowIn(m_lpShortcutTable, BOOKMARK_BEGINNING, 0);
- if (hr != hrSuccess)
- goto exit;
- hr = m_lpShortcutTable->QueryRows(1, 0, &~lpsRowsInternal);
- if (hr != hrSuccess)
- goto exit;
- if (lpsRowsInternal->cRows == 0) {
- hr = MAPI_E_CALL_FAILED;
- goto exit;
- }
- lpsRow = lpsRowsInternal->aRow;
- }
- }
- // Set the name of the folder, use alias if available otherwise displayname
- lpProps[cProps].ulPropTag = PR_DISPLAY_NAME;
- if (lpsRow != NULL && lpsRow->cValues == SHORTCUT_NUM && lpsRow->lpProps[SC_FAV_DISPLAY_ALIAS].ulPropTag == PR_FAV_DISPLAY_ALIAS) {
- lpProps[cProps++].Value.lpszA = lpsRow->lpProps[SC_FAV_DISPLAY_ALIAS].Value.lpszA;
- }else if (lpsRow != NULL && lpsRow->cValues == SHORTCUT_NUM && lpsRow->lpProps[SC_FAV_DISPLAY_NAME].ulPropTag == PR_FAV_DISPLAY_NAME) {
- lpProps[cProps++].Value.lpszA = lpsRow->lpProps[SC_FAV_DISPLAY_NAME].Value.lpszA;
- } else {
- assert(false);
- hr = MAPI_E_INVALID_PARAMETER;
- goto exit;
- }
- hr = lpFolderReal->GetProps(sPropsFolderReal, m_ulFlags, &ulPropsFolder, &~lpPropsFolder);
- if (FAILED(hr))
- goto exit;
- else
- hr = hrSuccess;
- for (ULONG i = 0; i < ulPropsFolder; ++i) {
- if (PROP_TYPE(lpPropsFolder[i].ulPropTag) == PT_ERROR)
- continue;
- if (lpPropsFolder[i].ulPropTag == PR_ACCESS) {
- lpPropsFolder[i].Value.ul &=~(MAPI_ACCESS_CREATE_HIERARCHY | MAPI_ACCESS_CREATE_ASSOCIATED);
- lpPropsFolder[i].Value.ul |=MAPI_ACCESS_DELETE;
- }
- if (lpPropsFolder[i].ulPropTag == PR_ENTRYID)
- ((LPENTRYID)lpPropsFolder[i].Value.bin.lpb)->abFlags[3] = KOPANO_FAVORITE;
- lpProps[cProps].ulPropTag = lpPropsFolder[i].ulPropTag;
- lpProps[cProps++].Value = lpPropsFolder[i].Value;
- }
- // Add the row in the list
- sKeyProp.ulPropTag = PR_ROWID;
- sKeyProp.Value.ul = ulRowId;
- hr = this->HrModifyRow(ulUpdateType, &sKeyProp, lpProps, cProps);
- if (hr != hrSuccess)
- goto exit;
- // Add relation id
- if (ulUpdateType == ECKeyTable::TABLE_ROW_ADD) {
- sRelFolder.ulRowID = ulRowId;
- sRelFolder.cbEntryID = cbEntryID;
- hr = MAPIAllocateBuffer(sRelFolder.cbEntryID, (void**)&sRelFolder.lpEntryID);
- if (hr != hrSuccess)
- goto exit;
- memcpy(sRelFolder.lpEntryID, lpEntryID, sRelFolder.cbEntryID);
- hr = lpFolderReal->QueryInterface(IID_IMAPIFolder, (void **)&sRelFolder.lpFolder);
- if (hr != hrSuccess)
- goto exit;
- if (lpFolderAdviseSink) { // is NULL when the notification is disabled
- hr = lpFolderAdviseSink->QueryInterface(IID_IMAPIAdviseSink, (void **)&sRelFolder.lpAdviseSink);
- if (hr != hrSuccess)
- goto exit;
- sRelFolder.ulAdviseConnectionId = ulConnection;
- } else {
- sRelFolder.lpAdviseSink = NULL;
- sRelFolder.ulAdviseConnectionId = 0;
- }
- m_mapRelation.insert(ECMAPFolderRelation::value_type(strInstanceKey, sRelFolder));
- ++m_ulRowId;
- }
- exit:
- if (hr != hrSuccess && ulConnection > 0)
- m_lpECParentFolder->GetMsgStore()->Unadvise(ulConnection);
- return hr;
- }
- HRESULT ECMemTablePublic::DelRow(SBinary* lpInstanceKey)
- {
- std::string strInstanceKey;
- SPropValue sKeyProp;
- ECMAPFolderRelation::iterator iterRel;
- if (lpInstanceKey == NULL)
- return MAPI_E_INVALID_PARAMETER;
- strInstanceKey.assign((char*)lpInstanceKey->lpb, lpInstanceKey->cb);
- iterRel = m_mapRelation.find(strInstanceKey);
- if (iterRel == m_mapRelation.cend())
- return hrSuccess;
- sKeyProp.ulPropTag = PR_ROWID;
- sKeyProp.Value.ul = iterRel->second.ulRowID;
-
- this->HrModifyRow(ECKeyTable::TABLE_ROW_DELETE, NULL, &sKeyProp, 1); //ignore error
-
- if (iterRel->second.ulAdviseConnectionId > 0)
- m_lpECParentFolder->GetMsgStore()->Unadvise(iterRel->second.ulAdviseConnectionId);
- FreeRelation(&iterRel->second);
- m_mapRelation.erase(iterRel);
- return hrSuccess;
- }
- void ECMemTablePublic::FreeRelation(t_sRelation* lpRelation)
- {
- if (lpRelation == NULL)
- return;
- if (lpRelation->lpFolder)
- lpRelation->lpFolder->Release();
- if (lpRelation->lpAdviseSink)
- lpRelation->lpAdviseSink->Release();
- MAPIFreeBuffer(lpRelation->lpEntryID);
- }
|