MAPINotifSink.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Affero General Public License, version 3,
  6. * as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. *
  16. */
  17. #include <chrono>
  18. #include <new>
  19. #include <kopano/platform.h>
  20. #include <kopano/lockhelper.hpp>
  21. #include "MAPINotifSink.h"
  22. #include <kopano/Util.h>
  23. #include <mapi.h>
  24. #include <mapix.h>
  25. namespace KC {
  26. /**
  27. * This is a special advisesink so that we can do notifications in Perl. What it does
  28. * is simply catch all notifications and store them. The notifications can then be requested
  29. * by calling GetNotifications() with or with fNonBlock. The only difference is that with the fNonBlock
  30. * flag function is non-blocking.
  31. *
  32. * This basically makes notifications single-threaded, requiring a Perl process to request the
  33. * notifications. However, the user can choose to use GetNotifications() from within a PERL thread,
  34. * which makes it possible to do real threaded notifications in Perl, without touching data in Perl
  35. * from a thread that it did not create.
  36. *
  37. * The reason we need to do this is that Perl has an interpreter-per-thread architecture, so if we
  38. * were to start fiddling in Perl structures from our own thread, this would very probably cause
  39. * segmentation faults.
  40. */
  41. static HRESULT MAPICopyMem(ULONG cb, void *lpb, void *lpBase, ULONG *lpCb,
  42. void **lpDest)
  43. {
  44. if(lpb == NULL) {
  45. *lpDest = NULL;
  46. *lpCb = 0;
  47. return hrSuccess;
  48. }
  49. HRESULT hr = MAPIAllocateMore(cb, lpBase, lpDest);
  50. if (hr != hrSuccess)
  51. return hr;
  52. memcpy(*lpDest, lpb, cb);
  53. *lpCb = cb;
  54. return hrSuccess;
  55. }
  56. HRESULT MAPICopyString(char *lpSrc, void *lpBase, char **lpDst)
  57. {
  58. if(lpSrc == NULL) {
  59. *lpDst = NULL;
  60. return hrSuccess;
  61. }
  62. HRESULT hr = MAPIAllocateMore(strlen(lpSrc) + 1, lpBase,
  63. reinterpret_cast<void **>(lpDst));
  64. if (hr != hrSuccess)
  65. return hr;
  66. strcpy(*lpDst, lpSrc);
  67. return hrSuccess;
  68. }
  69. HRESULT MAPICopyUnicode(WCHAR *lpSrc, void *lpBase, WCHAR **lpDst)
  70. {
  71. if(lpSrc == NULL) {
  72. *lpDst = NULL;
  73. return hrSuccess;
  74. }
  75. HRESULT hr = MAPIAllocateMore(wcslen(lpSrc) * sizeof(WCHAR) +
  76. sizeof(WCHAR), lpBase, reinterpret_cast<void **>(lpDst));
  77. if (hr != hrSuccess)
  78. return hr;
  79. wcscpy(*lpDst, lpSrc);
  80. return hrSuccess;
  81. }
  82. static HRESULT CopyMAPIERROR(const MAPIERROR *lpSrc, void *lpBase,
  83. MAPIERROR **lppDst)
  84. {
  85. MAPIERROR *lpDst = NULL;
  86. HRESULT hr = MAPIAllocateMore(sizeof(MAPIERROR), lpBase,
  87. reinterpret_cast<void **>(&lpDst));
  88. if (hr != hrSuccess)
  89. return hr;
  90. lpDst->ulVersion = lpSrc->ulVersion;
  91. // @todo we don't know if the strings were create with unicode anymore
  92. #ifdef UNICODE
  93. MAPICopyUnicode(lpSrc->lpszError, lpBase, &lpDst->lpszError);
  94. MAPICopyUnicode(lpSrc->lpszComponent, lpBase, &lpDst->lpszComponent);
  95. #else
  96. MAPICopyString(lpSrc->lpszError, lpBase, &lpDst->lpszError);
  97. MAPICopyString(lpSrc->lpszComponent, lpBase, &lpDst->lpszComponent);
  98. #endif
  99. lpDst->ulLowLevelError = lpSrc->ulLowLevelError;
  100. lpDst->ulContext = lpSrc->ulContext;
  101. *lppDst = lpDst;
  102. return hrSuccess;
  103. }
  104. static HRESULT CopyNotification(const NOTIFICATION *lpSrc, void *lpBase,
  105. NOTIFICATION *lpDst)
  106. {
  107. HRESULT hr;
  108. memset(lpDst, 0, sizeof(NOTIFICATION));
  109. lpDst->ulEventType = lpSrc->ulEventType;
  110. switch(lpSrc->ulEventType) {
  111. case fnevCriticalError:
  112. MAPICopyMem(lpSrc->info.err.cbEntryID, lpSrc->info.err.lpEntryID, lpBase, &lpDst->info.err.cbEntryID, (void**)&lpDst->info.err.lpEntryID);
  113. lpDst->info.err.scode = lpSrc->info.err.scode;
  114. lpDst->info.err.ulFlags = lpSrc->info.err.ulFlags;
  115. CopyMAPIERROR(lpSrc->info.err.lpMAPIError, lpBase, &lpDst->info.err.lpMAPIError);
  116. break;
  117. case fnevNewMail:
  118. MAPICopyMem(lpSrc->info.newmail.cbEntryID, lpSrc->info.newmail.lpEntryID, lpBase, &lpDst->info.newmail.cbEntryID, (void**)&lpDst->info.newmail.lpEntryID);
  119. MAPICopyMem(lpSrc->info.newmail.cbParentID, lpSrc->info.newmail.lpParentID, lpBase, &lpDst->info.newmail.cbParentID, (void**)&lpDst->info.newmail.lpParentID);
  120. lpDst->info.newmail.ulFlags = lpSrc->info.newmail.ulFlags;
  121. if (lpSrc->info.newmail.ulFlags&MAPI_UNICODE)
  122. MAPICopyUnicode((LPWSTR)lpSrc->info.newmail.lpszMessageClass, lpBase, (LPWSTR*)&lpDst->info.newmail.lpszMessageClass);
  123. else
  124. MAPICopyString((char*)lpSrc->info.newmail.lpszMessageClass, lpBase, (char**)&lpDst->info.newmail.lpszMessageClass);
  125. lpDst->info.newmail.ulMessageFlags = lpSrc->info.newmail.ulMessageFlags;
  126. break;
  127. case fnevObjectCreated:
  128. case fnevObjectDeleted:
  129. case fnevObjectModified:
  130. case fnevObjectMoved:
  131. case fnevObjectCopied:
  132. case fnevSearchComplete:
  133. lpDst->info.obj.ulObjType = lpSrc->info.obj.ulObjType;
  134. MAPICopyMem(lpSrc->info.obj.cbEntryID, lpSrc->info.obj.lpEntryID, lpBase, &lpDst->info.obj.cbEntryID, (void**)&lpDst->info.obj.lpEntryID);
  135. MAPICopyMem(lpSrc->info.obj.cbParentID, lpSrc->info.obj.lpParentID, lpBase, &lpDst->info.obj.cbParentID, (void**)&lpDst->info.obj.lpParentID);
  136. MAPICopyMem(lpSrc->info.obj.cbOldID, lpSrc->info.obj.lpOldID, lpBase, &lpDst->info.obj.cbOldID, (void**)&lpDst->info.obj.lpOldID);
  137. MAPICopyMem(lpSrc->info.obj.cbOldParentID, lpSrc->info.obj.lpOldParentID, lpBase, &lpDst->info.obj.cbOldParentID, (void**)&lpDst->info.obj.lpOldParentID);
  138. if(lpSrc->info.obj.lpPropTagArray)
  139. MAPICopyMem(CbSPropTagArray(lpSrc->info.obj.lpPropTagArray), lpSrc->info.obj.lpPropTagArray, lpBase, NULL, (void**)&lpDst->info.obj.lpPropTagArray);
  140. break;
  141. case fnevTableModified:
  142. lpDst->info.tab.ulTableEvent = lpSrc->info.tab.ulTableEvent;
  143. lpDst->info.tab.hResult = lpSrc->info.tab.hResult;
  144. hr = Util::HrCopyProperty(&lpDst->info.tab.propPrior, &lpSrc->info.tab.propPrior, lpBase);
  145. if (hr != hrSuccess)
  146. return hr;
  147. hr = Util::HrCopyProperty(&lpDst->info.tab.propIndex, &lpSrc->info.tab.propIndex, lpBase);
  148. if (hr != hrSuccess)
  149. return hr;
  150. if ((hr = MAPIAllocateMore(lpSrc->info.tab.row.cValues * sizeof(SPropValue), lpBase, (void **)&lpDst->info.tab.row.lpProps)) != hrSuccess)
  151. return hr;
  152. hr = Util::HrCopyPropertyArray(lpSrc->info.tab.row.lpProps, lpSrc->info.tab.row.cValues, lpDst->info.tab.row.lpProps, lpBase);
  153. if (hr != hrSuccess)
  154. return hr;
  155. lpDst->info.tab.row.cValues = lpSrc->info.tab.row.cValues;
  156. break;
  157. case fnevStatusObjectModified:
  158. MAPICopyMem(lpSrc->info.statobj.cbEntryID, lpSrc->info.statobj.lpEntryID, lpBase, &lpDst->info.statobj.cbEntryID, (void**)&lpDst->info.statobj.lpEntryID);
  159. if ((hr = MAPIAllocateMore(lpSrc->info.statobj.cValues * sizeof(SPropValue), lpBase, (void **)&lpDst->info.statobj.lpPropVals)) != hrSuccess)
  160. return hr;
  161. hr = Util::HrCopyPropertyArray(lpSrc->info.statobj.lpPropVals, lpSrc->info.statobj.cValues, lpDst->info.statobj.lpPropVals, lpBase);
  162. if (hr != hrSuccess)
  163. return hr;
  164. lpDst->info.statobj.cValues = lpSrc->info.statobj.cValues;
  165. break;
  166. }
  167. return hrSuccess;
  168. }
  169. HRESULT MAPINotifSink::Create(MAPINotifSink **lppSink)
  170. {
  171. auto lpSink = new(std::nothrow) MAPINotifSink;
  172. if (lpSink == nullptr)
  173. return MAPI_E_NOT_ENOUGH_MEMORY;
  174. lpSink->AddRef();
  175. *lppSink = lpSink;
  176. return hrSuccess;
  177. }
  178. MAPINotifSink::~MAPINotifSink() {
  179. m_bExit = true;
  180. m_hCond.notify_all();
  181. for (auto n : m_lstNotifs)
  182. MAPIFreeBuffer(n);
  183. m_lstNotifs.clear();
  184. }
  185. // Add a notification to the queue; Normally called as notification sink
  186. ULONG MAPINotifSink::OnNotify(ULONG cNotifications, LPNOTIFICATION lpNotifications)
  187. {
  188. ULONG rc = 0;
  189. LPNOTIFICATION lpNotif;
  190. ulock_normal biglock(m_hMutex);
  191. for (unsigned int i = 0; i < cNotifications; ++i) {
  192. if (MAPIAllocateBuffer(sizeof(NOTIFICATION), (LPVOID*)&lpNotif) != hrSuccess) {
  193. rc = 1;
  194. break;
  195. }
  196. if (CopyNotification(&lpNotifications[i], lpNotif, lpNotif) == 0)
  197. m_lstNotifs.push_back(lpNotif);
  198. }
  199. biglock.unlock();
  200. m_hCond.notify_all();
  201. return rc;
  202. }
  203. // Get All notifications off the queue
  204. HRESULT MAPINotifSink::GetNotifications(ULONG *lpcNotif, LPNOTIFICATION *lppNotifications, BOOL fNonBlock, ULONG timeout)
  205. {
  206. HRESULT hr = hrSuccess;
  207. ULONG cNotifs = 0;
  208. struct timespec t;
  209. double now = GetTimeOfDay();
  210. now += (float)timeout / 1000;
  211. t.tv_sec = now;
  212. t.tv_nsec = (now-t.tv_sec) * 1000000000.0;
  213. ulock_normal biglock(m_hMutex);
  214. if (!fNonBlock) {
  215. while(m_lstNotifs.empty() && !m_bExit && (timeout == 0 || GetTimeOfDay() < now)) {
  216. if (timeout > 0)
  217. m_hCond.wait_for(biglock, std::chrono::milliseconds(timeout));
  218. else
  219. m_hCond.wait(biglock);
  220. }
  221. }
  222. LPNOTIFICATION lpNotifications = NULL;
  223. if ((hr = MAPIAllocateBuffer(sizeof(NOTIFICATION) * m_lstNotifs.size(), (void **) &lpNotifications)) == hrSuccess) {
  224. for (auto n : m_lstNotifs) {
  225. if (CopyNotification(n, lpNotifications, &lpNotifications[cNotifs]) == 0)
  226. ++cNotifs;
  227. MAPIFreeBuffer(n);
  228. }
  229. }
  230. m_lstNotifs.clear();
  231. biglock.unlock();
  232. *lppNotifications = lpNotifications;
  233. *lpcNotif = cNotifs;
  234. return hr;
  235. }
  236. HRESULT MAPINotifSink::QueryInterface(REFIID iid, void **lpvoid) {
  237. if (iid == IID_IMAPIAdviseSink) {
  238. AddRef();
  239. *lpvoid = (LPVOID)this;
  240. return hrSuccess;
  241. }
  242. return MAPI_E_INTERFACE_NOT_SUPPORTED;
  243. }
  244. ULONG MAPINotifSink::AddRef()
  245. {
  246. return ++m_cRef;
  247. }
  248. ULONG MAPINotifSink::Release()
  249. {
  250. ULONG ref = --m_cRef;
  251. if(ref == 0)
  252. delete this;
  253. return ref;
  254. }
  255. } /* namespace */