123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- /*
- * 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 <kopano/platform.h>
- #include <mapitags.h>
- #include <mapidefs.h>
- #include <mapicode.h>
- #include <list>
- #include "Mem.h"
- #include "ECNamedProp.h"
- #include "WSTransport.h"
- /*
- * How our named properties work
- *
- * Basically, named properties in objects with the same PR_MAPPING_SIGNATURE should have the same
- * mapping of named properties to property IDs and vice-versa. We can use this information, together
- * with the information that Outlook mainly uses a fixed set of named properties to speed up things
- * considerably;
- *
- * Normally, each GetIDsFromNames calls would have to consult the server for an ID, and then cache
- * and return the value to the client. This is a rather time-consuming thing to do as Outlook requests
- * quite a few different named properties at startup. We can make this much faster by hard-wiring
- * a bunch of known named properties into the CLIENT-side DLL. This makes sure that most (say 90%) of
- * GetIDsFromNames calls can be handled locally without any reference to the server, while any other
- * (new) named properties can be handled in the standard way. This reduces client-server communications
- * dramatically, resulting in both a faster client as less datacommunication between client and server.
- *
- * In fact, most of the time, the named property mechanism will work even if the server is down...
- */
- /*
- * Currently, serverside named properties are cached locally in a map<> object,
- * however, in the future, a bimap<> may be used to speed up reverse lookups (ie
- * getNamesFromIDs) but this is not used frequently so we can leave it like
- * this for now
- *
- * For the most part, this implementation is rather fast, (possible faster than
- * Exchange) due to the fact that we know which named properties are likely to be
- * requested. This means that we have
- */
- /* Our local names
- *
- * It is VERY IMPORTANT that these values are NOT MODIFIED, otherwise the mapping of
- * named properties will change, which will BREAK THINGS BADLY
- *
- * Special constraints for this structure:
- * - The ulMappedIds must not overlap the previous row
- * - The ulMappedIds must be in ascending order
- */
- static const struct _sLocalNames {
- GUID guid;
- LONG ulMin;
- LONG ulMax;
- ULONG ulMappedId; // mapped ID of the FIRST property in the range
- } sLocalNames[] = {{{ 0x62002, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8200, 0x826F, 0x8000 },
- {{ 0x62003, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8100, 0x813F, 0x8070 },
- {{ 0x62004, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8000, 0x80EF, 0x80B0 },
- {{ 0x62008, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8500, 0x85FF, 0x81A0 },
- {{ 0x6200A, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8700, 0x871F, 0x82A0 },
- {{ 0x6200B, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8800, 0x881F, 0x82C0 },
- {{ 0x6200E, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8B00, 0x8B1F, 0x82E0 },
- {{ 0x62013, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8D00, 0x8D1F, 0x8300 },
- {{ 0x62014, 0x0, 0x0, { 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46 } }, 0x8F00, 0x8F1F, 0x8320 },
- {{ 0x6ED8DA90, 0x450B, 0x101B, { 0x98, 0xDA, 0x00, 0xAA, 0x00, 0x3F, 0x13, 0x05} } , 0x0000, 0x003F, 0x8340}};
- #define SERVER_NAMED_OFFSET 0x8500
- ECNamedProp::ECNamedProp(WSTransport *lpTransport)
- {
- this->lpTransport = lpTransport;
- lpTransport->AddRef();
- }
- ECNamedProp::~ECNamedProp()
- {
- // Clear all the cached names
- for (const auto &p : mapNames)
- if (p.first)
- ECFreeBuffer(p.first);
- if(lpTransport)
- lpTransport->Release();
- }
- HRESULT ECNamedProp::GetNamesFromIDs(LPSPropTagArray *lppPropTags, LPGUID lpPropSetGuid, ULONG ulFlags, ULONG *lpcPropNames, LPMAPINAMEID **lpppPropNames)
- {
- HRESULT hr = hrSuccess;
- unsigned int i = 0;
- LPSPropTagArray lpsPropTags = NULL;
- ecmem_ptr<MAPINAMEID *> lppPropNames, lppResolved;
- ecmem_ptr<SPropTagArray> lpsUnresolved;
- ULONG cResolved = 0;
- ULONG cUnresolved = 0;
- // Exchange doesn't support this, so neither do we
- if(lppPropTags == NULL || *lppPropTags == NULL) {
- hr = MAPI_E_TOO_BIG;
- goto exit;
- }
- lpsPropTags = *lppPropTags;
- // Allocate space for properties
- hr = ECAllocateBuffer(sizeof(LPMAPINAMEID) * lpsPropTags->cValues, &~lppPropNames);
- if (hr != hrSuccess)
- goto exit;
- // Pass 1, local reverse mapping (FAST)
- for (i = 0; i < lpsPropTags->cValues; ++i)
- if (ResolveReverseLocal(PROP_ID(lpsPropTags->aulPropTag[i]),
- lpPropSetGuid, ulFlags, lppPropNames,
- &lppPropNames[i]) != hrSuccess)
- lppPropNames[i] = NULL;
- // Pass 2, cache reverse mapping (FAST)
- for (i = 0; i < lpsPropTags->cValues; ++i) {
- if (lppPropNames[i] != NULL)
- continue;
- if (PROP_ID(lpsPropTags->aulPropTag[i]) > SERVER_NAMED_OFFSET)
- ResolveReverseCache(PROP_ID(lpsPropTags->aulPropTag[i]), lpPropSetGuid, ulFlags, lppPropNames, &lppPropNames[i]);
- // else { Hmmm, so here is a named property, which is < SERVER_NAMED_OFFSET, but CANNOT be
- // resolved internally. Looks like somebody's pulling our leg ... We just leave it unknown }
- }
- hr = ECAllocateBuffer(CbNewSPropTagArray(lpsPropTags->cValues), &~lpsUnresolved);
- if (hr != hrSuccess)
- goto exit;
- cUnresolved = 0;
- // Pass 3, server reverse lookup (SLOW)
- for (i = 0; i < lpsPropTags->cValues; ++i)
- if (lppPropNames[i] == NULL)
- if(PROP_ID(lpsPropTags->aulPropTag[i]) > SERVER_NAMED_OFFSET) {
- lpsUnresolved->aulPropTag[cUnresolved] = PROP_ID(lpsPropTags->aulPropTag[i]) - SERVER_NAMED_OFFSET;
- ++cUnresolved;
- }
- lpsUnresolved->cValues = cUnresolved;
- if(cUnresolved > 0) {
- hr = lpTransport->HrGetNamesFromIDs(lpsUnresolved, &~lppResolved, &cResolved);
- if(hr != hrSuccess)
- goto exit;
- // Put the resolved values from the server into the cache
- if(cResolved != cUnresolved) {
- hr = MAPI_E_CALL_FAILED;
- goto exit;
- }
- for (i = 0; i < cResolved; ++i)
- if(lppResolved[i] != NULL)
- UpdateCache(lpsUnresolved->aulPropTag[i] + SERVER_NAMED_OFFSET, lppResolved[i]);
- // re-scan the cache
- for (i = 0; i < lpsPropTags->cValues; ++i)
- if (lppPropNames[i] == NULL)
- if (PROP_ID(lpsPropTags->aulPropTag[i]) > SERVER_NAMED_OFFSET)
- ResolveReverseCache(PROP_ID(lpsPropTags->aulPropTag[i]), lpPropSetGuid, ulFlags, lppPropNames, &lppPropNames[i]);
- }
- // Check for errors
- for (i = 0; i < lpsPropTags->cValues; ++i)
- if(lppPropNames[i] == NULL)
- hr = MAPI_W_ERRORS_RETURNED;
- *lpppPropNames = lppPropNames.release();
- *lpcPropNames = lpsPropTags->cValues;
- exit:
- return hr;
- }
- HRESULT ECNamedProp::GetIDsFromNames(ULONG cPropNames, LPMAPINAMEID *lppPropNames, ULONG ulFlags, LPSPropTagArray *lppPropTags)
- {
- HRESULT hr = hrSuccess;
- unsigned int i=0;
- LPSPropTagArray lpsPropTagArray = NULL;
- std::unique_ptr<MAPINAMEID *[]> lppPropNamesUnresolved;
- ULONG cUnresolved = 0;
- ULONG* lpServerIDs = NULL;
- // Exchange doesn't support this, so neither do we
- if(cPropNames == 0 || lppPropNames == NULL) {
- hr = MAPI_E_TOO_BIG;
- goto exit;
- }
- // Sanity check input
- for (i = 0; i < cPropNames; ++i) {
- if(lppPropNames[i] == NULL) {
- hr = MAPI_E_INVALID_PARAMETER;
- goto exit;
- }
- }
- // Allocate memory for the return structure
- hr = ECAllocateBuffer(CbNewSPropTagArray(cPropNames), (void **)&lpsPropTagArray);
- if(hr != hrSuccess)
- goto exit;
- lpsPropTagArray->cValues = cPropNames;
- // Pass 1, resolve static (local) names (FAST)
- for (i = 0; i < cPropNames; ++i)
- if(lppPropNames[i] == NULL || ResolveLocal(lppPropNames[i], &lpsPropTagArray->aulPropTag[i]) != hrSuccess)
- lpsPropTagArray->aulPropTag[i] = PROP_TAG(PT_ERROR, 0);
- // Pass 2, resolve names from local cache (FAST)
- for (i = 0; i < cPropNames; ++i)
- if (lppPropNames[i] != NULL && lpsPropTagArray->aulPropTag[i] == PROP_TAG(PT_ERROR, 0))
- ResolveCache(lppPropNames[i], &lpsPropTagArray->aulPropTag[i]);
- // Pass 3, resolve names from server (SLOW, but decreases in frequency with store lifetime)
- lppPropNamesUnresolved.reset(new MAPINAMEID *[lpsPropTagArray->cValues]); // over-allocated
- // Get a list of unresolved names
- for (i = 0; i < cPropNames; ++i)
- if(lpsPropTagArray->aulPropTag[i] == PROP_TAG(PT_ERROR, 0) && lppPropNames[i] != NULL ) {
- lppPropNamesUnresolved[cUnresolved] = lppPropNames[i];
- ++cUnresolved;
- }
- if(cUnresolved) {
- // Let the server resolve these names
- hr = lpTransport->HrGetIDsFromNames(lppPropNamesUnresolved.get(), cUnresolved, ulFlags, &lpServerIDs);
- if(hr != hrSuccess)
- goto exit;
- // Put the names into the local cache for all the IDs the server gave us
- for (i = 0; i < cUnresolved; ++i)
- if(lpServerIDs[i] != 0)
- UpdateCache(lpServerIDs[i] + SERVER_NAMED_OFFSET, lppPropNamesUnresolved[i]);
- // Pass 4, re-resolve from local cache (FAST)
- for (i = 0; i < cPropNames; ++i)
- if (lppPropNames[i] != NULL &&
- lpsPropTagArray->aulPropTag[i] == PROP_TAG(PT_ERROR, 0))
- ResolveCache(lppPropNames[i], &lpsPropTagArray->aulPropTag[i]);
- }
-
- // Finally, check for any errors left in the returned structure
- hr = hrSuccess;
- for (i = 0; i < cPropNames; ++i)
- if(lpsPropTagArray->aulPropTag[i] == PROP_TAG(PT_ERROR, 0)) {
- hr = MAPI_W_ERRORS_RETURNED;
- break;
- }
- *lppPropTags = lpsPropTagArray;
- lpsPropTagArray = NULL;
- exit:
- if(lpsPropTagArray)
- ECFreeBuffer(lpsPropTagArray);
- if(lpServerIDs)
- ECFreeBuffer(lpServerIDs);
- return hr;
- }
- HRESULT ECNamedProp::ResolveLocal(MAPINAMEID *lpName, ULONG *ulPropTag)
- {
- // We can only locally resolve MNID_ID types of named properties
- if (lpName->ulKind != MNID_ID)
- return MAPI_E_NOT_FOUND;
- // Loop through our local names to see if the named property is in there
- for (size_t i = 0; i < ARRAY_SIZE(sLocalNames); ++i) {
- if(memcmp(&sLocalNames[i].guid,lpName->lpguid,sizeof(GUID))==0 && sLocalNames[i].ulMin <= lpName->Kind.lID && sLocalNames[i].ulMax >= lpName->Kind.lID) {
- // Found it, calculate the ID and return it.
- *ulPropTag = PROP_TAG(PT_UNSPECIFIED, sLocalNames[i].ulMappedId + lpName->Kind.lID - sLocalNames[i].ulMin);
- return hrSuccess;
- }
- }
- // Couldn't find it ...
- return MAPI_E_NOT_FOUND;
- }
- HRESULT ECNamedProp::ResolveReverseCache(ULONG ulId, LPGUID lpGuid, ULONG ulFlags, void *lpBase, MAPINAMEID **lppName)
- {
- HRESULT hr = MAPI_E_NOT_FOUND;
- // Loop through the map to find the reverse-lookup of the named property. This could be speeded up by
- // used a bimap (bi-directional map)
- for (const auto &p : mapNames)
- if (p.second == ulId) { // FIXME match GUID
- if (lpGuid != nullptr)
- assert(memcmp(lpGuid, p.first->lpguid, sizeof(GUID)) == 0); // TEST michel
- // found it
- hr = HrCopyNameId(p.first, lppName, lpBase);
- break;
- }
- return hr;
- }
- HRESULT ECNamedProp::ResolveReverseLocal(ULONG ulId, LPGUID lpGuid, ULONG ulFlags, void *lpBase, MAPINAMEID **lppName)
- {
- MAPINAMEID* lpName = NULL;
- // Local mapping is only for MNID_ID
- if (ulFlags & MAPI_NO_IDS)
- return MAPI_E_NOT_FOUND;
- // Loop through the local names to see if we can reverse-map the id
- for (size_t i = 0; i < ARRAY_SIZE(sLocalNames); ++i) {
- if((lpGuid == NULL || memcmp(&sLocalNames[i].guid, lpGuid, sizeof(GUID)) == 0) && ulId >= sLocalNames[i].ulMappedId && ulId < sLocalNames[i].ulMappedId + (sLocalNames[i].ulMax - sLocalNames[i].ulMin + 1)) {
- // Found it !
- auto hr = ECAllocateMore(sizeof(MAPINAMEID), lpBase, reinterpret_cast<void **>(&lpName));
- if (hr != hrSuccess)
- return hr;
- hr = ECAllocateMore(sizeof(GUID), lpBase, reinterpret_cast<void **>(&lpName->lpguid));
- if (hr != hrSuccess)
- return hr;
- lpName->ulKind = MNID_ID;
- memcpy(lpName->lpguid, &sLocalNames[i].guid, sizeof(GUID));
- lpName->Kind.lID = sLocalNames[i].ulMin + (ulId - sLocalNames[i].ulMappedId);
- break;
- }
- }
- if (lpName == NULL)
- return MAPI_E_NOT_FOUND;
- *lppName = lpName;
- return hrSuccess;
- }
- // Update the cache with the given data
- HRESULT ECNamedProp::UpdateCache(ULONG ulId, MAPINAMEID *lpName)
- {
- HRESULT hr = hrSuccess;
- MAPINAMEID* lpNameCopy = NULL;
- if(mapNames.find(lpName) != mapNames.end()) {
- // Already in the cache!
- hr = MAPI_E_NOT_FOUND;
- goto exit;
- }
- hr = HrCopyNameId(lpName, &lpNameCopy, NULL);
- if(hr != hrSuccess)
- goto exit;
- mapNames[lpNameCopy] = ulId;
- exit:
- if(hr != hrSuccess && lpNameCopy)
- ECFreeBuffer(lpNameCopy);
- return hr;
- }
- HRESULT ECNamedProp::ResolveCache(MAPINAMEID *lpName, ULONG *lpulPropTag)
- {
- std::map<MAPINAMEID *, ULONG, ltmap>::const_iterator iterMap;
- iterMap = mapNames.find(lpName);
- if (iterMap == mapNames.cend())
- return MAPI_E_NOT_FOUND;
- *lpulPropTag = PROP_TAG(PT_UNSPECIFIED, iterMap->second);
- return hrSuccess;
- }
- /* This copies a MAPINAMEID struct using ECAllocate* functions. Therefore, the
- * memory allocated here is *not* traced by the debug functions. Make sure you
- * release all memory allocated from this function! (or make sure the client application
- * does)
- */
- HRESULT ECNamedProp::HrCopyNameId(LPMAPINAMEID lpSrc, LPMAPINAMEID *lppDst, void *lpBase)
- {
- HRESULT hr = hrSuccess;
- LPMAPINAMEID lpDst = NULL;
- if(lpBase == NULL)
- hr = ECAllocateBuffer(sizeof(MAPINAMEID), (void **) &lpDst);
- else
- hr = ECAllocateMore(sizeof(MAPINAMEID), lpBase, (void **) &lpDst);
- if(hr != hrSuccess)
- goto exit;
- lpDst->ulKind = lpSrc->ulKind;
- if(lpSrc->lpguid) {
- if(lpBase)
- hr = ECAllocateMore(sizeof(GUID), lpBase, (void **) &lpDst->lpguid);
- else
- hr = ECAllocateMore(sizeof(GUID), lpDst, (void **) &lpDst->lpguid);
- if(hr != hrSuccess)
- goto exit;
- memcpy(lpDst->lpguid, lpSrc->lpguid, sizeof(GUID));
- } else {
- lpDst->lpguid = NULL;
- }
- switch(lpSrc->ulKind) {
- case MNID_ID:
- lpDst->Kind.lID = lpSrc->Kind.lID;
- break;
- case MNID_STRING:
- if(lpBase)
- hr = ECAllocateMore(wcslen(lpSrc->Kind.lpwstrName) * sizeof(wchar_t) + sizeof(wchar_t),
- lpBase, reinterpret_cast<void **>(&lpDst->Kind.lpwstrName));
- else
- hr = ECAllocateMore(wcslen(lpSrc->Kind.lpwstrName) * sizeof(wchar_t) + sizeof(wchar_t),
- lpDst, reinterpret_cast<void **>(&lpDst->Kind.lpwstrName));
- if (hr != hrSuccess)
- return hr;
- wcscpy(lpDst->Kind.lpwstrName, lpSrc->Kind.lpwstrName);
- break;
- default:
- hr = MAPI_E_INVALID_TYPE;
- goto exit;
- }
- *lppDst = lpDst;
- exit:
- if(hr != hrSuccess && !lpBase && lpDst)
- ECFreeBuffer(lpDst);
- return hr;
- }
|