123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- /*
- * 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 <chrono>
- #include <new>
- #include <kopano/platform.h>
- #include <kopano/lockhelper.hpp>
- #include "MAPINotifSink.h"
- #include <kopano/Util.h>
- #include <mapi.h>
- #include <mapix.h>
- namespace KC {
- /**
- * This is a special advisesink so that we can do notifications in Perl. What it does
- * is simply catch all notifications and store them. The notifications can then be requested
- * by calling GetNotifications() with or with fNonBlock. The only difference is that with the fNonBlock
- * flag function is non-blocking.
- *
- * This basically makes notifications single-threaded, requiring a Perl process to request the
- * notifications. However, the user can choose to use GetNotifications() from within a PERL thread,
- * which makes it possible to do real threaded notifications in Perl, without touching data in Perl
- * from a thread that it did not create.
- *
- * The reason we need to do this is that Perl has an interpreter-per-thread architecture, so if we
- * were to start fiddling in Perl structures from our own thread, this would very probably cause
- * segmentation faults.
- */
- static HRESULT MAPICopyMem(ULONG cb, void *lpb, void *lpBase, ULONG *lpCb,
- void **lpDest)
- {
- if(lpb == NULL) {
- *lpDest = NULL;
- *lpCb = 0;
- return hrSuccess;
- }
-
- HRESULT hr = MAPIAllocateMore(cb, lpBase, lpDest);
- if (hr != hrSuccess)
- return hr;
-
- memcpy(*lpDest, lpb, cb);
- *lpCb = cb;
- return hrSuccess;
- }
- HRESULT MAPICopyString(char *lpSrc, void *lpBase, char **lpDst)
- {
- if(lpSrc == NULL) {
- *lpDst = NULL;
- return hrSuccess;
- }
-
- HRESULT hr = MAPIAllocateMore(strlen(lpSrc) + 1, lpBase,
- reinterpret_cast<void **>(lpDst));
- if (hr != hrSuccess)
- return hr;
- strcpy(*lpDst, lpSrc);
- return hrSuccess;
- }
- HRESULT MAPICopyUnicode(WCHAR *lpSrc, void *lpBase, WCHAR **lpDst)
- {
- if(lpSrc == NULL) {
- *lpDst = NULL;
- return hrSuccess;
- }
-
- HRESULT hr = MAPIAllocateMore(wcslen(lpSrc) * sizeof(WCHAR) +
- sizeof(WCHAR), lpBase, reinterpret_cast<void **>(lpDst));
- if (hr != hrSuccess)
- return hr;
- wcscpy(*lpDst, lpSrc);
- return hrSuccess;
- }
- static HRESULT CopyMAPIERROR(const MAPIERROR *lpSrc, void *lpBase,
- MAPIERROR **lppDst)
- {
- MAPIERROR *lpDst = NULL;
-
- HRESULT hr = MAPIAllocateMore(sizeof(MAPIERROR), lpBase,
- reinterpret_cast<void **>(&lpDst));
- if (hr != hrSuccess)
- return hr;
- lpDst->ulVersion = lpSrc->ulVersion;
- // @todo we don't know if the strings were create with unicode anymore
- #ifdef UNICODE
- MAPICopyUnicode(lpSrc->lpszError, lpBase, &lpDst->lpszError);
- MAPICopyUnicode(lpSrc->lpszComponent, lpBase, &lpDst->lpszComponent);
- #else
- MAPICopyString(lpSrc->lpszError, lpBase, &lpDst->lpszError);
- MAPICopyString(lpSrc->lpszComponent, lpBase, &lpDst->lpszComponent);
- #endif
- lpDst->ulLowLevelError = lpSrc->ulLowLevelError;
- lpDst->ulContext = lpSrc->ulContext;
-
- *lppDst = lpDst;
- return hrSuccess;
- }
- static HRESULT CopyNotification(const NOTIFICATION *lpSrc, void *lpBase,
- NOTIFICATION *lpDst)
- {
- HRESULT hr;
- memset(lpDst, 0, sizeof(NOTIFICATION));
- lpDst->ulEventType = lpSrc->ulEventType;
-
- switch(lpSrc->ulEventType) {
- case fnevCriticalError:
- MAPICopyMem(lpSrc->info.err.cbEntryID, lpSrc->info.err.lpEntryID, lpBase, &lpDst->info.err.cbEntryID, (void**)&lpDst->info.err.lpEntryID);
-
- lpDst->info.err.scode = lpSrc->info.err.scode;
- lpDst->info.err.ulFlags = lpSrc->info.err.ulFlags;
-
- CopyMAPIERROR(lpSrc->info.err.lpMAPIError, lpBase, &lpDst->info.err.lpMAPIError);
-
- break;
- case fnevNewMail:
- MAPICopyMem(lpSrc->info.newmail.cbEntryID, lpSrc->info.newmail.lpEntryID, lpBase, &lpDst->info.newmail.cbEntryID, (void**)&lpDst->info.newmail.lpEntryID);
- MAPICopyMem(lpSrc->info.newmail.cbParentID, lpSrc->info.newmail.lpParentID, lpBase, &lpDst->info.newmail.cbParentID, (void**)&lpDst->info.newmail.lpParentID);
-
- lpDst->info.newmail.ulFlags = lpSrc->info.newmail.ulFlags;
- if (lpSrc->info.newmail.ulFlags&MAPI_UNICODE)
- MAPICopyUnicode((LPWSTR)lpSrc->info.newmail.lpszMessageClass, lpBase, (LPWSTR*)&lpDst->info.newmail.lpszMessageClass);
- else
- MAPICopyString((char*)lpSrc->info.newmail.lpszMessageClass, lpBase, (char**)&lpDst->info.newmail.lpszMessageClass);
-
- lpDst->info.newmail.ulMessageFlags = lpSrc->info.newmail.ulMessageFlags;
-
- break;
- case fnevObjectCreated:
- case fnevObjectDeleted:
- case fnevObjectModified:
- case fnevObjectMoved:
- case fnevObjectCopied:
- case fnevSearchComplete:
- lpDst->info.obj.ulObjType = lpSrc->info.obj.ulObjType;
-
- MAPICopyMem(lpSrc->info.obj.cbEntryID, lpSrc->info.obj.lpEntryID, lpBase, &lpDst->info.obj.cbEntryID, (void**)&lpDst->info.obj.lpEntryID);
- MAPICopyMem(lpSrc->info.obj.cbParentID, lpSrc->info.obj.lpParentID, lpBase, &lpDst->info.obj.cbParentID, (void**)&lpDst->info.obj.lpParentID);
- MAPICopyMem(lpSrc->info.obj.cbOldID, lpSrc->info.obj.lpOldID, lpBase, &lpDst->info.obj.cbOldID, (void**)&lpDst->info.obj.lpOldID);
- MAPICopyMem(lpSrc->info.obj.cbOldParentID, lpSrc->info.obj.lpOldParentID, lpBase, &lpDst->info.obj.cbOldParentID, (void**)&lpDst->info.obj.lpOldParentID);
- if(lpSrc->info.obj.lpPropTagArray)
- MAPICopyMem(CbSPropTagArray(lpSrc->info.obj.lpPropTagArray), lpSrc->info.obj.lpPropTagArray, lpBase, NULL, (void**)&lpDst->info.obj.lpPropTagArray);
- break;
- case fnevTableModified:
- lpDst->info.tab.ulTableEvent = lpSrc->info.tab.ulTableEvent;
- lpDst->info.tab.hResult = lpSrc->info.tab.hResult;
- hr = Util::HrCopyProperty(&lpDst->info.tab.propPrior, &lpSrc->info.tab.propPrior, lpBase);
- if (hr != hrSuccess)
- return hr;
- hr = Util::HrCopyProperty(&lpDst->info.tab.propIndex, &lpSrc->info.tab.propIndex, lpBase);
- if (hr != hrSuccess)
- return hr;
- if ((hr = MAPIAllocateMore(lpSrc->info.tab.row.cValues * sizeof(SPropValue), lpBase, (void **)&lpDst->info.tab.row.lpProps)) != hrSuccess)
- return hr;
- hr = Util::HrCopyPropertyArray(lpSrc->info.tab.row.lpProps, lpSrc->info.tab.row.cValues, lpDst->info.tab.row.lpProps, lpBase);
- if (hr != hrSuccess)
- return hr;
- lpDst->info.tab.row.cValues = lpSrc->info.tab.row.cValues;
- break;
- case fnevStatusObjectModified:
- MAPICopyMem(lpSrc->info.statobj.cbEntryID, lpSrc->info.statobj.lpEntryID, lpBase, &lpDst->info.statobj.cbEntryID, (void**)&lpDst->info.statobj.lpEntryID);
- if ((hr = MAPIAllocateMore(lpSrc->info.statobj.cValues * sizeof(SPropValue), lpBase, (void **)&lpDst->info.statobj.lpPropVals)) != hrSuccess)
- return hr;
- hr = Util::HrCopyPropertyArray(lpSrc->info.statobj.lpPropVals, lpSrc->info.statobj.cValues, lpDst->info.statobj.lpPropVals, lpBase);
- if (hr != hrSuccess)
- return hr;
- lpDst->info.statobj.cValues = lpSrc->info.statobj.cValues;
- break;
- }
- return hrSuccess;
- }
- HRESULT MAPINotifSink::Create(MAPINotifSink **lppSink)
- {
- auto lpSink = new(std::nothrow) MAPINotifSink;
- if (lpSink == nullptr)
- return MAPI_E_NOT_ENOUGH_MEMORY;
- lpSink->AddRef();
-
- *lppSink = lpSink;
-
- return hrSuccess;
- }
- MAPINotifSink::~MAPINotifSink() {
- m_bExit = true;
- m_hCond.notify_all();
- for (auto n : m_lstNotifs)
- MAPIFreeBuffer(n);
- m_lstNotifs.clear();
- }
- // Add a notification to the queue; Normally called as notification sink
- ULONG MAPINotifSink::OnNotify(ULONG cNotifications, LPNOTIFICATION lpNotifications)
- {
- ULONG rc = 0;
- LPNOTIFICATION lpNotif;
- ulock_normal biglock(m_hMutex);
- for (unsigned int i = 0; i < cNotifications; ++i) {
- if (MAPIAllocateBuffer(sizeof(NOTIFICATION), (LPVOID*)&lpNotif) != hrSuccess) {
- rc = 1;
- break;
- }
- if (CopyNotification(&lpNotifications[i], lpNotif, lpNotif) == 0)
- m_lstNotifs.push_back(lpNotif);
- }
- biglock.unlock();
- m_hCond.notify_all();
- return rc;
- }
- // Get All notifications off the queue
- HRESULT MAPINotifSink::GetNotifications(ULONG *lpcNotif, LPNOTIFICATION *lppNotifications, BOOL fNonBlock, ULONG timeout)
- {
- HRESULT hr = hrSuccess;
- ULONG cNotifs = 0;
- struct timespec t;
-
- double now = GetTimeOfDay();
- now += (float)timeout / 1000;
- t.tv_sec = now;
- t.tv_nsec = (now-t.tv_sec) * 1000000000.0;
- ulock_normal biglock(m_hMutex);
- if (!fNonBlock) {
- while(m_lstNotifs.empty() && !m_bExit && (timeout == 0 || GetTimeOfDay() < now)) {
- if (timeout > 0)
- m_hCond.wait_for(biglock, std::chrono::milliseconds(timeout));
- else
- m_hCond.wait(biglock);
- }
- }
-
- LPNOTIFICATION lpNotifications = NULL;
-
- if ((hr = MAPIAllocateBuffer(sizeof(NOTIFICATION) * m_lstNotifs.size(), (void **) &lpNotifications)) == hrSuccess) {
- for (auto n : m_lstNotifs) {
- if (CopyNotification(n, lpNotifications, &lpNotifications[cNotifs]) == 0)
- ++cNotifs;
- MAPIFreeBuffer(n);
- }
- }
- m_lstNotifs.clear();
- biglock.unlock();
- *lppNotifications = lpNotifications;
- *lpcNotif = cNotifs;
- return hr;
- }
- HRESULT MAPINotifSink::QueryInterface(REFIID iid, void **lpvoid) {
- if (iid == IID_IMAPIAdviseSink) {
- AddRef();
- *lpvoid = (LPVOID)this;
- return hrSuccess;
- }
- return MAPI_E_INTERFACE_NOT_SUPPORTED;
- }
- ULONG MAPINotifSink::AddRef()
- {
- return ++m_cRef;
- }
- ULONG MAPINotifSink::Release()
- {
- ULONG ref = --m_cRef;
-
- if(ref == 0)
- delete this;
-
- return ref;
- }
- } /* namespace */
|